grape 2.3.0 → 3.0.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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -0
  3. data/CONTRIBUTING.md +2 -10
  4. data/README.md +106 -43
  5. data/UPGRADING.md +90 -1
  6. data/grape.gemspec +4 -4
  7. data/lib/grape/api/instance.rb +51 -73
  8. data/lib/grape/api.rb +56 -89
  9. data/lib/grape/cookies.rb +31 -25
  10. data/lib/grape/dry_types.rb +48 -4
  11. data/lib/grape/dsl/callbacks.rb +8 -58
  12. data/lib/grape/dsl/desc.rb +8 -67
  13. data/lib/grape/dsl/headers.rb +1 -1
  14. data/lib/grape/dsl/helpers.rb +60 -65
  15. data/lib/grape/dsl/inside_route.rb +26 -61
  16. data/lib/grape/dsl/logger.rb +3 -6
  17. data/lib/grape/dsl/middleware.rb +22 -40
  18. data/lib/grape/dsl/parameters.rb +10 -19
  19. data/lib/grape/dsl/request_response.rb +136 -139
  20. data/lib/grape/dsl/routing.rb +230 -194
  21. data/lib/grape/dsl/settings.rb +22 -134
  22. data/lib/grape/dsl/validations.rb +37 -45
  23. data/lib/grape/endpoint.rb +91 -126
  24. data/lib/grape/error_formatter/base.rb +2 -0
  25. data/lib/grape/exceptions/base.rb +1 -1
  26. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  27. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  28. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  29. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  30. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  31. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  32. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  33. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
  34. data/lib/grape/extensions/hash.rb +2 -1
  35. data/lib/grape/extensions/hashie/mash.rb +3 -5
  36. data/lib/grape/locale/en.yml +44 -44
  37. data/lib/grape/middleware/auth/base.rb +11 -32
  38. data/lib/grape/middleware/auth/dsl.rb +22 -29
  39. data/lib/grape/middleware/base.rb +30 -11
  40. data/lib/grape/middleware/error.rb +14 -32
  41. data/lib/grape/middleware/formatter.rb +40 -72
  42. data/lib/grape/middleware/stack.rb +28 -38
  43. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -4
  44. data/lib/grape/middleware/versioner/base.rb +30 -56
  45. data/lib/grape/middleware/versioner/header.rb +2 -2
  46. data/lib/grape/middleware/versioner/param.rb +2 -3
  47. data/lib/grape/middleware/versioner/path.rb +1 -1
  48. data/lib/grape/namespace.rb +11 -0
  49. data/lib/grape/params_builder/base.rb +20 -0
  50. data/lib/grape/params_builder/hash.rb +11 -0
  51. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  52. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  53. data/lib/grape/params_builder.rb +32 -0
  54. data/lib/grape/request.rb +161 -22
  55. data/lib/grape/router/route.rb +1 -1
  56. data/lib/grape/router.rb +27 -8
  57. data/lib/grape/util/api_description.rb +56 -0
  58. data/lib/grape/util/base_inheritable.rb +5 -2
  59. data/lib/grape/util/inheritable_setting.rb +7 -0
  60. data/lib/grape/util/media_type.rb +1 -1
  61. data/lib/grape/util/registry.rb +1 -1
  62. data/lib/grape/validations/contract_scope.rb +2 -2
  63. data/lib/grape/validations/params_documentation.rb +50 -0
  64. data/lib/grape/validations/params_scope.rb +46 -56
  65. data/lib/grape/validations/types/array_coercer.rb +2 -3
  66. data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
  67. data/lib/grape/validations/types/primitive_coercer.rb +1 -28
  68. data/lib/grape/validations/types.rb +10 -25
  69. data/lib/grape/validations/validators/base.rb +2 -9
  70. data/lib/grape/validations/validators/except_values_validator.rb +1 -1
  71. data/lib/grape/validations/validators/presence_validator.rb +1 -1
  72. data/lib/grape/validations/validators/regexp_validator.rb +1 -1
  73. data/lib/grape/version.rb +1 -1
  74. data/lib/grape.rb +18 -9
  75. metadata +35 -20
  76. data/lib/grape/api/helpers.rb +0 -9
  77. data/lib/grape/dsl/api.rb +0 -19
  78. data/lib/grape/dsl/configuration.rb +0 -15
  79. data/lib/grape/error_formatter/jsonapi.rb +0 -7
  80. data/lib/grape/http/headers.rb +0 -56
  81. data/lib/grape/middleware/helpers.rb +0 -12
  82. data/lib/grape/parser/jsonapi.rb +0 -7
  83. data/lib/grape/types/invalid_value.rb +0 -8
  84. data/lib/grape/util/lazy/object.rb +0 -45
  85. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  86. data/lib/grape/validations/attributes_doc.rb +0 -60
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ # Documents parameters of an endpoint. If documentation isn't needed (for instance, it is an
6
+ # internal API), the class only cleans up attributes to avoid junk in RAM.
7
+
8
+ module ParamsDocumentation
9
+ def document_params(attrs, validations, type = nil, values = nil, except_values = nil)
10
+ return validations.except!(:desc, :description, :documentation) if @api.inheritable_setting.namespace_inheritable[:do_not_document]
11
+
12
+ documented_attrs = attrs.each_with_object({}) do |name, memo|
13
+ memo[full_name(name)] = extract_details(validations, type, values, except_values)
14
+ end
15
+ @api.inheritable_setting.namespace_stackable[:params] = documented_attrs
16
+ end
17
+
18
+ private
19
+
20
+ def extract_details(validations, type, values, except_values)
21
+ {}.tap do |details|
22
+ details[:required] = validations.key?(:presence)
23
+ details[:type] = TypeCache[type] if type
24
+ details[:values] = values if values
25
+ details[:except_values] = except_values if except_values
26
+ details[:default] = validations[:default] if validations.key?(:default)
27
+ if validations.key?(:length)
28
+ details[:min_length] = validations[:length][:min] if validations[:length].key?(:min)
29
+ details[:max_length] = validations[:length][:max] if validations[:length].key?(:max)
30
+ end
31
+
32
+ desc = validations.delete(:desc) || validations.delete(:description)
33
+ details[:desc] = desc if desc
34
+
35
+ documentation = validations.delete(:documentation)
36
+ details[:documentation] = documentation if documentation
37
+ end
38
+ end
39
+
40
+ class TypeCache < Grape::Util::Cache
41
+ def initialize
42
+ super
43
+ @cache = Hash.new do |h, type|
44
+ h[type] = type.to_s
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -4,9 +4,10 @@ module Grape
4
4
  module Validations
5
5
  class ParamsScope
6
6
  attr_accessor :element, :parent, :index
7
- attr_reader :type
7
+ attr_reader :type, :params_meeting_dependency
8
8
 
9
9
  include Grape::DSL::Parameters
10
+ include Grape::Validations::ParamsDocumentation
10
11
 
11
12
  # There are a number of documentation options on entities that don't have
12
13
  # corresponding validators. Since there is nowhere that enumerates them all,
@@ -67,6 +68,7 @@ module Grape
67
68
  @type = opts[:type]
68
69
  @group = opts[:group]
69
70
  @dependent_on = opts[:dependent_on]
71
+ @params_meeting_dependency = []
70
72
  @declared_params = []
71
73
  @index = nil
72
74
 
@@ -94,7 +96,11 @@ module Grape
94
96
  def meets_dependency?(params, request_params)
95
97
  return true unless @dependent_on
96
98
  return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
97
- return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
99
+
100
+ if params.is_a?(Array)
101
+ @params_meeting_dependency = params.flatten.filter { |param| meets_dependency?(param, request_params) }
102
+ return @params_meeting_dependency.present?
103
+ end
98
104
 
99
105
  meets_hash_dependency?(params)
100
106
  end
@@ -127,7 +133,7 @@ module Grape
127
133
  def full_name(name, index: nil)
128
134
  if nested?
129
135
  # Find our containing element's name, and append ours.
130
- "#{@parent.full_name(@element)}#{brackets(@index || index)}#{brackets(name)}"
136
+ "#{@parent.full_name(@element)}#{brackets(index || @index)}#{brackets(name)}"
131
137
  elsif lateral?
132
138
  # Find the name of the element as if it was at the same nesting level
133
139
  # as our parent. We need to forward our index upward to achieve this.
@@ -205,28 +211,32 @@ module Grape
205
211
  # @param new_name [String, Symbol] the new name of the parameter (the
206
212
  # renamed name, with the +as: ...+ semantic)
207
213
  def push_renamed_param(path, new_name)
208
- base = @api.route_setting(:renamed_params) || {}
214
+ api_route_setting = @api.inheritable_setting.route
215
+ base = api_route_setting[:renamed_params] || {}
209
216
  base[Array(path).map(&:to_s)] = new_name.to_s
210
- @api.route_setting(:renamed_params, base)
217
+ api_route_setting[:renamed_params] = base
211
218
  end
212
219
 
213
220
  def require_required_and_optional_fields(context, opts)
221
+ except_fields = Array.wrap(opts[:except])
222
+ using_fields = opts[:using].keys.delete_if { |f| except_fields.include?(f) }
223
+
214
224
  if context == :all
215
- optional_fields = Array.wrap(opts[:except])
216
- required_fields = opts[:using].keys.delete_if { |f| optional_fields.include?(f) }
225
+ optional_fields = except_fields
226
+ required_fields = using_fields
217
227
  else # context == :none
218
- required_fields = Array.wrap(opts[:except])
219
- optional_fields = opts[:using].keys.delete_if { |f| required_fields.include?(f) }
228
+ required_fields = except_fields
229
+ optional_fields = using_fields
220
230
  end
221
231
  required_fields.each do |field|
222
232
  field_opts = opts[:using][field]
223
233
  raise ArgumentError, "required field not exist: #{field}" unless field_opts
224
234
 
225
- requires(field, field_opts)
235
+ requires(field, **field_opts)
226
236
  end
227
237
  optional_fields.each do |field|
228
238
  field_opts = opts[:using][field]
229
- optional(field, field_opts) if field_opts
239
+ optional(field, **field_opts) if field_opts
230
240
  end
231
241
  end
232
242
 
@@ -238,7 +248,7 @@ module Grape
238
248
  end
239
249
  optional_fields.each do |field|
240
250
  field_opts = opts[:using][field]
241
- optional(field, field_opts) if field_opts
251
+ optional(field, **field_opts) if field_opts
242
252
  end
243
253
  end
244
254
 
@@ -255,9 +265,9 @@ module Grape
255
265
  # @param optional [Boolean] whether the parameter this are nested under
256
266
  # is optional or not (and hence, whether this block's params will be).
257
267
  # @yield parameter scope
258
- def new_scope(attrs, optional = false, &block)
268
+ def new_scope(attrs, opts, optional = false, &block)
259
269
  # if required params are grouped and no type or unsupported type is provided, raise an error
260
- type = attrs[1] ? attrs[1][:type] : nil
270
+ type = opts[:type]
261
271
  if attrs.first && !optional
262
272
  raise Grape::Exceptions::MissingGroupType if type.nil?
263
273
  raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
@@ -266,7 +276,7 @@ module Grape
266
276
  self.class.new(
267
277
  api: @api,
268
278
  element: attrs.first,
269
- element_renamed: attrs[1][:as],
279
+ element_renamed: opts[:as],
270
280
  parent: self,
271
281
  optional: optional,
272
282
  type: type || Array,
@@ -310,7 +320,7 @@ module Grape
310
320
  if nested?
311
321
  @parent.push_declared_params [element => @declared_params]
312
322
  else
313
- @api.namespace_stackable(:declared_params, @declared_params)
323
+ @api.inheritable_setting.namespace_stackable[:declared_params] = @declared_params
314
324
  end
315
325
 
316
326
  # params were stored in settings, it can be cleaned from the params scope
@@ -318,56 +328,40 @@ module Grape
318
328
  end
319
329
 
320
330
  def validates(attrs, validations)
321
- doc = AttributesDoc.new @api, self
322
- doc.extract_details validations
323
-
324
331
  coerce_type = infer_coercion(validations)
325
-
326
- doc.type = coerce_type
327
-
332
+ required = validations.key?(:presence)
328
333
  default = validations[:default]
329
-
330
- if (values_hash = validations[:values]).is_a? Hash
331
- values = values_hash[:value]
332
- # NB: excepts is deprecated
333
- excepts = values_hash[:except]
334
- else
335
- values = validations[:values]
336
- end
337
-
338
- doc.values = values
339
-
340
- except_values = options_key?(:except_values, :value, validations) ? validations[:except_values][:value] : validations[:except_values]
334
+ values = validations[:values].is_a?(Hash) ? validations.dig(:values, :value) : validations[:values]
335
+ except_values = validations[:except_values].is_a?(Hash) ? validations.dig(:except_values, :value) : validations[:except_values]
341
336
 
342
337
  # NB. values and excepts should be nil, Proc, Array, or Range.
343
338
  # Specifically, values should NOT be a Hash
344
-
345
339
  # use values or excepts to guess coerce type when stated type is Array
346
- coerce_type = guess_coerce_type(coerce_type, values, except_values, excepts)
340
+ coerce_type = guess_coerce_type(coerce_type, values, except_values)
347
341
 
348
342
  # default value should be present in values array, if both exist and are not procs
349
- check_incompatible_option_values(default, values, except_values, excepts)
343
+ check_incompatible_option_values(default, values, except_values)
350
344
 
351
345
  # type should be compatible with values array, if both exist
352
- validate_value_coercion(coerce_type, values, except_values, excepts)
346
+ validate_value_coercion(coerce_type, values, except_values)
353
347
 
354
- doc.document attrs
348
+ document_params attrs, validations, coerce_type, values, except_values
355
349
 
356
350
  opts = derive_validator_options(validations)
357
351
 
358
352
  # Validate for presence before any other validators
359
- validates_presence(validations, attrs, doc, opts)
353
+ validates_presence(validations, attrs, opts)
360
354
 
361
355
  # Before we run the rest of the validators, let's handle
362
356
  # whatever coercion so that we are working with correctly
363
357
  # type casted values
364
- coerce_type validations, attrs, doc, opts
358
+ coerce_type validations, attrs, required, opts
365
359
 
366
360
  validations.each do |type, options|
367
361
  # Don't try to look up validators for documentation params that don't have one.
368
362
  next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)
369
363
 
370
- validate(type, options, attrs, doc, opts)
364
+ validate(type, options, attrs, required, opts)
371
365
  end
372
366
  end
373
367
 
@@ -431,7 +425,7 @@ module Grape
431
425
  # composited from more than one +requires+/+optional+
432
426
  # parameter, and needs to be run before most other
433
427
  # validations.
434
- def coerce_type(validations, attrs, doc, opts)
428
+ def coerce_type(validations, attrs, required, opts)
435
429
  check_coerce_with(validations)
436
430
 
437
431
  return unless validations.key?(:coerce)
@@ -441,7 +435,7 @@ module Grape
441
435
  method: validations[:coerce_with],
442
436
  message: validations[:coerce_message]
443
437
  }
444
- validate('coerce', coerce_options, attrs, doc, opts)
438
+ validate('coerce', coerce_options, attrs, required, opts)
445
439
  validations.delete(:coerce_with)
446
440
  validations.delete(:coerce)
447
441
  validations.delete(:coerce_message)
@@ -457,30 +451,26 @@ module Grape
457
451
  coerce_type
458
452
  end
459
453
 
460
- def check_incompatible_option_values(default, values, except_values, excepts)
454
+ def check_incompatible_option_values(default, values, except_values)
461
455
  return unless default && !default.is_a?(Proc)
462
456
 
463
457
  raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && !Array(default).all? { |def_val| values.include?(def_val) }
464
458
 
465
- if except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
466
- raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
467
- end
459
+ return unless except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
468
460
 
469
- return unless excepts && !excepts.is_a?(Proc)
470
- raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, excepts) \
471
- unless Array(default).none? { |def_val| excepts.include?(def_val) }
461
+ raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
472
462
  end
473
463
 
474
- def validate(type, options, attrs, doc, opts)
464
+ def validate(type, options, attrs, required, opts)
475
465
  validator_options = {
476
466
  attributes: attrs,
477
467
  options: options,
478
- required: doc.required,
468
+ required: required,
479
469
  params_scope: self,
480
470
  opts: opts,
481
471
  validator_class: Validations.require_validator(type)
482
472
  }
483
- @api.namespace_stackable(:validations, validator_options)
473
+ @api.inheritable_setting.namespace_stackable[:validations] = validator_options
484
474
  end
485
475
 
486
476
  def validate_value_coercion(coerce_type, *values_list)
@@ -522,10 +512,10 @@ module Grape
522
512
  }
523
513
  end
524
514
 
525
- def validates_presence(validations, attrs, doc, opts)
515
+ def validates_presence(validations, attrs, opts)
526
516
  return unless validations.key?(:presence) && validations[:presence]
527
517
 
528
- validate('presence', validations.delete(:presence), attrs, doc, opts)
518
+ validate('presence', validations.delete(:presence), attrs, true, opts)
529
519
  validations.delete(:message) if validations.key?(:message)
530
520
  end
531
521
  end
@@ -7,15 +7,14 @@ module Grape
7
7
  # an array of arrays of integers.
8
8
  #
9
9
  # It could've been possible to use an +of+
10
- # method (https://dry-rb.org/gems/dry-types/1.2/array-with-member/)
10
+ # method (https://dry-rb.org/gems/dry-types/main/array-with-member/)
11
11
  # provided by dry-types. Unfortunately, it doesn't work for Grape because of
12
12
  # behavior of Virtus which was used earlier, a `Grape::Validations::Types::PrimitiveCoercer`
13
13
  # maintains Virtus behavior in coercing.
14
14
  class ArrayCoercer < DryTypeCoercer
15
15
  def initialize(type, strict = false)
16
16
  super
17
-
18
- @coercer = scope::Array
17
+ @coercer = strict ? DryTypes::Strict::Array : DryTypes::Params::Array
19
18
  @subtype = type.first
20
19
  end
21
20
 
@@ -1,19 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module DryTypes
4
- # Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
5
- # a container in this case. Check documentation for more information
6
- # https://dry-rb.org/gems/dry-types/1.2/getting-started/
7
- include Dry.Types()
8
- end
9
-
10
3
  module Grape
11
4
  module Validations
12
5
  module Types
13
6
  # A base class for classes which must identify a coercer to be used.
14
7
  # If the +strict+ argument is true, it won't coerce the given value
15
8
  # but check its type. More information there
16
- # https://dry-rb.org/gems/dry-types/1.2/built-in-types/
9
+ # https://dry-rb.org/gems/dry-types/main/built-in-types/
17
10
  class DryTypeCoercer
18
11
  class << self
19
12
  # Returns a collection coercer which corresponds to a given type.
@@ -42,7 +35,7 @@ module Grape
42
35
  def initialize(type, strict = false)
43
36
  @type = type
44
37
  @strict = strict
45
- @scope = strict ? DryTypes::Strict : DryTypes::Params
38
+ @cache_coercer = strict ? DryTypes::StrictCache : DryTypes::ParamsCache
46
39
  end
47
40
 
48
41
  # Coerces the given value to a type which was specified during
@@ -53,13 +46,13 @@ module Grape
53
46
  return if val.nil?
54
47
 
55
48
  @coercer[val]
56
- rescue Dry::Types::CoercionError => _e
49
+ rescue Dry::Types::CoercionError
57
50
  InvalidValue.new
58
51
  end
59
52
 
60
53
  protected
61
54
 
62
- attr_reader :scope, :type, :strict
55
+ attr_reader :type, :strict, :cache_coercer
63
56
  end
64
57
  end
65
58
  end
@@ -7,37 +7,10 @@ module Grape
7
7
  # initialization. When +strict+ is true, it doesn't coerce a value but check
8
8
  # that it has the proper type.
9
9
  class PrimitiveCoercer < DryTypeCoercer
10
- MAPPING = {
11
- Grape::API::Boolean => DryTypes::Params::Bool,
12
- BigDecimal => DryTypes::Params::Decimal,
13
- Numeric => DryTypes::Params::Integer | DryTypes::Params::Float | DryTypes::Params::Decimal,
14
- TrueClass => DryTypes::Params::Bool.constrained(eql: true),
15
- FalseClass => DryTypes::Params::Bool.constrained(eql: false),
16
-
17
- # unfortunately, a +Params+ scope doesn't contain String
18
- String => DryTypes::Coercible::String
19
- }.freeze
20
-
21
- STRICT_MAPPING = {
22
- Grape::API::Boolean => DryTypes::Strict::Bool,
23
- BigDecimal => DryTypes::Strict::Decimal,
24
- Numeric => DryTypes::Strict::Integer | DryTypes::Strict::Float | DryTypes::Strict::Decimal,
25
- TrueClass => DryTypes::Strict::Bool.constrained(eql: true),
26
- FalseClass => DryTypes::Strict::Bool.constrained(eql: false)
27
- }.freeze
28
-
29
10
  def initialize(type, strict = false)
30
11
  super
31
12
 
32
- @type = type
33
-
34
- @coercer = (strict ? STRICT_MAPPING : MAPPING).fetch(type) do
35
- scope.const_get(type.name, false)
36
- rescue NameError
37
- raise ArgumentError, "type #{type} should support coercion via `[]`" unless type.respond_to?(:[])
38
-
39
- type
40
- end
13
+ @coercer = cache_coercer[type]
41
14
  end
42
15
 
43
16
  def call(val)
@@ -155,9 +155,10 @@ module Grape
155
155
  # @return [Object] object to be used
156
156
  # for coercion and type validation
157
157
  def build_coercer(type, method: nil, strict: false)
158
- cache_instance(type, method, strict) do
159
- create_coercer_instance(type, method, strict)
160
- end
158
+ # no cache since unique
159
+ return create_coercer_instance(type, method, strict) if method.respond_to?(:call)
160
+
161
+ CoercerCache[[type, method, strict]]
161
162
  end
162
163
 
163
164
  def create_coercer_instance(type, method, strict)
@@ -184,30 +185,14 @@ module Grape
184
185
  end
185
186
  end
186
187
 
187
- def cache_instance(type, method, strict, &_block)
188
- key = cache_key(type, method, strict)
189
-
190
- return @__cache[key] if @__cache.key?(key)
191
-
192
- instance = yield
193
-
194
- @__cache_write_lock.synchronize do
195
- @__cache[key] = instance
188
+ class CoercerCache < Grape::Util::Cache
189
+ def initialize
190
+ super
191
+ @cache = Hash.new do |h, (type, method, strict)|
192
+ h[[type, method, strict]] = Grape::Validations::Types.create_coercer_instance(type, method, strict)
193
+ end
196
194
  end
197
-
198
- instance
199
195
  end
200
-
201
- def cache_key(type, method, strict)
202
- [type, method, strict].each_with_object(+'_') do |val, memo|
203
- next if val.nil?
204
-
205
- memo << '_' << val.to_s
206
- end
207
- end
208
-
209
- instance_variable_set(:@__cache, {})
210
- instance_variable_set(:@__cache_write_lock, Mutex.new)
211
196
  end
212
197
  end
213
198
  end
@@ -49,7 +49,7 @@ module Grape
49
49
  next if !@scope.required? && empty_val
50
50
  next unless @scope.meets_dependency?(val, params)
51
51
 
52
- validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
52
+ validate_param!(attr_name, val) if @required || val.try(:key?, attr_name)
53
53
  rescue Grape::Exceptions::Validation => e
54
54
  array_errors << e
55
55
  end
@@ -69,7 +69,7 @@ module Grape
69
69
 
70
70
  def options_key?(key, options = nil)
71
71
  options = instance_variable_get(:@option) if options.nil?
72
- options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
72
+ options.try(:key?, key) && !options[key].nil?
73
73
  end
74
74
 
75
75
  def fail_fast?
@@ -79,10 +79,3 @@ module Grape
79
79
  end
80
80
  end
81
81
  end
82
-
83
- Grape::Validations::Base = Class.new(Grape::Validations::Validators::Base) do
84
- def self.inherited(*)
85
- Grape.deprecator.warn 'Grape::Validations::Base is deprecated! Use Grape::Validations::Validators::Base instead.'
86
- super
87
- end
88
- end
@@ -10,7 +10,7 @@ module Grape
10
10
  end
11
11
 
12
12
  def validate_param!(attr_name, params)
13
- return unless params.respond_to?(:key?) && params.key?(attr_name)
13
+ return unless params.try(:key?, attr_name)
14
14
 
15
15
  excepts = @except.is_a?(Proc) ? @except.call : @except
16
16
  return if excepts.nil?
@@ -5,7 +5,7 @@ module Grape
5
5
  module Validators
6
6
  class PresenceValidator < Base
7
7
  def validate_param!(attr_name, params)
8
- return if params.respond_to?(:key?) && params.key?(attr_name)
8
+ return if params.try(:key?, attr_name)
9
9
 
10
10
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
11
11
  end
@@ -5,7 +5,7 @@ module Grape
5
5
  module Validators
6
6
  class RegexpValidator < Base
7
7
  def validate_param!(attr_name, params)
8
- return unless params.respond_to?(:key?) && params.key?(attr_name)
8
+ return unless params.try(:key?, attr_name)
9
9
  return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.scrub.match?((options_key?(:value) ? @option[:value] : @option)) }
10
10
 
11
11
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
data/lib/grape/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Grape
4
4
  # The current version of Grape.
5
- VERSION = '2.3.0'
5
+ VERSION = '3.0.0'
6
6
  end
data/lib/grape.rb CHANGED
@@ -3,15 +3,14 @@
3
3
  require 'logger'
4
4
  require 'active_support'
5
5
  require 'active_support/concern'
6
- require 'active_support/configurable'
7
6
  require 'active_support/version'
8
- require 'active_support/isolated_execution_state' if ActiveSupport::VERSION::MAJOR > 6
7
+ require 'active_support/isolated_execution_state'
9
8
  require 'active_support/core_ext/array/conversions'
10
- require 'active_support/core_ext/array/extract_options'
11
9
  require 'active_support/core_ext/array/wrap'
12
10
  require 'active_support/core_ext/enumerable'
13
11
  require 'active_support/core_ext/hash/conversions'
14
12
  require 'active_support/core_ext/hash/deep_merge'
13
+ require 'active_support/core_ext/hash/deep_transform_values'
15
14
  require 'active_support/core_ext/hash/except'
16
15
  require 'active_support/core_ext/hash/indifferent_access'
17
16
  require 'active_support/core_ext/hash/keys'
@@ -20,6 +19,7 @@ require 'active_support/core_ext/hash/slice'
20
19
  require 'active_support/core_ext/module/delegation'
21
20
  require 'active_support/core_ext/object/blank'
22
21
  require 'active_support/core_ext/object/deep_dup'
22
+ require 'active_support/core_ext/object/try'
23
23
  require 'active_support/core_ext/object/duplicable'
24
24
  require 'active_support/core_ext/string/output_safety'
25
25
  require 'active_support/core_ext/string/exclude'
@@ -27,6 +27,7 @@ require 'active_support/deprecation'
27
27
  require 'active_support/inflector'
28
28
  require 'active_support/ordered_options'
29
29
  require 'active_support/notifications'
30
+ require 'dry-configurable'
30
31
 
31
32
  require 'English'
32
33
  require 'bigdecimal'
@@ -56,16 +57,24 @@ loader.setup
56
57
  I18n.load_path << File.expand_path('grape/locale/en.yml', __dir__)
57
58
 
58
59
  module Grape
59
- include ActiveSupport::Configurable
60
+ extend Dry::Configurable
61
+
62
+ setting :param_builder, default: :hash_with_indifferent_access
63
+ setting :lint, default: false
64
+
65
+ HTTP_SUPPORTED_METHODS = [
66
+ Rack::GET,
67
+ Rack::POST,
68
+ Rack::PUT,
69
+ Rack::PATCH,
70
+ Rack::DELETE,
71
+ Rack::HEAD,
72
+ Rack::OPTIONS
73
+ ].freeze
60
74
 
61
75
  def self.deprecator
62
76
  @deprecator ||= ActiveSupport::Deprecation.new('2.0', 'Grape')
63
77
  end
64
-
65
- configure do |config|
66
- config.param_builder = Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
67
- config.compile_methods!
68
- end
69
78
  end
70
79
 
71
80
  # https://api.rubyonrails.org/classes/ActiveSupport/Deprecation.html