grape 1.3.3 → 1.6.2

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 (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -2
  3. data/CONTRIBUTING.md +2 -1
  4. data/README.md +135 -23
  5. data/UPGRADING.md +237 -46
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +34 -42
  8. data/lib/grape/api.rb +21 -16
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dsl/callbacks.rb +1 -1
  11. data/lib/grape/dsl/desc.rb +3 -5
  12. data/lib/grape/dsl/headers.rb +5 -2
  13. data/lib/grape/dsl/helpers.rb +8 -5
  14. data/lib/grape/dsl/inside_route.rb +72 -53
  15. data/lib/grape/dsl/middleware.rb +4 -4
  16. data/lib/grape/dsl/parameters.rb +11 -7
  17. data/lib/grape/dsl/request_response.rb +9 -6
  18. data/lib/grape/dsl/routing.rb +8 -9
  19. data/lib/grape/dsl/settings.rb +5 -5
  20. data/lib/grape/dsl/validations.rb +18 -1
  21. data/lib/grape/eager_load.rb +1 -1
  22. data/lib/grape/endpoint.rb +29 -42
  23. data/lib/grape/error_formatter/json.rb +2 -6
  24. data/lib/grape/error_formatter/xml.rb +2 -6
  25. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  26. data/lib/grape/exceptions/validation.rb +2 -3
  27. data/lib/grape/exceptions/validation_errors.rb +1 -1
  28. data/lib/grape/formatter/json.rb +1 -0
  29. data/lib/grape/formatter/serializable_hash.rb +2 -1
  30. data/lib/grape/formatter/xml.rb +1 -0
  31. data/lib/grape/locale/en.yml +1 -1
  32. data/lib/grape/middleware/auth/base.rb +3 -3
  33. data/lib/grape/middleware/auth/dsl.rb +7 -1
  34. data/lib/grape/middleware/base.rb +6 -3
  35. data/lib/grape/middleware/error.rb +11 -13
  36. data/lib/grape/middleware/formatter.rb +7 -7
  37. data/lib/grape/middleware/stack.rb +10 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  39. data/lib/grape/middleware/versioner/header.rb +6 -4
  40. data/lib/grape/middleware/versioner/param.rb +1 -0
  41. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  42. data/lib/grape/middleware/versioner/path.rb +2 -0
  43. data/lib/grape/parser/json.rb +1 -1
  44. data/lib/grape/parser/xml.rb +1 -1
  45. data/lib/grape/path.rb +1 -0
  46. data/lib/grape/request.rb +4 -1
  47. data/lib/grape/router/attribute_translator.rb +3 -3
  48. data/lib/grape/router/pattern.rb +1 -1
  49. data/lib/grape/router/route.rb +2 -2
  50. data/lib/grape/router.rb +31 -30
  51. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  52. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  53. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  54. data/lib/grape/util/base_inheritable.rb +2 -2
  55. data/lib/grape/util/inheritable_setting.rb +1 -3
  56. data/lib/grape/util/lazy_value.rb +4 -2
  57. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  58. data/lib/grape/validations/attributes_iterator.rb +8 -0
  59. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  60. data/lib/grape/validations/params_scope.rb +97 -62
  61. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  62. data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
  63. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  64. data/lib/grape/validations/types/invalid_value.rb +24 -0
  65. data/lib/grape/validations/types/json.rb +2 -1
  66. data/lib/grape/validations/types/primitive_coercer.rb +4 -5
  67. data/lib/grape/validations/types.rb +1 -4
  68. data/lib/grape/validations/validator_factory.rb +1 -1
  69. data/lib/grape/validations/validators/all_or_none.rb +8 -5
  70. data/lib/grape/validations/validators/allow_blank.rb +9 -7
  71. data/lib/grape/validations/validators/as.rb +6 -8
  72. data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
  73. data/lib/grape/validations/validators/base.rb +74 -69
  74. data/lib/grape/validations/validators/coerce.rb +63 -76
  75. data/lib/grape/validations/validators/default.rb +36 -34
  76. data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
  77. data/lib/grape/validations/validators/except_values.rb +13 -11
  78. data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
  79. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
  80. data/lib/grape/validations/validators/presence.rb +7 -4
  81. data/lib/grape/validations/validators/regexp.rb +8 -5
  82. data/lib/grape/validations/validators/same_as.rb +18 -15
  83. data/lib/grape/validations/validators/values.rb +61 -56
  84. data/lib/grape/validations.rb +6 -0
  85. data/lib/grape/version.rb +1 -1
  86. data/lib/grape.rb +7 -3
  87. data/spec/grape/api/custom_validations_spec.rb +77 -45
  88. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  89. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  90. data/spec/grape/api/invalid_format_spec.rb +2 -0
  91. data/spec/grape/api/recognize_path_spec.rb +1 -1
  92. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  93. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  94. data/spec/grape/api_remount_spec.rb +25 -19
  95. data/spec/grape/api_spec.rb +576 -211
  96. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  97. data/spec/grape/dsl/headers_spec.rb +39 -9
  98. data/spec/grape/dsl/helpers_spec.rb +3 -2
  99. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  100. data/spec/grape/dsl/logger_spec.rb +16 -18
  101. data/spec/grape/dsl/middleware_spec.rb +2 -1
  102. data/spec/grape/dsl/parameters_spec.rb +2 -0
  103. data/spec/grape/dsl/request_response_spec.rb +1 -0
  104. data/spec/grape/dsl/routing_spec.rb +10 -7
  105. data/spec/grape/endpoint/declared_spec.rb +848 -0
  106. data/spec/grape/endpoint_spec.rb +77 -589
  107. data/spec/grape/entity_spec.rb +29 -23
  108. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  109. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  110. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  111. data/spec/grape/exceptions/validation_spec.rb +5 -3
  112. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  113. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  114. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  115. data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
  116. data/spec/grape/loading_spec.rb +8 -8
  117. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  118. data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
  119. data/spec/grape/middleware/base_spec.rb +24 -15
  120. data/spec/grape/middleware/error_spec.rb +3 -3
  121. data/spec/grape/middleware/exception_spec.rb +111 -161
  122. data/spec/grape/middleware/formatter_spec.rb +28 -7
  123. data/spec/grape/middleware/globals_spec.rb +7 -4
  124. data/spec/grape/middleware/stack_spec.rb +15 -12
  125. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  126. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  127. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  128. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  129. data/spec/grape/middleware/versioner_spec.rb +1 -1
  130. data/spec/grape/parser_spec.rb +4 -0
  131. data/spec/grape/path_spec.rb +52 -52
  132. data/spec/grape/presenters/presenter_spec.rb +7 -6
  133. data/spec/grape/request_spec.rb +6 -4
  134. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  135. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  136. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  137. data/spec/grape/util/stackable_values_spec.rb +7 -5
  138. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  139. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
  140. data/spec/grape/validations/params_scope_spec.rb +72 -10
  141. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
  142. data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
  143. data/spec/grape/validations/types_spec.rb +8 -8
  144. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  145. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  146. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  147. data/spec/grape/validations/validators/coerce_spec.rb +248 -33
  148. data/spec/grape/validations/validators/default_spec.rb +121 -78
  149. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  150. data/spec/grape/validations/validators/except_values_spec.rb +4 -3
  151. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  152. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  153. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  154. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  155. data/spec/grape/validations/validators/values_spec.rb +183 -178
  156. data/spec/grape/validations_spec.rb +342 -29
  157. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  158. data/spec/integration/multi_json/json_spec.rb +1 -1
  159. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  160. data/spec/shared/versioning_examples.rb +32 -29
  161. data/spec/spec_helper.rb +12 -12
  162. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  163. data/spec/support/chunks.rb +14 -0
  164. data/spec/support/versioned_helpers.rb +4 -6
  165. metadata +110 -102
@@ -21,7 +21,7 @@ module Grape
21
21
  let(:env) { default_env }
22
22
 
23
23
  let(:request) do
24
- Grape::Request.new(env)
24
+ described_class.new(env)
25
25
  end
26
26
 
27
27
  describe '#params' do
@@ -38,7 +38,7 @@ module Grape
38
38
 
39
39
  context 'when build_params_with: Grape::Extensions::Hash::ParamBuilder is specified' do
40
40
  let(:request) do
41
- Grape::Request.new(env, build_params_with: Grape::Extensions::Hash::ParamBuilder)
41
+ described_class.new(env, build_params_with: Grape::Extensions::Hash::ParamBuilder)
42
42
  end
43
43
 
44
44
  it 'returns symbolized params' do
@@ -65,6 +65,8 @@ module Grape
65
65
  end
66
66
 
67
67
  describe 'when the param_builder is set to Hashie' do
68
+ subject(:request_params) { described_class.new(env, **opts).params }
69
+
68
70
  before do
69
71
  Grape.configure do |config|
70
72
  config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
@@ -75,15 +77,15 @@ module Grape
75
77
  Grape.config.reset
76
78
  end
77
79
 
78
- subject(:request_params) { Grape::Request.new(env, opts).params }
79
-
80
80
  context 'when the API does not include a specific param builder' do
81
81
  let(:opts) { {} }
82
+
82
83
  it { is_expected.to be_a(Hashie::Mash) }
83
84
  end
84
85
 
85
86
  context 'when the API includes a specific param builder' do
86
87
  let(:opts) { { build_params_with: Grape::Extensions::Hash::ParamBuilder } }
88
+
87
89
  it { is_expected.to be_a(Hash) }
88
90
  end
89
91
  end
@@ -4,12 +4,12 @@ require 'spec_helper'
4
4
  module Grape
5
5
  module Util
6
6
  describe InheritableSetting do
7
- before :each do
8
- InheritableSetting.reset_global!
7
+ before do
8
+ described_class.reset_global!
9
9
  end
10
10
 
11
11
  let(:parent) do
12
- Grape::Util::InheritableSetting.new.tap do |settings|
12
+ described_class.new.tap do |settings|
13
13
  settings.global[:global_thing] = :global_foo_bar
14
14
  settings.namespace[:namespace_thing] = :namespace_foo_bar
15
15
  settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar
@@ -20,7 +20,7 @@ module Grape
20
20
  end
21
21
 
22
22
  let(:other_parent) do
23
- Grape::Util::InheritableSetting.new.tap do |settings|
23
+ described_class.new.tap do |settings|
24
24
  settings.namespace[:namespace_thing] = :namespace_foo_bar_other
25
25
  settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar_other
26
26
  settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar_other
@@ -29,7 +29,7 @@ module Grape
29
29
  end
30
30
  end
31
31
 
32
- before :each do
32
+ before do
33
33
  subject.inherit_from parent
34
34
  end
35
35
 
@@ -50,7 +50,7 @@ module Grape
50
50
  expect(parent.global[:global_thing]).to eq :global_new_foo_bar
51
51
  end
52
52
 
53
- it 'should handle different parents' do
53
+ it 'handles different parents' do
54
54
  subject.global[:global_thing] = :global_new_foo_bar
55
55
 
56
56
  subject.inherit_from other_parent
@@ -89,7 +89,7 @@ module Grape
89
89
  expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar
90
90
  end
91
91
 
92
- it 'should handle different parents' do
92
+ it 'handles different parents' do
93
93
  expect(subject.namespace_inheritable[:namespace_inheritable_thing]).to eq :namespace_inheritable_foo_bar
94
94
 
95
95
  subject.inherit_from other_parent
@@ -4,8 +4,9 @@ require 'spec_helper'
4
4
  module Grape
5
5
  module Util
6
6
  describe InheritableValues do
7
- let(:parent) { InheritableValues.new }
8
- subject { InheritableValues.new(parent) }
7
+ subject { described_class.new(parent) }
8
+
9
+ let(:parent) { described_class.new }
9
10
 
10
11
  describe '#delete' do
11
12
  it 'deletes a key' do
@@ -4,9 +4,10 @@ require 'spec_helper'
4
4
  module Grape
5
5
  module Util
6
6
  describe ReverseStackableValues do
7
- let(:parent) { described_class.new }
8
7
  subject { described_class.new(parent) }
9
8
 
9
+ let(:parent) { described_class.new }
10
+
10
11
  describe '#keys' do
11
12
  it 'returns all keys' do
12
13
  subject[:some_thing] = :foo_bar
@@ -102,6 +103,7 @@ module Grape
102
103
 
103
104
  describe '#clone' do
104
105
  let(:obj_cloned) { subject.clone }
106
+
105
107
  it 'copies all values' do
106
108
  parent = described_class.new
107
109
  child = described_class.new parent
@@ -4,8 +4,9 @@ require 'spec_helper'
4
4
  module Grape
5
5
  module Util
6
6
  describe StackableValues do
7
- let(:parent) { StackableValues.new }
8
- subject { StackableValues.new(parent) }
7
+ subject { described_class.new(parent) }
8
+
9
+ let(:parent) { described_class.new }
9
10
 
10
11
  describe '#keys' do
11
12
  it 'returns all keys' do
@@ -99,10 +100,11 @@ module Grape
99
100
 
100
101
  describe '#clone' do
101
102
  let(:obj_cloned) { subject.clone }
103
+
102
104
  it 'copies all values' do
103
- parent = StackableValues.new
104
- child = StackableValues.new parent
105
- grandchild = StackableValues.new child
105
+ parent = described_class.new
106
+ child = described_class.new parent
107
+ grandchild = described_class.new child
106
108
 
107
109
  parent[:some_thing] = :foo
108
110
  child[:some_thing] = %i[bar more]
@@ -4,7 +4,7 @@ require 'spec_helper'
4
4
 
5
5
  describe 'Validator with instance variables' do
6
6
  let(:validator_type) do
7
- Class.new(Grape::Validations::Base) do
7
+ Class.new(Grape::Validations::Validators::Base) do
8
8
  def validate_param!(_attr_name, _params)
9
9
  if instance_variable_defined?(:@instance_variable) && @instance_variable
10
10
  raise Grape::Exceptions::Validation.new(params: ['params'],
@@ -14,15 +14,6 @@ describe 'Validator with instance variables' do
14
14
  end
15
15
  end
16
16
  end
17
-
18
- before do
19
- Grape::Validations.register_validator('instance_validator', validator_type)
20
- end
21
-
22
- after do
23
- Grape::Validations.deregister_validator('instance_validator')
24
- end
25
-
26
17
  let(:app) do
27
18
  Class.new(Grape::API) do
28
19
  params do
@@ -35,6 +26,14 @@ describe 'Validator with instance variables' do
35
26
  end
36
27
  end
37
28
 
29
+ before do
30
+ Grape::Validations.register_validator('instance_validator', validator_type)
31
+ end
32
+
33
+ after do
34
+ Grape::Validations.deregister_validator('instance_validator')
35
+ end
36
+
38
37
  it 'passes validation every time' do
39
38
  expect(validator_type).to receive(:new).exactly(4).times.and_call_original
40
39
 
@@ -5,6 +5,7 @@ require 'spec_helper'
5
5
  describe Grape::Validations::MultipleAttributesIterator do
6
6
  describe '#each' do
7
7
  subject(:iterator) { described_class.new(validator, scope, params) }
8
+
8
9
  let(:scope) { Grape::Validations::ParamsScope.new(api: Class.new(Grape::API)) }
9
10
  let(:validator) { double(attrs: %i[first second third]) }
10
11
 
@@ -13,8 +14,8 @@ describe Grape::Validations::MultipleAttributesIterator do
13
14
  { first: 'string', second: 'string' }
14
15
  end
15
16
 
16
- it 'yields the whole params hash without the list of attrs' do
17
- expect { |b| iterator.each(&b) }.to yield_with_args(params)
17
+ it 'yields the whole params hash and the skipped flag without the list of attrs' do
18
+ expect { |b| iterator.each(&b) }.to yield_with_args(params, false)
18
19
  end
19
20
  end
20
21
 
@@ -24,7 +25,17 @@ describe Grape::Validations::MultipleAttributesIterator do
24
25
  end
25
26
 
26
27
  it 'yields each element of the array without the list of attrs' do
27
- expect { |b| iterator.each(&b) }.to yield_successive_args(params[0], params[1])
28
+ expect { |b| iterator.each(&b) }.to yield_successive_args([params[0], false], [params[1], false])
29
+ end
30
+ end
31
+
32
+ context 'when params is empty optional placeholder' do
33
+ let(:params) do
34
+ [Grape::DSL::Parameters::EmptyOptionalValue, { first: 'string2', second: 'string2' }]
35
+ end
36
+
37
+ it 'yields each element of the array without the list of attrs' do
38
+ expect { |b| iterator.each(&b) }.to yield_successive_args([Grape::DSL::Parameters::EmptyOptionalValue, true], [params[1], false])
28
39
  end
29
40
  end
30
41
  end
@@ -98,6 +98,7 @@ describe Grape::Validations::ParamsScope do
98
98
 
99
99
  def self.parse(value)
100
100
  raise if value == 'invalid'
101
+
101
102
  new(value)
102
103
  end
103
104
 
@@ -144,7 +145,7 @@ describe Grape::Validations::ParamsScope do
144
145
  get '/renaming-coerced', foo: ' there we go '
145
146
 
146
147
  expect(last_response.status).to eq(200)
147
- expect(last_response.body).to eq('there we go-')
148
+ expect(last_response.body).to eq('-there we go')
148
149
  end
149
150
 
150
151
  it do
@@ -180,6 +181,28 @@ describe Grape::Validations::ParamsScope do
180
181
  expect(last_response.status).to eq(200)
181
182
  expect(last_response.body).to eq('{"baz":{"qux":"any"}}')
182
183
  end
184
+
185
+ it 'renaming can be defined before default' do
186
+ subject.params do
187
+ optional :foo, as: :bar, default: 'before'
188
+ end
189
+ subject.get('/rename-before-default') { declared(params)[:bar] }
190
+ get '/rename-before-default'
191
+
192
+ expect(last_response.status).to eq(200)
193
+ expect(last_response.body).to eq('before')
194
+ end
195
+
196
+ it 'renaming can be defined after default' do
197
+ subject.params do
198
+ optional :foo, default: 'after', as: :bar
199
+ end
200
+ subject.get('/rename-after-default') { declared(params)[:bar] }
201
+ get '/rename-after-default'
202
+
203
+ expect(last_response.status).to eq(200)
204
+ expect(last_response.body).to eq('after')
205
+ end
183
206
  end
184
207
 
185
208
  context 'array without coerce type explicitly given' do
@@ -269,7 +292,7 @@ describe Grape::Validations::ParamsScope do
269
292
  it 'does not raise an exception' do
270
293
  expect do
271
294
  subject.params { optional :numbers, type: Array[Integer], values: 0..2, default: 0..2 }
272
- end.to_not raise_error
295
+ end.not_to raise_error
273
296
  end
274
297
  end
275
298
 
@@ -277,7 +300,7 @@ describe Grape::Validations::ParamsScope do
277
300
  it 'does not raise an exception' do
278
301
  expect do
279
302
  subject.params { optional :numbers, type: Array[Integer], values: [0, 1, 2], default: [1, 0] }
280
- end.to_not raise_error
303
+ end.not_to raise_error
281
304
  end
282
305
  end
283
306
  end
@@ -501,7 +524,7 @@ describe Grape::Validations::ParamsScope do
501
524
  requires :c
502
525
  end
503
526
  end
504
- end.to_not raise_error
527
+ end.not_to raise_error
505
528
  end
506
529
 
507
530
  it 'does not raise an error if when using nested given' do
@@ -517,7 +540,7 @@ describe Grape::Validations::ParamsScope do
517
540
  end
518
541
  end
519
542
  end
520
- end.to_not raise_error
543
+ end.not_to raise_error
521
544
  end
522
545
 
523
546
  it 'allows nested dependent parameters' do
@@ -562,13 +585,13 @@ describe Grape::Validations::ParamsScope do
562
585
  body = JSON.parse(last_response.body)
563
586
 
564
587
  expect(body.keys).to include('c')
565
- expect(body.keys).to_not include('b')
588
+ expect(body.keys).not_to include('b')
566
589
  end
567
590
 
568
591
  it 'allows renaming of dependent on parameter' do
569
592
  subject.params do
570
593
  optional :a, as: :b
571
- given b: ->(val) { val == 'x' } do
594
+ given a: ->(val) { val == 'x' } do
572
595
  requires :c
573
596
  end
574
597
  end
@@ -582,7 +605,7 @@ describe Grape::Validations::ParamsScope do
582
605
  expect(last_response.status).to eq 200
583
606
  end
584
607
 
585
- it 'raises an error if the dependent parameter is not the renamed one' do
608
+ it 'does not raise if the dependent parameter is not the renamed one' do
586
609
  expect do
587
610
  subject.params do
588
611
  optional :a, as: :b
@@ -590,6 +613,17 @@ describe Grape::Validations::ParamsScope do
590
613
  requires :c
591
614
  end
592
615
  end
616
+ end.not_to raise_error
617
+ end
618
+
619
+ it 'raises an error if the dependent parameter is the renamed one' do
620
+ expect do
621
+ subject.params do
622
+ optional :a, as: :b
623
+ given :b do
624
+ requires :c
625
+ end
626
+ end
593
627
  end.to raise_error(Grape::Exceptions::UnknownParameter)
594
628
  end
595
629
 
@@ -633,6 +667,32 @@ describe Grape::Validations::ParamsScope do
633
667
  expect(last_response.status).to eq(200)
634
668
  end
635
669
 
670
+ it 'detect unmet nested dependency' do
671
+ subject.params do
672
+ requires :a, type: String, allow_blank: false, values: %w[x y z]
673
+ given a: ->(val) { val == 'z' } do
674
+ requires :inner3, type: Array, allow_blank: false do
675
+ requires :bar, type: String, allow_blank: false
676
+ given bar: ->(val) { val == 'b' } do
677
+ requires :baz, type: Array do
678
+ optional :baz_category, type: String
679
+ end
680
+ end
681
+ given bar: ->(val) { val == 'c' } do
682
+ requires :baz, type: Array do
683
+ requires :baz_category, type: String
684
+ end
685
+ end
686
+ end
687
+ end
688
+ end
689
+ subject.get('/nested-dependency') { declared(params).to_json }
690
+
691
+ get '/nested-dependency', a: 'z', inner3: [{ bar: 'c', baz: [{ unrelated: 'nope' }] }]
692
+ expect(last_response.status).to eq(400)
693
+ expect(last_response.body).to eq 'inner3[0][baz][0][baz_category] is missing'
694
+ end
695
+
636
696
  it 'includes the parameter within #declared(params)' do
637
697
  get '/test', a: true, b: true
638
698
 
@@ -727,7 +787,7 @@ describe Grape::Validations::ParamsScope do
727
787
  subject.get('/test') { 'ok' }
728
788
  end
729
789
 
730
- it 'should pass none Hash params' do
790
+ it 'passes none Hash params' do
731
791
  get '/test', foos: ['']
732
792
  expect(last_response.status).to eq(200)
733
793
  expect(last_response.body).to eq('ok')
@@ -903,6 +963,7 @@ describe Grape::Validations::ParamsScope do
903
963
  expect(last_response.body).to eq('one is missing, two is missing, three is missing')
904
964
  end
905
965
  end
966
+
906
967
  context 'when fail_fast is defined it stops the validation' do
907
968
  it 'of other params' do
908
969
  subject.params do
@@ -915,6 +976,7 @@ describe Grape::Validations::ParamsScope do
915
976
  expect(last_response.status).to eq(400)
916
977
  expect(last_response.body).to eq('one is missing')
917
978
  end
979
+
918
980
  it 'for a single param' do
919
981
  subject.params do
920
982
  requires :one, allow_blank: false, regexp: /[0-9]+/, fail_fast: true
@@ -965,7 +1027,7 @@ describe Grape::Validations::ParamsScope do
965
1027
  end
966
1028
 
967
1029
  it 'prioritizes parameter validation over group validation' do
968
- expect(last_response.body).to_not include('address is empty')
1030
+ expect(last_response.body).not_to include('address is empty')
969
1031
  end
970
1032
  end
971
1033
  end
@@ -5,6 +5,7 @@ require 'spec_helper'
5
5
  describe Grape::Validations::SingleAttributeIterator do
6
6
  describe '#each' do
7
7
  subject(:iterator) { described_class.new(validator, scope, params) }
8
+
8
9
  let(:scope) { Grape::Validations::ParamsScope.new(api: Class.new(Grape::API)) }
9
10
  let(:validator) { double(attrs: %i[first second]) }
10
11
 
@@ -15,7 +16,7 @@ describe Grape::Validations::SingleAttributeIterator do
15
16
 
16
17
  it 'yields params and every single attribute from the list' do
17
18
  expect { |b| iterator.each(&b) }
18
- .to yield_successive_args([params, :first, false], [params, :second, false])
19
+ .to yield_successive_args([params, :first, false, false], [params, :second, false, false])
19
20
  end
20
21
  end
21
22
 
@@ -26,8 +27,8 @@ describe Grape::Validations::SingleAttributeIterator do
26
27
 
27
28
  it 'yields every single attribute from the list for each of the array elements' do
28
29
  expect { |b| iterator.each(&b) }.to yield_successive_args(
29
- [params[0], :first, false], [params[0], :second, false],
30
- [params[1], :first, false], [params[1], :second, false]
30
+ [params[0], :first, false, false], [params[0], :second, false, false],
31
+ [params[1], :first, false, false], [params[1], :second, false, false]
31
32
  )
32
33
  end
33
34
 
@@ -36,9 +37,20 @@ describe Grape::Validations::SingleAttributeIterator do
36
37
 
37
38
  it 'marks params with empty values' do
38
39
  expect { |b| iterator.each(&b) }.to yield_successive_args(
39
- [params[0], :first, true], [params[0], :second, true],
40
- [params[1], :first, true], [params[1], :second, true],
41
- [params[2], :first, false], [params[2], :second, false]
40
+ [params[0], :first, true, false], [params[0], :second, true, false],
41
+ [params[1], :first, true, false], [params[1], :second, true, false],
42
+ [params[2], :first, false, false], [params[2], :second, false, false]
43
+ )
44
+ end
45
+ end
46
+
47
+ context 'when missing optional value' do
48
+ let(:params) { [Grape::DSL::Parameters::EmptyOptionalValue, 10] }
49
+
50
+ it 'marks params with skipped values' do
51
+ expect { |b| iterator.each(&b) }.to yield_successive_args(
52
+ [params[0], :first, false, true], [params[0], :second, false, true],
53
+ [params[1], :first, false, false], [params[1], :second, false, false]
42
54
  )
43
55
  end
44
56
  end
@@ -3,11 +3,23 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Grape::Validations::Types::PrimitiveCoercer do
6
- let(:strict) { false }
7
-
8
6
  subject { described_class.new(type, strict) }
9
7
 
8
+ let(:strict) { false }
9
+
10
10
  describe '#call' do
11
+ context 'BigDecimal' do
12
+ let(:type) { BigDecimal }
13
+
14
+ it 'coerces to BigDecimal' do
15
+ expect(subject.call(5)).to eq(BigDecimal('5'))
16
+ end
17
+
18
+ it 'coerces an empty string to nil' do
19
+ expect(subject.call('')).to be_nil
20
+ end
21
+ end
22
+
11
23
  context 'Boolean' do
12
24
  let(:type) { Grape::API::Boolean }
13
25
 
@@ -32,19 +44,63 @@ describe Grape::Validations::Types::PrimitiveCoercer do
32
44
  end
33
45
  end
34
46
 
47
+ context 'DateTime' do
48
+ let(:type) { DateTime }
49
+
50
+ it 'coerces an empty string to nil' do
51
+ expect(subject.call('')).to be_nil
52
+ end
53
+ end
54
+
55
+ context 'Float' do
56
+ let(:type) { Float }
57
+
58
+ it 'coerces an empty string to nil' do
59
+ expect(subject.call('')).to be_nil
60
+ end
61
+ end
62
+
63
+ context 'Integer' do
64
+ let(:type) { Integer }
65
+
66
+ it 'coerces an empty string to nil' do
67
+ expect(subject.call('')).to be_nil
68
+ end
69
+ end
70
+
71
+ context 'Numeric' do
72
+ let(:type) { Numeric }
73
+
74
+ it 'coerces an empty string to nil' do
75
+ expect(subject.call('')).to be_nil
76
+ end
77
+ end
78
+
79
+ context 'Time' do
80
+ let(:type) { Time }
81
+
82
+ it 'coerces an empty string to nil' do
83
+ expect(subject.call('')).to be_nil
84
+ end
85
+ end
86
+
35
87
  context 'String' do
36
88
  let(:type) { String }
37
89
 
38
90
  it 'coerces to String' do
39
91
  expect(subject.call(10)).to eq('10')
40
92
  end
93
+
94
+ it 'does not coerce an empty string to nil' do
95
+ expect(subject.call('')).to eq('')
96
+ end
41
97
  end
42
98
 
43
- context 'BigDecimal' do
44
- let(:type) { BigDecimal }
99
+ context 'Symbol' do
100
+ let(:type) { Symbol }
45
101
 
46
- it 'coerces to BigDecimal' do
47
- expect(subject.call(5)).to eq(BigDecimal(5))
102
+ it 'coerces an empty string to nil' do
103
+ expect(subject.call('')).to be_nil
48
104
  end
49
105
  end
50
106
 
@@ -71,7 +127,7 @@ describe Grape::Validations::Types::PrimitiveCoercer do
71
127
  end
72
128
 
73
129
  it 'returns a value as it is when the given value is BigDecimal' do
74
- expect(subject.call(BigDecimal(0))).to eq(BigDecimal(0))
130
+ expect(subject.call(BigDecimal('0'))).to eq(BigDecimal('0'))
75
131
  end
76
132
  end
77
133
  end
@@ -20,13 +20,13 @@ describe Grape::Validations::Types do
20
20
  Date, DateTime, Time
21
21
  ].each do |type|
22
22
  it "recognizes #{type} as a primitive" do
23
- expect(described_class.primitive?(type)).to be_truthy
23
+ expect(described_class).to be_primitive(type)
24
24
  end
25
25
  end
26
26
 
27
27
  it 'identifies unknown types' do
28
- expect(described_class.primitive?(Object)).to be_falsy
29
- expect(described_class.primitive?(TypesSpec::FooType)).to be_falsy
28
+ expect(described_class).not_to be_primitive(Object)
29
+ expect(described_class).not_to be_primitive(TypesSpec::FooType)
30
30
  end
31
31
  end
32
32
 
@@ -35,7 +35,7 @@ describe Grape::Validations::Types do
35
35
  Hash, Array, Set
36
36
  ].each do |type|
37
37
  it "recognizes #{type} as a structure" do
38
- expect(described_class.structure?(type)).to be_truthy
38
+ expect(described_class).to be_structure(type)
39
39
  end
40
40
  end
41
41
  end
@@ -45,22 +45,22 @@ describe Grape::Validations::Types do
45
45
  JSON, Array[JSON], File, Rack::Multipart::UploadedFile
46
46
  ].each do |type|
47
47
  it "provides special handling for #{type.inspect}" do
48
- expect(described_class.special?(type)).to be_truthy
48
+ expect(described_class).to be_special(type)
49
49
  end
50
50
  end
51
51
  end
52
52
 
53
53
  describe '::custom?' do
54
54
  it 'returns false if the type does not respond to :parse' do
55
- expect(described_class.custom?(Object)).to be_falsy
55
+ expect(described_class).not_to be_custom(Object)
56
56
  end
57
57
 
58
58
  it 'returns true if the type responds to :parse with one argument' do
59
- expect(described_class.custom?(TypesSpec::FooType)).to be_truthy
59
+ expect(described_class).to be_custom(TypesSpec::FooType)
60
60
  end
61
61
 
62
62
  it 'returns false if the type\'s #parse method takes other than one argument' do
63
- expect(described_class.custom?(TypesSpec::BarType)).to be_falsy
63
+ expect(described_class).not_to be_custom(TypesSpec::BarType)
64
64
  end
65
65
  end
66
66