grape-path-helpers 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.gitlab-ci.yml +18 -0
- data/.rubocop.yml +10 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +42 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +99 -0
- data/LICENSE.txt +22 -0
- data/README.md +172 -0
- data/Rakefile +17 -0
- data/grape-path-helpers.gemspec +21 -0
- data/lib/grape-path-helpers.rb +16 -0
- data/lib/grape-path-helpers/all_routes.rb +18 -0
- data/lib/grape-path-helpers/decorated_route.rb +168 -0
- data/lib/grape-path-helpers/named_route_matcher.rb +36 -0
- data/lib/grape-path-helpers/railtie.rb +9 -0
- data/lib/grape-path-helpers/route_displayer.rb +31 -0
- data/lib/grape-path-helpers/tasks.rb +1 -0
- data/lib/grape-path-helpers/version.rb +4 -0
- data/lib/grape/path_helpers.rb +1 -0
- data/lib/tasks/grape_path_helpers.rake +6 -0
- data/spec/grape_path_helpers/all_routes_spec.rb +37 -0
- data/spec/grape_path_helpers/decorated_route_spec.rb +241 -0
- data/spec/grape_path_helpers/named_route_matcher_spec.rb +173 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/api.rb +77 -0
- metadata +154 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'grape'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext/class'
|
4
|
+
|
5
|
+
require 'grape-path-helpers/decorated_route'
|
6
|
+
require 'grape-path-helpers/named_route_matcher'
|
7
|
+
require 'grape-path-helpers/all_routes'
|
8
|
+
require 'grape-path-helpers/route_displayer'
|
9
|
+
|
10
|
+
# Load the Grape route helper for Rails
|
11
|
+
module GrapePathHelpers
|
12
|
+
require 'grape-path-helpers/railtie' if defined?(Rails)
|
13
|
+
end
|
14
|
+
|
15
|
+
Grape::API.extend GrapePathHelpers::AllRoutes
|
16
|
+
Grape::Endpoint.send(:include, GrapePathHelpers::NamedRouteMatcher)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module GrapePathHelpers
|
2
|
+
# methods to extend Grape::API's behavior so it can get a
|
3
|
+
# list of routes from all APIs and decorate them with
|
4
|
+
# the DecoratedRoute class
|
5
|
+
module AllRoutes
|
6
|
+
def decorated_routes
|
7
|
+
# memoize so that construction of decorated routes happens once
|
8
|
+
@decorated_routes ||= all_routes
|
9
|
+
.map { |r| DecoratedRoute.new(r) }
|
10
|
+
.sort_by { |r| -r.dynamic_path_segments.count }
|
11
|
+
end
|
12
|
+
|
13
|
+
def all_routes
|
14
|
+
routes = subclasses.flat_map { |s| s.send(:prepare_routes) }
|
15
|
+
routes.uniq { |r| r.options.merge(path: r.path) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module GrapePathHelpers
|
2
|
+
# wrapper around Grape::Route that adds a helper method
|
3
|
+
class DecoratedRoute
|
4
|
+
attr_reader :route, :helper_names, :helper_arguments,
|
5
|
+
:extension, :route_options
|
6
|
+
|
7
|
+
def self.sanitize_method_name(string)
|
8
|
+
string.gsub(/\W|^[0-9]/, '_')
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(route)
|
12
|
+
@route = route
|
13
|
+
@route_options = route.options
|
14
|
+
@helper_names = []
|
15
|
+
@helper_arguments = required_helper_segments
|
16
|
+
@extension = default_extension
|
17
|
+
define_path_helpers
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_extension
|
21
|
+
pattern = /\((\.\:?\w+)\)$/
|
22
|
+
match = route_path.match(pattern)
|
23
|
+
return '' unless match
|
24
|
+
ext = match.captures.first
|
25
|
+
if ext == '.:format'
|
26
|
+
''
|
27
|
+
else
|
28
|
+
ext
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_path_helpers
|
33
|
+
route_versions.each do |version|
|
34
|
+
route_attributes = { version: version, format: extension }
|
35
|
+
method_name = path_helper_name(route_attributes)
|
36
|
+
@helper_names << method_name
|
37
|
+
define_path_helper(method_name, route_attributes)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def define_path_helper(method_name, route_attributes)
|
42
|
+
method_body = <<-RUBY
|
43
|
+
def #{method_name}(attributes = {})
|
44
|
+
attrs = #{route_attributes}.merge(attributes)
|
45
|
+
|
46
|
+
query_params = attrs.delete(:params)
|
47
|
+
content_type = attrs.delete(:format)
|
48
|
+
path = '/' + path_segments_with_values(attrs).join('/')
|
49
|
+
|
50
|
+
path + content_type + query_string(query_params)
|
51
|
+
end
|
52
|
+
RUBY
|
53
|
+
instance_eval method_body
|
54
|
+
end
|
55
|
+
|
56
|
+
def query_string(params)
|
57
|
+
if params.nil?
|
58
|
+
''
|
59
|
+
else
|
60
|
+
'?' + params.to_param
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def route_versions
|
65
|
+
if route_version.is_a?(Array)
|
66
|
+
route_version
|
67
|
+
elsif route_version
|
68
|
+
version_pattern = /[^\[",\]\s]+/
|
69
|
+
route_version.scan(version_pattern)
|
70
|
+
else
|
71
|
+
[nil]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def path_helper_name(opts = {})
|
76
|
+
if route_options[:as]
|
77
|
+
name = route_options[:as].to_s
|
78
|
+
else
|
79
|
+
segments = path_segments_with_values(opts)
|
80
|
+
|
81
|
+
name = if segments.empty?
|
82
|
+
'root'
|
83
|
+
else
|
84
|
+
segments.join('_')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
sanitized_name = self.class.sanitize_method_name(name)
|
89
|
+
sanitized_name + '_path'
|
90
|
+
end
|
91
|
+
|
92
|
+
def segment_to_value(segment, opts = {})
|
93
|
+
if dynamic_segment?(segment)
|
94
|
+
options = route.options.merge(stringify_keys(opts))
|
95
|
+
key = segment.slice(1..-1).to_sym
|
96
|
+
options[key]
|
97
|
+
else
|
98
|
+
segment
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def path_segments_with_values(opts)
|
103
|
+
segments = path_segments.map { |s| segment_to_value(s, opts) }
|
104
|
+
segments.reject(&:blank?)
|
105
|
+
end
|
106
|
+
|
107
|
+
def path_segments
|
108
|
+
pattern = %r{\(/?\.:?\w+\)|/|\??\*}
|
109
|
+
route_path.split(pattern).reject(&:blank?)
|
110
|
+
end
|
111
|
+
|
112
|
+
def dynamic_path_segments
|
113
|
+
segments = path_segments.select do |segment|
|
114
|
+
dynamic_segment?(segment)
|
115
|
+
end
|
116
|
+
segments.map { |s| s.slice(1..-1) }
|
117
|
+
end
|
118
|
+
|
119
|
+
def dynamic_segment?(segment)
|
120
|
+
segment.start_with?(':')
|
121
|
+
end
|
122
|
+
|
123
|
+
def required_helper_segments
|
124
|
+
segments_in_options = dynamic_path_segments.select do |segment|
|
125
|
+
route.options[segment.to_sym]
|
126
|
+
end
|
127
|
+
dynamic_path_segments - segments_in_options
|
128
|
+
end
|
129
|
+
|
130
|
+
def special_keys
|
131
|
+
%w[format params]
|
132
|
+
end
|
133
|
+
|
134
|
+
def uses_segments_in_path_helper?(segments)
|
135
|
+
segments = segments.reject { |x| special_keys.include?(x) }
|
136
|
+
|
137
|
+
if required_helper_segments.empty? && segments.any?
|
138
|
+
false
|
139
|
+
else
|
140
|
+
required_helper_segments.all? { |x| segments.include?(x) }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def route_path
|
145
|
+
route.path
|
146
|
+
end
|
147
|
+
|
148
|
+
def route_version
|
149
|
+
route.version
|
150
|
+
end
|
151
|
+
|
152
|
+
def route_namespace
|
153
|
+
route.namespace
|
154
|
+
end
|
155
|
+
|
156
|
+
def route_method
|
157
|
+
route.request_method
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def stringify_keys(original)
|
163
|
+
original.each_with_object({}) do |(key, value), hash|
|
164
|
+
hash[key.to_sym] = value
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module GrapePathHelpers
|
2
|
+
# methods to extend Grape::Endpoint so that calls
|
3
|
+
# to unknown methods will look for a route with a matching
|
4
|
+
# helper function name
|
5
|
+
module NamedRouteMatcher
|
6
|
+
def method_missing(method_id, *arguments)
|
7
|
+
super unless method_id.to_s =~ /_path$/
|
8
|
+
segments = arguments.first || {}
|
9
|
+
|
10
|
+
route = Grape::API.decorated_routes.detect do |r|
|
11
|
+
route_match?(r, method_id, segments)
|
12
|
+
end
|
13
|
+
|
14
|
+
if route
|
15
|
+
route.send(method_id, *arguments)
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_to_missing?(method_name, _include_private = false)
|
22
|
+
Grape::API.decorated_routes.detect do |r|
|
23
|
+
route_match?(r, method_name, {})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def route_match?(route, method_name, segments)
|
28
|
+
return false unless route.respond_to?(method_name)
|
29
|
+
# rubocop:disable Metrics/LineLength
|
30
|
+
raise ArgumentError, 'Helper options must be a hash' unless segments.is_a?(Hash)
|
31
|
+
# rubocop:enable Metrics/LineLength
|
32
|
+
requested_segments = segments.keys.map(&:to_s)
|
33
|
+
route.uses_segments_in_path_helper?(requested_segments)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GrapePathHelpers
|
2
|
+
# class for displaying the path, helper method name,
|
3
|
+
# and required arguments for every Grape::Route.
|
4
|
+
class RouteDisplayer
|
5
|
+
def route_attributes
|
6
|
+
Grape::API.decorated_routes.map do |route|
|
7
|
+
{
|
8
|
+
route_path: route.route_path,
|
9
|
+
route_method: route.route_method,
|
10
|
+
helper_names: route.helper_names,
|
11
|
+
helper_arguments: route.helper_arguments
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def display
|
17
|
+
puts("== GRAPE ROUTE HELPERS ==\n\n")
|
18
|
+
route_attributes.each do |attributes|
|
19
|
+
printf("%s: %s\n", 'Verb', attributes[:route_method])
|
20
|
+
printf("%s: %s\n", 'Path', attributes[:route_path])
|
21
|
+
printf("%s: %s\n",
|
22
|
+
'Helper Method',
|
23
|
+
attributes[:helper_names].join(', '))
|
24
|
+
printf("%s: %s\n",
|
25
|
+
'Arguments',
|
26
|
+
attributes[:helper_arguments].join(', '))
|
27
|
+
puts("\n")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), '../tasks/*.rake')].each { |f| load f }
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'grape-path-helpers'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GrapePathHelpers::AllRoutes do
|
4
|
+
Grape::API.extend described_class
|
5
|
+
|
6
|
+
describe '#all_routes' do
|
7
|
+
context 'when API is mounted within another API' do
|
8
|
+
let(:mounting_api) { Spec::Support::MountedAPI }
|
9
|
+
|
10
|
+
it 'does not include the same route twice' do
|
11
|
+
mounting_api
|
12
|
+
|
13
|
+
# A route is unique if no other route shares the same set of options
|
14
|
+
all_route_options = Grape::API.all_routes.map do |r|
|
15
|
+
r.instance_variable_get(:@options).merge(path: r.path)
|
16
|
+
end
|
17
|
+
|
18
|
+
duplicates = all_route_options.select do |o|
|
19
|
+
all_route_options.count(o) > 1
|
20
|
+
end
|
21
|
+
|
22
|
+
expect(duplicates).to be_empty
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# rubocop:disable Metrics/LineLength
|
27
|
+
context 'when there are multiple POST routes with the same namespace in the same API' do
|
28
|
+
it 'returns all POST routes' do
|
29
|
+
expected_routes = Spec::Support::MultiplePostsAPI.routes.map(&:path)
|
30
|
+
|
31
|
+
all_routes = Grape::API.all_routes
|
32
|
+
expect(all_routes.map(&:path)).to include(*expected_routes)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# rubocop:enable Metrics/LineLength
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/BlockLength
|
4
|
+
describe GrapePathHelpers::DecoratedRoute do
|
5
|
+
let(:api) { Spec::Support::API }
|
6
|
+
|
7
|
+
let(:routes) do
|
8
|
+
api.routes.map do |route|
|
9
|
+
described_class.new(route)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:index_route) do
|
14
|
+
routes.detect { |route| route.route_namespace == '/cats' }
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:show_route) do
|
18
|
+
routes.detect { |route| route.route_namespace == '/cats/:id' }
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:catch_all_route) do
|
22
|
+
routes.detect { |route| route.route_path =~ /\*/ }
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:custom_route) do
|
26
|
+
routes.detect { |route| route.route_path =~ /custom_name/ }
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:ping_route) do
|
30
|
+
routes.detect { |route| route.route_path =~ /ping/ }
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#sanitize_method_name' do
|
34
|
+
it 'removes characters that are illegal in Ruby method names' do
|
35
|
+
illegal_names = ['beta-1', 'name_with_+', 'name_with_(']
|
36
|
+
sanitized = illegal_names.map do |name|
|
37
|
+
described_class.sanitize_method_name(name)
|
38
|
+
end
|
39
|
+
expect(sanitized).to match_array(%w[beta_1 name_with__ name_with__])
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'only replaces integers if they appear at the beginning' do
|
43
|
+
illegal_name = '1'
|
44
|
+
legal_name = 'v1'
|
45
|
+
expect(described_class.sanitize_method_name(illegal_name)).to eq('_')
|
46
|
+
expect(described_class.sanitize_method_name(legal_name)).to eq('v1')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#helper_names' do
|
51
|
+
context 'when a route is given a custom helper name' do
|
52
|
+
it 'uses the custom name instead of the dynamically generated one' do
|
53
|
+
expect(custom_route.helper_names.first)
|
54
|
+
.to eq('my_custom_route_name_path')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns the correct path' do
|
58
|
+
expect(
|
59
|
+
custom_route.my_custom_route_name_path
|
60
|
+
).to eq('/api/v1/custom_name.json')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when an API has multiple POST routes in a resource' do
|
65
|
+
let(:api) { Spec::Support::MultiplePostsAPI }
|
66
|
+
|
67
|
+
it 'it creates a helper for each POST route' do
|
68
|
+
expect(routes.size).to eq(2)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when an API has multiple versions' do
|
73
|
+
let(:api) { Spec::Support::APIWithMultipleVersions }
|
74
|
+
|
75
|
+
it "returns the route's helper name for each version" do
|
76
|
+
helper_names = ping_route.helper_names
|
77
|
+
expect(helper_names.size).to eq(api.versions.size)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns an array of the routes versions' do
|
81
|
+
expect(ping_route.route_version).to eq(%w[beta alpha v1])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when an API has one version' do
|
86
|
+
it "returns the route's helper name for that version" do
|
87
|
+
helper_name = show_route.helper_names.first
|
88
|
+
expect(helper_name).to eq('api_v1_cats_path')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#helper_arguments' do
|
94
|
+
context 'when no user input is needed to generate the correct path' do
|
95
|
+
it 'returns an empty array' do
|
96
|
+
expect(index_route.helper_arguments).to eq([])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when user input is needed to generate the correct path' do
|
101
|
+
it 'returns an array of required segments' do
|
102
|
+
expect(show_route.helper_arguments).to eq(['id'])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#path_segments_with_values' do
|
108
|
+
context 'when path has dynamic segments' do
|
109
|
+
it 'replaces segments with corresponding values found in @options' do
|
110
|
+
opts = { id: 1 }
|
111
|
+
result = show_route.path_segments_with_values(opts)
|
112
|
+
expect(result).to include(1)
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when options contains string keys' do
|
116
|
+
it 'replaces segments with corresponding values found in the options' do
|
117
|
+
opts = { 'id' => 1 }
|
118
|
+
result = show_route.path_segments_with_values(opts)
|
119
|
+
expect(result).to include(1)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '#path_helper_name' do
|
126
|
+
it "returns the name of a route's helper method" do
|
127
|
+
expect(index_route.path_helper_name).to eq('api_v1_cats_path')
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when the path is the root path' do
|
131
|
+
let(:api_with_root) do
|
132
|
+
Class.new(Grape::API) do
|
133
|
+
get '/' do
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
let(:root_route) do
|
139
|
+
grape_route = api_with_root.routes.first
|
140
|
+
described_class.new(grape_route)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns "root_path"' do
|
144
|
+
result = root_route.path_helper_name
|
145
|
+
expect(result).to eq('root_path')
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when the path is a catch-all path' do
|
150
|
+
it 'returns a name without the glob star' do
|
151
|
+
result = catch_all_route.path_helper_name
|
152
|
+
expect(result).to eq('api_v1_path_path')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#segment_to_value' do
|
158
|
+
context 'when segment is dynamic' do
|
159
|
+
it 'returns the value the segment corresponds to' do
|
160
|
+
result = index_route.segment_to_value(':version')
|
161
|
+
expect(result).to eq('v1')
|
162
|
+
end
|
163
|
+
|
164
|
+
context 'when segment is found in options' do
|
165
|
+
it 'returns the value found in options' do
|
166
|
+
options = { id: 1 }
|
167
|
+
result = show_route.segment_to_value(':id', options)
|
168
|
+
expect(result).to eq(1)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when segment is static' do
|
174
|
+
it 'returns the segment' do
|
175
|
+
result = index_route.segment_to_value('api')
|
176
|
+
expect(result).to eq('api')
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe 'path helper method' do
|
182
|
+
context 'when given a "params" key' do
|
183
|
+
context 'when value under "params" key is a hash' do
|
184
|
+
it 'creates a query string' do
|
185
|
+
query = { foo: :bar, baz: :zot }
|
186
|
+
path = index_route.api_v1_cats_path(params: query)
|
187
|
+
expect(path).to eq('/api/v1/cats.json?' + query.to_param)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'when value under "params" is not a hash' do
|
192
|
+
it 'coerces the value into a string' do
|
193
|
+
path = index_route.api_v1_cats_path(params: 1)
|
194
|
+
expect(path).to eq('/api/v1/cats.json?1')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# handle different Grape::Route#route_path formats in Grape 0.12.0
|
200
|
+
context 'when route_path contains a specific format' do
|
201
|
+
it 'returns the correct path with the correct format' do
|
202
|
+
path = index_route.api_v1_cats_path
|
203
|
+
expect(path).to eq('/api/v1/cats.json')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'when helper does not require arguments' do
|
208
|
+
it 'returns the correct path' do
|
209
|
+
path = index_route.api_v1_cats_path
|
210
|
+
expect(path).to eq('/api/v1/cats.json')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'when arguments are needed required to construct the right path' do
|
215
|
+
context 'when not missing arguments' do
|
216
|
+
it 'returns the correct path' do
|
217
|
+
path = show_route.api_v1_cats_path(id: 1)
|
218
|
+
expect(path).to eq('/api/v1/cats/1.json')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context "when a route's API has multiple versions" do
|
224
|
+
let(:api) { Spec::Support::APIWithMultipleVersions }
|
225
|
+
|
226
|
+
it 'returns a path for each version' do
|
227
|
+
expect(ping_route.alpha_ping_path).to eq('/alpha/ping')
|
228
|
+
expect(ping_route.beta_ping_path).to eq('/beta/ping')
|
229
|
+
expect(ping_route.v1_ping_path).to eq('/v1/ping')
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'when a format is given' do
|
234
|
+
it 'returns the path with a correct extension' do
|
235
|
+
path = show_route.api_v1_cats_path(id: 1, format: '.xml')
|
236
|
+
expect(path).to eq('/api/v1/cats/1.xml')
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
# rubocop:enable Metrics/BlockLength
|