Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* [#2712](https://github.com/ruby-grape/grape/pull/2712): Pass a `Grape::Exceptions::ErrorResponse` value object to `error_formatter#call` instead of separate kwargs - [@ericproulx](https://github.com/ericproulx).
* [#2714](https://github.com/ruby-grape/grape/pull/2714): Drop unused `Grape::Middleware::Globals` and its `grape.request*` env constants - [@ericproulx](https://github.com/ericproulx).
* [#2717](https://github.com/ruby-grape/grape/pull/2717): Convert `Grape::Exceptions::ErrorResponse` to a `Data` value object - [@ericproulx](https://github.com/ericproulx).
* [#2722](https://github.com/ruby-grape/grape/pull/2722): Introduce `Grape::Validations::CoerceOptions` value object for the internal coerce options - [@ericproulx](https://github.com/ericproulx).
* [#2721](https://github.com/ruby-grape/grape/pull/2721): Use an internal `Grape::Validations::SharedOptions` value object in `Validators::Base` (public `opts` Hash contract unchanged) - [@ericproulx](https://github.com/ericproulx).
* [#2719](https://github.com/ruby-grape/grape/pull/2719): Move content-type helpers from `Middleware::Base` into `PrecomputedContentTypes` - [@ericproulx](https://github.com/ericproulx).
* [#2716](https://github.com/ruby-grape/grape/pull/2716): Refactor `DSL::Routing#version`: guard clause, explicit kwargs in place of `**options`, and a `Grape::DSL::VersionOptions` value object stored internally - [@ericproulx](https://github.com/ericproulx).
Expand Down
21 changes: 21 additions & 0 deletions lib/grape/validations/coerce_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Grape
module Validations
# Immutable value object describing how a parameter is coerced. Assembled
# by {ValidationsSpec#coerce_options} from the parsed +type+/+coerce_with+/
# +coerce_message+ declaration — never written by the user — and consumed
# by {ParamsScope#check_coerce_with} / {ParamsScope#validate_coerce} and by
# {Validators::Validators::CoerceValidator} (which receives it as its
# +options+ argument).
#
# All three fields may be +nil+ (e.g. a remountable API evaluated on its
# base instance has no resolved +type+ yet).
# +coerce_method+ (not +method+) avoids shadowing +Object#method+.
CoerceOptions = Data.define(:type, :coerce_method, :message) do
def initialize(type: nil, coerce_method: nil, message: nil)
super
end
end
end
end
10 changes: 5 additions & 5 deletions lib/grape/validations/params_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,13 @@ def validates(attrs, validations)
end
end

# Enforce correct usage of :coerce_with on a coerce_options hash.
# Enforce correct usage of :coerce_with on a CoerceOptions.
# We do not allow coercion without a type, nor with +JSON+ as a type
# since that defines its own coercion method.
def check_coerce_with(coerce_options)
return unless coerce_options[:method]
raise ArgumentError, 'must supply type for coerce_with' unless coerce_options[:type]
return unless SPECIAL_JSON.include?(coerce_options[:type])
return unless coerce_options.coerce_method
raise ArgumentError, 'must supply type for coerce_with' unless coerce_options.type
return unless SPECIAL_JSON.include?(coerce_options.type)

raise ArgumentError, 'coerce_with disallowed for type: JSON'
end
Expand All @@ -373,7 +373,7 @@ def validate_coerce(spec, attrs)
# configuration[:some_type] evaluates to nil. Skipping instantiation
# here is correct — the real mounted instance will replay this step
# with the actual type value.
return unless coerce_options[:type]
return unless coerce_options.type

validate('coerce', coerce_options, attrs, spec.required?, spec.shared_opts)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/validations/validations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def required?
end

def coerce_options
{ type: @coerce_type, method: @coerce_method, message: @coerce_message }
CoerceOptions.new(type: @coerce_type, coerce_method: @coerce_method, message: @coerce_message)
end

private
Expand Down
9 changes: 7 additions & 2 deletions lib/grape/validations/validators/coerce_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ class CoerceValidator < Base
def initialize(attrs, options, required, scope, opts)
super

raw_type = @options[:type]
# +@options+ is a Grape::Validations::CoerceOptions. +Base#message+
# can't see a custom message off a Data (it probes Hash-like
# +key?+), so restore it here to preserve `type: { value:, message: }`.
@exception_message = @options.message unless @options.message.nil?

raw_type = @options.type
type = hash_like?(raw_type) ? raw_type[:value] : raw_type
@converter =
if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
type
else
Types.build_coercer(type, method: @options[:method])
Types.build_coercer(type, method: @options.coerce_method)
end
end

Expand Down
Loading