|
1 | 1 | Upgrading Grape |
2 | 2 | =============== |
3 | 3 |
|
| 4 | +### Upgrading to >= 3.2 |
| 5 | + |
| 6 | +#### Validators Instantiated at Definition Time |
| 7 | + |
| 8 | +Previously, validators were instantiated at request time but they are now instantiated at definition time. This reduces object allocations since instances are reused across requests. |
| 9 | + |
| 10 | +#### `Grape::Util::Translation` Module |
| 11 | + |
| 12 | +I18n translation logic (translate with fallback locale) has been extracted into `Grape::Util::Translation`, included by both `Grape::Exceptions::Base` and `Grape::Validations::Validators::Base`. The `FALLBACK_LOCALE` constant has moved from `Grape::Exceptions::Base` to `Grape::Util::Translation`. |
| 13 | + |
| 14 | +When `I18n.enforce_available_locales` is `true` and `:en` is not in `I18n.available_locales`, the fallback now returns the bare key as a string (e.g. `"presence"`) instead of the full scope path (e.g. `"grape.errors.messages.presence"`). |
| 15 | + |
| 16 | +#### `Grape::Exceptions::Base#translate_message` Supports Hash Messages |
| 17 | + |
| 18 | +`translate_message` now accepts a Hash with a `:key` and interpolation parameters for deferred I18n translation: |
| 19 | + |
| 20 | +```ruby |
| 21 | +# Symbol (unchanged) |
| 22 | +translate_message(:presence) |
| 23 | + |
| 24 | +# Hash (new) — key + interpolation params, translated at error-raise time |
| 25 | +translate_message({ key: :length, min: 2, max: 5 }) |
| 26 | +``` |
| 27 | + |
| 28 | +This is used by validators that need locale-sensitive messages with interpolation (e.g. `LengthValidator`, `SameAsValidator`). |
| 29 | + |
| 30 | +#### `Grape::Exceptions::Validation` Changes |
| 31 | + |
| 32 | +**`params` and `message_key` are now read-only.** `attr_accessor` has been changed to `attr_reader`. If you were assigning to these after initialization, set them via the constructor keyword arguments instead. |
| 33 | + |
| 34 | +**`params` is now always coerced to an array.** You can now pass a single string instead of wrapping it in an array: |
| 35 | + |
| 36 | +```ruby |
| 37 | +# Before |
| 38 | +Grape::Exceptions::Validation.new(params: ['my_param'], message: 'is invalid') |
| 39 | + |
| 40 | +# After (both work, single string is now accepted) |
| 41 | +Grape::Exceptions::Validation.new(params: 'my_param', message: 'is invalid') |
| 42 | +Grape::Exceptions::Validation.new(params: ['my_param'], message: 'is invalid') |
| 43 | +``` |
| 44 | + |
| 45 | +#### `Validators::Base` Method Visibility Changes |
| 46 | + |
| 47 | +The following methods on `Grape::Validations::Validators::Base` are now **private**: `validate!`, `message`, `options_key?`. If your custom validator subclass calls these via `super` from a private method, no change is needed. If you were calling them from outside the class, you'll need to adjust. |
| 48 | + |
| 49 | +New private helpers have been added: |
| 50 | +- `hash_like?(obj)` — returns `obj.respond_to?(:key?)` |
| 51 | +- `option_value` — returns `@option[:value]` if present, otherwise `@option` |
| 52 | +- `scrub(value)` — scrubs invalid-encoding strings |
| 53 | +- `translate_message(key, **)` — translates a message key using the `grape.errors.messages` I18n scope with fallback locale support |
| 54 | + |
| 55 | +`validate_param!` now has a base implementation that raises `NotImplementedError`. Custom validators that override `validate!` directly are unaffected, but any subclass that relies on `validate_param!` being absent (e.g. calling `super` expecting no-op behaviour) will now receive a `NotImplementedError`. |
| 56 | + |
| 57 | +#### `Validators::Base#message` Now Accepts a Block |
| 58 | + |
| 59 | +`message` now accepts an optional block for lazy default message generation. When no custom `:message` option is set and no `default_key` is provided, the block is called: |
| 60 | + |
| 61 | +```ruby |
| 62 | +# Before |
| 63 | +def message(default_key = nil) |
| 64 | + options_key?(:message) ? @option[:message] : default_key |
| 65 | +end |
| 66 | + |
| 67 | +# After |
| 68 | +def message(default_key = nil) |
| 69 | + key = options_key?(:message) ? @option[:message] : default_key |
| 70 | + return key if key |
| 71 | + |
| 72 | + yield if block_given? |
| 73 | +end |
| 74 | +``` |
| 75 | + |
| 76 | +If your custom validator overrides `message` or passes a `default_key`, the behavior is unchanged. If you relied on `message` returning `nil` when no custom message and no default key were set, it now yields to the block instead. |
| 77 | + |
| 78 | +#### `ContractScopeValidator` No Longer Inherits from `Base` |
| 79 | + |
| 80 | +`ContractScopeValidator` is now a standalone class that no longer inherits from `Grape::Validations::Validators::Base`. Its constructor takes a single `schema:` keyword argument instead of the standard 5-argument validator signature: |
| 81 | + |
| 82 | +```ruby |
| 83 | +# Before |
| 84 | +ContractScopeValidator.new(attrs, options, required, scope, opts) |
| 85 | + |
| 86 | +# After |
| 87 | +ContractScopeValidator.new(schema: contract) |
| 88 | +``` |
| 89 | + |
| 90 | +Because it no longer inherits from `Base`, it is not registered via `Validations.register` and will not appear in `Grape::Validations.validators`. |
| 91 | + |
| 92 | +#### `endpoint_run_validators.grape` Notification No Longer Fires Without Validators |
| 93 | + |
| 94 | +The `endpoint_run_validators.grape` ActiveSupport notification is no longer emitted for routes that have no validators. Previously it fired unconditionally (with an empty `validators` array); now the instrumentation block is skipped entirely via an early return. If your observability or tracing code subscribes to this notification and expects it for every request, you will need to handle its absence for validator-free routes. |
| 95 | + |
| 96 | +#### Validator Constructor Caching |
| 97 | + |
| 98 | +All built-in validators now eagerly compute and cache values in their constructors (exception messages, option values, lambdas for proc-based defaults/values). This is transparent to API consumers but relevant if you subclass built-in validators and override `initialize` — ensure you call `super` so caching is properly set up. |
| 99 | + |
4 | 100 | ### Upgrading to >= 3.1 |
5 | 101 |
|
6 | 102 | #### Explicit kwargs for `namespace` and `route_param` |
|
0 commit comments