grape 1.3.3 → 1.6.2

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