Commit 636e9bdd authored by Douwe Maan's avatar Douwe Maan

Support a string source in the route map

parent 3f1bc337
...@@ -459,6 +459,10 @@ A Route Map is a file inside the repository at `.gitlab/route-map.yml`, which co ...@@ -459,6 +459,10 @@ A Route Map is a file inside the repository at `.gitlab/route-map.yml`, which co
This is an example of a route map for [Middleman](https://middlemanapp.com) static websites like [http://about.gitlab.com](https://gitlab.com/gitlab-com/www-gitlab-com): This is an example of a route map for [Middleman](https://middlemanapp.com) static websites like [http://about.gitlab.com](https://gitlab.com/gitlab-com/www-gitlab-com):
```yaml ```yaml
# Team data
- source: 'data/team.yml' # data/team.yml
public: 'team/' # team/
# Blogposts # Blogposts
- source: /source\/posts\/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.+?)\..*/ # source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb - source: /source\/posts\/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.+?)\..*/ # source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb
public: '\1/\2/\3/\4/' # 2017/01/30/around-the-world-in-6-releases/ public: '\1/\2/\3/\4/' # 2017/01/30/around-the-world-in-6-releases/
...@@ -474,10 +478,18 @@ This is an example of a route map for [Middleman](https://middlemanapp.com) stat ...@@ -474,10 +478,18 @@ This is an example of a route map for [Middleman](https://middlemanapp.com) stat
Mappings are defined as entries in the root YAML array, and are identified by a `-` prefix. Within an entry, we have a hash map with two keys: Mappings are defined as entries in the root YAML array, and are identified by a `-` prefix. Within an entry, we have a hash map with two keys:
- `source`: a regular expression, starting and ending with `/`. Can include capture groups denoted by `()` that can be referred to in the `public` path. Slashes (`/`) can, but don't have to be, escaped as `\/`. - `source`
- `public`: a string, starting and ending with `'`. Can include `\N` expressions to refer to capture groups in the `source` regular expression in order of their occurence, starting with `\1`. - a string, starting and ending with `'`, for an exact match
- a regular expression, starting and ending with `/`, for a pattern match
The public path for a source path is determined by finding the first `source` expression that matches it, and returning the corresponding `public` path, replacing the `\N` expressions with the values of the `()` capture groups. - The regular expression needs to match the entire source path - `^` and `$` anchors are implied.
- Can include capture groups denoted by `()` that can be referred to in the `public` path.
- Slashes (`/`) can, but don't have to, be escaped as `\/`.
- Literal periods (`.`) should be escaped as `\.`.
- `public`
- a string, starting and ending with `'`.
- Can include `\N` expressions to refer to capture groups in the `source` regular expression in order of their occurence, starting with `\1`.
The public path for a source path is determined by finding the first `source` expression that matches it, and returning the corresponding `public` path, replacing the `\N` expressions with the values of the `()` capture groups if appropriate.
In the example above, the fact that mappings are evaluated in order of their definition is used to ensure that `source/index.html.haml` will match `/source\/(.+?\.html).*/` instead of `/source\/(.*)/`, and will result in a public path of `index.html`, instead of `index.html.haml`. In the example above, the fact that mappings are evaluated in order of their definition is used to ensure that `source/index.html.haml` will match `/source\/(.+?\.html).*/` instead of `/source\/(.*)/`, and will result in a public path of `index.html`, instead of `index.html.haml`.
......
...@@ -6,16 +6,16 @@ module Gitlab ...@@ -6,16 +6,16 @@ module Gitlab
begin begin
entries = YAML.safe_load(data) entries = YAML.safe_load(data)
rescue rescue
raise FormatError, 'Route map needs to be valid YAML' raise FormatError, 'Route map is not valid YAML'
end end
raise FormatError, 'Route map needs to be an array' unless entries.is_a?(Array) raise FormatError, 'Route map is not an array' unless entries.is_a?(Array)
@map = entries.map { |entry| parse_entry(entry) } @map = entries.map { |entry| parse_entry(entry) }
end end
def public_path_for_source_path(path) def public_path_for_source_path(path)
mapping = @map.find { |mapping| path =~ mapping[:source] } mapping = @map.find { |mapping| mapping[:source] === path }
return unless mapping return unless mapping
path.sub(mapping[:source], mapping[:public]) path.sub(mapping[:source], mapping[:public])
...@@ -24,27 +24,25 @@ module Gitlab ...@@ -24,27 +24,25 @@ module Gitlab
private private
def parse_entry(entry) def parse_entry(entry)
raise FormatError, 'Route map entry needs to be a hash' unless entry.is_a?(Hash) raise FormatError, 'Route map entry is not a hash' unless entry.is_a?(Hash)
raise FormatError, 'Route map entry requires a source key' unless entry.has_key?('source') raise FormatError, 'Route map entry does not have a source key' unless entry.has_key?('source')
raise FormatError, 'Route map entry requires a public key' unless entry.has_key?('public') raise FormatError, 'Route map entry does not have a public key' unless entry.has_key?('public')
source_regexp = entry['source'] source_pattern = entry['source']
public_path = entry['public'] public_path = entry['public']
unless source_regexp.start_with?('/') && source_regexp.end_with?('/') if source_pattern.start_with?('/') && source_pattern.end_with?('/')
raise FormatError, 'Route map entry source needs to start and end in a slash (/)' source_pattern = source_pattern[1...-1].gsub('\/', '/')
end
source_regexp = source_regexp[1...-1].gsub('\/', '/')
begin begin
source_regexp = Regexp.new("^#{source_regexp}$") source_pattern = Regexp.new("^#{source_pattern}$")
rescue RegexpError => e rescue RegexpError => e
raise FormatError, "Route map entry source needs to be a valid regular expression: #{e}" raise FormatError, "Route map entry source is not a valid regular expression: #{e}"
end
end end
{ {
source: source_regexp, source: source_pattern,
public: public_path public: public_path
} }
end end
......
...@@ -37,13 +37,6 @@ describe Gitlab::RouteMap, lib: true do ...@@ -37,13 +37,6 @@ describe Gitlab::RouteMap, lib: true do
end end
end end
context 'when an entry source does not start and end with a slash' do
it 'raises an error' do
expect { described_class.new(YAML.dump([{ 'source' => 'index.html', 'public' => 'index.html' }])) }.
to raise_error(Gitlab::RouteMap::FormatError, /a slash/)
end
end
context 'when an entry source is not a valid regex' do context 'when an entry source is not a valid regex' do
it 'raises an error' do it 'raises an error' do
expect { described_class.new(YAML.dump([{ 'source' => '/[/', 'public' => 'index.html' }])) }. expect { described_class.new(YAML.dump([{ 'source' => '/[/', 'public' => 'index.html' }])) }.
...@@ -53,9 +46,10 @@ describe Gitlab::RouteMap, lib: true do ...@@ -53,9 +46,10 @@ describe Gitlab::RouteMap, lib: true do
context 'when all is good' do context 'when all is good' do
it 'returns a route map' do it 'returns a route map' do
route_map = described_class.new(YAML.dump([{ 'source' => '/index\.html/', 'public' => 'index.html' }])) route_map = described_class.new(YAML.dump([{ 'source' => 'index.haml', 'public' => 'index.html' }, { 'source' => '/(.*)\.md/', 'public' => '\1.html' }]))
expect(route_map.public_path_for_source_path('index.html')).to eq('index.html') expect(route_map.public_path_for_source_path('index.haml')).to eq('index.html')
expect(route_map.public_path_for_source_path('foo.md')).to eq('foo.html')
end end
end end
end end
...@@ -63,6 +57,10 @@ describe Gitlab::RouteMap, lib: true do ...@@ -63,6 +57,10 @@ describe Gitlab::RouteMap, lib: true do
describe '#public_path_for_source_path' do describe '#public_path_for_source_path' do
subject do subject do
described_class.new(<<-'MAP'.strip_heredoc) described_class.new(<<-'MAP'.strip_heredoc)
# Team data
- source: 'data/team.yml'
public: 'team/'
# Blogposts # Blogposts
- source: /source/posts/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.+?)\..*/ # source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb - source: /source/posts/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.+?)\..*/ # source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb
public: '\1/\2/\3/\4/' # 2017/01/30/around-the-world-in-6-releases/ public: '\1/\2/\3/\4/' # 2017/01/30/around-the-world-in-6-releases/
...@@ -78,6 +76,8 @@ describe Gitlab::RouteMap, lib: true do ...@@ -78,6 +76,8 @@ describe Gitlab::RouteMap, lib: true do
end end
it 'returns the public path for a provided source path' do it 'returns the public path for a provided source path' do
expect(subject.public_path_for_source_path('data/team.yml')).to eq('team/')
expect(subject.public_path_for_source_path('source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb')).to eq('2017/01/30/around-the-world-in-6-releases/') expect(subject.public_path_for_source_path('source/posts/2017-01-30-around-the-world-in-6-releases.html.md.erb')).to eq('2017/01/30/around-the-world-in-6-releases/')
expect(subject.public_path_for_source_path('source/index.html.haml')).to eq('index.html') expect(subject.public_path_for_source_path('source/index.html.haml')).to eq('index.html')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment