diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c579e464..eb5a5f0f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ * [#2733](https://github.com/ruby-grape/grape/pull/2733): Drop the dead `active_support/core_ext/hash/reverse_merge` require; call `ActiveSupport::HashWithIndifferentAccess.new(...)` directly at call sites - [@ericproulx](https://github.com/ericproulx). * [#2734](https://github.com/ruby-grape/grape/pull/2734): Extract `options_route_enabled` from the Endpoint options Hash into a dedicated `attr_accessor` - [@ericproulx](https://github.com/ericproulx). * [#2736](https://github.com/ruby-grape/grape/pull/2736): Collapse `Endpoint#run_validators` rescue branches via `ValidationErrors` flatten; `ValidationErrors#initialize` keyword renamed `errors:` → `exceptions:` - [@ericproulx](https://github.com/ericproulx). +* [#2747](https://github.com/ruby-grape/grape/pull/2747): Drop `Enumerable` from `Grape::Exceptions::ValidationErrors` and remove its public `#each`; rewrite `#full_messages` to walk `#errors` directly - [@ericproulx](https://github.com/ericproulx). * Your contribution here. #### Fixes diff --git a/UPGRADING.md b/UPGRADING.md index eb3ed9ed5..5d7873696 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -13,6 +13,24 @@ Grape::Exceptions::ValidationErrors.new(errors: [validation, validation_array_er # after Grape::Exceptions::ValidationErrors.new(exceptions: [validation, validation_array_errors], headers:) +#### `Grape::Exceptions::ValidationErrors` no longer mixes in `Enumerable` + +`Grape::Exceptions::ValidationErrors` no longer includes `Enumerable` and no longer defines a public `#each`. The Enumerable surface (`#each`, `#map`, `#select`, `#to_a`, etc.) was undocumented and untested; the documented accessors — `#errors`, `#full_messages`, `#message`, `#as_json` — are unchanged. + +If a `rescue_from` block iterated over the exception instance, switch to `#errors`: + +```ruby +# before +rescue_from Grape::Exceptions::ValidationErrors do |e| + e.each { |attribute, error| ... } +end + +# after +rescue_from Grape::Exceptions::ValidationErrors do |e| + e.errors.each do |attributes, errs| + errs.each { |error| ... } + end +end ``` #### `auth`, `http_basic` and `http_digest` now take keyword arguments diff --git a/lib/grape/exceptions/validation_errors.rb b/lib/grape/exceptions/validation_errors.rb index 69006201b..9fad6f94c 100644 --- a/lib/grape/exceptions/validation_errors.rb +++ b/lib/grape/exceptions/validation_errors.rb @@ -3,8 +3,6 @@ module Grape module Exceptions class ValidationErrors < Base - include Enumerable - attr_reader :errors def initialize(exceptions: [], headers: {}) @@ -12,14 +10,6 @@ def initialize(exceptions: [], headers: {}) super(message: full_messages.join(', '), status: 400, headers:) end - def each - errors.each_pair do |attribute, errors| - errors.each do |error| - yield attribute, error - end - end - end - def as_json(**_opts) errors.map do |k, v| { @@ -34,14 +24,16 @@ def to_json(*_opts) end def full_messages - messages = map do |attributes, error| - translate( - :format, - scope: 'grape.errors', - default: '%s %s', - attributes: translate_attributes(attributes), - message: error.message - ) + messages = errors.flat_map do |attributes, errs| + errs.map do |error| + translate( + :format, + scope: 'grape.errors', + default: '%s %s', + attributes: translate_attributes(attributes), + message: error.message + ) + end end messages.uniq! messages