grape 1.1.0 → 1.2.5

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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -43
  3. data/LICENSE +1 -1
  4. data/README.md +394 -47
  5. data/UPGRADING.md +111 -0
  6. data/grape.gemspec +3 -1
  7. data/lib/grape.rb +98 -66
  8. data/lib/grape/api.rb +136 -175
  9. data/lib/grape/api/instance.rb +280 -0
  10. data/lib/grape/config.rb +32 -0
  11. data/lib/grape/dsl/callbacks.rb +20 -0
  12. data/lib/grape/dsl/desc.rb +39 -7
  13. data/lib/grape/dsl/inside_route.rb +12 -6
  14. data/lib/grape/dsl/middleware.rb +7 -0
  15. data/lib/grape/dsl/parameters.rb +9 -4
  16. data/lib/grape/dsl/routing.rb +5 -1
  17. data/lib/grape/dsl/validations.rb +4 -3
  18. data/lib/grape/eager_load.rb +18 -0
  19. data/lib/grape/endpoint.rb +42 -26
  20. data/lib/grape/error_formatter.rb +1 -1
  21. data/lib/grape/exceptions/base.rb +9 -1
  22. data/lib/grape/exceptions/invalid_response.rb +9 -0
  23. data/lib/grape/exceptions/validation_errors.rb +4 -2
  24. data/lib/grape/formatter.rb +1 -1
  25. data/lib/grape/locale/en.yml +2 -0
  26. data/lib/grape/middleware/auth/base.rb +2 -4
  27. data/lib/grape/middleware/base.rb +2 -0
  28. data/lib/grape/middleware/error.rb +9 -4
  29. data/lib/grape/middleware/helpers.rb +10 -0
  30. data/lib/grape/middleware/stack.rb +1 -1
  31. data/lib/grape/middleware/versioner/header.rb +4 -4
  32. data/lib/grape/parser.rb +1 -1
  33. data/lib/grape/request.rb +1 -1
  34. data/lib/grape/router/attribute_translator.rb +2 -0
  35. data/lib/grape/router/route.rb +2 -2
  36. data/lib/grape/util/base_inheritable.rb +34 -0
  37. data/lib/grape/util/endpoint_configuration.rb +6 -0
  38. data/lib/grape/util/inheritable_values.rb +5 -25
  39. data/lib/grape/util/lazy_block.rb +25 -0
  40. data/lib/grape/util/lazy_value.rb +95 -0
  41. data/lib/grape/util/reverse_stackable_values.rb +7 -36
  42. data/lib/grape/util/stackable_values.rb +19 -22
  43. data/lib/grape/validations/attributes_iterator.rb +5 -3
  44. data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
  45. data/lib/grape/validations/params_scope.rb +20 -14
  46. data/lib/grape/validations/single_attribute_iterator.rb +13 -0
  47. data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
  48. data/lib/grape/validations/types/file.rb +1 -1
  49. data/lib/grape/validations/validator_factory.rb +6 -11
  50. data/lib/grape/validations/validators/all_or_none.rb +6 -13
  51. data/lib/grape/validations/validators/as.rb +2 -3
  52. data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
  53. data/lib/grape/validations/validators/base.rb +11 -10
  54. data/lib/grape/validations/validators/coerce.rb +4 -0
  55. data/lib/grape/validations/validators/default.rb +1 -1
  56. data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
  57. data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
  58. data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
  59. data/lib/grape/validations/validators/same_as.rb +23 -0
  60. data/lib/grape/version.rb +1 -1
  61. data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
  62. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  63. data/spec/grape/api_remount_spec.rb +466 -0
  64. data/spec/grape/api_spec.rb +379 -1
  65. data/spec/grape/config_spec.rb +17 -0
  66. data/spec/grape/dsl/desc_spec.rb +40 -16
  67. data/spec/grape/dsl/middleware_spec.rb +8 -0
  68. data/spec/grape/dsl/routing_spec.rb +10 -0
  69. data/spec/grape/endpoint_spec.rb +40 -4
  70. data/spec/grape/exceptions/base_spec.rb +65 -0
  71. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  72. data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
  73. data/spec/grape/integration/rack_spec.rb +22 -6
  74. data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
  75. data/spec/grape/middleware/base_spec.rb +8 -0
  76. data/spec/grape/middleware/exception_spec.rb +1 -1
  77. data/spec/grape/middleware/formatter_spec.rb +15 -5
  78. data/spec/grape/middleware/versioner/header_spec.rb +6 -0
  79. data/spec/grape/named_api_spec.rb +19 -0
  80. data/spec/grape/request_spec.rb +24 -0
  81. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
  82. data/spec/grape/validations/params_scope_spec.rb +184 -8
  83. data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
  84. data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
  85. data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
  86. data/spec/grape/validations/validators/coerce_spec.rb +10 -2
  87. data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
  88. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
  89. data/spec/grape/validations/validators/same_as_spec.rb +63 -0
  90. data/spec/grape/validations_spec.rb +33 -21
  91. data/spec/spec_helper.rb +4 -1
  92. metadata +35 -23
  93. data/Appraisals +0 -32
  94. data/Dangerfile +0 -2
  95. data/Gemfile +0 -33
  96. data/Gemfile.lock +0 -231
  97. data/Guardfile +0 -10
  98. data/RELEASING.md +0 -111
  99. data/Rakefile +0 -25
  100. data/benchmark/simple.rb +0 -27
  101. data/benchmark/simple_with_type_coercer.rb +0 -22
  102. data/gemfiles/multi_json.gemfile +0 -35
  103. data/gemfiles/multi_xml.gemfile +0 -35
  104. data/gemfiles/rack_1.5.2.gemfile +0 -35
  105. data/gemfiles/rack_edge.gemfile +0 -35
  106. data/gemfiles/rails_3.gemfile +0 -36
  107. data/gemfiles/rails_4.gemfile +0 -35
  108. data/gemfiles/rails_5.gemfile +0 -35
  109. data/gemfiles/rails_edge.gemfile +0 -35
  110. data/pkg/grape-0.17.0.gem +0 -0
  111. data/pkg/grape-0.19.0.gem +0 -0
@@ -21,36 +21,60 @@ module Grape
21
21
 
22
22
  it 'can be set with a block' do
23
23
  expected_options = {
24
+ summary: 'summary',
24
25
  description: 'The description',
25
26
  detail: 'more details',
26
27
  params: { first: :param },
27
28
  entity: Object,
28
29
  http_codes: [[401, 'Unauthorized', 'Entities::Error']],
29
30
  named: 'My named route',
30
- headers: [XAuthToken: {
31
- description: 'Valdates your identity',
32
- required: true
33
- },
34
- XOptionalHeader: {
35
- description: 'Not really needed',
36
- required: false
37
- }]
31
+ body_name: 'My body name',
32
+ headers: [
33
+ XAuthToken: {
34
+ description: 'Valdates your identity',
35
+ required: true
36
+ },
37
+ XOptionalHeader: {
38
+ description: 'Not really needed',
39
+ required: false
40
+ }
41
+ ],
42
+ hidden: false,
43
+ deprecated: false,
44
+ is_array: true,
45
+ nickname: 'nickname',
46
+ produces: %w[array of mime_types],
47
+ consumes: %w[array of mime_types],
48
+ tags: %w[tag1 tag2],
49
+ security: %w[array of security schemes]
38
50
  }
39
51
 
40
52
  subject.desc 'The description' do
53
+ summary 'summary'
41
54
  detail 'more details'
42
55
  params(first: :param)
43
56
  success Object
44
57
  failure [[401, 'Unauthorized', 'Entities::Error']]
45
58
  named 'My named route'
46
- headers [XAuthToken: {
47
- description: 'Valdates your identity',
48
- required: true
49
- },
50
- XOptionalHeader: {
51
- description: 'Not really needed',
52
- required: false
53
- }]
59
+ body_name 'My body name'
60
+ headers [
61
+ XAuthToken: {
62
+ description: 'Valdates your identity',
63
+ required: true
64
+ },
65
+ XOptionalHeader: {
66
+ description: 'Not really needed',
67
+ required: false
68
+ }
69
+ ]
70
+ hidden false
71
+ deprecated false
72
+ is_array true
73
+ nickname 'nickname'
74
+ produces %w[array of mime_types]
75
+ consumes %w[array of mime_types]
76
+ tags %w[tag1 tag2]
77
+ security %w[array of security schemes]
54
78
  end
55
79
 
56
80
  expect(subject.namespace_setting(:description)).to eq(expected_options)
@@ -22,6 +22,14 @@ module Grape
22
22
  end
23
23
  end
24
24
 
25
+ describe '.insert' do
26
+ it 'adds a middleware with the right operation' do
27
+ expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert, 0, :arg1, proc])
28
+
29
+ subject.insert 0, :arg1, &proc
30
+ end
31
+ end
32
+
25
33
  describe '.insert_before' do
26
34
  it 'adds a middleware with the right operation' do
27
35
  expect(subject).to receive(:namespace_stackable).with(:middleware, [:insert_before, foo_middleware, :arg1, proc])
@@ -70,6 +70,16 @@ module Grape
70
70
 
71
71
  expect(app2.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1', '/app2'])
72
72
  end
73
+
74
+ it 'mounts multiple routes at once' do
75
+ base_app = Class.new(Grape::API)
76
+ app1 = Class.new(Grape::API)
77
+ app2 = Class.new(Grape::API)
78
+ base_app.mount(app1 => '/app1', app2 => '/app2')
79
+
80
+ expect(app1.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app1'])
81
+ expect(app2.inheritable_setting.to_hash[:namespace_stackable]).to eq(mount_path: ['/app2'])
82
+ end
73
83
  end
74
84
 
75
85
  describe '.route' do
@@ -8,7 +8,7 @@ describe Grape::Endpoint do
8
8
  end
9
9
 
10
10
  describe '.before_each' do
11
- after { Grape::Endpoint.before_each(nil) }
11
+ after { Grape::Endpoint.before_each.clear }
12
12
 
13
13
  it 'is settable via block' do
14
14
  block = ->(_endpoint) { 'noop' }
@@ -472,12 +472,12 @@ describe Grape::Endpoint do
472
472
  expect(last_response.status).to eq(200)
473
473
  end
474
474
 
475
- it 'does not include aliased missing attributes if that option is passed' do
475
+ it 'does not include renamed missing attributes if that option is passed' do
476
476
  subject.params do
477
- optional :aliased_original, as: :aliased
477
+ optional :renamed_original, as: :renamed
478
478
  end
479
479
  subject.get '/declared' do
480
- error! 'expected nil', 400 if declared(params, include_missing: false).key?(:aliased)
480
+ error! 'expected nil', 400 if declared(params, include_missing: false).key?(:renamed)
481
481
  ''
482
482
  end
483
483
 
@@ -1089,6 +1089,36 @@ describe Grape::Endpoint do
1089
1089
  expect(last_response.headers['X-Custom']).to eq('value')
1090
1090
  end
1091
1091
 
1092
+ it 'merges additional headers with headers set before call' do
1093
+ subject.before do
1094
+ header 'X-Before-Test', 'before-sample'
1095
+ end
1096
+
1097
+ subject.get '/hey' do
1098
+ header 'X-Test', 'test-sample'
1099
+ error!({ 'dude' => 'rad' }, 403, 'X-Error' => 'error')
1100
+ end
1101
+
1102
+ get '/hey.json'
1103
+ expect(last_response.headers['X-Before-Test']).to eq('before-sample')
1104
+ expect(last_response.headers['X-Test']).to eq('test-sample')
1105
+ expect(last_response.headers['X-Error']).to eq('error')
1106
+ end
1107
+
1108
+ it 'does not merges additional headers with headers set after call' do
1109
+ subject.after do
1110
+ header 'X-After-Test', 'after-sample'
1111
+ end
1112
+
1113
+ subject.get '/hey' do
1114
+ error!({ 'dude' => 'rad' }, 403, 'X-Error' => 'error')
1115
+ end
1116
+
1117
+ get '/hey.json'
1118
+ expect(last_response.headers['X-Error']).to eq('error')
1119
+ expect(last_response.headers['X-After-Test']).to be_nil
1120
+ end
1121
+
1092
1122
  it 'sets the status code for the endpoint' do
1093
1123
  memoized_endpoint = nil
1094
1124
 
@@ -1492,6 +1522,9 @@ describe Grape::Endpoint do
1492
1522
  have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1493
1523
  filters: [],
1494
1524
  type: :after }),
1525
+ have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1526
+ filters: [],
1527
+ type: :finally }),
1495
1528
  have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1496
1529
  env: an_instance_of(Hash) }),
1497
1530
  have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
@@ -1518,6 +1551,9 @@ describe Grape::Endpoint do
1518
1551
  have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1519
1552
  filters: [],
1520
1553
  type: :after }),
1554
+ have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1555
+ filters: [],
1556
+ type: :finally }),
1521
1557
  have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
1522
1558
  formatter: a_kind_of(Module) })
1523
1559
  )
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Exceptions::Base do
4
+ describe '#compose_message' do
5
+ subject { described_class.new.send(:compose_message, key, attributes) }
6
+
7
+ let(:key) { :invalid_formatter }
8
+ let(:attributes) { { klass: String, to_format: 'xml' } }
9
+
10
+ after do
11
+ I18n.enforce_available_locales = true
12
+ I18n.available_locales = %i[en]
13
+ I18n.locale = :en
14
+ I18n.default_locale = :en
15
+ I18n.reload!
16
+ end
17
+
18
+ context 'when I18n enforces available locales' do
19
+ before { I18n.enforce_available_locales = true }
20
+
21
+ context 'when the fallback locale is available' do
22
+ before do
23
+ I18n.available_locales = %i[de en]
24
+ I18n.default_locale = :de
25
+ end
26
+
27
+ it 'returns the translated message' do
28
+ expect(subject).to eq('cannot convert String to xml')
29
+ end
30
+ end
31
+
32
+ context 'when the fallback locale is not available' do
33
+ before do
34
+ I18n.available_locales = %i[de jp]
35
+ I18n.locale = :de
36
+ I18n.default_locale = :de
37
+ end
38
+
39
+ it 'returns the translation string' do
40
+ expect(subject).to eq("grape.errors.messages.#{key}")
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'when I18n does not enforce available locales' do
46
+ before { I18n.enforce_available_locales = false }
47
+
48
+ context 'when the fallback locale is available' do
49
+ before { I18n.available_locales = %i[de en] }
50
+
51
+ it 'returns the translated message' do
52
+ expect(subject).to eq('cannot convert String to xml')
53
+ end
54
+ end
55
+
56
+ context 'when the fallback locale is not available' do
57
+ before { I18n.available_locales = %i[de jp] }
58
+
59
+ it 'returns the translated message' do
60
+ expect(subject).to eq('cannot convert String to xml')
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Exceptions::InvalidResponse do
4
+ describe '#message' do
5
+ let(:error) { described_class.new }
6
+
7
+ it 'contains the problem in the message' do
8
+ expect(error.message).to include('Invalid response')
9
+ end
10
+ end
11
+ end
@@ -77,10 +77,12 @@ describe Grape::Exceptions::ValidationErrors do
77
77
  end
78
78
  get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
79
79
  expect(last_response.status).to eq(400)
80
- expect(JSON.parse(last_response.body)).to eq([
81
- 'params' => %w[beer wine],
82
- 'messages' => ['are mutually exclusive']
83
- ])
80
+ expect(JSON.parse(last_response.body)).to eq(
81
+ [
82
+ 'params' => %w[beer wine juice],
83
+ 'messages' => ['are missing, exactly one parameter must be provided']
84
+ ]
85
+ )
84
86
  end
85
87
  end
86
88
  end
@@ -19,16 +19,32 @@ describe Rack do
19
19
  }
20
20
  env = Rack::MockRequest.env_for('/', options)
21
21
 
22
- unless RUBY_PLATFORM == 'java'
23
- major, minor, patch = Rack.release.split('.').map(&:to_i)
24
- patch ||= 0 # rack <= 1.5.2 does not specify patch version
25
- pending 'Rack 1.5.3 or 1.6.1 required' unless major >= 2 || (major >= 1 && ((minor == 5 && patch >= 3) || (minor >= 6)))
26
- end
27
-
28
22
  expect(JSON.parse(app.call(env)[2].body.first)['params_keys']).to match_array('test')
29
23
  ensure
30
24
  input.close
31
25
  input.unlink
32
26
  end
33
27
  end
28
+
29
+ context 'when the app is mounted' do
30
+ def app
31
+ @main_app ||= Class.new(Grape::API) do
32
+ get 'ping'
33
+ end
34
+ end
35
+
36
+ let!(:base) do
37
+ app_to_mount = app
38
+ Class.new(Grape::API) do
39
+ namespace 'namespace' do
40
+ mount app_to_mount
41
+ end
42
+ end
43
+ end
44
+
45
+ it 'finds the app on the namespace' do
46
+ get '/namespace/ping'
47
+ expect(last_response.status).to eq 200
48
+ end
49
+ end
34
50
  end
@@ -15,15 +15,15 @@ describe Grape::Middleware::Auth::DSL do
15
15
 
16
16
  describe '.auth' do
17
17
  it 'stets auth parameters' do
18
- expect(subject).to receive(:use).with(Grape::Middleware::Auth::Base, settings)
18
+ expect(subject.base_instance).to receive(:use).with(Grape::Middleware::Auth::Base, settings)
19
19
 
20
20
  subject.auth :http_digest, realm: settings[:realm], opaque: settings[:opaque], &settings[:proc]
21
21
  expect(subject.auth).to eq(settings)
22
22
  end
23
23
 
24
24
  it 'can be called multiple times' do
25
- expect(subject).to receive(:use).with(Grape::Middleware::Auth::Base, settings)
26
- expect(subject).to receive(:use).with(Grape::Middleware::Auth::Base, settings.merge(realm: 'super_secret'))
25
+ expect(subject.base_instance).to receive(:use).with(Grape::Middleware::Auth::Base, settings)
26
+ expect(subject.base_instance).to receive(:use).with(Grape::Middleware::Auth::Base, settings.merge(realm: 'super_secret'))
27
27
 
28
28
  subject.auth :http_digest, realm: settings[:realm], opaque: settings[:opaque], &settings[:proc]
29
29
  first_settings = subject.auth
@@ -114,6 +114,14 @@ describe Grape::Middleware::Base do
114
114
  end
115
115
  end
116
116
 
117
+ describe '#context' do
118
+ subject { Grape::Middleware::Base.new(blank_app) }
119
+ it 'allows access to response context' do
120
+ subject.call(Grape::Env::API_ENDPOINT => { header: 'some header' })
121
+ expect(subject.context).to eq(header: 'some header')
122
+ end
123
+ end
124
+
117
125
  context 'options' do
118
126
  it 'persists options passed at initialization' do
119
127
  expect(Grape::Middleware::Base.new(blank_app, abc: true).options[:abc]).to be true
@@ -128,7 +128,7 @@ describe Grape::Middleware::Error do
128
128
  subject do
129
129
  Rack::Builder.app do
130
130
  use Spec::Support::EndpointFaker
131
- use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => -> { [200, {}, 'rescued'] } }
131
+ use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } }
132
132
  run ExceptionSpec::OtherExceptionApp
133
133
  end
134
134
  end
@@ -213,7 +213,13 @@ describe Grape::Middleware::Formatter do
213
213
  context 'no content responses' do
214
214
  let(:no_content_response) { ->(status) { [status, {}, ['']] } }
215
215
 
216
- Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.each do |status|
216
+ STATUSES_WITHOUT_BODY = if Gem::Version.new(Rack.release) >= Gem::Version.new('2.1.0')
217
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.keys
218
+ else
219
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY
220
+ end
221
+
222
+ STATUSES_WITHOUT_BODY.each do |status|
217
223
  it "does not modify a #{status} response" do
218
224
  expected_response = no_content_response[status]
219
225
  allow(app).to receive(:call).and_return(expected_response)
@@ -247,7 +253,7 @@ describe Grape::Middleware::Formatter do
247
253
  let(:content_type) { 'application/atom+xml' }
248
254
 
249
255
  it 'returns a 415 HTTP error status' do
250
- error = catch(:error) {
256
+ error = catch(:error) do
251
257
  subject.call(
252
258
  'PATH_INFO' => '/info',
253
259
  'REQUEST_METHOD' => method,
@@ -255,7 +261,7 @@ describe Grape::Middleware::Formatter do
255
261
  'rack.input' => io,
256
262
  'CONTENT_LENGTH' => io.length
257
263
  )
258
- }
264
+ end
259
265
  expect(error[:status]).to eq(415)
260
266
  expect(error[:message]).to eq("The provided content-type 'application/atom+xml' is not supported.")
261
267
  end
@@ -391,6 +397,10 @@ describe Grape::Middleware::Formatter do
391
397
  Grape::Formatter.register :invalid, InvalidFormatter
392
398
  Grape::ContentTypes::CONTENT_TYPES[:invalid] = 'application/x-invalid'
393
399
  end
400
+ after do
401
+ Grape::ContentTypes::CONTENT_TYPES.delete(:invalid)
402
+ Grape::Formatter.default_elements.delete(:invalid)
403
+ end
394
404
 
395
405
  it 'returns response by invalid formatter' do
396
406
  env = { 'PATH_INFO' => '/hello.invalid', 'HTTP_ACCEPT' => 'application/x-invalid' }
@@ -407,7 +417,7 @@ describe Grape::Middleware::Formatter do
407
417
  parsers: { json: ->(_object, _env) { raise StandardError, 'fail' } }
408
418
  )
409
419
  io = StringIO.new('{invalid}')
410
- error = catch(:error) {
420
+ error = catch(:error) do
411
421
  subject.call(
412
422
  'PATH_INFO' => '/info',
413
423
  'REQUEST_METHOD' => 'POST',
@@ -415,7 +425,7 @@ describe Grape::Middleware::Formatter do
415
425
  'rack.input' => io,
416
426
  'CONTENT_LENGTH' => io.length
417
427
  )
418
- }
428
+ end
419
429
 
420
430
  expect(error[:message]).to eq 'fail'
421
431
  expect(error[:backtrace].size).to be >= 1
@@ -160,6 +160,12 @@ describe Grape::Middleware::Versioner::Header do
160
160
  expect(subject.call({}).first).to eq(200)
161
161
  end
162
162
 
163
+ it 'succeeds if :strict is set to false and given an invalid header' do
164
+ @options[:version_options][:strict] = false
165
+ expect(subject.call('HTTP_ACCEPT' => 'yaml').first).to eq(200)
166
+ expect(subject.call({}).first).to eq(200)
167
+ end
168
+
163
169
  context 'when :strict is set' do
164
170
  before do
165
171
  @options[:versions] = ['v1']
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'A named API' do
4
+ subject(:api_name) { NamedAPI.endpoints.last.options[:for].to_s }
5
+
6
+ let(:api) do
7
+ Class.new(Grape::API) do
8
+ get 'test' do
9
+ 'response'
10
+ end
11
+ end
12
+ end
13
+
14
+ before { stub_const('NamedAPI', api) }
15
+
16
+ it 'can access the name of the API' do
17
+ expect(api_name).to eq 'NamedAPI'
18
+ end
19
+ end