diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf870dd7..ec0e96fee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). +* [#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). * Your contribution here. diff --git a/lib/grape/validations/shared_options.rb b/lib/grape/validations/shared_options.rb new file mode 100644 index 000000000..c6f029739 --- /dev/null +++ b/lib/grape/validations/shared_options.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Grape + module Validations + # Immutable value object holding the two options every validator reads at + # construction time: +allow_blank+ and +fail_fast+. Internal to + # {Validators::Base}, which builds it from the +opts+ Hash so the public + # 5th-argument contract stays a plain Hash — not part of any wire contract. + # + # Defaults mirror the prior +opts.values_at+ behaviour: +allow_blank+ is + # +nil+ when the declaration didn't supply it (validators treat nil as + # "not set"), +fail_fast+ defaults to +false+. + SharedOptions = Data.define(:allow_blank, :fail_fast) do + def initialize(allow_blank: nil, fail_fast: false) + super + end + end + end +end diff --git a/lib/grape/validations/validators/base.rb b/lib/grape/validations/validators/base.rb index 9d82f575c..2c290a9c1 100644 --- a/lib/grape/validations/validators/base.rb +++ b/lib/grape/validations/validators/base.rb @@ -12,10 +12,17 @@ module Validators # from them are frozen by construction. Lazy ivar assignment # (e.g. +memoize+, ||=) will raise +FrozenError+ at request time. class Base + extend Forwardable include Grape::Util::Translation attr_reader :attrs + # +allow_blank+ / +fail_fast+ are read straight off the internal + # {Grape::Validations::SharedOptions} value object; no per-validator + # ivars. The object is built from the +opts+ Hash in #initialize, so + # the public 5th-argument contract stays a plain Hash. + def_delegators :@opts, :allow_blank, :fail_fast + class << self # Declares the default I18n message key used by +validation_error!+. # Subclasses that only need a single fixed error message can declare it @@ -52,14 +59,15 @@ def inherited(klass) # @param options [Object] implementation-dependent Validator options; deep-frozen on assignment # @param required [Boolean] attribute(s) are required or optional # @param scope [ParamsScope] parent scope for this Validator - # @param opts [Hash] additional validation options + # @param opts [Hash] shared validator options; only +:allow_blank+ and + # +:fail_fast+ are consulted (other keys ignored, as before) def initialize(attrs, options, required, scope, opts) @attrs = Array(attrs).freeze @options = Grape::Util::DeepFreeze.deep_freeze(options) @option = @options # TODO: remove in next major release @required = required @scope = scope - @fail_fast, @allow_blank = opts.values_at(:fail_fast, :allow_blank) + @opts = SharedOptions.new(**opts.slice(:allow_blank, :fail_fast)) @exception_message = message(self.class.default_message_key) if self.class.default_message_key end @@ -75,7 +83,7 @@ def validate(request) end def fail_fast? - @fail_fast + fail_fast end # Validates a given parameter hash. diff --git a/lib/grape/validations/validators/values_validator.rb b/lib/grape/validations/validators/values_validator.rb index 15e766130..fa53f3bb5 100644 --- a/lib/grape/validations/validators/values_validator.rb +++ b/lib/grape/validations/validators/values_validator.rb @@ -28,7 +28,7 @@ def validate_param!(attr_name, params) val = scrub(params[attr_name]) return if val.nil? && !required_for_root_scope? - return if val != false && val.blank? && @allow_blank + return if val != false && val.blank? && allow_blank return if check_values?(val, attr_name) validation_error!(attr_name)