Generalize middleware options to per-class Options Data value objects#2718
Open
ericproulx wants to merge 1 commit into
Open
Generalize middleware options to per-class Options Data value objects#2718ericproulx wants to merge 1 commit into
ericproulx wants to merge 1 commit into
Conversation
Danger ReportNo issues found. |
e707766 to
13add91
Compare
6fc81d9 to
b4ab849
Compare
Following the per-feature `VersionOptions` / `RescueOptions` work in #2712 and #2716, middlewares that want typed option accessors now declare a per-class `Options = Data.define(...)`. `Middleware::Base#initialize`, when the subclass declares an `Options` constant (ancestor search included), routes the kwargs through `Options.new(**options)`: - `@config` exposes the typed Data instance (named accessors). - `@options` keeps returning a frozen `Hash` representation (`config.to_h.freeze`) for back-compat with subclasses that read `options[:key]`. Subclasses without an `Options` constant still flow through the `DEFAULT_OPTIONS` Hash + `deep_merge` path (`Filter`, `Auth::*`) unchanged. - `Middleware::Formatter` (5 fields). - `Middleware::Error` (14 fields). `rescue_options:` defaults to `Grape::DSL::RescueOptions.new`; an explicit nil (passed by `Endpoint#error_middleware_options` when no `rescue_from` was called) is coerced to the default in the initializer. - `Middleware::Versioner::Base` (7 fields). Adds `content_types:` / `format:` so the `PrecomputedContentTypes` mixin's accessor reads land cleanly. `version_options:` defaults to `Grape::DSL::VersionOptions.new`. Each `Options` field list is alphabetic. - `attr_reader :options` still returns a frozen `Hash` for converted middlewares (now derived from `config.to_h`). Subclasses that read `options[:key]` keep working. - `DEFAULT_OPTIONS` is restored as `Options.new.to_h.freeze` on each converted middleware so existing references continue to resolve. Marked `@deprecated` in the YARD comment; will be removed in a future release. - `Options#[]` is defined on each Data class as a Hash-style shim that emits a deprecation warning via `Grape.deprecator` and forwards to the named accessor — gives any code that discovers the Data and reaches in with `[:key]` a migration nudge. Passing an unknown kwarg to a converted middleware now raises `ArgumentError` instead of being silently swallowed by `**options`. One formatter spec was passing `rescue_options:` (dead weight; Formatter doesn't read it) — dropped. That stricter contract is exactly what made `version_options` / `rescue_options` cleaner in their respective PRs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b4ab849 to
99ee21a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Following the per-feature
VersionOptions/RescueOptionswork in #2712 and #2716, middlewares that want typed option accessors now declare a per-classOptions = Data.define(…).Middleware::Baseroutes the kwargs through it on init and exposes both a frozenHash(options, back-compat) and the typedDatainstance (config).Mechanism
Middleware::Base#initialize, when the subclass declares anOptionsconstant (ancestor search included so subclasses likeVersioner::PathinheritVersioner::Base::Options):@config = self.class::Options.new(**options)@options = @config.to_h.freeze(frozen Hash view of the Data, for legacyoptions[:key]callers)Subclasses without an
Optionsconstant (Filter,Auth::*) continue to flow through the legacyDEFAULT_OPTIONSHash +deep_mergepath unchanged.Converted middlewares
Middleware::Formatter(5 fields).Middleware::Error(14 fields).rescue_options:defaults toGrape::DSL::RescueOptions.new; an explicit nil (passed byEndpoint#error_middleware_optionswhen norescue_fromwas called) is coerced to the default in the Data'sinitialize.Middleware::Versioner::Base(7 fields). Addscontent_types:/format:so thePrecomputedContentTypesmixin's accessor reads land cleanly.version_options:defaults toGrape::DSL::VersionOptions.new.Each
Optionsfield list,def_delegators :config, andOptions#initializekwarg list is alphabetic.Back-compat surface
attr_reader :optionsstill returns a frozenHashrepresentation of the Data (config.to_h.freeze) for converted middlewares. Subclasses that readoptions[:key]keep working.DEFAULT_OPTIONSis restored asOptions.new.to_h.freezeon each converted middleware so existing references resolve. Marked@deprecatedin the YARD comment; will be removed in a future release.Options#[]is defined on each Data class as a Hash-style shim that emits a deprecation warning viaGrape.deprecatorand forwards to the named accessor. Any code that reaches into the Data with[:key]gets a migration nudge.Contract change (documented in UPGRADING)
Passing an unknown kwarg to a converted middleware now raises
ArgumentErrorinstead of being silently swallowed by**options. One formatter spec was passingrescue_options:(dead weight;Formatterdoesn't read it) — dropped.That stricter contract is exactly what made
version_options/rescue_optionscleaner in their respective PRs.Test plan
bundle exec rspec— 2313 examples, 0 failuresbundle exec rubocop lib/grape/middleware/— clean🤖 Generated with Claude Code