grape-path-helpers 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|