grape 1.6.0 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/CONTRIBUTING.md +1 -0
- data/README.md +9 -1
- data/lib/grape/api.rb +12 -0
- data/lib/grape/dry_types.rb +12 -0
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +1 -1
- data/lib/grape/middleware/auth/dsl.rb +7 -1
- data/lib/grape/middleware/base.rb +1 -1
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -10
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +5 -7
- data/lib/grape/validations/types/set_coercer.rb +0 -3
- data/lib/grape/validations/types.rb +83 -9
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
- data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
- data/lib/grape/validations/validators/as_validator.rb +14 -0
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
- data/lib/grape/validations/validators/base.rb +73 -71
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/default_validator.rb +51 -0
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
- data/lib/grape/validations/validators/except_values_validator.rb +24 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -22
- data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
- data/lib/grape/validations/validators/presence_validator.rb +15 -0
- data/lib/grape/validations/validators/regexp_validator.rb +16 -0
- data/lib/grape/validations/validators/same_as_validator.rb +29 -0
- data/lib/grape/validations/validators/values_validator.rb +88 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +59 -24
- data/spec/grape/api/custom_validations_spec.rb +77 -46
- data/spec/grape/api/deeply_included_options_spec.rb +3 -3
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +1 -1
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
- data/spec/grape/api_remount_spec.rb +16 -15
- data/spec/grape/api_spec.rb +317 -193
- data/spec/grape/dsl/callbacks_spec.rb +1 -0
- data/spec/grape/dsl/headers_spec.rb +39 -9
- data/spec/grape/dsl/helpers_spec.rb +3 -2
- data/spec/grape/dsl/inside_route_spec.rb +6 -4
- data/spec/grape/dsl/logger_spec.rb +16 -18
- data/spec/grape/dsl/middleware_spec.rb +1 -0
- data/spec/grape/dsl/parameters_spec.rb +1 -0
- data/spec/grape/dsl/request_response_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +9 -6
- data/spec/grape/endpoint/declared_spec.rb +12 -12
- data/spec/grape/endpoint_spec.rb +59 -50
- data/spec/grape/entity_spec.rb +13 -13
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
- data/spec/grape/exceptions/validation_spec.rb +5 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
- data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
- data/spec/grape/loading_spec.rb +8 -8
- data/spec/grape/middleware/auth/dsl_spec.rb +14 -5
- data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
- data/spec/grape/middleware/base_spec.rb +24 -15
- data/spec/grape/middleware/error_spec.rb +1 -0
- data/spec/grape/middleware/exception_spec.rb +111 -161
- data/spec/grape/middleware/formatter_spec.rb +25 -4
- data/spec/grape/middleware/globals_spec.rb +7 -4
- data/spec/grape/middleware/stack_spec.rb +11 -11
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
- data/spec/grape/middleware/versioner/header_spec.rb +14 -13
- data/spec/grape/middleware/versioner/param_spec.rb +7 -1
- data/spec/grape/middleware/versioner/path_spec.rb +5 -1
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +4 -0
- data/spec/grape/path_spec.rb +52 -52
- data/spec/grape/presenters/presenter_spec.rb +7 -6
- data/spec/grape/request_spec.rb +6 -4
- data/spec/grape/util/inheritable_setting_spec.rb +7 -7
- data/spec/grape/util/inheritable_values_spec.rb +3 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
- data/spec/grape/util/stackable_values_spec.rb +7 -5
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
- data/spec/grape/validations/params_scope_spec.rb +9 -7
- data/spec/grape/validations/single_attribute_iterator_spec.rb +1 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
- data/spec/grape/validations/types_spec.rb +8 -8
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
- data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
- data/spec/grape/validations/validators/coerce_spec.rb +10 -12
- data/spec/grape/validations/validators/default_spec.rb +72 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
- data/spec/grape/validations/validators/except_values_spec.rb +1 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
- data/spec/grape/validations/validators/presence_spec.rb +16 -1
- data/spec/grape/validations/validators/regexp_spec.rb +25 -31
- data/spec/grape/validations/validators/same_as_spec.rb +14 -20
- data/spec/grape/validations/validators/values_spec.rb +172 -171
- data/spec/grape/validations_spec.rb +45 -16
- data/spec/integration/eager_load/eager_load_spec.rb +2 -2
- data/spec/integration/multi_json/json_spec.rb +1 -1
- data/spec/integration/multi_xml/xml_spec.rb +1 -1
- data/spec/shared/versioning_examples.rb +10 -7
- data/spec/spec_helper.rb +11 -1
- metadata +116 -116
- data/lib/grape/validations/types/build_coercer.rb +0 -94
- data/lib/grape/validations/validators/all_or_none.rb +0 -16
- data/lib/grape/validations/validators/allow_blank.rb +0 -18
- data/lib/grape/validations/validators/as.rb +0 -12
- data/lib/grape/validations/validators/at_least_one_of.rb +0 -15
- data/lib/grape/validations/validators/coerce.rb +0 -87
- data/lib/grape/validations/validators/default.rb +0 -49
- data/lib/grape/validations/validators/exactly_one_of.rb +0 -17
- data/lib/grape/validations/validators/except_values.rb +0 -22
- data/lib/grape/validations/validators/mutual_exclusion.rb +0 -16
- data/lib/grape/validations/validators/presence.rb +0 -13
- data/lib/grape/validations/validators/regexp.rb +0 -14
- data/lib/grape/validations/validators/same_as.rb +0 -27
- data/lib/grape/validations/validators/values.rb +0 -86
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e8e622245d5871dfbaa651eac26abfd8bc4d36accd176f39fcc8ec09200cbe5
|
4
|
+
data.tar.gz: d92fe82ca78e6140bc98832671e682c2f090210e1ceaee907d9f3c4f812463b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eed721c5ebd94fa07d2fa2a5420f0a943959890e85c71a941d4b1caede3605dbff836d4f42fabf0e15dec2a3bda944c7cd29988dcc1b1fd13eeea350c81a62de
|
7
|
+
data.tar.gz: ac951a0fbcd4d467ecf6f9b25f0d0d460a7cd132bb4ae708e2e0f55623ccc40d77809ca9c5663bde77c5d59708b264288a7592158340f1ca8336621007c8787b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
### 1.6.1 (2021/12/28)
|
2
|
+
|
3
|
+
#### Features
|
4
|
+
|
5
|
+
* [#2196](https://github.com/ruby-grape/grape/pull/2196): Add support for `passwords_hashed` param for `digest_auth` - [@lHydra](https://github.com/lhydra).
|
6
|
+
* [#2208](https://github.com/ruby-grape/grape/pull/2208): Added Rails 7 support - [@ericproulx](https://github.com/ericproulx).
|
7
|
+
|
8
|
+
#### Fixes
|
9
|
+
|
10
|
+
* [#2206](https://github.com/ruby-grape/grape/pull/2206): Require main active_support lib before any of its extension definitions - [@annih](https://github.com/Annih).
|
11
|
+
* [#2193](https://github.com/ruby-grape/grape/pull/2193): Fixed the broken ruby-head NoMethodError spec - [@Jack12816](https://github.com/Jack12816).
|
12
|
+
* [#2192](https://github.com/ruby-grape/grape/pull/2192): Memoize the result of Grape::Middleware::Base#response - [@Jack12816](https://github.com/Jack12816).
|
13
|
+
* [#2200](https://github.com/ruby-grape/grape/pull/2200): Add validators module to all validators - [@ericproulx](https://github.com/ericproulx).
|
14
|
+
* [#2202](https://github.com/ruby-grape/grape/pull/2202): Fix random mock spec error - [@ericproulx](https://github.com/ericproulx).
|
15
|
+
* [#2203](https://github.com/ruby-grape/grape/pull/2203): Add rubocop-rspec - [@ericproulx](https://github.com/ericproulx).
|
16
|
+
* [#2207](https://github.com/ruby-grape/grape/pull/2207): Autoload Validations/Validators - [@ericproulx](https://github.com/ericproulx).
|
17
|
+
* [#2209](https://github.com/ruby-grape/grape/pull/2209): Autoload Validations/Types - [@ericproulx](https://github.com/ericproulx).
|
18
|
+
|
1
19
|
### 1.6.0 (2021/10/04)
|
2
20
|
|
3
21
|
#### Features
|
data/CONTRIBUTING.md
CHANGED
data/README.md
CHANGED
@@ -158,7 +158,7 @@ content negotiation, versioning and much more.
|
|
158
158
|
|
159
159
|
## Stable Release
|
160
160
|
|
161
|
-
You're reading the documentation for the stable release of Grape, **1.6.
|
161
|
+
You're reading the documentation for the stable release of Grape, **1.6.1**.
|
162
162
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
163
163
|
|
164
164
|
## Project Resources
|
@@ -3296,12 +3296,20 @@ http_basic do |username, password|
|
|
3296
3296
|
end
|
3297
3297
|
```
|
3298
3298
|
|
3299
|
+
Digest auth supports clear-text passwords and password hashes.
|
3300
|
+
|
3299
3301
|
```ruby
|
3300
3302
|
http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
|
3301
3303
|
# lookup the user's password here
|
3302
3304
|
end
|
3303
3305
|
```
|
3304
3306
|
|
3307
|
+
```ruby
|
3308
|
+
http_digest(realm: { realm: 'Test Api', opaque: 'app secret', passwords_hashed: true }) do |username|
|
3309
|
+
# lookup the user's password hash here
|
3310
|
+
end
|
3311
|
+
```
|
3312
|
+
|
3305
3313
|
### Register custom middleware for authentication
|
3306
3314
|
|
3307
3315
|
Grape can use custom Middleware for authentication. How to implement these
|
data/lib/grape/api.rb
CHANGED
@@ -10,6 +10,18 @@ module Grape
|
|
10
10
|
# Class methods that we want to call on the API rather than on the API object
|
11
11
|
NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile! inherited]).freeze
|
12
12
|
|
13
|
+
class Boolean
|
14
|
+
def self.build(val)
|
15
|
+
return nil if val != true && val != false
|
16
|
+
|
17
|
+
new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Instance
|
22
|
+
Boolean = Grape::API::Boolean
|
23
|
+
end
|
24
|
+
|
13
25
|
class << self
|
14
26
|
attr_accessor :base_instance, :instances
|
15
27
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-types'
|
4
|
+
|
5
|
+
module Grape
|
6
|
+
module DryTypes
|
7
|
+
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
|
8
|
+
# a container in this case. Check documentation for more information
|
9
|
+
# https://dry-rb.org/gems/dry-types/1.2/getting-started/
|
10
|
+
include Dry.Types()
|
11
|
+
end
|
12
|
+
end
|
data/lib/grape/dsl/headers.rb
CHANGED
@@ -3,8 +3,11 @@
|
|
3
3
|
module Grape
|
4
4
|
module DSL
|
5
5
|
module Headers
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# This method has four responsibilities:
|
7
|
+
# 1. Set a specifc header value by key
|
8
|
+
# 2. Retrieve a specifc header value by key
|
9
|
+
# 3. Retrieve all headers that have been set
|
10
|
+
# 4. Delete a specifc header key-value pair
|
8
11
|
def header(key = nil, val = nil)
|
9
12
|
if key
|
10
13
|
val ? header[key.to_s] = val : header.delete(key.to_s)
|
data/lib/grape/dsl/helpers.rb
CHANGED
@@ -32,7 +32,13 @@ module Grape
|
|
32
32
|
|
33
33
|
def http_digest(options = {}, &block)
|
34
34
|
options[:realm] ||= 'API Authorization'
|
35
|
-
|
35
|
+
|
36
|
+
if options[:realm].respond_to?(:values_at)
|
37
|
+
options[:realm][:opaque] ||= 'secret'
|
38
|
+
else
|
39
|
+
options[:opaque] ||= 'secret'
|
40
|
+
end
|
41
|
+
|
36
42
|
auth :http_digest, options, &block
|
37
43
|
end
|
38
44
|
end
|
@@ -60,7 +60,7 @@ module Grape
|
|
60
60
|
def response
|
61
61
|
return @app_response if @app_response.is_a?(Rack::Response)
|
62
62
|
|
63
|
-
Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
|
63
|
+
@app_response = Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
|
64
64
|
end
|
65
65
|
|
66
66
|
def content_type_for(format)
|
data/lib/grape/util/json.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry-types'
|
4
|
-
|
5
|
-
module DryTypes
|
6
|
-
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
|
7
|
-
# a container in this case. Check documentation for more information
|
8
|
-
# https://dry-rb.org/gems/dry-types/1.2/getting-started/
|
9
|
-
include Dry.Types()
|
10
|
-
end
|
11
|
-
|
12
3
|
module Grape
|
13
4
|
module Validations
|
14
5
|
module Types
|
@@ -52,7 +43,7 @@ module Grape
|
|
52
43
|
def initialize(type, strict = false)
|
53
44
|
@type = type
|
54
45
|
@strict = strict
|
55
|
-
@scope = strict ? DryTypes::Strict : DryTypes::Params
|
46
|
+
@scope = strict ? Grape::DryTypes::Strict : Grape::DryTypes::Params
|
56
47
|
end
|
57
48
|
|
58
49
|
# Coerces the given value to a type which was specified during
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'dry_type_coercer'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module Validations
|
7
5
|
module Types
|
@@ -10,16 +8,16 @@ module Grape
|
|
10
8
|
# that it has the proper type.
|
11
9
|
class PrimitiveCoercer < DryTypeCoercer
|
12
10
|
MAPPING = {
|
13
|
-
Grape::API::Boolean => DryTypes::Params::Bool,
|
14
|
-
BigDecimal => DryTypes::Params::Decimal,
|
11
|
+
Grape::API::Boolean => Grape::DryTypes::Params::Bool,
|
12
|
+
BigDecimal => Grape::DryTypes::Params::Decimal,
|
15
13
|
|
16
14
|
# unfortunately, a +Params+ scope doesn't contain String
|
17
|
-
String => DryTypes::Coercible::String
|
15
|
+
String => Grape::DryTypes::Coercible::String
|
18
16
|
}.freeze
|
19
17
|
|
20
18
|
STRICT_MAPPING = {
|
21
|
-
Grape::API::Boolean => DryTypes::Strict::Bool,
|
22
|
-
BigDecimal => DryTypes::Strict::Decimal
|
19
|
+
Grape::API::Boolean => Grape::DryTypes::Strict::Bool,
|
20
|
+
BigDecimal => Grape::DryTypes::Strict::Decimal
|
23
21
|
}.freeze
|
24
22
|
|
25
23
|
def initialize(type, strict = false)
|
@@ -1,14 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'types/build_coercer'
|
4
|
-
require_relative 'types/custom_type_coercer'
|
5
|
-
require_relative 'types/custom_type_collection_coercer'
|
6
|
-
require_relative 'types/multiple_type_coercer'
|
7
|
-
require_relative 'types/variant_collection_coercer'
|
8
|
-
require_relative 'types/json'
|
9
|
-
require_relative 'types/file'
|
10
|
-
require_relative 'types/invalid_value'
|
11
|
-
|
12
3
|
module Grape
|
13
4
|
module Validations
|
14
5
|
# Module for code related to grape's system for
|
@@ -143,6 +134,89 @@ module Grape
|
|
143
134
|
def self.map_special(type)
|
144
135
|
SPECIAL.fetch(type, type)
|
145
136
|
end
|
137
|
+
|
138
|
+
# Chooses the best coercer for the given type. For example, if the type
|
139
|
+
# is Integer, it will return a coercer which will be able to coerce a value
|
140
|
+
# to the integer.
|
141
|
+
#
|
142
|
+
# There are a few very special coercers which might be returned.
|
143
|
+
#
|
144
|
+
# +Grape::Types::MultipleTypeCoercer+ is a coercer which is returned when
|
145
|
+
# the given type implies values in an array with different types.
|
146
|
+
# For example, +[Integer, String]+ allows integer and string values in
|
147
|
+
# an array.
|
148
|
+
#
|
149
|
+
# +Grape::Types::CustomTypeCoercer+ is a coercer which is returned when
|
150
|
+
# a method is specified by a user with +coerce_with+ option or the user
|
151
|
+
# specifies a custom type which implements requirments of
|
152
|
+
# +Grape::Types::CustomTypeCoercer+.
|
153
|
+
#
|
154
|
+
# +Grape::Types::CustomTypeCollectionCoercer+ is a very similar to the
|
155
|
+
# previous one, but it expects an array or set of values having a custom
|
156
|
+
# type implemented by the user.
|
157
|
+
#
|
158
|
+
# There is also a group of custom types implemented by Grape, check
|
159
|
+
# +Grape::Validations::Types::SPECIAL+ to get the full list.
|
160
|
+
#
|
161
|
+
# @param type [Class] the type to which input strings
|
162
|
+
# should be coerced
|
163
|
+
# @param method [Class,#call] the coercion method to use
|
164
|
+
# @return [Object] object to be used
|
165
|
+
# for coercion and type validation
|
166
|
+
def self.build_coercer(type, method: nil, strict: false)
|
167
|
+
cache_instance(type, method, strict) do
|
168
|
+
create_coercer_instance(type, method, strict)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.create_coercer_instance(type, method, strict)
|
173
|
+
# Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
|
174
|
+
type = Types.map_special(type)
|
175
|
+
|
176
|
+
# Use a special coercer for multiply-typed parameters.
|
177
|
+
if Types.multiple?(type)
|
178
|
+
MultipleTypeCoercer.new(type, method)
|
179
|
+
|
180
|
+
# Use a special coercer for custom types and coercion methods.
|
181
|
+
elsif method || Types.custom?(type)
|
182
|
+
CustomTypeCoercer.new(type, method)
|
183
|
+
|
184
|
+
# Special coercer for collections of types that implement a parse method.
|
185
|
+
# CustomTypeCoercer (above) already handles such types when an explicit coercion
|
186
|
+
# method is supplied.
|
187
|
+
elsif Types.collection_of_custom?(type)
|
188
|
+
Types::CustomTypeCollectionCoercer.new(
|
189
|
+
Types.map_special(type.first), type.is_a?(Set)
|
190
|
+
)
|
191
|
+
else
|
192
|
+
DryTypeCoercer.coercer_instance_for(type, strict)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.cache_instance(type, method, strict, &_block)
|
197
|
+
key = cache_key(type, method, strict)
|
198
|
+
|
199
|
+
return @__cache[key] if @__cache.key?(key)
|
200
|
+
|
201
|
+
instance = yield
|
202
|
+
|
203
|
+
@__cache_write_lock.synchronize do
|
204
|
+
@__cache[key] = instance
|
205
|
+
end
|
206
|
+
|
207
|
+
instance
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.cache_key(type, method, strict)
|
211
|
+
[type, method, strict].each_with_object(+'_') do |val, memo|
|
212
|
+
next if val.nil?
|
213
|
+
|
214
|
+
memo << '_' << val.to_s
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
instance_variable_set(:@__cache, {})
|
219
|
+
instance_variable_set(:@__cache_write_lock, Mutex.new)
|
146
220
|
end
|
147
221
|
end
|
148
222
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class AllOrNoneOfValidator < MultipleParamsBase
|
7
|
+
def validate_params!(params)
|
8
|
+
keys = keys_in_common(params)
|
9
|
+
return if keys.empty? || keys.length == all_keys.length
|
10
|
+
|
11
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class AllowBlankValidator < Base
|
7
|
+
def validate_param!(attr_name, params)
|
8
|
+
return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
|
9
|
+
|
10
|
+
value = params[attr_name]
|
11
|
+
value = value.strip if value.respond_to?(:strip)
|
12
|
+
|
13
|
+
return if value == false || value.present?
|
14
|
+
|
15
|
+
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class AsValidator < Base
|
7
|
+
# We use a validator for renaming parameters. This is just a marker for
|
8
|
+
# the parameter scope to handle the renaming. No actual validation
|
9
|
+
# happens here.
|
10
|
+
def validate_param!(*); end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Validators
|
6
|
+
class AtLeastOneOfValidator < MultipleParamsBase
|
7
|
+
def validate_params!(params)
|
8
|
+
return unless keys_in_common(params).empty?
|
9
|
+
|
10
|
+
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -2,91 +2,93 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
5
|
+
module Validators
|
6
|
+
class Base
|
7
|
+
attr_reader :attrs
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
# Creates a new Validator from options specified
|
10
|
+
# by a +requires+ or +optional+ directive during
|
11
|
+
# parameter definition.
|
12
|
+
# @param attrs [Array] names of attributes to which the Validator applies
|
13
|
+
# @param options [Object] implementation-dependent Validator options
|
14
|
+
# @param required [Boolean] attribute(s) are required or optional
|
15
|
+
# @param scope [ParamsScope] parent scope for this Validator
|
16
|
+
# @param opts [Array] additional validation options
|
17
|
+
def initialize(attrs, options, required, scope, *opts)
|
18
|
+
@attrs = Array(attrs)
|
19
|
+
@option = options
|
20
|
+
@required = required
|
21
|
+
@scope = scope
|
22
|
+
opts = opts.any? ? opts.shift : {}
|
23
|
+
@fail_fast = opts.fetch(:fail_fast, false)
|
24
|
+
@allow_blank = opts.fetch(:allow_blank, false)
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
# Validates a given request.
|
28
|
+
# @note Override #validate! unless you need to access the entire request.
|
29
|
+
# @param request [Grape::Request] the request currently being handled
|
30
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
31
|
+
# @return [void]
|
32
|
+
def validate(request)
|
33
|
+
return unless @scope.should_validate?(request.params)
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
validate!(request.params)
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
38
|
+
# Validates a given parameter hash.
|
39
|
+
# @note Override #validate if you need to access the entire request.
|
40
|
+
# @param params [Hash] parameters to validate
|
41
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
42
|
+
# @return [void]
|
43
|
+
def validate!(params)
|
44
|
+
attributes = SingleAttributeIterator.new(self, @scope, params)
|
45
|
+
# we collect errors inside array because
|
46
|
+
# there may be more than one error per field
|
47
|
+
array_errors = []
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
attributes.each do |val, attr_name, empty_val, skip_value|
|
50
|
+
next if skip_value
|
51
|
+
next if !@scope.required? && empty_val
|
52
|
+
next unless @scope.meets_dependency?(val, params)
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
begin
|
55
|
+
validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
|
56
|
+
rescue Grape::Exceptions::Validation => e
|
57
|
+
array_errors << e
|
58
|
+
end
|
57
59
|
end
|
58
|
-
end
|
59
60
|
|
60
|
-
|
61
|
-
|
61
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
64
|
+
def self.convert_to_short_name(klass)
|
65
|
+
ret = klass.name.gsub(/::/, '/')
|
66
|
+
ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
67
|
+
ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
68
|
+
ret.tr!('-', '_')
|
69
|
+
ret.downcase!
|
70
|
+
File.basename(ret, '_validator')
|
71
|
+
end
|
71
72
|
|
72
|
-
|
73
|
-
|
73
|
+
def self.inherited(klass)
|
74
|
+
return unless klass.name.present?
|
74
75
|
|
75
|
-
|
76
|
-
|
76
|
+
Validations.register_validator(convert_to_short_name(klass), klass)
|
77
|
+
end
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
def message(default_key = nil)
|
80
|
+
options = instance_variable_get(:@option)
|
81
|
+
options_key?(:message) ? options[:message] : default_key
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
def options_key?(key, options = nil)
|
85
|
+
options = instance_variable_get(:@option) if options.nil?
|
86
|
+
options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
|
87
|
+
end
|
87
88
|
|
88
|
-
|
89
|
-
|
89
|
+
def fail_fast?
|
90
|
+
@fail_fast
|
91
|
+
end
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|