grape 0.9.0 → 0.10.0

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.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -66
  3. data/.rubocop_todo.yml +78 -17
  4. data/.travis.yml +7 -3
  5. data/Appraisals +7 -0
  6. data/CHANGELOG.md +24 -0
  7. data/CONTRIBUTING.md +7 -0
  8. data/Gemfile +1 -7
  9. data/Guardfile +1 -1
  10. data/README.md +560 -94
  11. data/RELEASING.md +1 -1
  12. data/Rakefile +10 -11
  13. data/UPGRADING.md +211 -3
  14. data/gemfiles/rails_3.gemfile +14 -0
  15. data/gemfiles/rails_4.gemfile +14 -0
  16. data/grape.gemspec +10 -9
  17. data/lib/backports/active_support/deep_dup.rb +49 -0
  18. data/lib/backports/active_support/duplicable.rb +88 -0
  19. data/lib/grape.rb +29 -2
  20. data/lib/grape/api.rb +59 -65
  21. data/lib/grape/dsl/api.rb +19 -0
  22. data/lib/grape/dsl/callbacks.rb +6 -4
  23. data/lib/grape/dsl/configuration.rb +49 -5
  24. data/lib/grape/dsl/helpers.rb +7 -8
  25. data/lib/grape/dsl/inside_route.rb +22 -10
  26. data/lib/grape/dsl/middleware.rb +5 -5
  27. data/lib/grape/dsl/parameters.rb +6 -2
  28. data/lib/grape/dsl/request_response.rb +23 -20
  29. data/lib/grape/dsl/routing.rb +52 -49
  30. data/lib/grape/dsl/settings.rb +110 -0
  31. data/lib/grape/dsl/validations.rb +14 -6
  32. data/lib/grape/endpoint.rb +104 -88
  33. data/lib/grape/exceptions/base.rb +2 -2
  34. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  35. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  36. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  37. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
  38. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  39. data/lib/grape/exceptions/missing_option.rb +1 -1
  40. data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
  41. data/lib/grape/exceptions/unknown_options.rb +1 -1
  42. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  43. data/lib/grape/exceptions/validation.rb +1 -1
  44. data/lib/grape/exceptions/validation_errors.rb +2 -2
  45. data/lib/grape/formatter/serializable_hash.rb +1 -1
  46. data/lib/grape/formatter/xml.rb +1 -1
  47. data/lib/grape/locale/en.yml +2 -0
  48. data/lib/grape/middleware/auth/dsl.rb +26 -21
  49. data/lib/grape/middleware/auth/strategies.rb +1 -1
  50. data/lib/grape/middleware/auth/strategy_info.rb +0 -2
  51. data/lib/grape/middleware/base.rb +2 -2
  52. data/lib/grape/middleware/error.rb +1 -1
  53. data/lib/grape/middleware/formatter.rb +5 -5
  54. data/lib/grape/middleware/versioner.rb +1 -1
  55. data/lib/grape/middleware/versioner/header.rb +3 -3
  56. data/lib/grape/middleware/versioner/param.rb +2 -2
  57. data/lib/grape/middleware/versioner/path.rb +1 -1
  58. data/lib/grape/namespace.rb +1 -1
  59. data/lib/grape/path.rb +9 -3
  60. data/lib/grape/util/content_types.rb +16 -8
  61. data/lib/grape/util/inheritable_setting.rb +74 -0
  62. data/lib/grape/util/inheritable_values.rb +51 -0
  63. data/lib/grape/util/stackable_values.rb +52 -0
  64. data/lib/grape/util/strict_hash_configuration.rb +106 -0
  65. data/lib/grape/validations.rb +0 -220
  66. data/lib/grape/validations/attributes_iterator.rb +21 -0
  67. data/lib/grape/validations/params_scope.rb +176 -0
  68. data/lib/grape/validations/validators/all_or_none.rb +20 -0
  69. data/lib/grape/validations/validators/allow_blank.rb +30 -0
  70. data/lib/grape/validations/validators/at_least_one_of.rb +20 -0
  71. data/lib/grape/validations/validators/base.rb +37 -0
  72. data/lib/grape/validations/{coerce.rb → validators/coerce.rb} +3 -3
  73. data/lib/grape/validations/{default.rb → validators/default.rb} +1 -1
  74. data/lib/grape/validations/validators/exactly_one_of.rb +20 -0
  75. data/lib/grape/validations/validators/multiple_params_base.rb +26 -0
  76. data/lib/grape/validations/validators/mutual_exclusion.rb +25 -0
  77. data/lib/grape/validations/{presence.rb → validators/presence.rb} +2 -2
  78. data/lib/grape/validations/validators/regexp.rb +12 -0
  79. data/lib/grape/validations/validators/values.rb +26 -0
  80. data/lib/grape/version.rb +1 -1
  81. data/spec/grape/api_spec.rb +522 -343
  82. data/spec/grape/dsl/callbacks_spec.rb +4 -4
  83. data/spec/grape/dsl/configuration_spec.rb +48 -9
  84. data/spec/grape/dsl/helpers_spec.rb +6 -13
  85. data/spec/grape/dsl/inside_route_spec.rb +43 -4
  86. data/spec/grape/dsl/middleware_spec.rb +1 -10
  87. data/spec/grape/dsl/parameters_spec.rb +8 -1
  88. data/spec/grape/dsl/request_response_spec.rb +16 -22
  89. data/spec/grape/dsl/routing_spec.rb +21 -5
  90. data/spec/grape/dsl/settings_spec.rb +219 -0
  91. data/spec/grape/dsl/validations_spec.rb +8 -11
  92. data/spec/grape/endpoint_spec.rb +115 -86
  93. data/spec/grape/entity_spec.rb +33 -33
  94. data/spec/grape/exceptions/invalid_formatter_spec.rb +3 -5
  95. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +4 -6
  96. data/spec/grape/exceptions/missing_mime_type_spec.rb +5 -6
  97. data/spec/grape/exceptions/missing_option_spec.rb +3 -5
  98. data/spec/grape/exceptions/unknown_options_spec.rb +3 -5
  99. data/spec/grape/exceptions/unknown_validator_spec.rb +3 -5
  100. data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
  101. data/spec/grape/loading_spec.rb +44 -0
  102. data/spec/grape/middleware/auth/base_spec.rb +0 -4
  103. data/spec/grape/middleware/auth/dsl_spec.rb +2 -4
  104. data/spec/grape/middleware/auth/strategies_spec.rb +5 -6
  105. data/spec/grape/middleware/exception_spec.rb +8 -10
  106. data/spec/grape/middleware/formatter_spec.rb +13 -15
  107. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +10 -10
  108. data/spec/grape/middleware/versioner/header_spec.rb +25 -25
  109. data/spec/grape/middleware/versioner/param_spec.rb +15 -17
  110. data/spec/grape/middleware/versioner/path_spec.rb +1 -2
  111. data/spec/grape/middleware/versioner_spec.rb +0 -1
  112. data/spec/grape/path_spec.rb +66 -45
  113. data/spec/grape/util/inheritable_setting_spec.rb +217 -0
  114. data/spec/grape/util/inheritable_values_spec.rb +63 -0
  115. data/spec/grape/util/stackable_values_spec.rb +115 -0
  116. data/spec/grape/util/strict_hash_configuration_spec.rb +38 -0
  117. data/spec/grape/validations/attributes_iterator_spec.rb +4 -0
  118. data/spec/grape/validations/params_scope_spec.rb +57 -0
  119. data/spec/grape/validations/validators/all_or_none_spec.rb +60 -0
  120. data/spec/grape/validations/validators/allow_blank_spec.rb +170 -0
  121. data/spec/grape/validations/{at_least_one_of_spec.rb → validators/at_least_one_of_spec.rb} +7 -3
  122. data/spec/grape/validations/{coerce_spec.rb → validators/coerce_spec.rb} +8 -11
  123. data/spec/grape/validations/{default_spec.rb → validators/default_spec.rb} +7 -9
  124. data/spec/grape/validations/{exactly_one_of_spec.rb → validators/exactly_one_of_spec.rb} +15 -11
  125. data/spec/grape/validations/{mutual_exclusion_spec.rb → validators/mutual_exclusion_spec.rb} +11 -9
  126. data/spec/grape/validations/{presence_spec.rb → validators/presence_spec.rb} +30 -30
  127. data/spec/grape/validations/{regexp_spec.rb → validators/regexp_spec.rb} +2 -4
  128. data/spec/grape/validations/{values_spec.rb → validators/values_spec.rb} +95 -23
  129. data/spec/grape/validations/{zh-CN.yml → validators/zh-CN.yml} +0 -0
  130. data/spec/grape/validations_spec.rb +335 -70
  131. data/spec/shared/versioning_examples.rb +7 -8
  132. data/spec/spec_helper.rb +2 -0
  133. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  134. data/spec/support/content_type_helpers.rb +1 -1
  135. data/spec/support/versioned_helpers.rb +3 -3
  136. metadata +80 -33
  137. data/lib/grape/util/deep_merge.rb +0 -23
  138. data/lib/grape/util/hash_stack.rb +0 -120
  139. data/lib/grape/validations/at_least_one_of.rb +0 -25
  140. data/lib/grape/validations/exactly_one_of.rb +0 -26
  141. data/lib/grape/validations/mutual_exclusion.rb +0 -25
  142. data/lib/grape/validations/regexp.rb +0 -12
  143. data/lib/grape/validations/values.rb +0 -23
  144. data/spec/grape/util/hash_stack_spec.rb +0 -132
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ module Grape
3
+ module Util
4
+ describe InheritableValues do
5
+ let(:parent){ InheritableValues.new }
6
+ subject { InheritableValues.new(parent) }
7
+
8
+ describe '#delete' do
9
+ it 'deletes a key' do
10
+ subject[:some_thing] = :new_foo_bar
11
+ subject.delete :some_thing
12
+ expect(subject[:some_thing]).to be_nil
13
+ end
14
+
15
+ it 'does not delete parent values' do
16
+ parent[:some_thing] = :foo
17
+ subject[:some_thing] = :new_foo_bar
18
+ subject.delete :some_thing
19
+ expect(subject[:some_thing]).to eq :foo
20
+ end
21
+ end
22
+
23
+ describe '#[]' do
24
+ it 'returns a value' do
25
+ subject[:some_thing] = :foo
26
+ expect(subject[:some_thing]).to eq :foo
27
+ end
28
+
29
+ it 'returns parent value when no value is set' do
30
+ parent[:some_thing] = :foo
31
+ expect(subject[:some_thing]).to eq :foo
32
+ end
33
+
34
+ it 'overwrites parent value with the current one' do
35
+ parent[:some_thing] = :foo
36
+ subject[:some_thing] = :foo_bar
37
+ expect(subject[:some_thing]).to eq :foo_bar
38
+ end
39
+
40
+ it 'parent values are not changed' do
41
+ parent[:some_thing] = :foo
42
+ subject[:some_thing] = :foo_bar
43
+ expect(parent[:some_thing]).to eq :foo
44
+ end
45
+ end
46
+
47
+ describe '#[]=' do
48
+ it 'sets a value' do
49
+ subject[:some_thing] = :foo
50
+ expect(subject[:some_thing]).to eq :foo
51
+ end
52
+ end
53
+
54
+ describe '#to_hash' do
55
+ it 'returns a Hash representation' do
56
+ parent[:some_thing] = :foo
57
+ subject[:some_thing_more] = :foo_bar
58
+ expect(subject.to_hash).to eq(some_thing: :foo, some_thing_more: :foo_bar)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+ module Grape
3
+ module Util
4
+ describe StackableValues do
5
+ let(:parent){ StackableValues.new }
6
+ subject { StackableValues.new(parent) }
7
+
8
+ describe '#keys' do
9
+ it 'returns all key' do
10
+ subject[:some_thing] = :foo_bar
11
+ subject[:some_thing_else] = :foo_bar
12
+ expect(subject.keys).to eq [:some_thing, :some_thing_else].sort
13
+ end
14
+
15
+ it 'returns merged keys with parent' do
16
+ parent[:some_thing] = :foo
17
+ parent[:some_thing_else] = :foo
18
+
19
+ subject[:some_thing] = :foo_bar
20
+ subject[:some_thing_more] = :foo_bar
21
+
22
+ expect(subject.keys).to eq [:some_thing, :some_thing_else, :some_thing_more].sort
23
+ end
24
+ end
25
+
26
+ describe '#delete' do
27
+ it 'deletes a key' do
28
+ subject[:some_thing] = :new_foo_bar
29
+ subject.delete :some_thing
30
+ expect(subject[:some_thing]).to eq []
31
+ end
32
+
33
+ it 'does not delete parent values' do
34
+ parent[:some_thing] = :foo
35
+ subject[:some_thing] = :new_foo_bar
36
+ subject.delete :some_thing
37
+ expect(subject[:some_thing]).to eq [:foo]
38
+ end
39
+ end
40
+
41
+ describe '#[]' do
42
+ it 'returns an array of values' do
43
+ subject[:some_thing] = :foo
44
+ expect(subject[:some_thing]).to eq [:foo]
45
+ end
46
+
47
+ it 'returns parent value when no value is set' do
48
+ parent[:some_thing] = :foo
49
+ expect(subject[:some_thing]).to eq [:foo]
50
+ end
51
+
52
+ it 'combines parent and actual values' do
53
+ parent[:some_thing] = :foo
54
+ subject[:some_thing] = :foo_bar
55
+ expect(subject[:some_thing]).to eq [:foo, :foo_bar]
56
+ end
57
+
58
+ it 'parent values are not changed' do
59
+ parent[:some_thing] = :foo
60
+ subject[:some_thing] = :foo_bar
61
+ expect(parent[:some_thing]).to eq [:foo]
62
+ end
63
+ end
64
+
65
+ describe '#[]=' do
66
+ it 'sets a value' do
67
+ subject[:some_thing] = :foo
68
+ expect(subject[:some_thing]).to eq [:foo]
69
+ end
70
+
71
+ it 'pushes further values' do
72
+ subject[:some_thing] = :foo
73
+ subject[:some_thing] = :bar
74
+ expect(subject[:some_thing]).to eq [:foo, :bar]
75
+ end
76
+
77
+ it 'can handle array values' do
78
+ subject[:some_thing] = :foo
79
+ subject[:some_thing] = [:bar, :more]
80
+ expect(subject[:some_thing]).to eq [:foo, [:bar, :more]]
81
+
82
+ parent[:some_thing_else] = [:foo, :bar]
83
+ subject[:some_thing_else] = [:some, :bar, :foo]
84
+
85
+ expect(subject[:some_thing_else]).to eq [[:foo, :bar], [:some, :bar, :foo]]
86
+ end
87
+ end
88
+
89
+ describe '#to_hash' do
90
+ it 'returns a Hash representation' do
91
+ parent[:some_thing] = :foo
92
+ subject[:some_thing] = [:bar, :more]
93
+ subject[:some_thing_more] = :foo_bar
94
+ expect(subject.to_hash).to eq(some_thing: [:foo, [:bar, :more]], some_thing_more: [:foo_bar])
95
+ end
96
+ end
97
+
98
+ describe '#clone' do
99
+ let(:obj_cloned) { subject.clone }
100
+ it 'copies all values' do
101
+ parent = StackableValues.new
102
+ child = StackableValues.new parent
103
+ grandchild = StackableValues.new child
104
+
105
+ parent[:some_thing] = :foo
106
+ child[:some_thing] = [:bar, :more]
107
+ grandchild[:some_thing] = :grand_foo_bar
108
+ grandchild[:some_thing_more] = :foo_bar
109
+
110
+ expect(grandchild.clone.to_hash).to eq(some_thing: [:foo, [:bar, :more], :grand_foo_bar], some_thing_more: [:foo_bar])
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ module Grape
3
+ module Util
4
+ describe 'StrictHashConfiguration' do
5
+ subject do
6
+ Class.new do
7
+ include Grape::Util::StrictHashConfiguration.module(:config1, :config2, config3: [:config4], config5: [config6: [:config7, :config8]])
8
+ end
9
+ end
10
+
11
+ it 'set nested configs' do
12
+ subject.configure do
13
+ config1 'alpha'
14
+ config2 'beta'
15
+
16
+ config3 do
17
+ config4 'gamma'
18
+ end
19
+
20
+ local_var = 8
21
+
22
+ config5 do
23
+ config6 do
24
+ config7 7
25
+ config8 local_var
26
+ end
27
+ end
28
+ end
29
+
30
+ expect(subject.settings).to eq(config1: 'alpha',
31
+ config2: 'beta',
32
+ config3: { config4: 'gamma' },
33
+ config5: { config6: { config7: 7, config8: 8 } }
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::AttributesIterator do
4
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::ParamsScope do
4
+ subject do
5
+ Class.new(Grape::API)
6
+ end
7
+
8
+ def app
9
+ subject
10
+ end
11
+
12
+ context 'setting description' do
13
+ [:desc, :description].each do |description_type|
14
+ it "allows setting #{description_type}" do
15
+ subject.params do
16
+ requires :int, type: Integer, description_type => 'My very nice integer'
17
+ end
18
+ subject.get '/single' do
19
+ 'int works'
20
+ end
21
+ get '/single', int: 420
22
+ expect(last_response.status).to eq(200)
23
+ expect(last_response.body).to eq('int works')
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'array without coerce type explicitly given' do
29
+ it 'sets the type based on first element' do
30
+ subject.params do
31
+ requires :periods, type: Array, values: -> { %w(day month) }
32
+ end
33
+ subject.get('/required') { 'required works' }
34
+
35
+ get '/required', periods: %w(day month)
36
+ expect(last_response.status).to eq(200)
37
+ expect(last_response.body).to eq('required works')
38
+ end
39
+
40
+ it 'fails to call API without Array type' do
41
+ subject.params do
42
+ requires :periods, type: Array, values: -> { %w(day month) }
43
+ end
44
+ subject.get('/required') { 'required works' }
45
+
46
+ get '/required', periods: 'day'
47
+ expect(last_response.status).to eq(400)
48
+ expect(last_response.body).to eq('periods is invalid')
49
+ end
50
+
51
+ it 'raises exception when values are of different type' do
52
+ expect do
53
+ subject.params { requires :numbers, type: Array, values: [1, 'definitely not a number', 3] }
54
+ end.to raise_error Grape::Exceptions::IncompatibleOptionValues
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::AllOrNoneOfValidator do
4
+ describe '#validate!' do
5
+ let(:scope) do
6
+ Struct.new(:opts) do
7
+ def params(arg)
8
+ arg
9
+ end
10
+
11
+ def required?; end
12
+ end
13
+ end
14
+ let(:all_or_none_params) { [:beer, :wine, :grapefruit] }
15
+ let(:validator) { described_class.new(all_or_none_params, {}, false, scope.new) }
16
+
17
+ context 'when all restricted params are present' do
18
+ let(:params) { { beer: true, wine: true, grapefruit: true } }
19
+
20
+ it 'does not raise a validation exception' do
21
+ expect(validator.validate!(params)).to eql params
22
+ end
23
+
24
+ context 'mixed with other params' do
25
+ let(:mixed_params) { params.merge!(other: true, andanother: true) }
26
+
27
+ it 'does not raise a validation exception' do
28
+ expect(validator.validate!(mixed_params)).to eql mixed_params
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'when none of the restricted params is selected' do
34
+ let(:params) { { somethingelse: true } }
35
+
36
+ it 'does not raise a validation exception' do
37
+ expect(validator.validate!(params)).to eql params
38
+ end
39
+ end
40
+
41
+ context 'when only a subset of restricted params are present' do
42
+ let(:params) { { beer: true, grapefruit: true } }
43
+
44
+ it 'raises a validation exception' do
45
+ expect do
46
+ validator.validate! params
47
+ end.to raise_error(Grape::Exceptions::Validation)
48
+ end
49
+ context 'mixed with other params' do
50
+ let(:mixed_params) { params.merge!(other: true, andanother: true) }
51
+
52
+ it 'raise a validation exception' do
53
+ expect do
54
+ validator.validate! params
55
+ end.to raise_error(Grape::Exceptions::Validation)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,170 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::AllowBlankValidator do
4
+ module ValidationsSpec
5
+ module AllowBlankValidatorSpec
6
+ class API < Grape::API
7
+ default_format :json
8
+
9
+ params do
10
+ requires :name, allow_blank: false
11
+ end
12
+ get
13
+
14
+ params do
15
+ optional :name, allow_blank: false
16
+ end
17
+ get '/disallow_blank_optional_param'
18
+
19
+ params do
20
+ requires :name, allow_blank: true
21
+ end
22
+ get '/allow_blank'
23
+
24
+ params do
25
+ optional :user, type: Hash do
26
+ requires :name, allow_blank: false
27
+ end
28
+ end
29
+ get '/disallow_blank_required_param_in_an_optional_group'
30
+
31
+ params do
32
+ optional :user, type: Hash do
33
+ optional :name, allow_blank: false
34
+ requires :age
35
+ end
36
+ end
37
+ get '/disallow_blank_optional_param_in_an_optional_group'
38
+
39
+ params do
40
+ requires :user, type: Hash do
41
+ requires :name, allow_blank: false
42
+ end
43
+ end
44
+ get '/disallow_blank_required_param_in_a_required_group'
45
+
46
+ params do
47
+ requires :user, type: Hash do
48
+ requires :name, allow_blank: false
49
+ end
50
+ end
51
+ get '/disallow_string_value_in_a_required_hash_group'
52
+
53
+ params do
54
+ requires :user, type: Hash do
55
+ optional :name, allow_blank: false
56
+ end
57
+ end
58
+ get '/disallow_blank_optional_param_in_a_required_group'
59
+
60
+ params do
61
+ optional :user, type: Hash do
62
+ optional :name, allow_blank: false
63
+ end
64
+ end
65
+ get '/disallow_string_value_in_an_optional_hash_group'
66
+ end
67
+ end
68
+ end
69
+
70
+ def app
71
+ ValidationsSpec::AllowBlankValidatorSpec::API
72
+ end
73
+
74
+ context 'invalid input' do
75
+ it 'refuses empty string' do
76
+ get '/', name: ''
77
+ expect(last_response.status).to eq(400)
78
+ end
79
+
80
+ it 'refuses only whitespaces' do
81
+ get '/', name: ' '
82
+ expect(last_response.status).to eq(400)
83
+
84
+ get '/', name: " \n "
85
+ expect(last_response.status).to eq(400)
86
+
87
+ get '/', name: "\n"
88
+ expect(last_response.status).to eq(400)
89
+ end
90
+
91
+ it 'refuses nil' do
92
+ get '/', name: nil
93
+ expect(last_response.status).to eq(400)
94
+ end
95
+ end
96
+
97
+ context 'valid input' do
98
+ it 'accepts valid input' do
99
+ get '/', name: 'bob'
100
+ expect(last_response.status).to eq(200)
101
+ end
102
+
103
+ it 'accepts empty input when allow_blank is false' do
104
+ get '/allow_blank', name: ''
105
+ expect(last_response.status).to eq(200)
106
+ end
107
+ end
108
+
109
+ context 'in an optional group' do
110
+ context 'as a required param' do
111
+ it 'accepts a missing group, even with a disallwed blank param' do
112
+ get '/disallow_blank_required_param_in_an_optional_group'
113
+ expect(last_response.status).to eq(200)
114
+ end
115
+
116
+ it 'refuses a blank value in an existing group' do
117
+ get '/disallow_blank_required_param_in_an_optional_group', user: { name: '' }
118
+ expect(last_response.status).to eq(400)
119
+ end
120
+ end
121
+
122
+ context 'as an optional param' do
123
+ it 'accepts a missing group, even with a disallwed blank param' do
124
+ get '/disallow_blank_optional_param_in_an_optional_group'
125
+ expect(last_response.status).to eq(200)
126
+ end
127
+
128
+ it 'accepts a nested missing optional value' do
129
+ get '/disallow_blank_optional_param_in_an_optional_group', user: { age: '29' }
130
+ expect(last_response.status).to eq(200)
131
+ end
132
+
133
+ it 'refuses a blank existing value in an existing scope' do
134
+ get '/disallow_blank_optional_param_in_an_optional_group', user: { age: '29', name: '' }
135
+ expect(last_response.status).to eq(400)
136
+ end
137
+ end
138
+ end
139
+
140
+ context 'in a required group' do
141
+ context 'as a required param' do
142
+ it 'refuses a blank value in a required existing group' do
143
+ get '/disallow_blank_required_param_in_a_required_group', user: { name: '' }
144
+ expect(last_response.status).to eq(400)
145
+ end
146
+
147
+ it 'refuses a string value in a required hash group' do
148
+ get '/disallow_string_value_in_a_required_hash_group', user: ''
149
+ expect(last_response.status).to eq(400)
150
+ end
151
+ end
152
+
153
+ context 'as an optional param' do
154
+ it 'accepts a nested missing value' do
155
+ get '/disallow_blank_optional_param_in_a_required_group', user: { age: '29' }
156
+ expect(last_response.status).to eq(200)
157
+ end
158
+
159
+ it 'refuses a blank existing value in an existing scope' do
160
+ get '/disallow_blank_optional_param_in_a_required_group', user: { age: '29', name: '' }
161
+ expect(last_response.status).to eq(400)
162
+ end
163
+
164
+ it 'refuses a string value in an optional hash group' do
165
+ get '/disallow_string_value_in_an_optional_hash_group', user: ''
166
+ expect(last_response.status).to eq(400)
167
+ end
168
+ end
169
+ end
170
+ end