grape 2.1.3 → 2.3.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +9 -7
  4. data/UPGRADING.md +27 -0
  5. data/grape.gemspec +7 -6
  6. data/lib/grape/api/instance.rb +22 -58
  7. data/lib/grape/api.rb +2 -11
  8. data/lib/grape/content_types.rb +13 -8
  9. data/lib/grape/dsl/desc.rb +27 -24
  10. data/lib/grape/dsl/helpers.rb +7 -3
  11. data/lib/grape/dsl/inside_route.rb +18 -24
  12. data/lib/grape/dsl/parameters.rb +2 -2
  13. data/lib/grape/dsl/request_response.rb +14 -18
  14. data/lib/grape/dsl/routing.rb +5 -12
  15. data/lib/grape/endpoint.rb +90 -82
  16. data/lib/grape/error_formatter/base.rb +51 -21
  17. data/lib/grape/error_formatter/json.rb +7 -15
  18. data/lib/grape/error_formatter/jsonapi.rb +7 -0
  19. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  20. data/lib/grape/error_formatter/txt.rb +13 -20
  21. data/lib/grape/error_formatter/xml.rb +3 -13
  22. data/lib/grape/error_formatter.rb +5 -25
  23. data/lib/grape/exceptions/base.rb +18 -30
  24. data/lib/grape/exceptions/validation.rb +5 -4
  25. data/lib/grape/exceptions/validation_errors.rb +2 -2
  26. data/lib/grape/formatter/base.rb +16 -0
  27. data/lib/grape/formatter/json.rb +4 -6
  28. data/lib/grape/formatter/serializable_hash.rb +1 -1
  29. data/lib/grape/formatter/txt.rb +3 -5
  30. data/lib/grape/formatter/xml.rb +4 -6
  31. data/lib/grape/formatter.rb +7 -25
  32. data/lib/grape/http/headers.rb +1 -0
  33. data/lib/grape/locale/en.yml +1 -0
  34. data/lib/grape/middleware/base.rb +14 -13
  35. data/lib/grape/middleware/error.rb +13 -9
  36. data/lib/grape/middleware/formatter.rb +3 -3
  37. data/lib/grape/middleware/versioner/accept_version_header.rb +7 -30
  38. data/lib/grape/middleware/versioner/base.rb +82 -0
  39. data/lib/grape/middleware/versioner/header.rb +89 -10
  40. data/lib/grape/middleware/versioner/param.rb +4 -22
  41. data/lib/grape/middleware/versioner/path.rb +10 -32
  42. data/lib/grape/middleware/versioner.rb +7 -14
  43. data/lib/grape/namespace.rb +1 -1
  44. data/lib/grape/parser/base.rb +16 -0
  45. data/lib/grape/parser/json.rb +6 -8
  46. data/lib/grape/parser/jsonapi.rb +7 -0
  47. data/lib/grape/parser/xml.rb +6 -8
  48. data/lib/grape/parser.rb +5 -23
  49. data/lib/grape/path.rb +39 -56
  50. data/lib/grape/request.rb +2 -2
  51. data/lib/grape/router/base_route.rb +2 -2
  52. data/lib/grape/router/greedy_route.rb +2 -2
  53. data/lib/grape/router/pattern.rb +23 -18
  54. data/lib/grape/router/route.rb +13 -5
  55. data/lib/grape/router.rb +5 -5
  56. data/lib/grape/util/registry.rb +27 -0
  57. data/lib/grape/validations/contract_scope.rb +2 -39
  58. data/lib/grape/validations/params_scope.rb +7 -11
  59. data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
  60. data/lib/grape/validations/validator_factory.rb +2 -2
  61. data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
  62. data/lib/grape/validations/validators/base.rb +5 -9
  63. data/lib/grape/validations/validators/coerce_validator.rb +1 -1
  64. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  65. data/lib/grape/validations/validators/default_validator.rb +1 -1
  66. data/lib/grape/validations/validators/except_values_validator.rb +1 -1
  67. data/lib/grape/validations/validators/length_validator.rb +11 -4
  68. data/lib/grape/validations/validators/regexp_validator.rb +1 -1
  69. data/lib/grape/validations/validators/values_validator.rb +15 -57
  70. data/lib/grape/validations.rb +8 -17
  71. data/lib/grape/version.rb +1 -1
  72. data/lib/grape.rb +1 -1
  73. metadata +15 -12
  74. data/lib/grape/util/accept_header_handler.rb +0 -105
  75. data/lib/grape/util/registrable.rb +0 -15
  76. data/lib/grape/validations/types/build_coercer.rb +0 -92
@@ -4,7 +4,7 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class DefaultValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
7
+ def initialize(attrs, options, required, scope, opts = {})
8
8
  @default = options
9
9
  super
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class ExceptValuesValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
7
+ def initialize(attrs, options, required, scope, opts)
8
8
  @except = options.is_a?(Hash) ? options[:value] : options
9
9
  super
10
10
  end
@@ -4,23 +4,28 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class LengthValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
7
+ def initialize(attrs, options, required, scope, opts)
8
8
  @min = options[:min]
9
9
  @max = options[:max]
10
+ @is = options[:is]
10
11
 
11
12
  super
12
13
 
13
14
  raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
14
15
  raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
15
16
  raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
17
+
18
+ return if @is.nil?
19
+ raise ArgumentError, 'is must be an integer greater than zero' if !@is.is_a?(Integer) || !@is.positive?
20
+ raise ArgumentError, 'is cannot be combined with min or max' if !@min.nil? || !@max.nil?
16
21
  end
17
22
 
18
23
  def validate_param!(attr_name, params)
19
24
  param = params[attr_name]
20
25
 
21
- raise ArgumentError, "parameter #{param} does not support #length" unless param.respond_to?(:length)
26
+ return unless param.respond_to?(:length)
22
27
 
23
- return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max)
28
+ return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max) || (!@is.nil? && param.length != @is)
24
29
 
25
30
  raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
26
31
  end
@@ -32,8 +37,10 @@ module Grape
32
37
  format I18n.t(:length, scope: 'grape.errors.messages'), min: @min, max: @max
33
38
  elsif @min
34
39
  format I18n.t(:length_min, scope: 'grape.errors.messages'), min: @min
35
- else
40
+ elsif @max
36
41
  format I18n.t(:length_max, scope: 'grape.errors.messages'), max: @max
42
+ else
43
+ format I18n.t(:length_is, scope: 'grape.errors.messages'), is: @is
37
44
  end
38
45
  end
39
46
  end
@@ -6,7 +6,7 @@ module Grape
6
6
  class RegexpValidator < Base
7
7
  def validate_param!(attr_name, params)
8
8
  return unless params.respond_to?(:key?) && params.key?(attr_name)
9
- return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.match?((options_key?(:value) ? @option[:value] : @option)) }
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))
12
12
  end
@@ -4,23 +4,8 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class ValuesValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
8
- if options.is_a?(Hash)
9
- @excepts = options[:except]
10
- @values = options[:value]
11
- @proc = options[:proc]
12
-
13
- Grape.deprecator.warn('The values validator except option is deprecated. Use the except validator instead.') if @excepts
14
-
15
- raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
16
-
17
- Grape.deprecator.warn('The values validator proc option is deprecated. The lambda expression can now be assigned directly to values.') if @proc
18
- else
19
- @excepts = nil
20
- @values = nil
21
- @proc = nil
22
- @values = options
23
- end
7
+ def initialize(attrs, options, required, scope, opts)
8
+ @values = options.is_a?(Hash) ? options[:value] : options
24
9
  super
25
10
  end
26
11
 
@@ -31,57 +16,34 @@ module Grape
31
16
 
32
17
  return if val.nil? && !required_for_root_scope?
33
18
 
19
+ val = val.scrub if val.respond_to?(:scrub)
20
+
34
21
  # don't forget that +false.blank?+ is true
35
22
  return if val != false && val.blank? && @allow_blank
36
23
 
37
- param_array = val.nil? ? [nil] : Array.wrap(val)
38
-
39
- raise validation_exception(attr_name, except_message) \
40
- unless check_excepts(param_array)
41
-
42
- raise validation_exception(attr_name, message(:values)) \
43
- unless check_values(param_array, attr_name)
24
+ return if check_values?(val, attr_name)
44
25
 
45
- raise validation_exception(attr_name, message(:values)) \
46
- if @proc && !validate_proc(@proc, param_array)
26
+ raise Grape::Exceptions::Validation.new(
27
+ params: [@scope.full_name(attr_name)],
28
+ message: message(:values)
29
+ )
47
30
  end
48
31
 
49
32
  private
50
33
 
51
- def check_values(param_array, attr_name)
34
+ def check_values?(val, attr_name)
52
35
  values = @values.is_a?(Proc) && @values.arity.zero? ? @values.call : @values
53
36
  return true if values.nil?
54
37
 
38
+ param_array = val.nil? ? [nil] : Array.wrap(val)
39
+ return param_array.all? { |param| values.include?(param) } unless values.is_a?(Proc)
40
+
55
41
  begin
56
- return param_array.all? { |param| values.call(param) } if values.is_a? Proc
42
+ param_array.all? { |param| values.call(param) }
57
43
  rescue StandardError => e
58
44
  warn "Error '#{e}' raised while validating attribute '#{attr_name}'"
59
- return false
45
+ false
60
46
  end
61
- param_array.all? { |param| values.include?(param) }
62
- end
63
-
64
- def check_excepts(param_array)
65
- excepts = @excepts.is_a?(Proc) ? @excepts.call : @excepts
66
- return true if excepts.nil?
67
-
68
- param_array.none? { |param| excepts.include?(param) }
69
- end
70
-
71
- def validate_proc(proc, param_array)
72
- case proc.arity
73
- when 0
74
- param_array.all? { |_param| proc.call }
75
- when 1
76
- param_array.all? { |param| proc.call(param) }
77
- else
78
- raise ArgumentError, 'proc arity must be 0 or 1'
79
- end
80
- end
81
-
82
- def except_message
83
- options = instance_variable_get(:@option)
84
- options_key?(:except_message) ? options[:except_message] : message(:except_values)
85
47
  end
86
48
 
87
49
  def required_for_root_scope?
@@ -92,10 +54,6 @@ module Grape
92
54
 
93
55
  scope.root?
94
56
  end
95
-
96
- def validation_exception(attr_name, message)
97
- Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
98
- end
99
57
  end
100
58
  end
101
59
  end
@@ -2,29 +2,20 @@
2
2
 
3
3
  module Grape
4
4
  module Validations
5
+ extend Grape::Util::Registry
6
+
5
7
  module_function
6
8
 
7
- def validators
8
- @validators ||= {}
9
- end
9
+ def require_validator(short_name)
10
+ raise Grape::Exceptions::UnknownValidator, short_name unless registry.key?(short_name)
10
11
 
11
- # Register a new validator, so it can be used to validate parameters.
12
- # @param short_name [String] all lower-case, no spaces
13
- # @param klass [Class] the validator class. Should inherit from
14
- # Grape::Validations::Validators::Base.
15
- def register_validator(short_name, klass)
16
- validators[short_name] = klass
12
+ registry[short_name]
17
13
  end
18
14
 
19
- def deregister_validator(short_name)
20
- validators.delete(short_name)
21
- end
15
+ def build_short_name(klass)
16
+ return if klass.name.blank?
22
17
 
23
- def require_validator(short_name)
24
- str_name = short_name.to_s
25
- validators.fetch(str_name) { Grape::Validations::Validators.const_get(:"#{str_name.camelize}Validator") }
26
- rescue NameError
27
- raise Grape::Exceptions::UnknownValidator, short_name
18
+ klass.name.demodulize.underscore.delete_suffix('_validator')
28
19
  end
29
20
  end
30
21
  end
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.1.3'
5
+ VERSION = '2.3.0'
6
6
  end
data/lib/grape.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
3
4
  require 'active_support'
4
5
  require 'active_support/concern'
5
6
  require 'active_support/configurable'
@@ -33,7 +34,6 @@ require 'date'
33
34
  require 'dry-types'
34
35
  require 'forwardable'
35
36
  require 'json'
36
- require 'logger'
37
37
  require 'mustermann/grape'
38
38
  require 'pathname'
39
39
  require 'rack'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-07-13 00:00:00.000000000 Z
10
+ date: 2025-02-08 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -120,6 +119,8 @@ files:
120
119
  - lib/grape/error_formatter.rb
121
120
  - lib/grape/error_formatter/base.rb
122
121
  - lib/grape/error_formatter/json.rb
122
+ - lib/grape/error_formatter/jsonapi.rb
123
+ - lib/grape/error_formatter/serializable_hash.rb
123
124
  - lib/grape/error_formatter/txt.rb
124
125
  - lib/grape/error_formatter/xml.rb
125
126
  - lib/grape/exceptions/base.rb
@@ -149,6 +150,7 @@ files:
149
150
  - lib/grape/extensions/hash.rb
150
151
  - lib/grape/extensions/hashie/mash.rb
151
152
  - lib/grape/formatter.rb
153
+ - lib/grape/formatter/base.rb
152
154
  - lib/grape/formatter/json.rb
153
155
  - lib/grape/formatter/serializable_hash.rb
154
156
  - lib/grape/formatter/txt.rb
@@ -169,12 +171,15 @@ files:
169
171
  - lib/grape/middleware/stack.rb
170
172
  - lib/grape/middleware/versioner.rb
171
173
  - lib/grape/middleware/versioner/accept_version_header.rb
174
+ - lib/grape/middleware/versioner/base.rb
172
175
  - lib/grape/middleware/versioner/header.rb
173
176
  - lib/grape/middleware/versioner/param.rb
174
177
  - lib/grape/middleware/versioner/path.rb
175
178
  - lib/grape/namespace.rb
176
179
  - lib/grape/parser.rb
180
+ - lib/grape/parser/base.rb
177
181
  - lib/grape/parser/json.rb
182
+ - lib/grape/parser/jsonapi.rb
178
183
  - lib/grape/parser/xml.rb
179
184
  - lib/grape/path.rb
180
185
  - lib/grape/presenters/presenter.rb
@@ -189,7 +194,6 @@ files:
189
194
  - lib/grape/serve_stream/sendfile_response.rb
190
195
  - lib/grape/serve_stream/stream_response.rb
191
196
  - lib/grape/types/invalid_value.rb
192
- - lib/grape/util/accept_header_handler.rb
193
197
  - lib/grape/util/base_inheritable.rb
194
198
  - lib/grape/util/cache.rb
195
199
  - lib/grape/util/endpoint_configuration.rb
@@ -203,7 +207,7 @@ files:
203
207
  - lib/grape/util/lazy/value_enumerable.rb
204
208
  - lib/grape/util/lazy/value_hash.rb
205
209
  - lib/grape/util/media_type.rb
206
- - lib/grape/util/registrable.rb
210
+ - lib/grape/util/registry.rb
207
211
  - lib/grape/util/reverse_stackable_values.rb
208
212
  - lib/grape/util/stackable_values.rb
209
213
  - lib/grape/util/strict_hash_configuration.rb
@@ -216,7 +220,6 @@ files:
216
220
  - lib/grape/validations/single_attribute_iterator.rb
217
221
  - lib/grape/validations/types.rb
218
222
  - lib/grape/validations/types/array_coercer.rb
219
- - lib/grape/validations/types/build_coercer.rb
220
223
  - lib/grape/validations/types/custom_type_coercer.rb
221
224
  - lib/grape/validations/types/custom_type_collection_coercer.rb
222
225
  - lib/grape/validations/types/dry_type_coercer.rb
@@ -234,6 +237,7 @@ files:
234
237
  - lib/grape/validations/validators/at_least_one_of_validator.rb
235
238
  - lib/grape/validations/validators/base.rb
236
239
  - lib/grape/validations/validators/coerce_validator.rb
240
+ - lib/grape/validations/validators/contract_scope_validator.rb
237
241
  - lib/grape/validations/validators/default_validator.rb
238
242
  - lib/grape/validations/validators/exactly_one_of_validator.rb
239
243
  - lib/grape/validations/validators/except_values_validator.rb
@@ -251,10 +255,10 @@ licenses:
251
255
  - MIT
252
256
  metadata:
253
257
  bug_tracker_uri: https://github.com/ruby-grape/grape/issues
254
- changelog_uri: https://github.com/ruby-grape/grape/blob/v2.1.3/CHANGELOG.md
255
- documentation_uri: https://www.rubydoc.info/gems/grape/2.1.3
256
- source_code_uri: https://github.com/ruby-grape/grape/tree/v2.1.3
257
- post_install_message:
258
+ changelog_uri: https://github.com/ruby-grape/grape/blob/v2.3.0/CHANGELOG.md
259
+ documentation_uri: https://www.rubydoc.info/gems/grape/2.3.0
260
+ source_code_uri: https://github.com/ruby-grape/grape/tree/v2.3.0
261
+ rubygems_mfa_required: 'true'
258
262
  rdoc_options: []
259
263
  require_paths:
260
264
  - lib
@@ -269,8 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
269
273
  - !ruby/object:Gem::Version
270
274
  version: '0'
271
275
  requirements: []
272
- rubygems_version: 3.5.13
273
- signing_key:
276
+ rubygems_version: 3.6.2
274
277
  specification_version: 4
275
278
  summary: A simple Ruby framework for building REST-like APIs.
276
279
  test_files: []
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grape
4
- module Util
5
- class AcceptHeaderHandler
6
- attr_reader :accept_header, :versions, :vendor, :strict, :cascade
7
-
8
- def initialize(accept_header:, versions:, **options)
9
- @accept_header = accept_header
10
- @versions = versions
11
- @vendor = options.fetch(:vendor, nil)
12
- @strict = options.fetch(:strict, false)
13
- @cascade = options.fetch(:cascade, true)
14
- end
15
-
16
- def match_best_quality_media_type!(content_types: Grape::ContentTypes::CONTENT_TYPES, allowed_methods: nil)
17
- return unless vendor
18
-
19
- strict_header_checks!
20
- media_type = Grape::Util::MediaType.best_quality(accept_header, available_media_types(content_types))
21
- if media_type
22
- yield media_type
23
- else
24
- fail!(allowed_methods)
25
- end
26
- end
27
-
28
- private
29
-
30
- def strict_header_checks!
31
- return unless strict
32
-
33
- accept_header_check!
34
- version_and_vendor_check!
35
- end
36
-
37
- def accept_header_check!
38
- return if accept_header.present?
39
-
40
- invalid_accept_header!('Accept header must be set.')
41
- end
42
-
43
- def version_and_vendor_check!
44
- return if versions.blank? || version_and_vendor?
45
-
46
- invalid_accept_header!('API vendor or version not found.')
47
- end
48
-
49
- def q_values_mime_types
50
- @q_values_mime_types ||= Rack::Utils.q_values(accept_header).map(&:first)
51
- end
52
-
53
- def version_and_vendor?
54
- q_values_mime_types.any? { |mime_type| Grape::Util::MediaType.match?(mime_type) }
55
- end
56
-
57
- def invalid_accept_header!(message)
58
- raise Grape::Exceptions::InvalidAcceptHeader.new(message, error_headers)
59
- end
60
-
61
- def invalid_version_header!(message)
62
- raise Grape::Exceptions::InvalidVersionHeader.new(message, error_headers)
63
- end
64
-
65
- def fail!(grape_allowed_methods)
66
- return grape_allowed_methods if grape_allowed_methods.present?
67
-
68
- media_types = q_values_mime_types.map { |mime_type| Grape::Util::MediaType.parse(mime_type) }
69
- vendor_not_found!(media_types) || version_not_found!(media_types)
70
- end
71
-
72
- def vendor_not_found!(media_types)
73
- return unless media_types.all? { |media_type| media_type&.vendor && media_type.vendor != vendor }
74
-
75
- invalid_accept_header!('API vendor not found.')
76
- end
77
-
78
- def version_not_found!(media_types)
79
- return unless media_types.all? { |media_type| media_type&.version && versions.exclude?(media_type.version) }
80
-
81
- invalid_version_header!('API version not found.')
82
- end
83
-
84
- def error_headers
85
- cascade ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
86
- end
87
-
88
- def available_media_types(content_types)
89
- [].tap do |available_media_types|
90
- base_media_type = "application/vnd.#{vendor}"
91
- content_types.each_key do |extension|
92
- versions&.reverse_each do |version|
93
- available_media_types << "#{base_media_type}-#{version}+#{extension}"
94
- available_media_types << "#{base_media_type}-#{version}"
95
- end
96
- available_media_types << "#{base_media_type}+#{extension}"
97
- end
98
-
99
- available_media_types << base_media_type
100
- available_media_types.concat(content_types.values.flatten)
101
- end
102
- end
103
- end
104
- end
105
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grape
4
- module Util
5
- module Registrable
6
- def default_elements
7
- @default_elements ||= {}
8
- end
9
-
10
- def register(format, element)
11
- default_elements[format] = element unless default_elements[format]
12
- end
13
- end
14
- end
15
- end
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Grape
4
- module Validations
5
- module Types
6
- module BuildCoercer
7
- # Chooses the best coercer for the given type. For example, if the type
8
- # is Integer, it will return a coercer which will be able to coerce a value
9
- # to the integer.
10
- #
11
- # There are a few very special coercers which might be returned.
12
- #
13
- # +Grape::Types::MultipleTypeCoercer+ is a coercer which is returned when
14
- # the given type implies values in an array with different types.
15
- # For example, +[Integer, String]+ allows integer and string values in
16
- # an array.
17
- #
18
- # +Grape::Types::CustomTypeCoercer+ is a coercer which is returned when
19
- # a method is specified by a user with +coerce_with+ option or the user
20
- # specifies a custom type which implements requirments of
21
- # +Grape::Types::CustomTypeCoercer+.
22
- #
23
- # +Grape::Types::CustomTypeCollectionCoercer+ is a very similar to the
24
- # previous one, but it expects an array or set of values having a custom
25
- # type implemented by the user.
26
- #
27
- # There is also a group of custom types implemented by Grape, check
28
- # +Grape::Validations::Types::SPECIAL+ to get the full list.
29
- #
30
- # @param type [Class] the type to which input strings
31
- # should be coerced
32
- # @param method [Class,#call] the coercion method to use
33
- # @return [Object] object to be used
34
- # for coercion and type validation
35
- def self.build_coercer(type, method: nil, strict: false)
36
- cache_instance(type, method, strict) do
37
- create_coercer_instance(type, method, strict)
38
- end
39
- end
40
-
41
- def self.create_coercer_instance(type, method, strict)
42
- # Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
43
- type = Types.map_special(type)
44
-
45
- # Use a special coercer for multiply-typed parameters.
46
- if Types.multiple?(type)
47
- MultipleTypeCoercer.new(type, method)
48
-
49
- # Use a special coercer for custom types and coercion methods.
50
- elsif method || Types.custom?(type)
51
- CustomTypeCoercer.new(type, method)
52
-
53
- # Special coercer for collections of types that implement a parse method.
54
- # CustomTypeCoercer (above) already handles such types when an explicit coercion
55
- # method is supplied.
56
- elsif Types.collection_of_custom?(type)
57
- Types::CustomTypeCollectionCoercer.new(
58
- Types.map_special(type.first), type.is_a?(Set)
59
- )
60
- else
61
- DryTypeCoercer.coercer_instance_for(type, strict)
62
- end
63
- end
64
-
65
- def self.cache_instance(type, method, strict, &_block)
66
- key = cache_key(type, method, strict)
67
-
68
- return @__cache[key] if @__cache.key?(key)
69
-
70
- instance = yield
71
-
72
- @__cache_write_lock.synchronize do
73
- @__cache[key] = instance
74
- end
75
-
76
- instance
77
- end
78
-
79
- def self.cache_key(type, method, strict)
80
- [type, method, strict].each_with_object(+'_') do |val, memo|
81
- next if val.nil?
82
-
83
- memo << '_' << val.to_s
84
- end
85
- end
86
-
87
- instance_variable_set(:@__cache, {})
88
- instance_variable_set(:@__cache_write_lock, Mutex.new)
89
- end
90
- end
91
- end
92
- end