Repository navigation
Raise when setting unknown config.view_component options - #2755
Conversation
|
Thanks for picking this up so quickly, @finalburn! I ran the full suite on this branch (Ruby 4.0.9, Rails 8.1): all green, apart from one flaky system test that passes on its own. On the "overcomplicated" part, two ideas to slim it down:
One small gap: Happy to help if you'd like! |
753e608 to
4ff6843
Compare
`config.view_component` and its `previews` and `generate` namespaces were plain `ActiveSupport::OrderedOptions`, so any key could be set and options renamed or removed in 4.0 (such as `preview_controller`) were silently ignored, as were typos. They're now `ViewComponent::Config::Options`, which raises `ViewComponent::UnknownConfigOptionError` when an unknown option is set. For options renamed or removed in 4.0, the error names the replacement. Reading an unknown option still returns `nil`. `Options` also supports deprecating an option before it starts raising: an old name listed in `deprecated` warns and forwards to its replacement, and a test fails once the deprecation horizon is reached while any deprecated options remain. Fixes #2754 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4ff6843 to
5dba83c
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Inherited Hash mutation methods bypass validation and namespace wrapping, allowing removed keys and unvalidated nested configuration to be accepted silently.
3 open issues
What changed in this PR
This PR adds fail-fast validation to ViewComponent’s Rails configuration, helping applications catch obsolete settings and typos instead of silently ignoring them.
Changes:
- Introduces validated configuration options with migration guidance for settings renamed or removed in 4.0.
- Preserves validation when replacing nested configuration with hashes, while keeping unknown reads non-raising.
- Adds regression tests and documents the behavior change.
| File | Description |
|---|---|
test/sandbox/test/config_test.rb |
Tests valid settings, rejected keys, unknown reads, and nested replacements. |
lib/view_component/errors.rb |
Adds configuration errors with replacement guidance and valid-option lists. |
lib/view_component/config.rb |
Introduces validated options and mappings for obsolete settings. |
docs/CHANGELOG.md |
Documents stricter configuration validation. |
Review effort: Max
| @removed = removed | ||
| end | ||
|
|
||
| def []=(key, value) |
| "#{Options::ROOT_NAMESPACE}.generate", | ||
| %i[path preview_path parent_class sidecar stimulus_controller typescript locale distinct_locale_files preview use_component_path_for_rspec_tests], | ||
| default: false, | ||
| renamed: RENAMED_OPTIONS |
| }) | ||
| options = Options.new( | ||
| Options::ROOT_NAMESPACE, | ||
| %i[generate previews instrumentation_enabled], |
|
Thanks for wrapping this one up @joelhawksley! |


Fixes #2754.
What
config.view_component,config.view_component.previewsandconfig.view_component.generatewere plainActiveSupport::OrderedOptions, so setting any key worked and nothing read it. Options renamed or removed in 4.0 (preview_controller,test_controller, …) and typos (instrumentaton_enabled) were silently ignored.They're now
ViewComponent::Config::Options, a subclass that raisesViewComponent::UnknownConfigOptionErrorwhen an unknown option is set, at the line in the app's config that sets it:The 4.0 renames and removals live in frozen constants on
ViewComponent::Config(RENAMED_OPTIONS,RENAMED_GENERATE_OPTIONS,REMOVED_OPTIONS). Reading an unknown option still returnsnil, so gems that check old keys don't break at boot.Future renames
Optionstakes adeprecated:map so a future rename can warn before it raises:deprecated. Setting or reading the old name emits aViewComponent::Deprecationwarning and uses the new option.deprecatedis empty for now. A test fails once the major version reachesDEPRECATION_HORIZONwhile any deprecated options remain.Things to note
Testing
New tests in
test/sandbox/test/config_test.rb. I couldn't run the full suite locally:bundle installfails becauserubydexandcruiseneed a Rust toolchain. Instead I ranconfig_test.rbagainst the installed ActiveSupport with a stubtest_helper, which covers all the new tests andtest_all_methods_are_documented, andstandardrb lib testpasses. The full suite will run in CI.🤖 Generated with Claude Code