grape 1.6.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/CONTRIBUTING.md +1 -0
  4. data/README.md +9 -1
  5. data/lib/grape/api.rb +12 -0
  6. data/lib/grape/dry_types.rb +12 -0
  7. data/lib/grape/dsl/headers.rb +5 -2
  8. data/lib/grape/dsl/helpers.rb +1 -1
  9. data/lib/grape/middleware/auth/dsl.rb +7 -1
  10. data/lib/grape/middleware/base.rb +1 -1
  11. data/lib/grape/util/json.rb +2 -0
  12. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  13. data/lib/grape/validations/types/array_coercer.rb +0 -2
  14. data/lib/grape/validations/types/dry_type_coercer.rb +1 -10
  15. data/lib/grape/validations/types/json.rb +0 -2
  16. data/lib/grape/validations/types/primitive_coercer.rb +5 -7
  17. data/lib/grape/validations/types/set_coercer.rb +0 -3
  18. data/lib/grape/validations/types.rb +83 -9
  19. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  20. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  21. data/lib/grape/validations/validators/as_validator.rb +14 -0
  22. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  23. data/lib/grape/validations/validators/base.rb +73 -71
  24. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  25. data/lib/grape/validations/validators/default_validator.rb +51 -0
  26. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  27. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  28. data/lib/grape/validations/validators/multiple_params_base.rb +24 -22
  29. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  30. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  31. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  32. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  33. data/lib/grape/validations/validators/values_validator.rb +88 -0
  34. data/lib/grape/version.rb +1 -1
  35. data/lib/grape.rb +59 -24
  36. data/spec/grape/api/custom_validations_spec.rb +77 -46
  37. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  38. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  39. data/spec/grape/api/invalid_format_spec.rb +2 -0
  40. data/spec/grape/api/recognize_path_spec.rb +1 -1
  41. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  42. data/spec/grape/api_remount_spec.rb +16 -15
  43. data/spec/grape/api_spec.rb +317 -193
  44. data/spec/grape/dsl/callbacks_spec.rb +1 -0
  45. data/spec/grape/dsl/headers_spec.rb +39 -9
  46. data/spec/grape/dsl/helpers_spec.rb +3 -2
  47. data/spec/grape/dsl/inside_route_spec.rb +6 -4
  48. data/spec/grape/dsl/logger_spec.rb +16 -18
  49. data/spec/grape/dsl/middleware_spec.rb +1 -0
  50. data/spec/grape/dsl/parameters_spec.rb +1 -0
  51. data/spec/grape/dsl/request_response_spec.rb +1 -0
  52. data/spec/grape/dsl/routing_spec.rb +9 -6
  53. data/spec/grape/endpoint/declared_spec.rb +12 -12
  54. data/spec/grape/endpoint_spec.rb +59 -50
  55. data/spec/grape/entity_spec.rb +13 -13
  56. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  57. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  58. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  59. data/spec/grape/exceptions/validation_spec.rb +5 -3
  60. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  61. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  62. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  63. data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
  64. data/spec/grape/loading_spec.rb +8 -8
  65. data/spec/grape/middleware/auth/dsl_spec.rb +14 -5
  66. data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
  67. data/spec/grape/middleware/base_spec.rb +24 -15
  68. data/spec/grape/middleware/error_spec.rb +1 -0
  69. data/spec/grape/middleware/exception_spec.rb +111 -161
  70. data/spec/grape/middleware/formatter_spec.rb +25 -4
  71. data/spec/grape/middleware/globals_spec.rb +7 -4
  72. data/spec/grape/middleware/stack_spec.rb +11 -11
  73. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  74. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  75. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  76. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  77. data/spec/grape/middleware/versioner_spec.rb +1 -1
  78. data/spec/grape/parser_spec.rb +4 -0
  79. data/spec/grape/path_spec.rb +52 -52
  80. data/spec/grape/presenters/presenter_spec.rb +7 -6
  81. data/spec/grape/request_spec.rb +6 -4
  82. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  83. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  84. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  85. data/spec/grape/util/stackable_values_spec.rb +7 -5
  86. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  87. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
  88. data/spec/grape/validations/params_scope_spec.rb +9 -7
  89. data/spec/grape/validations/single_attribute_iterator_spec.rb +1 -0
  90. data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
  91. data/spec/grape/validations/types_spec.rb +8 -8
  92. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  93. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  94. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  95. data/spec/grape/validations/validators/coerce_spec.rb +10 -12
  96. data/spec/grape/validations/validators/default_spec.rb +72 -78
  97. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  98. data/spec/grape/validations/validators/except_values_spec.rb +1 -1
  99. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  100. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  101. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  102. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  103. data/spec/grape/validations/validators/values_spec.rb +172 -171
  104. data/spec/grape/validations_spec.rb +45 -16
  105. data/spec/integration/eager_load/eager_load_spec.rb +2 -2
  106. data/spec/integration/multi_json/json_spec.rb +1 -1
  107. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  108. data/spec/shared/versioning_examples.rb +10 -7
  109. data/spec/spec_helper.rb +11 -1
  110. metadata +116 -116
  111. data/lib/grape/validations/types/build_coercer.rb +0 -94
  112. data/lib/grape/validations/validators/all_or_none.rb +0 -16
  113. data/lib/grape/validations/validators/allow_blank.rb +0 -18
  114. data/lib/grape/validations/validators/as.rb +0 -12
  115. data/lib/grape/validations/validators/at_least_one_of.rb +0 -15
  116. data/lib/grape/validations/validators/coerce.rb +0 -87
  117. data/lib/grape/validations/validators/default.rb +0 -49
  118. data/lib/grape/validations/validators/exactly_one_of.rb +0 -17
  119. data/lib/grape/validations/validators/except_values.rb +0 -22
  120. data/lib/grape/validations/validators/mutual_exclusion.rb +0 -16
  121. data/lib/grape/validations/validators/presence.rb +0 -13
  122. data/lib/grape/validations/validators/regexp.rb +0 -14
  123. data/lib/grape/validations/validators/same_as.rb +0 -27
  124. data/lib/grape/validations/validators/values.rb +0 -86
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7af0abe5fd1cb203ceda93732e74ded372552f7a859c6a3c6b847d1ac31c9c97
4
- data.tar.gz: 103a38ad4d41eccbfef3240cd8b04fe17f2bc080a4265e4353b43076ce03fb15
3
+ metadata.gz: 1e8e622245d5871dfbaa651eac26abfd8bc4d36accd176f39fcc8ec09200cbe5
4
+ data.tar.gz: d92fe82ca78e6140bc98832671e682c2f090210e1ceaee907d9f3c4f812463b1
5
5
  SHA512:
6
- metadata.gz: 8c8fb085cbaa5ea150e072a54e98546f4336966871225ae5587d70d115da62d420e1f67b2459f83c39c5d29717f311e93d09e0440dd4bf4a75d1f1cec7573022
7
- data.tar.gz: c0f6eb027e3f43c309c59643ceb28c8f7d783466c0663f3b65e93be52a0c3148123110d9aa81dda842027ace7f0f319519b5cdc1fec7102d65811a84fbc2b2ed
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
@@ -35,6 +35,7 @@ bundle exec rake
35
35
  Run tests against all supported versions of Rails.
36
36
 
37
37
  ```
38
+ gem install appraisal
38
39
  appraisal install
39
40
  appraisal rake spec
40
41
  ```
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.0**.
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
@@ -3,8 +3,11 @@
3
3
  module Grape
4
4
  module DSL
5
5
  module Headers
6
- # Set an individual header or retrieve
7
- # all headers that have been set.
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)
@@ -68,7 +68,7 @@ module Grape
68
68
  def define_boolean_in_mod(mod)
69
69
  return if defined? mod::Boolean
70
70
 
71
- mod.const_set('Boolean', Grape::API::Boolean)
71
+ mod.const_set(:Boolean, Grape::API::Boolean)
72
72
  end
73
73
 
74
74
  def inject_api_helpers_to_mod(mod, &block)
@@ -32,7 +32,13 @@ module Grape
32
32
 
33
33
  def http_digest(options = {}, &block)
34
34
  options[:realm] ||= 'API Authorization'
35
- options[:opaque] ||= 'secret'
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)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module Grape
4
6
  if Object.const_defined? :MultiJson
5
7
  Json = ::MultiJson
@@ -65,7 +65,7 @@ module Grape
65
65
  end
66
66
  end
67
67
 
68
- define_method 'to_hash' do
68
+ define_method :to_hash do
69
69
  merge_hash = {}
70
70
  setting_name.each_key { |k| merge_hash[k] = send("#{k}_context").to_hash }
71
71
 
@@ -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
@@ -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 'json'
4
-
5
3
  module Grape
6
4
  module Validations
7
5
  module Types
@@ -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,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
- require_relative 'array_coercer'
5
-
6
3
  module Grape
7
4
  module Validations
8
5
  module Types
@@ -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
- class Base
6
- attr_reader :attrs
5
+ module Validators
6
+ class Base
7
+ attr_reader :attrs
7
8
 
8
- # Creates a new Validator from options specified
9
- # by a +requires+ or +optional+ directive during
10
- # parameter definition.
11
- # @param attrs [Array] names of attributes to which the Validator applies
12
- # @param options [Object] implementation-dependent Validator options
13
- # @param required [Boolean] attribute(s) are required or optional
14
- # @param scope [ParamsScope] parent scope for this Validator
15
- # @param opts [Array] additional validation options
16
- def initialize(attrs, options, required, scope, *opts)
17
- @attrs = Array(attrs)
18
- @option = options
19
- @required = required
20
- @scope = scope
21
- opts = opts.any? ? opts.shift : {}
22
- @fail_fast = opts.fetch(:fail_fast, false)
23
- @allow_blank = opts.fetch(:allow_blank, false)
24
- end
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
- # Validates a given request.
27
- # @note Override #validate! unless you need to access the entire request.
28
- # @param request [Grape::Request] the request currently being handled
29
- # @raise [Grape::Exceptions::Validation] if validation failed
30
- # @return [void]
31
- def validate(request)
32
- return unless @scope.should_validate?(request.params)
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
- validate!(request.params)
35
- end
35
+ validate!(request.params)
36
+ end
36
37
 
37
- # Validates a given parameter hash.
38
- # @note Override #validate if you need to access the entire request.
39
- # @param params [Hash] parameters to validate
40
- # @raise [Grape::Exceptions::Validation] if validation failed
41
- # @return [void]
42
- def validate!(params)
43
- attributes = SingleAttributeIterator.new(self, @scope, params)
44
- # we collect errors inside array because
45
- # there may be more than one error per field
46
- array_errors = []
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
- attributes.each do |val, attr_name, empty_val, skip_value|
49
- next if skip_value
50
- next if !@scope.required? && empty_val
51
- next unless @scope.meets_dependency?(val, params)
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
- begin
54
- validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
55
- rescue Grape::Exceptions::Validation => e
56
- array_errors << e
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
- raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
61
- end
61
+ raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
62
+ end
62
63
 
63
- def self.convert_to_short_name(klass)
64
- ret = klass.name.gsub(/::/, '/')
65
- ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
66
- ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
67
- ret.tr!('-', '_')
68
- ret.downcase!
69
- File.basename(ret, '_validator')
70
- end
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
- def self.inherited(klass)
73
- return unless klass.name.present?
73
+ def self.inherited(klass)
74
+ return unless klass.name.present?
74
75
 
75
- Validations.register_validator(convert_to_short_name(klass), klass)
76
- end
76
+ Validations.register_validator(convert_to_short_name(klass), klass)
77
+ end
77
78
 
78
- def message(default_key = nil)
79
- options = instance_variable_get(:@option)
80
- options_key?(:message) ? options[:message] : default_key
81
- end
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
- def options_key?(key, options = nil)
84
- options = instance_variable_get(:@option) if options.nil?
85
- options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
86
- end
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
- def fail_fast?
89
- @fail_fast
89
+ def fail_fast?
90
+ @fail_fast
91
+ end
90
92
  end
91
93
  end
92
94
  end