grape 1.6.0 → 1.6.1

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.
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