grape-swagger 0.20.3 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +10 -10
  3. data/.travis.yml +8 -3
  4. data/CHANGELOG.md +27 -0
  5. data/Gemfile +2 -0
  6. data/README.md +111 -12
  7. data/UPGRADING.md +9 -0
  8. data/grape-swagger.gemspec +1 -3
  9. data/lib/grape-swagger.rb +8 -1
  10. data/lib/grape-swagger/doc_methods/optional_object.rb +14 -2
  11. data/lib/grape-swagger/doc_methods/parse_params.rb +3 -4
  12. data/lib/grape-swagger/doc_methods/path_string.rb +4 -3
  13. data/lib/grape-swagger/endpoint.rb +25 -55
  14. data/lib/grape-swagger/errors.rb +3 -0
  15. data/lib/grape-swagger/grape/route.rb +2 -1
  16. data/lib/grape-swagger/model_parsers.rb +33 -0
  17. data/lib/grape-swagger/version.rb +1 -1
  18. data/spec/issues/403_versions_spec.rb +20 -4
  19. data/spec/lib/model_parsers_spec.rb +102 -0
  20. data/spec/lib/optional_object_spec.rb +15 -11
  21. data/spec/lib/path_string_spec.rb +72 -18
  22. data/spec/lib/produces_consumes_spec.rb +10 -5
  23. data/spec/spec_helper.rb +4 -2
  24. data/spec/support/empty_model_parser.rb +20 -0
  25. data/spec/support/mock_parser.rb +22 -0
  26. data/spec/support/model_parsers/entity_parser.rb +325 -0
  27. data/spec/support/{api_swagger_v2_result.rb → model_parsers/mock_parser.rb} +186 -60
  28. data/spec/support/model_parsers/representable_parser.rb +394 -0
  29. data/spec/support/the_paths_definitions.rb +7 -3
  30. data/spec/swagger_v2/api_swagger_v2_definitions-models_spec.rb +5 -4
  31. data/spec/swagger_v2/api_swagger_v2_detail_spec.rb +3 -3
  32. data/spec/swagger_v2/api_swagger_v2_extensions_spec.rb +1 -1
  33. data/spec/swagger_v2/api_swagger_v2_format-content_type_spec.rb +1 -1
  34. data/spec/swagger_v2/api_swagger_v2_headers_spec.rb +5 -3
  35. data/spec/swagger_v2/api_swagger_v2_hide_documentation_path_spec.rb +1 -1
  36. data/spec/swagger_v2/api_swagger_v2_mounted_spec.rb +1 -1
  37. data/spec/swagger_v2/api_swagger_v2_param_type_body_nested_spec.rb +25 -14
  38. data/spec/swagger_v2/api_swagger_v2_param_type_body_spec.rb +22 -12
  39. data/spec/swagger_v2/api_swagger_v2_param_type_spec.rb +30 -18
  40. data/spec/swagger_v2/api_swagger_v2_request_params_fix_spec.rb +6 -3
  41. data/spec/swagger_v2/api_swagger_v2_response_spec.rb +13 -40
  42. data/spec/swagger_v2/api_swagger_v2_spec.rb +4 -2
  43. data/spec/swagger_v2/api_swagger_v2_type-format_spec.rb +6 -36
  44. data/spec/swagger_v2/default_api_spec.rb +10 -2
  45. data/spec/swagger_v2/endpoint_versioned_path_spec.rb +30 -0
  46. data/spec/swagger_v2/errors_spec.rb +75 -0
  47. data/spec/swagger_v2/hide_api_spec.rb +22 -4
  48. data/spec/swagger_v2/mounted_target_class_spec.rb +6 -2
  49. data/spec/swagger_v2/namespace_tags_prefix_spec.rb +6 -3
  50. data/spec/swagger_v2/namespace_tags_spec.rb +6 -3
  51. data/spec/swagger_v2/params_array_spec.rb +4 -2
  52. data/spec/swagger_v2/params_hash_spec.rb +4 -2
  53. data/spec/swagger_v2/params_nested_spec.rb +4 -2
  54. data/spec/swagger_v2/simple_mounted_api_spec.rb +66 -24
  55. metadata +23 -40
  56. data/spec/support/the_api_entities.rb +0 -50
  57. data/spec/swagger_v2/response_model_spec.rb +0 -208
@@ -24,8 +24,8 @@ module Grape
24
24
  swagger: '2.0',
25
25
  produces: content_types_for(target_class),
26
26
  authorizations: options[:authorizations],
27
- host: GrapeSwagger::DocMethods::OptionalObject.build(:host, options, request.host_with_port),
28
- basePath: GrapeSwagger::DocMethods::OptionalObject.build(:base_path, options, request.env['SCRIPT_NAME']),
27
+ host: GrapeSwagger::DocMethods::OptionalObject.build(:host, options, request),
28
+ basePath: GrapeSwagger::DocMethods::OptionalObject.build(:base_path, options, request),
29
29
  tags: GrapeSwagger::DocMethods::TagNameDescription.build(options),
30
30
  schemes: options[:schemes].is_a?(String) ? [options[:schemes]] : options[:schemes]
31
31
  }.delete_if { |_, value| value.blank? }
@@ -86,7 +86,7 @@ module Grape
86
86
  routes.each do |route|
87
87
  next if hidden?(route)
88
88
 
89
- @item, path = GrapeSwagger::DocMethods::PathString.build(route.path, options)
89
+ @item, path = GrapeSwagger::DocMethods::PathString.build(route, options)
90
90
  @entity = route.entity || route.options[:success]
91
91
 
92
92
  verb, method_object = method_object(route, options, path)
@@ -103,6 +103,7 @@ module Grape
103
103
 
104
104
  def method_object(route, options, path)
105
105
  method = {}
106
+ method[:summary] = summary_object(route)
106
107
  method[:description] = description_object(route, options[:markdown])
107
108
  method[:produces] = produces_object(route, options[:produces] || options[:format])
108
109
  method[:consumes] = consumes_object(route, options[:format])
@@ -115,6 +116,14 @@ module Grape
115
116
  [route.request_method.downcase.to_sym, method]
116
117
  end
117
118
 
119
+ def summary_object(route)
120
+ summary = route.options[:desc] if route.options.key?(:desc)
121
+ summary = route.description if route.description.present?
122
+ summary = route.options[:summary] if route.options.key?(:summary)
123
+
124
+ summary
125
+ end
126
+
118
127
  def description_object(route, markdown)
119
128
  description = route.options[:desc] if route.options.key?(:desc)
120
129
  description = route.description if route.description.present?
@@ -226,53 +235,25 @@ module Grape
226
235
  end
227
236
  end
228
237
 
229
- def parse_response_params(params)
230
- return if params.nil?
238
+ def expose_params_from_model(model)
239
+ model_name = model_name(model)
231
240
 
232
- params.each_with_object({}) do |x, memo|
233
- next if x[1].fetch(:documentation, {}).fetch(:in, nil).to_s == 'header'
234
- x[0] = x.last[:as] if x.last[:as]
241
+ return model_name if @definitions.key?(model_name)
242
+ @definitions[model_name] = nil
235
243
 
236
- model = x.last[:using] if x.last[:using].present?
237
- model ||= x.last[:documentation][:type] if x.last[:documentation] && could_it_be_a_model?(x.last[:documentation])
244
+ properties = nil
245
+ parser = nil
238
246
 
239
- if model
240
- name = expose_params_from_model(model)
241
- memo[x.first] = if x.last[:documentation] && x.last[:documentation][:is_array]
242
- { 'type' => 'array', 'items' => { '$ref' => "#/definitions/#{name}" } }
243
- else
244
- { '$ref' => "#/definitions/#{name}" }
245
- end
246
- else
247
- documented_type = x.last[:type]
248
- documented_type ||= x.last[:documentation][:type] if x.last[:documentation]
249
- data_type = GrapeSwagger::DocMethods::DataType.call(documented_type)
250
-
251
- if GrapeSwagger::DocMethods::DataType.primitive?(data_type)
252
- data = GrapeSwagger::DocMethods::DataType.mapping(data_type)
253
- memo[x.first] = { type: data.first, format: data.last }
254
- else
255
- memo[x.first] = { type: data_type }
256
- end
257
-
258
- memo[x.first][:enum] = x.last[:values] if x.last[:values] && x.last[:values].is_a?(Array)
259
- end
260
- memo[x.first][:description] = x.last[:documentation][:desc] if x.last[:documentation] && x.last[:documentation][:desc]
247
+ GrapeSwagger.model_parsers.each do |klass, ancestor|
248
+ next unless model.ancestors.map(&:to_s).include?(ancestor)
249
+ parser = klass.new(model, self)
250
+ break
261
251
  end
262
- end
263
252
 
264
- def expose_params_from_model(model)
265
- model_name = model_name(model)
253
+ properties = parser.call unless parser.nil?
266
254
 
267
- # TODO: this should only be a temporary hack ;)
268
- if GrapeEntity::VERSION =~ /0\.4\.\d/
269
- parameters = model.exposures ? model.exposures : model.documentation
270
- elsif GrapeEntity::VERSION =~ /0\.5\.\d/
271
- parameters = model.root_exposures.each_with_object({}) do |value, memo|
272
- memo[value.attribute] = value.send(:options)
273
- end
274
- end
275
- properties = parse_response_params(parameters)
255
+ raise GrapeSwagger::Errors::UnregisteredParser, "No parser registered for #{model_name}." unless parser
256
+ raise GrapeSwagger::Errors::SwaggerSpec, "Empty model #{model_name}, swagger 2.0 doesn't support empty definitions." unless properties && properties.any?
276
257
 
277
258
  @definitions[model_name] = { type: 'object', properties: properties }
278
259
 
@@ -283,17 +264,6 @@ module Grape
283
264
  name.respond_to?(:name) ? name.name.demodulize.camelize : name.split('::').last
284
265
  end
285
266
 
286
- def could_it_be_a_model?(value)
287
- (
288
- value[:type].to_s.include?('Entity') || value[:type].to_s.include?('Entities')
289
- ) || (
290
- value[:type] &&
291
- value[:type].is_a?(Class) &&
292
- !GrapeSwagger::DocMethods::DataType.primitive?(value[:type].name.downcase) &&
293
- !value[:type] == Array
294
- )
295
- end
296
-
297
267
  def hidden?(route)
298
268
  route_hidden = route.options[:hidden]
299
269
  route_hidden = route_hidden.call if route_hidden.is_a?(Proc)
@@ -5,5 +5,8 @@ module GrapeSwagger
5
5
  super("Missing required dependency: #{missing_gem}")
6
6
  end
7
7
  end
8
+
9
+ class UnregisteredParser < StandardError; end
10
+ class SwaggerSpec < StandardError; end
8
11
  end
9
12
  end
@@ -1,7 +1,8 @@
1
1
  # backwards compatibility for Grape < 0.16.0
2
2
  module Grape
3
3
  class Route
4
- [:path, :prefix, :entity, :description, :settings, :params, :headers, :http_codes].each do |m|
4
+ [:path, :prefix, :entity, :description, :settings, :params, :headers, :http_codes, :version]
5
+ .each do |m|
5
6
  define_method m do
6
7
  send "route_#{m}"
7
8
  end
@@ -0,0 +1,33 @@
1
+ module GrapeSwagger
2
+ class ModelParsers
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @parsers = {}
7
+ end
8
+
9
+ def register(klass, ancestor)
10
+ @parsers[klass] = ancestor.to_s
11
+ end
12
+
13
+ def insert_before(before_klass, klass, ancestor)
14
+ subhash = @parsers.except(klass).to_a
15
+ insert_at = subhash.index(subhash.assoc(before_klass))
16
+ insert_at = subhash.length - 1 if insert_at.nil?
17
+ @parsers = Hash[subhash.insert(insert_at, [klass, ancestor])]
18
+ end
19
+
20
+ def insert_after(after_klass, klass, ancestor)
21
+ subhash = @parsers.except(klass).to_a
22
+ insert_at = subhash.index(subhash.assoc(after_klass))
23
+ insert_at = subhash.length - 1 if insert_at.nil?
24
+ @parsers = Hash[subhash.insert(insert_at + 1, [klass, ancestor])]
25
+ end
26
+
27
+ def each
28
+ @parsers.each_pair do |klass, ancestor|
29
+ yield klass, ancestor
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,3 +1,3 @@
1
1
  module GrapeSwagger
2
- VERSION = '0.20.3'.freeze
2
+ VERSION = '0.21.0'.freeze
3
3
  end
@@ -29,6 +29,7 @@ describe 'describing versions' do
29
29
  paths: {
30
30
  :'/nothings' => {
31
31
  get: {
32
+ summary: 'no versions given',
32
33
  description: 'no versions given',
33
34
  produces: ['application/json'],
34
35
  responses: {
@@ -36,7 +37,10 @@ describe 'describing versions' do
36
37
  },
37
38
  tags: ['nothings'],
38
39
  operationId: 'getNothings'
39
- } } })
40
+ }
41
+ }
42
+ }
43
+ )
40
44
  end
41
45
  end
42
46
 
@@ -68,6 +72,7 @@ describe 'describing versions' do
68
72
  paths: {
69
73
  :'/v2/api_version' => {
70
74
  get: {
75
+ summary: 'api versions given',
71
76
  description: 'api versions given',
72
77
  produces: ['application/json'],
73
78
  responses: {
@@ -75,7 +80,10 @@ describe 'describing versions' do
75
80
  },
76
81
  tags: ['api_version'],
77
82
  operationId: 'getV2ApiVersion'
78
- } } })
83
+ }
84
+ }
85
+ }
86
+ )
79
87
  end
80
88
  end
81
89
 
@@ -106,6 +114,7 @@ describe 'describing versions' do
106
114
  paths: {
107
115
  :'/doc_version' => {
108
116
  get: {
117
+ summary: 'doc versions given',
109
118
  description: 'doc versions given',
110
119
  produces: ['application/json'],
111
120
  responses: {
@@ -113,7 +122,10 @@ describe 'describing versions' do
113
122
  },
114
123
  tags: ['doc_version'],
115
124
  operationId: 'getDocVersion'
116
- } } })
125
+ }
126
+ }
127
+ }
128
+ )
117
129
  end
118
130
  end
119
131
 
@@ -145,6 +157,7 @@ describe 'describing versions' do
145
157
  paths: {
146
158
  :'/v2/both_versions' => {
147
159
  get: {
160
+ summary: 'both versions given',
148
161
  description: 'both versions given',
149
162
  produces: ['application/json'],
150
163
  responses: {
@@ -152,7 +165,10 @@ describe 'describing versions' do
152
165
  },
153
166
  tags: ['both_versions'],
154
167
  operationId: 'getV2BothVersions'
155
- } } })
168
+ }
169
+ }
170
+ }
171
+ )
156
172
  end
157
173
  end
158
174
  end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe GrapeSwagger::ModelParsers do
4
+ let(:model_parsers) { described_class.new }
5
+ let(:parser) { Class.new }
6
+ let(:parser2) { Class.new }
7
+ let(:parser3) { Class.new }
8
+
9
+ describe '#register' do
10
+ describe 'successfully register new parser' do
11
+ before do
12
+ model_parsers.register(parser, Class)
13
+ end
14
+
15
+ specify do
16
+ expect(model_parsers.to_a).to eq([[parser, 'Class']])
17
+ end
18
+ end
19
+
20
+ describe 'should be empty if no registered parsers' do
21
+ specify do
22
+ expect(model_parsers.to_a).to be_empty
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '#insert_before' do
28
+ describe 'SomeModelParser2 should be first parser' do
29
+ before do
30
+ model_parsers.register(parser, Class)
31
+ model_parsers.register(parser3, Class)
32
+ model_parsers.insert_before(parser, parser2, Class)
33
+ end
34
+
35
+ specify do
36
+ expect(model_parsers.count).to eq(3)
37
+ expect(model_parsers.to_a.first).to eq([parser2, Class])
38
+ end
39
+ end
40
+
41
+ describe 'SomeModelParser2 should be inserted anyway if SomeModelParser not registered' do
42
+ before do
43
+ model_parsers.register(parser3, Class)
44
+ model_parsers.insert_before(parser, parser2, Class)
45
+ end
46
+
47
+ specify do
48
+ expect(model_parsers.count).to eq(2)
49
+ expect(model_parsers.to_a).to include([parser2, Class])
50
+ end
51
+ end
52
+
53
+ describe 'SomeModelParser2 should be inserted anyway if model parsers is empty' do
54
+ before do
55
+ model_parsers.insert_before(parser, parser2, Class)
56
+ end
57
+
58
+ specify do
59
+ expect(model_parsers.count).to eq(1)
60
+ expect(model_parsers.to_a).to include([parser2, Class])
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#insert_after' do
66
+ describe 'SomeModelParser2 should be second parser' do
67
+ before do
68
+ model_parsers.register(parser, Class)
69
+ model_parsers.register(parser3, Class)
70
+ model_parsers.insert_after(parser, parser2, Class)
71
+ end
72
+
73
+ specify do
74
+ expect(model_parsers.count).to eq(3)
75
+ expect(model_parsers.to_a[1]).to eq([parser2, Class])
76
+ end
77
+ end
78
+
79
+ describe 'SomeModelParser2 should be inserted anyway if SomeModelParser not registered' do
80
+ before do
81
+ model_parsers.register(parser3, Class)
82
+ model_parsers.insert_after(parser, parser2, Class)
83
+ end
84
+
85
+ specify do
86
+ expect(model_parsers.count).to eq(2)
87
+ expect(model_parsers.to_a).to include([parser2, Class])
88
+ end
89
+ end
90
+
91
+ describe 'SomeModelParser2 should be inserted anyway if model parsers is empty' do
92
+ before do
93
+ model_parsers.insert_after(parser, parser2, Class)
94
+ end
95
+
96
+ specify do
97
+ expect(model_parsers.count).to eq(1)
98
+ expect(model_parsers.to_a).to include([parser2, Class])
99
+ end
100
+ end
101
+ end
102
+ end
@@ -7,33 +7,37 @@ describe GrapeSwagger::DocMethods::OptionalObject do
7
7
  specify { expect(subject).to respond_to :build }
8
8
 
9
9
  describe 'build' do
10
- let(:key) { :bar }
11
- let(:request) { 'somes/request/string' }
10
+ let(:key) { :host }
11
+ let!(:request) { Rack::Request.new(Rack::MockRequest.env_for('http://example.com:8080/')) }
12
12
 
13
- describe 'no option given for key' do
13
+ describe 'no option given for host, take from request' do
14
14
  let(:options) { { foo: 'foo' } }
15
15
  specify do
16
- expect(subject.build(key, options)).to be_nil
17
- expect(subject.build(key, options, request)).to eql request
16
+ expect(subject.build(key, options, request)).to eql request.host_with_port
18
17
  end
19
18
  end
20
19
 
21
- let(:value) { 'some optional value' }
20
+ let(:value) { 'grape-swagger.example.com' }
22
21
 
23
22
  describe 'option is a string' do
24
- let(:options) { { bar: value } }
23
+ let(:options) { { host: value } }
25
24
  specify do
26
- expect(subject.build(key, options)).to eql value
27
25
  expect(subject.build(key, options, request)).to eql value
28
26
  end
29
27
  end
30
28
 
31
- describe 'option is a proc' do
32
- let(:options) { { bar: -> { value } } }
29
+ describe 'option is a lambda' do
30
+ let(:options) { { host: -> { value } } }
33
31
  specify do
34
- expect(subject.build(key, options)).to eql value
35
32
  expect(subject.build(key, options, request)).to eql value
36
33
  end
37
34
  end
35
+
36
+ describe 'option is a proc' do
37
+ let(:options) { { host: proc { |request| request.host =~ /^example/ ? '/api-example' : '/api' } } }
38
+ specify do
39
+ expect(subject.build(key, options, request)).to eql '/api-example'
40
+ end
41
+ end
38
42
  end
39
43
  end
@@ -8,29 +8,83 @@ describe GrapeSwagger::DocMethods::PathString do
8
8
 
9
9
  describe 'operation_id_object' do
10
10
  describe 'version' do
11
- describe 'defaults: not given, false' do
11
+ describe 'defaults: given, true' do
12
+ let(:options) { { add_version: true } }
13
+ let(:route) { Struct.new(:version, :path).new('v1') }
14
+
15
+ specify 'The returned path includes version' do
16
+ route.path = '/{version}/thing(.json)'
17
+ expect(subject.build(route, options)).to eql ['Thing', '/v1/thing']
18
+ route.path = '/{version}/thing/foo(.json)'
19
+ expect(subject.build(route, options)).to eql ['Foo', '/v1/thing/foo']
20
+ route.path = '/{version}/thing(.:format)'
21
+ expect(subject.build(route, options)).to eql ['Thing', '/v1/thing']
22
+ route.path = '/{version}/thing/foo(.:format)'
23
+ expect(subject.build(route, options)).to eql ['Foo', '/v1/thing/foo']
24
+ route.path = '/{version}/thing/:id'
25
+ expect(subject.build(route, options)).to eql ['Thing', '/v1/thing/{id}']
26
+ route.path = '/{version}/thing/foo/:id'
27
+ expect(subject.build(route, options)).to eql ['Foo', '/v1/thing/foo/{id}']
28
+ end
29
+ end
30
+
31
+ describe 'defaults: not given, both false' do
12
32
  let(:options) { { add_version: false } }
33
+ let(:route) { Struct.new(:version, :path).new }
13
34
 
14
- specify do
15
- expect(subject.build('/thing(.json)', options)).to eql ['Thing', '/thing']
16
- expect(subject.build('/thing/foo(.json)', options)).to eql ['Foo', '/thing/foo']
17
- expect(subject.build('/thing(.:format)', options)).to eql ['Thing', '/thing']
18
- expect(subject.build('/thing/foo(.:format)', options)).to eql ['Foo', '/thing/foo']
19
- expect(subject.build('/thing/:id', options)).to eql ['Thing', '/thing/{id}']
20
- expect(subject.build('/thing/foo/:id', options)).to eql ['Foo', '/thing/foo/{id}']
35
+ specify 'The returned path does not include version' do
36
+ route.path = '/{version}/thing(.json)'
37
+ expect(subject.build(route, options)).to eql ['Thing', '/thing']
38
+ route.path = '/{version}/thing/foo(.json)'
39
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo']
40
+ route.path = '/{version}/thing(.:format)'
41
+ expect(subject.build(route, options)).to eql ['Thing', '/thing']
42
+ route.path = '/{version}/thing/foo(.:format)'
43
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo']
44
+ route.path = '/{version}/thing/:id'
45
+ expect(subject.build(route, options)).to eql ['Thing', '/thing/{id}']
46
+ route.path = '/{version}/thing/foo/:id'
47
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo/{id}']
21
48
  end
22
49
  end
23
50
 
24
- describe 'defaults: given, true' do
25
- let(:options) { { version: 'v1', add_version: true } }
26
-
27
- specify do
28
- expect(subject.build('/{version}/thing(.json)', options)).to eql ['Thing', '/v1/thing']
29
- expect(subject.build('/{version}/thing/foo(.json)', options)).to eql ['Foo', '/v1/thing/foo']
30
- expect(subject.build('/{version}/thing(.:format)', options)).to eql ['Thing', '/v1/thing']
31
- expect(subject.build('/{version}/thing/foo(.:format)', options)).to eql ['Foo', '/v1/thing/foo']
32
- expect(subject.build('/{version}/thing/:id', options)).to eql ['Thing', '/v1/thing/{id}']
33
- expect(subject.build('/{version}/thing/foo/:id', options)).to eql ['Foo', '/v1/thing/foo/{id}']
51
+ describe 'defaults: add_version false' do
52
+ let(:options) { { add_version: false } }
53
+ let(:route) { Struct.new(:version, :path).new('v1') }
54
+
55
+ specify 'The returned path does not include version' do
56
+ route.path = '/{version}/thing(.json)'
57
+ expect(subject.build(route, options)).to eql ['Thing', '/thing']
58
+ route.path = '/{version}/thing/foo(.json)'
59
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo']
60
+ route.path = '/{version}/thing(.:format)'
61
+ expect(subject.build(route, options)).to eql ['Thing', '/thing']
62
+ route.path = '/{version}/thing/foo(.:format)'
63
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo']
64
+ route.path = '/{version}/thing/:id'
65
+ expect(subject.build(route, options)).to eql ['Thing', '/thing/{id}']
66
+ route.path = '/{version}/thing/foo/:id'
67
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo/{id}']
68
+ end
69
+ end
70
+
71
+ describe 'defaults: root_version nil' do
72
+ let(:options) { { add_version: true } }
73
+ let(:route) { Struct.new(:version, :path).new }
74
+
75
+ specify 'The returned path does not include version' do
76
+ route.path = '/{version}/thing(.json)'
77
+ expect(subject.build(route, options)).to eql ['Thing', '/thing']
78
+ route.path = '/{version}/thing/foo(.json)'
79
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo']
80
+ route.path = '/{version}/thing(.:format)'
81
+ expect(subject.build(route, options)).to eql ['Thing', '/thing']
82
+ route.path = '/{version}/thing/foo(.:format)'
83
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo']
84
+ route.path = '/{version}/thing/:id'
85
+ expect(subject.build(route, options)).to eql ['Thing', '/thing/{id}']
86
+ route.path = '/{version}/thing/foo/:id'
87
+ expect(subject.build(route, options)).to eql ['Foo', '/thing/foo/{id}']
34
88
  end
35
89
  end
36
90
  end