diff --git a/Gemfile b/Gemfile index 7388add4c..47d64944e 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,7 @@ group :development, :test do gem "bundler", "~> 2" gem "capybara", "~> 3" gem "cuprite" + gem "dry-initializer", require: true gem "erb_lint" gem "haml", "~> 6" gem "jbuilder", "~> 2" diff --git a/Gemfile.lock b/Gemfile.lock index b1842db1a..69ef93aa2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -119,6 +119,7 @@ GEM diff-lcs (1.6.2) docile (1.4.1) drb (2.2.3) + dry-initializer (3.2.0) erb (5.0.1) erb_lint (0.9.0) activesupport @@ -393,6 +394,7 @@ DEPENDENCIES bundler (~> 2) capybara (~> 3) cuprite + dry-initializer erb_lint haml (~> 6) jbuilder (~> 2) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 45f257ede..bd39c3d38 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,10 @@ nav_order: 6 *Joel Hawksley* +* BREAKING: Support compatibility with `Dry::Initializer`. As a result, `EmptyOrInvalidInitializerError` will no longer be raised. + + *Joel Hawksley* + ## 4.0.0.alpha6 * BREAKING: Remove `config.test_controller` in favor of `vc_test_controller_class` test helper method. diff --git a/docs/api.md b/docs/api.md index 4a3f65f1c..5bbb08eac 100644 --- a/docs/api.md +++ b/docs/api.md @@ -466,14 +466,6 @@ It looks like a block was provided after calling `with_content` on COMPONENT, wh To fix this issue, use either `with_content` or a block. -### `EmptyOrInvalidInitializerError` - -The COMPONENT initializer is empty or invalid. It must accept the parameter `PARAMETER` to render it as a collection. - -To fix this issue, update the initializer to accept `PARAMETER`. - -See [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections. - ### `HelpersCalledBeforeRenderError` `#helpers` can't be used before rendering as it depends on the view context that only exists once a ViewComponent is passed to the Rails render pipeline. diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile index 41fa8ce93..cb1fff88e 100644 --- a/gemfiles/rails_7.1.gemfile +++ b/gemfiles/rails_7.1.gemfile @@ -14,6 +14,7 @@ group :development, :test do gem "bundler", "~> 2" gem "capybara", "~> 3" gem "cuprite" + gem "dry-initializer", require: true gem "erb_lint" gem "haml", "~> 6" gem "jbuilder", "~> 2" diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile index 1a7629cbd..83b36ba60 100644 --- a/gemfiles/rails_7.2.gemfile +++ b/gemfiles/rails_7.2.gemfile @@ -14,6 +14,7 @@ group :development, :test do gem "bundler", "~> 2" gem "capybara", "~> 3" gem "cuprite" + gem "dry-initializer", require: true gem "erb_lint" gem "haml", "~> 6" gem "jbuilder", "~> 2" diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile index 78f72aab2..ad46855a6 100644 --- a/gemfiles/rails_8.0.gemfile +++ b/gemfiles/rails_8.0.gemfile @@ -14,6 +14,7 @@ group :development, :test do gem "bundler", "~> 2" gem "capybara", "~> 3" gem "cuprite" + gem "dry-initializer", require: true gem "erb_lint" gem "haml", "~> 6" gem "jbuilder", "~> 2" diff --git a/gemfiles/rails_main.gemfile b/gemfiles/rails_main.gemfile index c43ca8c13..598dd76bb 100644 --- a/gemfiles/rails_main.gemfile +++ b/gemfiles/rails_main.gemfile @@ -15,6 +15,7 @@ group :development, :test do gem "bundler", "~> 2" gem "capybara", "~> 3" gem "cuprite" + gem "dry-initializer", require: true gem "erb_lint" gem "haml", "~> 6" gem "jbuilder", "~> 2" diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 0c6039a0e..0f08ab580 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -47,6 +47,7 @@ def config end include ActionView::Helpers + include Rails.application.routes.url_helpers if defined?(Rails) && Rails.application include ERB::Escape include ActiveSupport::CoreExt::ERBUtil @@ -68,8 +69,7 @@ def config 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 + class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false, default: false attr_accessor :__vc_original_view_context attr_reader :current_template @@ -89,6 +89,11 @@ def set_original_view_context(view_context) using RequestDetails + # Including `Rails.application.routes.url_helpers` defines an initializer that accepts (...), + # so we have to define our own empty initializer to overwrite it. + def initialize + end + # Entrypoint for rendering components. # # - `view_context`: ActionView context from calling view @@ -509,6 +514,11 @@ def with_collection(collection, spacer_component: nil, **args) Collection.new(self, collection, spacer_component, **args) end + # @private + def __vc_compile(raise_errors: false, force: false) + __vc_compiler.compile(raise_errors: raise_errors, force: force) + end + # @private def inherited(child) # Compile so child will inherit compiled `call_*` template methods that @@ -531,12 +541,6 @@ def render_template_for(requested_details) RUBY end - # If Rails application is loaded, add application url_helpers to the component context - # we need to check this to use this gem as a dependency - if defined?(Rails) && Rails.application && !(child < Rails.application.routes.url_helpers) - child.include Rails.application.routes.url_helpers - end - # Derive the source location of the component Ruby file from the call stack. # We need to ignore `inherited` frames here as they indicate that `inherited` # has been re-defined by the consuming application, likely in ApplicationComponent. @@ -568,11 +572,6 @@ def __vc_ensure_compiled __vc_compile unless __vc_compiled? end - # @private - def __vc_compile(raise_errors: false, force: false) - __vc_compiler.compile(raise_errors: raise_errors, force: force) - end - # @private def __vc_compiler @__vc_compiler ||= Compiler.new(self) @@ -622,13 +621,6 @@ def __vc_validate_collection_parameter!(validate_default: false) return unless parameter return if __vc_initialize_parameter_names.include?(parameter) || splatted_keyword_argument_present? - # If Ruby can't parse the component class, then the initialize - # parameters will be empty and ViewComponent will not be able to render - # the component. - if initialize_parameters.empty? - raise EmptyOrInvalidInitializerError.new(name, parameter) - end - raise MissingCollectionArgumentError.new(name, parameter) end @@ -670,8 +662,7 @@ def __vc_iteration_argument_present? private def splatted_keyword_argument_present? - initialize_parameters.flatten.include?(:keyrest) && - !initialize_parameters.include?([:keyrest, :**]) # Un-named splatted keyword args don't count! + initialize_parameters.flatten.include?(:keyrest) end def __vc_initialize_parameter_names diff --git a/lib/view_component/errors.rb b/lib/view_component/errors.rb index f101a91f5..b6aa38e4d 100644 --- a/lib/view_component/errors.rb +++ b/lib/view_component/errors.rb @@ -65,18 +65,6 @@ def initialize(klass_name) end end - class EmptyOrInvalidInitializerError < StandardError - MESSAGE = - "The COMPONENT initializer is empty or invalid. " \ - "It must accept the parameter `PARAMETER` to render it as a collection.\n\n" \ - "To fix this issue, update the initializer to accept `PARAMETER`.\n\n" \ - "See [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections." - - def initialize(klass_name, parameter) - super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s)) - end - end - class MissingCollectionArgumentError < StandardError MESSAGE = "The initializer for COMPONENT doesn't accept the parameter `PARAMETER`, " \ diff --git a/test/sandbox/app/components/item_component.rb b/test/sandbox/app/components/item_component.rb new file mode 100644 index 000000000..6794ea7c9 --- /dev/null +++ b/test/sandbox/app/components/item_component.rb @@ -0,0 +1,11 @@ +require "dry-initializer" + +class ItemComponent < ViewComponent::Base + extend Dry::Initializer + + option :item + + erb_template <<~ERB + <%= item.name %> + ERB +end diff --git a/test/sandbox/app/components/product_reader_oops_component.html.erb b/test/sandbox/app/components/product_reader_oops_component.html.erb deleted file mode 100644 index 1038ae1b1..000000000 --- a/test/sandbox/app/components/product_reader_oops_component.html.erb +++ /dev/null @@ -1 +0,0 @@ -