diff --git a/Gemfile.lock b/Gemfile.lock index 15b7abb05..82a702316 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -329,9 +329,6 @@ DEPENDENCIES jbuilder (~> 2) m (~> 1) minitest (~> 5.18) - net-imap - net-pop - net-smtp pry (~> 0.13) puma (~> 6) rails (~> 7.0.0) diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index 1a7f1e96b..e9290fa84 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -127,7 +127,18 @@ def render_in(view_context, &block) # parent template considering the current variant and emits the result without # double-rendering. def render_parent - @__vc_parent_call_block&.call + @__vc_parent_render_level ||= 0 # ensure a good starting value + + begin + target_render = self.class.instance_variable_get(:@__vc_ancestor_calls).reverse[@__vc_parent_render_level] + @__vc_parent_render_level += 1 + + target_render.bind_call(self) + ensure + @__vc_parent_render_level -= 1 + end + + # @__vc_parent_call_block&.call end # Optional content to be returned after the rendered template. @@ -459,6 +470,13 @@ def render_template_for(variant = nil) # Set collection parameter to the extended component child.with_collection_parameter provided_collection_parameter + if instance_methods(false).include?(:render_template_for) + __vc_ancestor_calls = defined?(@__vc_ancestor_calls) ? @__vc_ancestor_calls.dup : [] + + __vc_ancestor_calls.push(instance_method(:render_template_for)) + child.instance_variable_set(:@__vc_ancestor_calls, __vc_ancestor_calls) + end + super end diff --git a/lib/view_component/compiler.rb b/lib/view_component/compiler.rb index 51db120ec..872c2c986 100644 --- a/lib/view_component/compiler.rb +++ b/lib/view_component/compiler.rb @@ -65,13 +65,19 @@ def render_template_for(variant = nil) else templates.each do |template| method_name = call_method_name(template[:variant]) - template_info = { - path: template[:path], - lineno: -4, - body: compiled_template(template[:path]) - } + + redefinition_lock.synchronize do + component_class.silence_redefinition_of_method(method_name) + # rubocop:disable Style/EvalWithLocation + component_class.class_eval <<-RUBY, template[:path], 0 + def #{method_name} + #{compiled_template(template[:path])} + end + RUBY + # rubocop:enable Style/EvalWithLocation + end - define_compiled_template_methods(method_name, template_info) + # define_compiled_template_methods(method_name, template_info) end define_render_template_for @@ -136,15 +142,20 @@ def methodize(str) def define_render_template_for variant_elsifs = variants.compact.uniq.map do |variant| - "elsif variant.to_sym == :'#{variant}'\n #{call_method_name(variant)}" + safe_name = "_call_variant_#{normalized_variant_name(variant)}_#{component_class.name.underscore}" + component_class.define_method("_call_variant_#{normalized_variant_name(variant)}_#{component_class.name.underscore}", component_class.instance_method(call_method_name(variant))) + + "elsif variant.to_sym == :'#{variant}'\n #{safe_name}" end.join("\n") + component_class.define_method("_call_#{component_class.name.underscore.gsub("/", "__")}", component_class.instance_method(:call)) + body = <<-RUBY if variant.nil? - call + _call_#{component_class.name.underscore.gsub("/", "__")} #{variant_elsifs} else - call + _call_#{component_class.name.underscore.gsub("/", "__")} end RUBY