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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -66
- data/.rubocop_todo.yml +78 -17
- data/.travis.yml +7 -3
- data/Appraisals +7 -0
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +7 -0
- data/Gemfile +1 -7
- data/Guardfile +1 -1
- data/README.md +560 -94
- data/RELEASING.md +1 -1
- data/Rakefile +10 -11
- data/UPGRADING.md +211 -3
- data/gemfiles/rails_3.gemfile +14 -0
- data/gemfiles/rails_4.gemfile +14 -0
- data/grape.gemspec +10 -9
- data/lib/backports/active_support/deep_dup.rb +49 -0
- data/lib/backports/active_support/duplicable.rb +88 -0
- data/lib/grape.rb +29 -2
- data/lib/grape/api.rb +59 -65
- data/lib/grape/dsl/api.rb +19 -0
- data/lib/grape/dsl/callbacks.rb +6 -4
- data/lib/grape/dsl/configuration.rb +49 -5
- data/lib/grape/dsl/helpers.rb +7 -8
- data/lib/grape/dsl/inside_route.rb +22 -10
- data/lib/grape/dsl/middleware.rb +5 -5
- data/lib/grape/dsl/parameters.rb +6 -2
- data/lib/grape/dsl/request_response.rb +23 -20
- data/lib/grape/dsl/routing.rb +52 -49
- data/lib/grape/dsl/settings.rb +110 -0
- data/lib/grape/dsl/validations.rb +14 -6
- data/lib/grape/endpoint.rb +104 -88
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/validation.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/xml.rb +1 -1
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/dsl.rb +26 -21
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/auth/strategy_info.rb +0 -2
- data/lib/grape/middleware/base.rb +2 -2
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/formatter.rb +5 -5
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/path.rb +9 -3
- data/lib/grape/util/content_types.rb +16 -8
- data/lib/grape/util/inheritable_setting.rb +74 -0
- data/lib/grape/util/inheritable_values.rb +51 -0
- data/lib/grape/util/stackable_values.rb +52 -0
- data/lib/grape/util/strict_hash_configuration.rb +106 -0
- data/lib/grape/validations.rb +0 -220
- data/lib/grape/validations/attributes_iterator.rb +21 -0
- data/lib/grape/validations/params_scope.rb +176 -0
- data/lib/grape/validations/validators/all_or_none.rb +20 -0
- data/lib/grape/validations/validators/allow_blank.rb +30 -0
- data/lib/grape/validations/validators/at_least_one_of.rb +20 -0
- data/lib/grape/validations/validators/base.rb +37 -0
- data/lib/grape/validations/{coerce.rb → validators/coerce.rb} +3 -3
- data/lib/grape/validations/{default.rb → validators/default.rb} +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +20 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +26 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/{presence.rb → validators/presence.rb} +2 -2
- data/lib/grape/validations/validators/regexp.rb +12 -0
- data/lib/grape/validations/validators/values.rb +26 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +522 -343
- data/spec/grape/dsl/callbacks_spec.rb +4 -4
- data/spec/grape/dsl/configuration_spec.rb +48 -9
- data/spec/grape/dsl/helpers_spec.rb +6 -13
- data/spec/grape/dsl/inside_route_spec.rb +43 -4
- data/spec/grape/dsl/middleware_spec.rb +1 -10
- data/spec/grape/dsl/parameters_spec.rb +8 -1
- data/spec/grape/dsl/request_response_spec.rb +16 -22
- data/spec/grape/dsl/routing_spec.rb +21 -5
- data/spec/grape/dsl/settings_spec.rb +219 -0
- data/spec/grape/dsl/validations_spec.rb +8 -11
- data/spec/grape/endpoint_spec.rb +115 -86
- data/spec/grape/entity_spec.rb +33 -33
- data/spec/grape/exceptions/invalid_formatter_spec.rb +3 -5
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +4 -6
- data/spec/grape/exceptions/missing_mime_type_spec.rb +5 -6
- data/spec/grape/exceptions/missing_option_spec.rb +3 -5
- data/spec/grape/exceptions/unknown_options_spec.rb +3 -5
- data/spec/grape/exceptions/unknown_validator_spec.rb +3 -5
- data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
- data/spec/grape/loading_spec.rb +44 -0
- data/spec/grape/middleware/auth/base_spec.rb +0 -4
- data/spec/grape/middleware/auth/dsl_spec.rb +2 -4
- data/spec/grape/middleware/auth/strategies_spec.rb +5 -6
- data/spec/grape/middleware/exception_spec.rb +8 -10
- data/spec/grape/middleware/formatter_spec.rb +13 -15
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +10 -10
- data/spec/grape/middleware/versioner/header_spec.rb +25 -25
- data/spec/grape/middleware/versioner/param_spec.rb +15 -17
- data/spec/grape/middleware/versioner/path_spec.rb +1 -2
- data/spec/grape/middleware/versioner_spec.rb +0 -1
- data/spec/grape/path_spec.rb +66 -45
- data/spec/grape/util/inheritable_setting_spec.rb +217 -0
- data/spec/grape/util/inheritable_values_spec.rb +63 -0
- data/spec/grape/util/stackable_values_spec.rb +115 -0
- data/spec/grape/util/strict_hash_configuration_spec.rb +38 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +4 -0
- data/spec/grape/validations/params_scope_spec.rb +57 -0
- data/spec/grape/validations/validators/all_or_none_spec.rb +60 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +170 -0
- data/spec/grape/validations/{at_least_one_of_spec.rb → validators/at_least_one_of_spec.rb} +7 -3
- data/spec/grape/validations/{coerce_spec.rb → validators/coerce_spec.rb} +8 -11
- data/spec/grape/validations/{default_spec.rb → validators/default_spec.rb} +7 -9
- data/spec/grape/validations/{exactly_one_of_spec.rb → validators/exactly_one_of_spec.rb} +15 -11
- data/spec/grape/validations/{mutual_exclusion_spec.rb → validators/mutual_exclusion_spec.rb} +11 -9
- data/spec/grape/validations/{presence_spec.rb → validators/presence_spec.rb} +30 -30
- data/spec/grape/validations/{regexp_spec.rb → validators/regexp_spec.rb} +2 -4
- data/spec/grape/validations/{values_spec.rb → validators/values_spec.rb} +95 -23
- data/spec/grape/validations/{zh-CN.yml → validators/zh-CN.yml} +0 -0
- data/spec/grape/validations_spec.rb +335 -70
- data/spec/shared/versioning_examples.rb +7 -8
- data/spec/spec_helper.rb +2 -0
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/content_type_helpers.rb +1 -1
- data/spec/support/versioned_helpers.rb +3 -3
- metadata +80 -33
- data/lib/grape/util/deep_merge.rb +0 -23
- data/lib/grape/util/hash_stack.rb +0 -120
- data/lib/grape/validations/at_least_one_of.rb +0 -25
- data/lib/grape/validations/exactly_one_of.rb +0 -26
- data/lib/grape/validations/mutual_exclusion.rb +0 -25
- data/lib/grape/validations/regexp.rb +0 -12
- data/lib/grape/validations/values.rb +0 -23
- data/spec/grape/util/hash_stack_spec.rb +0 -132
| @@ -10,7 +10,6 @@ describe Grape::Validations::RegexpValidator do | |
| 10 10 | 
             
                      requires :name, regexp: /^[a-z]+$/
         | 
| 11 11 | 
             
                    end
         | 
| 12 12 | 
             
                    get do
         | 
| 13 | 
            -
             | 
| 14 13 | 
             
                    end
         | 
| 15 14 | 
             
                  end
         | 
| 16 15 | 
             
                end
         | 
| @@ -22,7 +21,7 @@ describe Grape::Validations::RegexpValidator do | |
| 22 21 |  | 
| 23 22 | 
             
              context 'invalid input' do
         | 
| 24 23 | 
             
                it 'refuses inapppopriate' do
         | 
| 25 | 
            -
                  get '/', name:  | 
| 24 | 
            +
                  get '/', name: 'invalid name'
         | 
| 26 25 | 
             
                  expect(last_response.status).to eq(400)
         | 
| 27 26 | 
             
                end
         | 
| 28 27 |  | 
| @@ -33,8 +32,7 @@ describe Grape::Validations::RegexpValidator do | |
| 33 32 | 
             
              end
         | 
| 34 33 |  | 
| 35 34 | 
             
              it 'accepts valid input' do
         | 
| 36 | 
            -
                get '/', name:  | 
| 35 | 
            +
                get '/', name: 'bob'
         | 
| 37 36 | 
             
                expect(last_response.status).to eq(200)
         | 
| 38 37 | 
             
              end
         | 
| 39 | 
            -
             | 
| 40 38 | 
             
            end
         | 
| @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            require 'spec_helper'
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe Grape::Validations::ValuesValidator do
         | 
| 4 | 
            -
             | 
| 5 4 | 
             
              class ValuesModel
         | 
| 6 5 | 
             
                DEFAULT_VALUES = ['valid-type1', 'valid-type2', 'valid-type3']
         | 
| 7 6 | 
             
                class << self
         | 
| @@ -44,15 +43,36 @@ describe Grape::Validations::ValuesValidator do | |
| 44 43 | 
             
                    end
         | 
| 45 44 |  | 
| 46 45 | 
             
                    params do
         | 
| 47 | 
            -
                       | 
| 46 | 
            +
                      optional :type, values: ValuesModel.values, default: -> { ValuesModel.values.sample }
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    get '/default_lambda' do
         | 
| 49 | 
            +
                      { type: params[:type] }
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    params do
         | 
| 53 | 
            +
                      optional :type, values: -> { ValuesModel.values }, default: -> { ValuesModel.values.sample }
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                    get '/default_and_values_lambda' do
         | 
| 56 | 
            +
                      { type: params[:type] }
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    params do
         | 
| 60 | 
            +
                      requires :type, type: Integer, desc: 'An integer', values: [10, 11], default: 10
         | 
| 48 61 | 
             
                    end
         | 
| 49 62 | 
             
                    get '/values/coercion' do
         | 
| 50 63 | 
             
                      { type: params[:type] }
         | 
| 51 64 | 
             
                    end
         | 
| 52 65 |  | 
| 66 | 
            +
                    params do
         | 
| 67 | 
            +
                      requires :type, type: Array[Integer], desc: 'An integer', values: [10, 11], default: 10
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                    get '/values/array_coercion' do
         | 
| 70 | 
            +
                      { type: params[:type] }
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
             | 
| 53 73 | 
             
                    params do
         | 
| 54 74 | 
             
                      optional :optional do
         | 
| 55 | 
            -
                        requires :type, values:  | 
| 75 | 
            +
                        requires :type, values: %w(a b)
         | 
| 56 76 | 
             
                      end
         | 
| 57 77 | 
             
                    end
         | 
| 58 78 | 
             
                    get '/optional_with_required_values'
         | 
| @@ -65,22 +85,22 @@ describe Grape::Validations::ValuesValidator do | |
| 65 85 | 
             
              end
         | 
| 66 86 |  | 
| 67 87 | 
             
              it 'allows a valid value for a parameter' do
         | 
| 68 | 
            -
                get( | 
| 88 | 
            +
                get('/', type: 'valid-type1')
         | 
| 69 89 | 
             
                expect(last_response.status).to eq 200
         | 
| 70 | 
            -
                expect(last_response.body).to eq({ type:  | 
| 90 | 
            +
                expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
         | 
| 71 91 | 
             
              end
         | 
| 72 92 |  | 
| 73 93 | 
             
              it 'does not allow an invalid value for a parameter' do
         | 
| 74 | 
            -
                get( | 
| 94 | 
            +
                get('/', type: 'invalid-type')
         | 
| 75 95 | 
             
                expect(last_response.status).to eq 400
         | 
| 76 | 
            -
                expect(last_response.body).to eq({ error:  | 
| 96 | 
            +
                expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
         | 
| 77 97 | 
             
              end
         | 
| 78 98 |  | 
| 79 99 | 
             
              context 'nil value for a parameter' do
         | 
| 80 100 | 
             
                it 'does not allow for root params scope' do
         | 
| 81 | 
            -
                  get( | 
| 101 | 
            +
                  get('/', type: nil)
         | 
| 82 102 | 
             
                  expect(last_response.status).to eq 400
         | 
| 83 | 
            -
                  expect(last_response.body).to eq({ error:  | 
| 103 | 
            +
                  expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
         | 
| 84 104 | 
             
                end
         | 
| 85 105 |  | 
| 86 106 | 
             
                it 'allows for a required param in child scope' do
         | 
| @@ -90,15 +110,15 @@ describe Grape::Validations::ValuesValidator do | |
| 90 110 | 
             
              end
         | 
| 91 111 |  | 
| 92 112 | 
             
              it 'allows a valid default value' do
         | 
| 93 | 
            -
                get( | 
| 113 | 
            +
                get('/default/valid')
         | 
| 94 114 | 
             
                expect(last_response.status).to eq 200
         | 
| 95 | 
            -
                expect(last_response.body).to eq({ type:  | 
| 115 | 
            +
                expect(last_response.body).to eq({ type: 'valid-type2' }.to_json)
         | 
| 96 116 | 
             
              end
         | 
| 97 117 |  | 
| 98 118 | 
             
              it 'allows a proc for values' do
         | 
| 99 119 | 
             
                get('/lambda', type: 'valid-type1')
         | 
| 100 120 | 
             
                expect(last_response.status).to eq 200
         | 
| 101 | 
            -
                expect(last_response.body).to eq({ type:  | 
| 121 | 
            +
                expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
         | 
| 102 122 | 
             
              end
         | 
| 103 123 |  | 
| 104 124 | 
             
              it 'does not validate updated values without proc' do
         | 
| @@ -106,7 +126,7 @@ describe Grape::Validations::ValuesValidator do | |
| 106 126 |  | 
| 107 127 | 
             
                get('/', type: 'valid-type4')
         | 
| 108 128 | 
             
                expect(last_response.status).to eq 400
         | 
| 109 | 
            -
                expect(last_response.body).to eq({ error:  | 
| 129 | 
            +
                expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
         | 
| 110 130 | 
             
              end
         | 
| 111 131 |  | 
| 112 132 | 
             
              it 'validates against values in a proc' do
         | 
| @@ -114,39 +134,91 @@ describe Grape::Validations::ValuesValidator do | |
| 114 134 |  | 
| 115 135 | 
             
                get('/lambda', type: 'valid-type4')
         | 
| 116 136 | 
             
                expect(last_response.status).to eq 200
         | 
| 117 | 
            -
                expect(last_response.body).to eq({ type:  | 
| 137 | 
            +
                expect(last_response.body).to eq({ type: 'valid-type4' }.to_json)
         | 
| 118 138 | 
             
              end
         | 
| 119 139 |  | 
| 120 140 | 
             
              it 'does not allow an invalid value for a parameter using lambda' do
         | 
| 121 | 
            -
                get( | 
| 141 | 
            +
                get('/lambda', type: 'invalid-type')
         | 
| 122 142 | 
             
                expect(last_response.status).to eq 400
         | 
| 123 | 
            -
                expect(last_response.body).to eq({ error:  | 
| 143 | 
            +
                expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
         | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
              it 'validates default value from proc' do
         | 
| 147 | 
            +
                get('/default_lambda')
         | 
| 148 | 
            +
                expect(last_response.status).to eq 200
         | 
| 149 | 
            +
              end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              it 'validates default value from proc against values in a proc' do
         | 
| 152 | 
            +
                get('/default_and_values_lambda')
         | 
| 153 | 
            +
                expect(last_response.status).to eq 200
         | 
| 154 | 
            +
              end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
              it 'raises IncompatibleOptionValues on an invalid default value from proc' do
         | 
| 157 | 
            +
                subject = Class.new(Grape::API)
         | 
| 158 | 
            +
                expect do
         | 
| 159 | 
            +
                  subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: ValuesModel.values.sample + '_invalid' }
         | 
| 160 | 
            +
                end.to raise_error Grape::Exceptions::IncompatibleOptionValues
         | 
| 124 161 | 
             
              end
         | 
| 125 162 |  | 
| 126 163 | 
             
              it 'raises IncompatibleOptionValues on an invalid default value' do
         | 
| 127 164 | 
             
                subject = Class.new(Grape::API)
         | 
| 128 | 
            -
                expect  | 
| 165 | 
            +
                expect do
         | 
| 129 166 | 
             
                  subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: 'invalid-type' }
         | 
| 130 | 
            -
                 | 
| 167 | 
            +
                end.to raise_error Grape::Exceptions::IncompatibleOptionValues
         | 
| 131 168 | 
             
              end
         | 
| 132 169 |  | 
| 133 170 | 
             
              it 'raises IncompatibleOptionValues when type is incompatible with values array' do
         | 
| 134 171 | 
             
                subject = Class.new(Grape::API)
         | 
| 135 | 
            -
                expect  | 
| 172 | 
            +
                expect do
         | 
| 136 173 | 
             
                  subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], type: Symbol }
         | 
| 137 | 
            -
                 | 
| 174 | 
            +
                end.to raise_error Grape::Exceptions::IncompatibleOptionValues
         | 
| 138 175 | 
             
              end
         | 
| 139 176 |  | 
| 140 177 | 
             
              it 'allows values to be a kind of the coerced type not just an instance of it' do
         | 
| 141 | 
            -
                get( | 
| 178 | 
            +
                get('/values/coercion', type: 10)
         | 
| 142 179 | 
             
                expect(last_response.status).to eq 200
         | 
| 143 180 | 
             
                expect(last_response.body).to eq({ type: 10 }.to_json)
         | 
| 144 181 | 
             
              end
         | 
| 145 182 |  | 
| 183 | 
            +
              it 'allows values to be a kind of the coerced type in an array' do
         | 
| 184 | 
            +
                get('/values/array_coercion', type: [10])
         | 
| 185 | 
            +
                expect(last_response.status).to eq 200
         | 
| 186 | 
            +
                expect(last_response.body).to eq({ type: [10] }.to_json)
         | 
| 187 | 
            +
              end
         | 
| 188 | 
            +
             | 
| 146 189 | 
             
              it 'raises IncompatibleOptionValues when values contains a value that is not a kind of the type' do
         | 
| 147 190 | 
             
                subject = Class.new(Grape::API)
         | 
| 148 | 
            -
                expect  | 
| 191 | 
            +
                expect do
         | 
| 149 192 | 
             
                  subject.params { requires :type, values: [10.5, 11], type: Integer }
         | 
| 150 | 
            -
                 | 
| 193 | 
            +
                end.to raise_error Grape::Exceptions::IncompatibleOptionValues
         | 
| 194 | 
            +
              end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
              context 'with a lambda values' do
         | 
| 197 | 
            +
                subject do
         | 
| 198 | 
            +
                  Class.new(Grape::API) do
         | 
| 199 | 
            +
                    params do
         | 
| 200 | 
            +
                      optional :type, type: String, values: -> { [SecureRandom.uuid] }, default: -> { SecureRandom.uuid }
         | 
| 201 | 
            +
                    end
         | 
| 202 | 
            +
                    get '/random_values'
         | 
| 203 | 
            +
                  end
         | 
| 204 | 
            +
                end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                def app
         | 
| 207 | 
            +
                  subject
         | 
| 208 | 
            +
                end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                before do
         | 
| 211 | 
            +
                  expect(SecureRandom).to receive(:uuid).and_return('foo').once
         | 
| 212 | 
            +
                end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                it 'only evaluates values dynamically with each request' do
         | 
| 215 | 
            +
                  get '/random_values', type: 'foo'
         | 
| 216 | 
            +
                  expect(last_response.status).to eq 200
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                it 'chooses default' do
         | 
| 220 | 
            +
                  get '/random_values'
         | 
| 221 | 
            +
                  expect(last_response.status).to eq 200
         | 
| 222 | 
            +
                end
         | 
| 151 223 | 
             
              end
         | 
| 152 224 | 
             
            end
         | 
| 
            File without changes
         | 
| @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            require 'spec_helper'
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe Grape::Validations do
         | 
| 4 | 
            -
             | 
| 5 4 | 
             
              subject { Class.new(Grape::API) }
         | 
| 6 5 |  | 
| 7 6 | 
             
              def app
         | 
| @@ -44,18 +43,17 @@ describe Grape::Validations do | |
| 44 43 | 
             
                    subject.params do
         | 
| 45 44 | 
             
                      optional :some_param
         | 
| 46 45 | 
             
                    end
         | 
| 47 | 
            -
                    expect(subject. | 
| 46 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([:some_param])
         | 
| 48 47 | 
             
                  end
         | 
| 49 48 | 
             
                end
         | 
| 50 49 |  | 
| 51 50 | 
             
                context 'required' do
         | 
| 52 51 | 
             
                  before do
         | 
| 53 52 | 
             
                    subject.params do
         | 
| 54 | 
            -
                      requires :key
         | 
| 55 | 
            -
                    end
         | 
| 56 | 
            -
                    subject.get '/required' do
         | 
| 57 | 
            -
                      'required works'
         | 
| 53 | 
            +
                      requires :key, type: String
         | 
| 58 54 | 
             
                    end
         | 
| 55 | 
            +
                    subject.get('/required') { 'required works' }
         | 
| 56 | 
            +
                    subject.put('/required') { { key: params[:key] }.to_json }
         | 
| 59 57 | 
             
                  end
         | 
| 60 58 |  | 
| 61 59 | 
             
                  it 'errors when param not present' do
         | 
| @@ -74,7 +72,13 @@ describe Grape::Validations do | |
| 74 72 | 
             
                    subject.params do
         | 
| 75 73 | 
             
                      requires :some_param
         | 
| 76 74 | 
             
                    end
         | 
| 77 | 
            -
                    expect(subject. | 
| 75 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([:some_param])
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  it 'works when required field is present but nil' do
         | 
| 79 | 
            +
                    put '/required', { key: nil }.to_json, 'CONTENT_TYPE' => 'application/json'
         | 
| 80 | 
            +
                    expect(last_response.status).to eq(200)
         | 
| 81 | 
            +
                    expect(JSON.parse(last_response.body)).to eq('key' => nil)
         | 
| 78 82 | 
             
                  end
         | 
| 79 83 | 
             
                end
         | 
| 80 84 |  | 
| @@ -97,7 +101,7 @@ describe Grape::Validations do | |
| 97 101 |  | 
| 98 102 | 
             
                  it 'adds entity documentation to declared params' do
         | 
| 99 103 | 
             
                    define_requires_all
         | 
| 100 | 
            -
                    expect(subject. | 
| 104 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
         | 
| 101 105 | 
             
                  end
         | 
| 102 106 |  | 
| 103 107 | 
             
                  it 'errors when required_field is not present' do
         | 
| @@ -132,7 +136,7 @@ describe Grape::Validations do | |
| 132 136 |  | 
| 133 137 | 
             
                  it 'adds entity documentation to declared params' do
         | 
| 134 138 | 
             
                    define_requires_none
         | 
| 135 | 
            -
                    expect(subject. | 
| 139 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
         | 
| 136 140 | 
             
                  end
         | 
| 137 141 |  | 
| 138 142 | 
             
                  it 'errors when required_field is not present' do
         | 
| @@ -162,7 +166,7 @@ describe Grape::Validations do | |
| 162 166 |  | 
| 163 167 | 
             
                    it 'adds only the entity documentation to declared params, nothing more' do
         | 
| 164 168 | 
             
                      define_requires_all
         | 
| 165 | 
            -
                      expect(subject. | 
| 169 | 
            +
                      expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
         | 
| 166 170 | 
             
                    end
         | 
| 167 171 | 
             
                  end
         | 
| 168 172 |  | 
| @@ -190,9 +194,8 @@ describe Grape::Validations do | |
| 190 194 | 
             
                        requires :key
         | 
| 191 195 | 
             
                      end
         | 
| 192 196 | 
             
                    end
         | 
| 193 | 
            -
                    subject.get | 
| 194 | 
            -
             | 
| 195 | 
            -
                    end
         | 
| 197 | 
            +
                    subject.get('/required') { 'required works' }
         | 
| 198 | 
            +
                    subject.put('/required') { { items: params[:items] }.to_json }
         | 
| 196 199 | 
             
                  end
         | 
| 197 200 |  | 
| 198 201 | 
             
                  it 'errors when param not present' do
         | 
| @@ -201,8 +204,8 @@ describe Grape::Validations do | |
| 201 204 | 
             
                    expect(last_response.body).to eq('items is missing')
         | 
| 202 205 | 
             
                  end
         | 
| 203 206 |  | 
| 204 | 
            -
                  it  | 
| 205 | 
            -
                    get '/required', items:  | 
| 207 | 
            +
                  it 'errors when param is not an Array' do
         | 
| 208 | 
            +
                    get '/required', items: 'hello'
         | 
| 206 209 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 207 210 | 
             
                    expect(last_response.body).to eq('items is invalid, items[key] is missing')
         | 
| 208 211 |  | 
| @@ -217,14 +220,10 @@ describe Grape::Validations do | |
| 217 220 | 
             
                    expect(last_response.body).to eq('required works')
         | 
| 218 221 | 
             
                  end
         | 
| 219 222 |  | 
| 220 | 
            -
                  it "doesn't  | 
| 221 | 
            -
                     | 
| 222 | 
            -
             | 
| 223 | 
            -
             | 
| 224 | 
            -
                          requires :key
         | 
| 225 | 
            -
                        end
         | 
| 226 | 
            -
                      end
         | 
| 227 | 
            -
                    }.to raise_error ArgumentError
         | 
| 223 | 
            +
                  it "doesn't throw a missing param when param is present but empty" do
         | 
| 224 | 
            +
                    put '/required', { items: [] }.to_json, 'CONTENT_TYPE' => 'application/json'
         | 
| 225 | 
            +
                    expect(last_response.status).to eq(200)
         | 
| 226 | 
            +
                    expect(JSON.parse(last_response.body)).to eq('items' => [])
         | 
| 228 227 | 
             
                  end
         | 
| 229 228 |  | 
| 230 229 | 
             
                  it 'adds to declared parameters' do
         | 
| @@ -233,7 +232,7 @@ describe Grape::Validations do | |
| 233 232 | 
             
                        requires :key
         | 
| 234 233 | 
             
                      end
         | 
| 235 234 | 
             
                    end
         | 
| 236 | 
            -
                    expect(subject. | 
| 235 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
         | 
| 237 236 | 
             
                  end
         | 
| 238 237 | 
             
                end
         | 
| 239 238 |  | 
| @@ -255,8 +254,8 @@ describe Grape::Validations do | |
| 255 254 | 
             
                    expect(last_response.body).to eq('items is missing, items[key] is missing')
         | 
| 256 255 | 
             
                  end
         | 
| 257 256 |  | 
| 258 | 
            -
                  it  | 
| 259 | 
            -
                    get '/required', items:  | 
| 257 | 
            +
                  it 'errors when param is not a Hash' do
         | 
| 258 | 
            +
                    get '/required', items: 'hello'
         | 
| 260 259 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 261 260 | 
             
                    expect(last_response.body).to eq('items is invalid, items[key] is missing')
         | 
| 262 261 |  | 
| @@ -271,23 +270,13 @@ describe Grape::Validations do | |
| 271 270 | 
             
                    expect(last_response.body).to eq('required works')
         | 
| 272 271 | 
             
                  end
         | 
| 273 272 |  | 
| 274 | 
            -
                  it "doesn't allow any key in the options hash other than type" do
         | 
| 275 | 
            -
                    expect {
         | 
| 276 | 
            -
                      subject.params do
         | 
| 277 | 
            -
                        requires(:items, desc: 'Foo') do
         | 
| 278 | 
            -
                          requires :key
         | 
| 279 | 
            -
                        end
         | 
| 280 | 
            -
                      end
         | 
| 281 | 
            -
                    }.to raise_error ArgumentError
         | 
| 282 | 
            -
                  end
         | 
| 283 | 
            -
             | 
| 284 273 | 
             
                  it 'adds to declared parameters' do
         | 
| 285 274 | 
             
                    subject.params do
         | 
| 286 275 | 
             
                      requires :items do
         | 
| 287 276 | 
             
                        requires :key
         | 
| 288 277 | 
             
                      end
         | 
| 289 278 | 
             
                    end
         | 
| 290 | 
            -
                    expect(subject. | 
| 279 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
         | 
| 291 280 | 
             
                  end
         | 
| 292 281 | 
             
                end
         | 
| 293 282 |  | 
| @@ -321,7 +310,85 @@ describe Grape::Validations do | |
| 321 310 | 
             
                        requires :key
         | 
| 322 311 | 
             
                      end
         | 
| 323 312 | 
             
                    end
         | 
| 324 | 
            -
                    expect(subject. | 
| 313 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
         | 
| 314 | 
            +
                  end
         | 
| 315 | 
            +
                end
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                context 'group params with nested params which has a type' do
         | 
| 318 | 
            +
                  let(:invalid_items){ { items: '' } }
         | 
| 319 | 
            +
             | 
| 320 | 
            +
                  before do
         | 
| 321 | 
            +
                    subject.params do
         | 
| 322 | 
            +
                      optional :items do
         | 
| 323 | 
            +
                        optional :key1, type: String
         | 
| 324 | 
            +
                        optional :key2, type: String
         | 
| 325 | 
            +
                      end
         | 
| 326 | 
            +
                    end
         | 
| 327 | 
            +
                    subject.post '/group_with_nested' do
         | 
| 328 | 
            +
                      'group with nested works'
         | 
| 329 | 
            +
                    end
         | 
| 330 | 
            +
                  end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                  it 'errors when group param is invalid'do
         | 
| 333 | 
            +
                    post '/group_with_nested', items: invalid_items
         | 
| 334 | 
            +
                    expect(last_response.status).to eq(400)
         | 
| 335 | 
            +
                  end
         | 
| 336 | 
            +
                end
         | 
| 337 | 
            +
             | 
| 338 | 
            +
                context 'custom validator for a Hash' do
         | 
| 339 | 
            +
                  module DateRangeValidations
         | 
| 340 | 
            +
                    class DateRangeValidator < Grape::Validations::Base
         | 
| 341 | 
            +
                      def validate_param!(attr_name, params)
         | 
| 342 | 
            +
                        unless params[attr_name][:from] <= params[attr_name][:to]
         | 
| 343 | 
            +
                          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'"
         | 
| 344 | 
            +
                        end
         | 
| 345 | 
            +
                      end
         | 
| 346 | 
            +
                    end
         | 
| 347 | 
            +
                  end
         | 
| 348 | 
            +
             | 
| 349 | 
            +
                  before do
         | 
| 350 | 
            +
                    subject.params do
         | 
| 351 | 
            +
                      optional :date_range, date_range: true, type: Hash do
         | 
| 352 | 
            +
                        requires :from, type: Integer
         | 
| 353 | 
            +
                        requires :to, type: Integer
         | 
| 354 | 
            +
                      end
         | 
| 355 | 
            +
                    end
         | 
| 356 | 
            +
                    subject.get('/optional') do
         | 
| 357 | 
            +
                      'optional works'
         | 
| 358 | 
            +
                    end
         | 
| 359 | 
            +
                    subject.params do
         | 
| 360 | 
            +
                      requires :date_range, date_range: true, type: Hash do
         | 
| 361 | 
            +
                        requires :from, type: Integer
         | 
| 362 | 
            +
                        requires :to, type: Integer
         | 
| 363 | 
            +
                      end
         | 
| 364 | 
            +
                    end
         | 
| 365 | 
            +
                    subject.get('/required') do
         | 
| 366 | 
            +
                      'required works'
         | 
| 367 | 
            +
                    end
         | 
| 368 | 
            +
                  end
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                  context 'which is optional' do
         | 
| 371 | 
            +
                    it "doesn't throw an error if the validation passes" do
         | 
| 372 | 
            +
                      get '/optional', date_range: { from: 1, to: 2 }
         | 
| 373 | 
            +
                      expect(last_response.status).to eq(200)
         | 
| 374 | 
            +
                    end
         | 
| 375 | 
            +
             | 
| 376 | 
            +
                    it 'errors if the validation fails' do
         | 
| 377 | 
            +
                      get '/optional', date_range: { from: 2, to: 1 }
         | 
| 378 | 
            +
                      expect(last_response.status).to eq(400)
         | 
| 379 | 
            +
                    end
         | 
| 380 | 
            +
                  end
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                  context 'which is required' do
         | 
| 383 | 
            +
                    it "doesn't throw an error if the validation passes" do
         | 
| 384 | 
            +
                      get '/required', date_range: { from: 1, to: 2 }
         | 
| 385 | 
            +
                      expect(last_response.status).to eq(200)
         | 
| 386 | 
            +
                    end
         | 
| 387 | 
            +
             | 
| 388 | 
            +
                    it 'errors if the validation fails' do
         | 
| 389 | 
            +
                      get '/required', date_range: { from: 2, to: 1 }
         | 
| 390 | 
            +
                      expect(last_response.status).to eq(400)
         | 
| 391 | 
            +
                    end
         | 
| 325 392 | 
             
                  end
         | 
| 326 393 | 
             
                end
         | 
| 327 394 |  | 
| @@ -371,9 +438,9 @@ describe Grape::Validations do | |
| 371 438 | 
             
                    expect(last_response.body).to eq('children[parents] is missing')
         | 
| 372 439 | 
             
                  end
         | 
| 373 440 |  | 
| 374 | 
            -
                  it  | 
| 441 | 
            +
                  it 'errors when param is not an Array' do
         | 
| 375 442 | 
             
                    # NOTE: would be nicer if these just returned 'children is invalid'
         | 
| 376 | 
            -
                    get '/within_array', children:  | 
| 443 | 
            +
                    get '/within_array', children: 'hello'
         | 
| 377 444 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 378 445 | 
             
                    expect(last_response.body).to eq('children is invalid, children[name] is missing, children[parents] is missing, children[parents] is invalid, children[parents][name] is missing')
         | 
| 379 446 |  | 
| @@ -428,7 +495,7 @@ describe Grape::Validations do | |
| 428 495 | 
             
                  end
         | 
| 429 496 |  | 
| 430 497 | 
             
                  it 'requires defaults to Array type' do
         | 
| 431 | 
            -
                    get '/req', planets:  | 
| 498 | 
            +
                    get '/req', planets: 'Jupiter, Saturn'
         | 
| 432 499 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 433 500 | 
             
                    expect(last_response.body).to eq('planets is invalid, planets[name] is missing')
         | 
| 434 501 |  | 
| @@ -444,26 +511,26 @@ describe Grape::Validations do | |
| 444 511 | 
             
                  end
         | 
| 445 512 |  | 
| 446 513 | 
             
                  it 'optional defaults to Array type' do
         | 
| 447 | 
            -
                    get '/opt', name:  | 
| 514 | 
            +
                    get '/opt', name: 'Jupiter', moons: 'Europa, Ganymede'
         | 
| 448 515 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 449 516 | 
             
                    expect(last_response.body).to eq('moons is invalid, moons[name] is missing')
         | 
| 450 517 |  | 
| 451 | 
            -
                    get '/opt', name:  | 
| 518 | 
            +
                    get '/opt', name: 'Jupiter', moons: { name: 'Ganymede' }
         | 
| 452 519 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 453 520 | 
             
                    expect(last_response.body).to eq('moons is invalid')
         | 
| 454 521 |  | 
| 455 | 
            -
                    get '/opt', name:  | 
| 522 | 
            +
                    get '/opt', name: 'Jupiter', moons: [{ name: 'Io' }, { name: 'Callisto' }]
         | 
| 456 523 | 
             
                    expect(last_response.status).to eq(200)
         | 
| 457 524 |  | 
| 458 | 
            -
                    put_with_json '/opt', name:  | 
| 525 | 
            +
                    put_with_json '/opt', name: 'Venus'
         | 
| 459 526 | 
             
                    expect(last_response.status).to eq(200)
         | 
| 460 527 |  | 
| 461 | 
            -
                    put_with_json '/opt', name:  | 
| 528 | 
            +
                    put_with_json '/opt', name: 'Mercury', moons: []
         | 
| 462 529 | 
             
                    expect(last_response.status).to eq(200)
         | 
| 463 530 | 
             
                  end
         | 
| 464 531 |  | 
| 465 532 | 
             
                  it 'group defaults to Array type' do
         | 
| 466 | 
            -
                    get '/grp', stars:  | 
| 533 | 
            +
                    get '/grp', stars: 'Sun'
         | 
| 467 534 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 468 535 | 
             
                    expect(last_response.body).to eq('stars is invalid, stars[name] is missing')
         | 
| 469 536 |  | 
| @@ -545,14 +612,14 @@ describe Grape::Validations do | |
| 545 612 | 
             
                    expect(last_response.body).to eq('optional group works')
         | 
| 546 613 | 
             
                  end
         | 
| 547 614 |  | 
| 548 | 
            -
                  it  | 
| 615 | 
            +
                  it 'errors when group is present, but required param is not' do
         | 
| 549 616 | 
             
                    get '/optional_group', items: [{ not_key: 'foo' }]
         | 
| 550 617 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 551 618 | 
             
                    expect(last_response.body).to eq('items[key] is missing')
         | 
| 552 619 | 
             
                  end
         | 
| 553 620 |  | 
| 554 621 | 
             
                  it "errors when param is present but isn't an Array" do
         | 
| 555 | 
            -
                    get '/optional_group', items:  | 
| 622 | 
            +
                    get '/optional_group', items: 'hello'
         | 
| 556 623 | 
             
                    expect(last_response.status).to eq(400)
         | 
| 557 624 | 
             
                    expect(last_response.body).to eq('items is invalid, items[key] is missing')
         | 
| 558 625 |  | 
| @@ -567,7 +634,7 @@ describe Grape::Validations do | |
| 567 634 | 
             
                        requires :key
         | 
| 568 635 | 
             
                      end
         | 
| 569 636 | 
             
                    end
         | 
| 570 | 
            -
                    expect(subject. | 
| 637 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
         | 
| 571 638 | 
             
                  end
         | 
| 572 639 | 
             
                end
         | 
| 573 640 |  | 
| @@ -631,7 +698,7 @@ describe Grape::Validations do | |
| 631 698 | 
             
                        requires(:required_subitems) { requires :value }
         | 
| 632 699 | 
             
                      end
         | 
| 633 700 | 
             
                    end
         | 
| 634 | 
            -
                    expect(subject. | 
| 701 | 
            +
                    expect(subject.route_setting(:declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
         | 
| 635 702 | 
             
                  end
         | 
| 636 703 | 
             
                end
         | 
| 637 704 |  | 
| @@ -656,10 +723,10 @@ describe Grape::Validations do | |
| 656 723 |  | 
| 657 724 | 
             
                context 'custom validation' do
         | 
| 658 725 | 
             
                  module CustomValidations
         | 
| 659 | 
            -
                    class Customvalidator < Grape::Validations:: | 
| 726 | 
            +
                    class Customvalidator < Grape::Validations::Base
         | 
| 660 727 | 
             
                      def validate_param!(attr_name, params)
         | 
| 661 728 | 
             
                        unless params[attr_name] == 'im custom'
         | 
| 662 | 
            -
                           | 
| 729 | 
            +
                          fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'is not custom!'
         | 
| 663 730 | 
             
                        end
         | 
| 664 731 | 
             
                      end
         | 
| 665 732 | 
             
                    end
         | 
| @@ -776,13 +843,13 @@ describe Grape::Validations do | |
| 776 843 | 
             
                        expect(last_response.body).to eq('custom is not custom!')
         | 
| 777 844 | 
             
                      end
         | 
| 778 845 |  | 
| 779 | 
            -
                      specify 'the nested  | 
| 846 | 
            +
                      specify 'the nested namespace inherits the custom validator' do
         | 
| 780 847 | 
             
                        get '/nested/nested/two', custom: 'im wrong, validate me'
         | 
| 781 848 | 
             
                        expect(last_response.status).to eq(400)
         | 
| 782 849 | 
             
                        expect(last_response.body).to eq('custom is not custom!')
         | 
| 783 850 | 
             
                      end
         | 
| 784 851 |  | 
| 785 | 
            -
                      specify 'peer  | 
| 852 | 
            +
                      specify 'peer namespaces does not have the validator' do
         | 
| 786 853 | 
             
                        get '/peer/one', custom: 'im not validated'
         | 
| 787 854 | 
             
                        expect(last_response.status).to eq(200)
         | 
| 788 855 | 
             
                        expect(last_response.body).to eq('no validation required')
         | 
| @@ -805,10 +872,10 @@ describe Grape::Validations do | |
| 805 872 |  | 
| 806 873 | 
             
                  context 'when using options on param' do
         | 
| 807 874 | 
             
                    module CustomValidations
         | 
| 808 | 
            -
                      class CustomvalidatorWithOptions < Grape::Validations:: | 
| 875 | 
            +
                      class CustomvalidatorWithOptions < Grape::Validations::Base
         | 
| 809 876 | 
             
                        def validate_param!(attr_name, params)
         | 
| 810 877 | 
             
                          unless params[attr_name] == @option[:text]
         | 
| 811 | 
            -
                             | 
| 878 | 
            +
                            fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: @option[:error_message]
         | 
| 812 879 | 
             
                          end
         | 
| 813 880 | 
             
                        end
         | 
| 814 881 | 
             
                      end
         | 
| @@ -816,7 +883,7 @@ describe Grape::Validations do | |
| 816 883 |  | 
| 817 884 | 
             
                    before do
         | 
| 818 885 | 
             
                      subject.params do
         | 
| 819 | 
            -
                        optional :custom, customvalidator_with_options: { text: 'im custom with options', error_message:  | 
| 886 | 
            +
                        optional :custom, customvalidator_with_options: { text: 'im custom with options', error_message: 'is not custom with options!' }
         | 
| 820 887 | 
             
                      end
         | 
| 821 888 | 
             
                      subject.get '/optional_custom' do
         | 
| 822 889 | 
             
                        'optional with custom works!'
         | 
| @@ -877,16 +944,15 @@ describe Grape::Validations do | |
| 877 944 | 
             
                      subject.params do
         | 
| 878 945 | 
             
                        use :pagination
         | 
| 879 946 | 
             
                      end
         | 
| 880 | 
            -
                      expect(subject. | 
| 947 | 
            +
                      expect(subject.route_setting(:declared_params)).to eq [:page, :per_page]
         | 
| 881 948 | 
             
                    end
         | 
| 882 949 |  | 
| 883 950 | 
             
                    it 'by #use with multiple params' do
         | 
| 884 951 | 
             
                      subject.params do
         | 
| 885 952 | 
             
                        use :pagination, :period
         | 
| 886 953 | 
             
                      end
         | 
| 887 | 
            -
                      expect(subject. | 
| 954 | 
            +
                      expect(subject.route_setting(:declared_params)).to eq [:page, :per_page, :start_date, :end_date]
         | 
| 888 955 | 
             
                    end
         | 
| 889 | 
            -
             | 
| 890 956 | 
             
                  end
         | 
| 891 957 |  | 
| 892 958 | 
             
                  context 'with block' do
         | 
| @@ -960,7 +1026,7 @@ describe Grape::Validations do | |
| 960 1026 |  | 
| 961 1027 | 
             
                      get '/mutually_exclusive', beer: 'string', wine: 'anotherstring'
         | 
| 962 1028 | 
             
                      expect(last_response.status).to eq(400)
         | 
| 963 | 
            -
                      expect(last_response.body).to eq  | 
| 1029 | 
            +
                      expect(last_response.body).to eq 'beer, wine are mutually exclusive'
         | 
| 964 1030 | 
             
                    end
         | 
| 965 1031 | 
             
                  end
         | 
| 966 1032 |  | 
| @@ -970,17 +1036,62 @@ describe Grape::Validations do | |
| 970 1036 | 
             
                        optional :beer
         | 
| 971 1037 | 
             
                        optional :wine
         | 
| 972 1038 | 
             
                        mutually_exclusive :beer, :wine
         | 
| 973 | 
            -
                        optional : | 
| 974 | 
            -
             | 
| 975 | 
            -
             | 
| 1039 | 
            +
                        optional :nested, type: Hash do
         | 
| 1040 | 
            +
                          optional :scotch
         | 
| 1041 | 
            +
                          optional :aquavit
         | 
| 1042 | 
            +
                          mutually_exclusive :scotch, :aquavit
         | 
| 1043 | 
            +
                        end
         | 
| 1044 | 
            +
                        optional :nested2, type: Array do
         | 
| 1045 | 
            +
                          optional :scotch2
         | 
| 1046 | 
            +
                          optional :aquavit2
         | 
| 1047 | 
            +
                          mutually_exclusive :scotch2, :aquavit2
         | 
| 1048 | 
            +
                        end
         | 
| 976 1049 | 
             
                      end
         | 
| 977 1050 | 
             
                      subject.get '/mutually_exclusive' do
         | 
| 978 1051 | 
             
                        'mutually_exclusive works!'
         | 
| 979 1052 | 
             
                      end
         | 
| 980 1053 |  | 
| 981 | 
            -
                      get '/mutually_exclusive', beer: 'true', wine: 'true', scotch: 'true', aquavit: 'true'
         | 
| 1054 | 
            +
                      get '/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
         | 
| 1055 | 
            +
                      expect(last_response.status).to eq(400)
         | 
| 1056 | 
            +
                      expect(last_response.body).to eq 'beer, wine are mutually exclusive, scotch, aquavit are mutually exclusive, scotch2, aquavit2 are mutually exclusive'
         | 
| 1057 | 
            +
                    end
         | 
| 1058 | 
            +
                  end
         | 
| 1059 | 
            +
             | 
| 1060 | 
            +
                  context 'in a group' do
         | 
| 1061 | 
            +
                    it 'works when only one from the set is present' do
         | 
| 1062 | 
            +
                      subject.params do
         | 
| 1063 | 
            +
                        group :drink, type: Hash do
         | 
| 1064 | 
            +
                          optional :wine
         | 
| 1065 | 
            +
                          optional :beer
         | 
| 1066 | 
            +
                          optional :juice
         | 
| 1067 | 
            +
             | 
| 1068 | 
            +
                          mutually_exclusive :beer, :wine, :juice
         | 
| 1069 | 
            +
                        end
         | 
| 1070 | 
            +
                      end
         | 
| 1071 | 
            +
                      subject.get '/mutually_exclusive_group' do
         | 
| 1072 | 
            +
                        'mutually_exclusive_group works!'
         | 
| 1073 | 
            +
                      end
         | 
| 1074 | 
            +
             | 
| 1075 | 
            +
                      get '/mutually_exclusive_group', drink: { beer: 'true' }
         | 
| 1076 | 
            +
                      expect(last_response.status).to eq(200)
         | 
| 1077 | 
            +
                    end
         | 
| 1078 | 
            +
             | 
| 1079 | 
            +
                    it 'errors when more than one from the set is present' do
         | 
| 1080 | 
            +
                      subject.params do
         | 
| 1081 | 
            +
                        group :drink, type: Hash do
         | 
| 1082 | 
            +
                          optional :wine
         | 
| 1083 | 
            +
                          optional :beer
         | 
| 1084 | 
            +
                          optional :juice
         | 
| 1085 | 
            +
             | 
| 1086 | 
            +
                          mutually_exclusive :beer, :wine, :juice
         | 
| 1087 | 
            +
                        end
         | 
| 1088 | 
            +
                      end
         | 
| 1089 | 
            +
                      subject.get '/mutually_exclusive_group' do
         | 
| 1090 | 
            +
                        'mutually_exclusive_group works!'
         | 
| 1091 | 
            +
                      end
         | 
| 1092 | 
            +
             | 
| 1093 | 
            +
                      get '/mutually_exclusive_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
         | 
| 982 1094 | 
             
                      expect(last_response.status).to eq(400)
         | 
| 983 | 
            -
                      expect(last_response.body).to eq "beer, wine are mutually exclusive, scotch, aquavit are mutually exclusive"
         | 
| 984 1095 | 
             
                    end
         | 
| 985 1096 | 
             
                  end
         | 
| 986 1097 | 
             
                end
         | 
| @@ -1002,7 +1113,7 @@ describe Grape::Validations do | |
| 1002 1113 | 
             
                    it 'errors when none are present' do
         | 
| 1003 1114 | 
             
                      get '/exactly_one_of'
         | 
| 1004 1115 | 
             
                      expect(last_response.status).to eq(400)
         | 
| 1005 | 
            -
                      expect(last_response.body).to eq  | 
| 1116 | 
            +
                      expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
         | 
| 1006 1117 | 
             
                    end
         | 
| 1007 1118 |  | 
| 1008 1119 | 
             
                    it 'succeeds when one is present' do
         | 
| @@ -1014,7 +1125,47 @@ describe Grape::Validations do | |
| 1014 1125 | 
             
                    it 'errors when two or more are present' do
         | 
| 1015 1126 | 
             
                      get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
         | 
| 1016 1127 | 
             
                      expect(last_response.status).to eq(400)
         | 
| 1017 | 
            -
                      expect(last_response.body).to eq  | 
| 1128 | 
            +
                      expect(last_response.body).to eq 'beer, wine are mutually exclusive'
         | 
| 1129 | 
            +
                    end
         | 
| 1130 | 
            +
                  end
         | 
| 1131 | 
            +
             | 
| 1132 | 
            +
                  context 'nested params' do
         | 
| 1133 | 
            +
                    before :each do
         | 
| 1134 | 
            +
                      subject.params do
         | 
| 1135 | 
            +
                        requires :nested, type: Hash do
         | 
| 1136 | 
            +
                          optional :beer_nested
         | 
| 1137 | 
            +
                          optional :wine_nested
         | 
| 1138 | 
            +
                          optional :juice_nested
         | 
| 1139 | 
            +
                          exactly_one_of :beer_nested, :wine_nested, :juice_nested
         | 
| 1140 | 
            +
                        end
         | 
| 1141 | 
            +
                        optional :nested2, type: Array do
         | 
| 1142 | 
            +
                          optional :beer_nested2
         | 
| 1143 | 
            +
                          optional :wine_nested2
         | 
| 1144 | 
            +
                          optional :juice_nested2
         | 
| 1145 | 
            +
                          exactly_one_of :beer_nested2, :wine_nested2, :juice_nested2
         | 
| 1146 | 
            +
                        end
         | 
| 1147 | 
            +
                      end
         | 
| 1148 | 
            +
                      subject.get '/exactly_one_of_nested' do
         | 
| 1149 | 
            +
                        'exactly_one_of works!'
         | 
| 1150 | 
            +
                      end
         | 
| 1151 | 
            +
                    end
         | 
| 1152 | 
            +
             | 
| 1153 | 
            +
                    it 'errors when none are present' do
         | 
| 1154 | 
            +
                      get '/exactly_one_of_nested'
         | 
| 1155 | 
            +
                      expect(last_response.status).to eq(400)
         | 
| 1156 | 
            +
                      expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, exactly one parameter must be provided'
         | 
| 1157 | 
            +
                    end
         | 
| 1158 | 
            +
             | 
| 1159 | 
            +
                    it 'succeeds when one is present' do
         | 
| 1160 | 
            +
                      get '/exactly_one_of_nested', nested: { beer_nested: 'string' }
         | 
| 1161 | 
            +
                      expect(last_response.status).to eq(200)
         | 
| 1162 | 
            +
                      expect(last_response.body).to eq 'exactly_one_of works!'
         | 
| 1163 | 
            +
                    end
         | 
| 1164 | 
            +
             | 
| 1165 | 
            +
                    it 'errors when two or more are present' do
         | 
| 1166 | 
            +
                      get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
         | 
| 1167 | 
            +
                      expect(last_response.status).to eq(400)
         | 
| 1168 | 
            +
                      expect(last_response.body).to eq 'beer_nested2, wine_nested2 are mutually exclusive'
         | 
| 1018 1169 | 
             
                    end
         | 
| 1019 1170 | 
             
                  end
         | 
| 1020 1171 | 
             
                end
         | 
| @@ -1036,7 +1187,7 @@ describe Grape::Validations do | |
| 1036 1187 | 
             
                    it 'errors when none are present' do
         | 
| 1037 1188 | 
             
                      get '/at_least_one_of'
         | 
| 1038 1189 | 
             
                      expect(last_response.status).to eq(400)
         | 
| 1039 | 
            -
                      expect(last_response.body).to eq  | 
| 1190 | 
            +
                      expect(last_response.body).to eq 'beer, wine, juice are missing, at least one parameter must be provided'
         | 
| 1040 1191 | 
             
                    end
         | 
| 1041 1192 |  | 
| 1042 1193 | 
             
                    it 'does not error when one is present' do
         | 
| @@ -1051,6 +1202,120 @@ describe Grape::Validations do | |
| 1051 1202 | 
             
                      expect(last_response.body).to eq 'at_least_one_of works!'
         | 
| 1052 1203 | 
             
                    end
         | 
| 1053 1204 | 
             
                  end
         | 
| 1205 | 
            +
             | 
| 1206 | 
            +
                  context 'nested params' do
         | 
| 1207 | 
            +
                    before :each do
         | 
| 1208 | 
            +
                      subject.params do
         | 
| 1209 | 
            +
                        requires :nested, type: Hash do
         | 
| 1210 | 
            +
                          optional :beer_nested
         | 
| 1211 | 
            +
                          optional :wine_nested
         | 
| 1212 | 
            +
                          optional :juice_nested
         | 
| 1213 | 
            +
                          at_least_one_of :beer_nested, :wine_nested, :juice_nested
         | 
| 1214 | 
            +
                        end
         | 
| 1215 | 
            +
                        optional :nested2, type: Array do
         | 
| 1216 | 
            +
                          optional :beer_nested2
         | 
| 1217 | 
            +
                          optional :wine_nested2
         | 
| 1218 | 
            +
                          optional :juice_nested2
         | 
| 1219 | 
            +
                          at_least_one_of :beer_nested2, :wine_nested2, :juice_nested2
         | 
| 1220 | 
            +
                        end
         | 
| 1221 | 
            +
                      end
         | 
| 1222 | 
            +
                      subject.get '/at_least_one_of_nested' do
         | 
| 1223 | 
            +
                        'at_least_one_of works!'
         | 
| 1224 | 
            +
                      end
         | 
| 1225 | 
            +
                    end
         | 
| 1226 | 
            +
             | 
| 1227 | 
            +
                    it 'errors when none are present' do
         | 
| 1228 | 
            +
                      get '/at_least_one_of_nested'
         | 
| 1229 | 
            +
                      expect(last_response.status).to eq(400)
         | 
| 1230 | 
            +
                      expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, at least one parameter must be provided'
         | 
| 1231 | 
            +
                    end
         | 
| 1232 | 
            +
             | 
| 1233 | 
            +
                    it 'does not error when one is present' do
         | 
| 1234 | 
            +
                      get '/at_least_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string' }]
         | 
| 1235 | 
            +
                      expect(last_response.status).to eq(200)
         | 
| 1236 | 
            +
                      expect(last_response.body).to eq 'at_least_one_of works!'
         | 
| 1237 | 
            +
                    end
         | 
| 1238 | 
            +
             | 
| 1239 | 
            +
                    it 'does not error when two are present' do
         | 
| 1240 | 
            +
                      get '/at_least_one_of_nested', nested: { beer_nested: 'string', wine_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'string' }]
         | 
| 1241 | 
            +
                      expect(last_response.status).to eq(200)
         | 
| 1242 | 
            +
                      expect(last_response.body).to eq 'at_least_one_of works!'
         | 
| 1243 | 
            +
                    end
         | 
| 1244 | 
            +
                  end
         | 
| 1245 | 
            +
                end
         | 
| 1246 | 
            +
             | 
| 1247 | 
            +
                context 'in a group' do
         | 
| 1248 | 
            +
                  it 'works when only one from the set is present' do
         | 
| 1249 | 
            +
                    subject.params do
         | 
| 1250 | 
            +
                      group :drink, type: Hash do
         | 
| 1251 | 
            +
                        optional :wine
         | 
| 1252 | 
            +
                        optional :beer
         | 
| 1253 | 
            +
                        optional :juice
         | 
| 1254 | 
            +
             | 
| 1255 | 
            +
                        exactly_one_of :beer, :wine, :juice
         | 
| 1256 | 
            +
                      end
         | 
| 1257 | 
            +
                    end
         | 
| 1258 | 
            +
                    subject.get '/exactly_one_of_group' do
         | 
| 1259 | 
            +
                      'exactly_one_of_group works!'
         | 
| 1260 | 
            +
                    end
         | 
| 1261 | 
            +
             | 
| 1262 | 
            +
                    get '/exactly_one_of_group', drink: { beer: 'true' }
         | 
| 1263 | 
            +
                    expect(last_response.status).to eq(200)
         | 
| 1264 | 
            +
                  end
         | 
| 1265 | 
            +
             | 
| 1266 | 
            +
                  it 'errors when no parameter from the set is present' do
         | 
| 1267 | 
            +
                    subject.params do
         | 
| 1268 | 
            +
                      group :drink, type: Hash do
         | 
| 1269 | 
            +
                        optional :wine
         | 
| 1270 | 
            +
                        optional :beer
         | 
| 1271 | 
            +
                        optional :juice
         | 
| 1272 | 
            +
             | 
| 1273 | 
            +
                        exactly_one_of :beer, :wine, :juice
         | 
| 1274 | 
            +
                      end
         | 
| 1275 | 
            +
                    end
         | 
| 1276 | 
            +
                    subject.get '/exactly_one_of_group' do
         | 
| 1277 | 
            +
                      'exactly_one_of_group works!'
         | 
| 1278 | 
            +
                    end
         | 
| 1279 | 
            +
             | 
| 1280 | 
            +
                    get '/exactly_one_of_group', drink: {}
         | 
| 1281 | 
            +
                    expect(last_response.status).to eq(400)
         | 
| 1282 | 
            +
                  end
         | 
| 1283 | 
            +
             | 
| 1284 | 
            +
                  it 'errors when more than one from the set is present' do
         | 
| 1285 | 
            +
                    subject.params do
         | 
| 1286 | 
            +
                      group :drink, type: Hash do
         | 
| 1287 | 
            +
                        optional :wine
         | 
| 1288 | 
            +
                        optional :beer
         | 
| 1289 | 
            +
                        optional :juice
         | 
| 1290 | 
            +
             | 
| 1291 | 
            +
                        exactly_one_of :beer, :wine, :juice
         | 
| 1292 | 
            +
                      end
         | 
| 1293 | 
            +
                    end
         | 
| 1294 | 
            +
                    subject.get '/exactly_one_of_group' do
         | 
| 1295 | 
            +
                      'exactly_one_of_group works!'
         | 
| 1296 | 
            +
                    end
         | 
| 1297 | 
            +
             | 
| 1298 | 
            +
                    get '/exactly_one_of_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
         | 
| 1299 | 
            +
                    expect(last_response.status).to eq(400)
         | 
| 1300 | 
            +
                  end
         | 
| 1301 | 
            +
             | 
| 1302 | 
            +
                  it 'does not falsely think the param is there if it is provided outside the block' do
         | 
| 1303 | 
            +
                    subject.params do
         | 
| 1304 | 
            +
                      group :drink, type: Hash do
         | 
| 1305 | 
            +
                        optional :wine
         | 
| 1306 | 
            +
                        optional :beer
         | 
| 1307 | 
            +
                        optional :juice
         | 
| 1308 | 
            +
             | 
| 1309 | 
            +
                        exactly_one_of :beer, :wine, :juice
         | 
| 1310 | 
            +
                      end
         | 
| 1311 | 
            +
                    end
         | 
| 1312 | 
            +
                    subject.get '/exactly_one_of_group' do
         | 
| 1313 | 
            +
                      'exactly_one_of_group works!'
         | 
| 1314 | 
            +
                    end
         | 
| 1315 | 
            +
             | 
| 1316 | 
            +
                    get '/exactly_one_of_group', drink: { foo: 'bar' }, beer: 'true'
         | 
| 1317 | 
            +
                    expect(last_response.status).to eq(400)
         | 
| 1318 | 
            +
                  end
         | 
| 1054 1319 | 
             
                end
         | 
| 1055 1320 | 
             
              end
         | 
| 1056 1321 | 
             
            end
         |