From 5627734eaf589c5b7c81f0a3ba3508f35822e641 Mon Sep 17 00:00:00 2001 From: Simon Fish Date: Sat, 15 Mar 2025 12:49:15 +0000 Subject: [PATCH 1/7] Introduce component-local config --- lib/view_component/base.rb | 2 + lib/view_component/component_local_config.rb | 48 ++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 lib/view_component/component_local_config.rb diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 95a2e0ca5..b98469cea 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -5,6 +5,7 @@ require "view_component/collection" require "view_component/compile_cache" require "view_component/compiler" +require "view_component/component_local_config" require "view_component/config" require "view_component/errors" require "view_component/inline_template" @@ -38,6 +39,7 @@ def config include ViewComponent::Slotable include ViewComponent::Translatable include ViewComponent::WithContentHelper + include ViewComponent::ComponentLocalConfig RESERVED_PARAMETER = :content VC_INTERNAL_DEFAULT_FORMAT = :html diff --git a/lib/view_component/component_local_config.rb b/lib/view_component/component_local_config.rb new file mode 100644 index 000000000..71cb06afa --- /dev/null +++ b/lib/view_component/component_local_config.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ViewComponent + module ComponentLocalConfig + class Configuration + def initialize + @config = ActiveSupport::OrderedOptions[] + end + + delegate_missing_to :@config + + def inheritable_copy + new.instance_variable_set(:@config, @config.inheritable_copy) + end + end + + extend ActiveSupport::Concern + + included do + def configuration + @_configuration ||= self.class.configuration.inheritable_copy + end + + private + + def inherited(child) + child.instance_variable_set(:@_configuration, nil) + super + end + end + + class_methods do + def configuration + @_configuration ||= if respond_to?(:superclass) && superclass.respond_to?(:configuration) + superclass.configuration.inheritable_copy + else + # create a new "anonymous" class that will host the compiled reader methods + Class.new(ActiveSupport::Configurable::Configuration).new + end + end + + def configure(&block) + configuration.instance_eval(&block) + configuration.compile_methods! + end + end + end +end \ No newline at end of file From b837dbfd5ff75cd9aacef5f2643b752c926e7a56 Mon Sep 17 00:00:00 2001 From: Simon Fish Date: Sat, 15 Mar 2025 12:54:38 +0000 Subject: [PATCH 2/7] Migrate strip_trailing_whitespace to component-local-config --- lib/view_component/base.rb | 22 ++++++++++++++------ lib/view_component/component_local_config.rb | 6 ++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index b98469cea..bbf3e05dd 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -50,10 +50,6 @@ def config # For Content Security Policy nonces delegate :content_security_policy_nonce, to: :helpers - # Config option that strips trailing whitespace in templates before compiling them. - class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false - self.__vc_strip_trailing_whitespace = false # class_attribute:default doesn't work until Rails 5.2 - attr_accessor :__vc_original_view_context # Components render in their own view context. Helpers and other functionality @@ -617,14 +613,28 @@ def with_collection_parameter(parameter) # # @param value [Boolean] Whether to strip newlines. def strip_trailing_whitespace(value = true) - self.__vc_strip_trailing_whitespace = value + ViewComponent::Deprecation.deprecation_warning( + "strip_trailing_whitespace", + %( + Use the new component-local configuration option instead: + + ```rb + class #{self.class.name} < ViewComponent::Base + configure do |config| + config.strip_trailing_whitespace = #{value} + end + end + ``` + ) + ) + configuration.strip_trailing_whitespace = value end # Whether trailing whitespace will be stripped before compilation. # # @return [Boolean] def strip_trailing_whitespace? - __vc_strip_trailing_whitespace + configuration.strip_trailing_whitespace end # Ensure the component initializer accepts the diff --git a/lib/view_component/component_local_config.rb b/lib/view_component/component_local_config.rb index 71cb06afa..bfb27b5bf 100644 --- a/lib/view_component/component_local_config.rb +++ b/lib/view_component/component_local_config.rb @@ -4,7 +4,9 @@ module ViewComponent module ComponentLocalConfig class Configuration def initialize - @config = ActiveSupport::OrderedOptions[] + @config = ActiveSupport::OrderedOptions[ + strip_trailing_whitespace: false + ] end delegate_missing_to :@config @@ -45,4 +47,4 @@ def configure(&block) end end end -end \ No newline at end of file +end From a5b32e0006f2d352ec491b399ae644ca56630a8f Mon Sep 17 00:00:00 2001 From: Simon Fish Date: Sat, 15 Mar 2025 12:59:58 +0000 Subject: [PATCH 3/7] Update changelog --- docs/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 19cf2f692..b25270663 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,10 @@ nav_order: 6 ## main +* Introduce component-local config and migrate `strip_trailing_whitespace` to use it under the hood. + + *Simon Fish* + * Add documentation on how ViewComponent works. *Joel Hawksley* From 7b1cc26c506c1ca2a9b15599dd881f5f97f9925f Mon Sep 17 00:00:00 2001 From: Simon Fish Date: Sat, 15 Mar 2025 13:43:28 +0000 Subject: [PATCH 4/7] Refine and test component-local config --- lib/view_component/component_local_config.rb | 18 ++++++++++++++---- .../app/components/configurable_component.rb | 7 +++++++ test/sandbox/test/base_test.rb | 8 ++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 test/sandbox/app/components/configurable_component.rb diff --git a/lib/view_component/component_local_config.rb b/lib/view_component/component_local_config.rb index bfb27b5bf..3061c1813 100644 --- a/lib/view_component/component_local_config.rb +++ b/lib/view_component/component_local_config.rb @@ -3,22 +3,31 @@ module ViewComponent module ComponentLocalConfig class Configuration - def initialize - @config = ActiveSupport::OrderedOptions[ + def self.defaults + ActiveSupport::Configurable::Configuration[ strip_trailing_whitespace: false ] end + def initialize(config = defaults) + @config = config + end + delegate_missing_to :@config def inheritable_copy - new.instance_variable_set(:@config, @config.inheritable_copy) + self.class.new(@config.inheritable_copy) end + + private + + delegate :defaults, to: :class end extend ActiveSupport::Concern included do + # :nocov: def configuration @_configuration ||= self.class.configuration.inheritable_copy end @@ -29,6 +38,7 @@ def inherited(child) child.instance_variable_set(:@_configuration, nil) super end + # :nocov: end class_methods do @@ -37,7 +47,7 @@ def configuration superclass.configuration.inheritable_copy else # create a new "anonymous" class that will host the compiled reader methods - Class.new(ActiveSupport::Configurable::Configuration).new + ViewComponent::ComponentLocalConfig::Configuration.new end end diff --git a/test/sandbox/app/components/configurable_component.rb b/test/sandbox/app/components/configurable_component.rb new file mode 100644 index 000000000..40cd79e24 --- /dev/null +++ b/test/sandbox/app/components/configurable_component.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ConfigurableComponent < ViewComponent::Base + configure do |config| + config.strip_trailing_whitespace = true + end +end diff --git a/test/sandbox/test/base_test.rb b/test/sandbox/test/base_test.rb index 0e7b26d84..dc6c0e921 100644 --- a/test/sandbox/test/base_test.rb +++ b/test/sandbox/test/base_test.rb @@ -197,4 +197,12 @@ def test_uses_module_configuration assert_equal "AnotherController", TestAlreadyConfigurableModule::SomeComponent.test_controller assert_equal "AnotherController", TestAlreadyConfiguredModule::SomeComponent.test_controller end + + def test_component_local_config_is_inheritable + assert_equal false, ViewComponent::Base.configuration.strip_trailing_whitespace + # This component doesn't call configure, so it should inherit the defaults. + assert_equal false, AnotherComponent.configuration.strip_trailing_whitespace + # This component overrides the defaults. + assert_equal true, ConfigurableComponent.configuration.strip_trailing_whitespace + end end From 4a023aa4416579ae0903b26be179dd88019997dd Mon Sep 17 00:00:00 2001 From: Simon Fish Date: Thu, 20 Mar 2025 15:46:21 +0000 Subject: [PATCH 5/7] Rename interface methods --- lib/view_component/base.rb | 6 +++--- lib/view_component/component_local_config.rb | 18 +++++++++--------- .../app/components/configurable_component.rb | 2 +- test/sandbox/test/base_test.rb | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index bbf3e05dd..123ff5c8d 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -620,21 +620,21 @@ def strip_trailing_whitespace(value = true) ```rb class #{self.class.name} < ViewComponent::Base - configure do |config| + configure_view_component do |config| config.strip_trailing_whitespace = #{value} end end ``` ) ) - configuration.strip_trailing_whitespace = value + view_component_config.strip_trailing_whitespace = value end # Whether trailing whitespace will be stripped before compilation. # # @return [Boolean] def strip_trailing_whitespace? - configuration.strip_trailing_whitespace + view_component_config.strip_trailing_whitespace end # Ensure the component initializer accepts the diff --git a/lib/view_component/component_local_config.rb b/lib/view_component/component_local_config.rb index 3061c1813..b26892152 100644 --- a/lib/view_component/component_local_config.rb +++ b/lib/view_component/component_local_config.rb @@ -28,32 +28,32 @@ def inheritable_copy included do # :nocov: - def configuration - @_configuration ||= self.class.configuration.inheritable_copy + def view_component_config + @__vc_config ||= self.class.view_component_config.inheritable_copy end private def inherited(child) - child.instance_variable_set(:@_configuration, nil) + child.instance_variable_set(:@__vc_config, nil) super end # :nocov: end class_methods do - def configuration - @_configuration ||= if respond_to?(:superclass) && superclass.respond_to?(:configuration) - superclass.configuration.inheritable_copy + def view_component_config + @__vc_config ||= if respond_to?(:superclass) && superclass.respond_to?(:view_component_config) + superclass.view_component_config.inheritable_copy else # create a new "anonymous" class that will host the compiled reader methods ViewComponent::ComponentLocalConfig::Configuration.new end end - def configure(&block) - configuration.instance_eval(&block) - configuration.compile_methods! + def configure_component(&block) + view_component_config.instance_eval(&block) + view_component_config.compile_methods! end end end diff --git a/test/sandbox/app/components/configurable_component.rb b/test/sandbox/app/components/configurable_component.rb index 40cd79e24..cfa8164e2 100644 --- a/test/sandbox/app/components/configurable_component.rb +++ b/test/sandbox/app/components/configurable_component.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ConfigurableComponent < ViewComponent::Base - configure do |config| + configure_component do |config| config.strip_trailing_whitespace = true end end diff --git a/test/sandbox/test/base_test.rb b/test/sandbox/test/base_test.rb index dc6c0e921..4091fe2e8 100644 --- a/test/sandbox/test/base_test.rb +++ b/test/sandbox/test/base_test.rb @@ -199,10 +199,10 @@ def test_uses_module_configuration end def test_component_local_config_is_inheritable - assert_equal false, ViewComponent::Base.configuration.strip_trailing_whitespace + assert_equal false, ViewComponent::Base.view_component_config.strip_trailing_whitespace # This component doesn't call configure, so it should inherit the defaults. - assert_equal false, AnotherComponent.configuration.strip_trailing_whitespace + assert_equal false, AnotherComponent.view_component_config.strip_trailing_whitespace # This component overrides the defaults. - assert_equal true, ConfigurableComponent.configuration.strip_trailing_whitespace + assert_equal true, ConfigurableComponent.view_component_config.strip_trailing_whitespace end end From c1aff767fcb2a03633292a0a185fc08c3ce846cf Mon Sep 17 00:00:00 2001 From: Simon Fish Date: Sun, 6 Apr 2025 13:01:17 +0100 Subject: [PATCH 6/7] Mark deprecation in docs --- docs/guide/templates.md | 10 +++++++--- lib/view_component/base.rb | 16 ++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/guide/templates.md b/docs/guide/templates.md index 2caf95ec0..e965c2fcb 100644 --- a/docs/guide/templates.md +++ b/docs/guide/templates.md @@ -181,14 +181,18 @@ end Code editors commonly add a trailing newline character to source files in keeping with the Unix standard. Including trailing whitespace in component templates can result in unwanted whitespace in the HTML, eg. if the component is rendered before the period at the end of a sentence. -To strip trailing whitespace from component templates, use the `strip_trailing_whitespace` class method. +To strip trailing whitespace from component templates, use the `strip_trailing_whitespace` component-local config option. ```ruby class MyComponent < ViewComponent::Base # do strip whitespace - strip_trailing_whitespace + configure_component do |config| + config.strip_trailing_whitespace = true + end # don't strip whitespace - strip_trailing_whitespace(false) + configure_component do |config| + config.strip_trailing_whitespace = false + end end ``` diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 123ff5c8d..e84c9ed86 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -611,21 +611,29 @@ def with_collection_parameter(parameter) # end # ``` # + # @deprecated Use the new component-local configuration option instead. + # + # ```ruby + # class MyComponent < ViewComponent::Base + # configure_component do |config| + # config.strip_trailing_whitespace = true + # end + # end + # ``` + # # @param value [Boolean] Whether to strip newlines. def strip_trailing_whitespace(value = true) ViewComponent::Deprecation.deprecation_warning( "strip_trailing_whitespace", - %( + <<~DOC Use the new component-local configuration option instead: - ```rb class #{self.class.name} < ViewComponent::Base configure_view_component do |config| config.strip_trailing_whitespace = #{value} end end - ``` - ) + DOC ) view_component_config.strip_trailing_whitespace = value end From 3fab9507519494327071d14f0520e0b6292cab25 Mon Sep 17 00:00:00 2001 From: Simon Fish Date: Sun, 6 Apr 2025 12:47:52 +0100 Subject: [PATCH 7/7] Rename `configure_component` to `configure_view_component` --- docs/guide/templates.md | 4 ++-- lib/view_component/base.rb | 2 +- lib/view_component/component_local_config.rb | 2 +- test/sandbox/app/components/configurable_component.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/guide/templates.md b/docs/guide/templates.md index e965c2fcb..9d7216633 100644 --- a/docs/guide/templates.md +++ b/docs/guide/templates.md @@ -186,12 +186,12 @@ To strip trailing whitespace from component templates, use the `strip_trailing_w ```ruby class MyComponent < ViewComponent::Base # do strip whitespace - configure_component do |config| + configure_view_component do |config| config.strip_trailing_whitespace = true end # don't strip whitespace - configure_component do |config| + configure_view_component do |config| config.strip_trailing_whitespace = false end end diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index e84c9ed86..8b55a13d3 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -615,7 +615,7 @@ def with_collection_parameter(parameter) # # ```ruby # class MyComponent < ViewComponent::Base - # configure_component do |config| + # configure_view_component do |config| # config.strip_trailing_whitespace = true # end # end diff --git a/lib/view_component/component_local_config.rb b/lib/view_component/component_local_config.rb index b26892152..2bebccdbe 100644 --- a/lib/view_component/component_local_config.rb +++ b/lib/view_component/component_local_config.rb @@ -51,7 +51,7 @@ def view_component_config end end - def configure_component(&block) + def configure_view_component(&block) view_component_config.instance_eval(&block) view_component_config.compile_methods! end diff --git a/test/sandbox/app/components/configurable_component.rb b/test/sandbox/app/components/configurable_component.rb index cfa8164e2..8209d4cc6 100644 --- a/test/sandbox/app/components/configurable_component.rb +++ b/test/sandbox/app/components/configurable_component.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ConfigurableComponent < ViewComponent::Base - configure_component do |config| + configure_view_component do |config| config.strip_trailing_whitespace = true end end