Skip to content

Commit

Permalink
Merge functionality instead
Browse files Browse the repository at this point in the history
  • Loading branch information
camertron committed Jul 17, 2023
1 parent 4439760 commit fa6e672
Show file tree
Hide file tree
Showing 12 changed files with 37 additions and 94 deletions.
31 changes: 7 additions & 24 deletions docs/guide/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,27 @@ end

### Rendering parent templates

Since 3.5.0
Since 2.55.0
{: .label }

To render a parent component's template from a subclass' template, use `yield :parent`:
To render a parent component's template from a subclass' template, use `#render_parent`:

```erb
<%# my_link_component.html.erb %>
<div class="base-component-template">
<% yield :parent %>
<%= render_parent %>
</div>
```

If the parent supports the current variant, the variant will automatically be rendered. `yield :parent` replaces the deprecated `#render_parent` method, which doesn't respect variants or multiple levels of inheritance.
If the parent supports the current variant, the variant will automatically be rendered.

`yield :parent` also works with inline templates:
`#render_parent` also works with inline templates:

```ruby
class MyComponent < ViewComponent::Base
erb_template <<~ERB
<div>
<% yield :parent %>
<%= render_parent %>
</div>
ERB
end
Expand All @@ -154,24 +154,7 @@ class MyComponent < ViewComponent::Base
end
```

`super` will attempt to call the `#call_phone` method on the parent class. If the parent class doesn't support the "phone" variant, Ruby will raise a `NoMethodError`. Consider using a template and `render :parent` to handle superclass variants automatically.

### render_parent

Since 2.55.0
{: .label }

Deprecated
{: .label .label-red }

To render a parent component's template from a subclass, call `render_parent`:

```erb
<%# my_link_component.html.erb %>
<div class="base-component-template">
<% render_parent %>
</div>
```
`super` will attempt to call the `#call_phone` method on the parent class. If the parent class doesn't support the "phone" variant, Ruby will raise a `NoMethodError`. Consider using a template and `#render_parent` to handle superclass variants automatically.

## Trailing whitespace

Expand Down
16 changes: 5 additions & 11 deletions lib/view_component/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,25 +115,19 @@ def render_in(view_context, &block)
@current_template = old_current_template
end

# DEPRECATED
#
# Subclass components that call `super` inside their template code will cause a
# double render if they emit the result:
# double render if they emit the result.
#
# ```erb
# <%= super %> # double-renders
# <% super %> # does not double-render
# ```
#
# Calls `super`, returning `nil` to avoid rendering the result twice.
# `super` also does not consider the current variant. `render_parent` penders the
# parent template considering the current variant and emits the result without
# double-rendering.
def render_parent
ViewComponent::Deprecation.deprecation_warning(
"render_parent", "Use `yield :parent` instead."
)

mtd = @__vc_variant ? "call_#{@__vc_variant}" : "call"
method(mtd).super_method.call
nil
@__vc_parent_call_block&.call
end

# Optional content to be returned after the rendered template.
Expand Down
39 changes: 20 additions & 19 deletions lib/view_component/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def compile(raise_errors: false, force: false)

template_info = {
path: template.path,
lineno: template.lineno - 1,
lineno: template.lineno - 4,
body: compiled_inline_template(template)
}

Expand All @@ -67,7 +67,7 @@ def render_template_for(variant = nil)
method_name = call_method_name(template[:variant])
template_info = {
path: template[:path],
lineno: -1,
lineno: -4,
body: compiled_template(template[:path])
}

Expand Down Expand Up @@ -98,25 +98,26 @@ def define_compiled_template_methods(method_name, template_info)

# rubocop:disable Style/EvalWithLocation
component_class.class_eval <<-RUBY, template_info[:path], template_info[:lineno]
private def #{unique_method_name}
if block_given?
#{template_info[:body]}
private def #{unique_method_name}(&block)
if block
@__vc_parent_call_block = block
begin
#{template_info[:body]}
end.tap do
@__vc_parent_call_block = nil
end
else
#{unique_method_name} do |msg|
case msg
when :parent
super_method_name = if @__vc_variant
super_variant_method_name = :"call_\#{@__vc_variant}__#{unique_superclass_name}"
respond_to?(super_variant_method_name, true) ? super_variant_method_name : nil
end
super_method_name ||= :call__#{unique_superclass_name}
send(super_method_name)
nil
else
raise UnexpectedTemplateYield.new(msg)
#{unique_method_name} do
super_method_name = if @__vc_variant
super_variant_method_name = :"call_\#{@__vc_variant}__#{unique_superclass_name}"
respond_to?(super_variant_method_name, true) ? super_variant_method_name : nil
end
super_method_name ||= :call__#{unique_superclass_name}
send(super_method_name)
nil
end
end
end
Expand Down
8 changes: 0 additions & 8 deletions lib/view_component/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,4 @@ def initialize(setter_method_name, setter_name)
super(MESSAGE.gsub("SETTER_METHOD_NAME", setter_method_name.to_s).gsub("SETTER_NAME", setter_name.to_s))
end
end

class UnexpectedTemplateYield < StandardError
MESSAGE = "An unexpected value 'YIELDED_VALUE' was yielded inside a component template. Only :parent is allowed."

def initialize(yielded_value)
super(MESSAGE.gsub("YIELDED_VALUE", yielded_value.inspect))
end
end
end

This file was deleted.

4 changes: 0 additions & 4 deletions test/sandbox/app/components/bad_yield_value_component.rb

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div class="level2-component variant">
<%= yield :parent %>
<%= render_parent %>
</div>
2 changes: 1 addition & 1 deletion test/sandbox/app/components/level2_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div class="level2-component base">
<%= yield :parent %>
<%= render_parent %>
</div>
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div class="level3-component variant">
<%= yield :parent %>
<%= render_parent %>
</div>
2 changes: 1 addition & 1 deletion test/sandbox/app/components/level3_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div class="level3-component base">
<%= yield :parent %>
<%= render_parent %>
</div>
16 changes: 1 addition & 15 deletions test/sandbox/test/inline_template_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,10 @@ def initialize(name)
end
end

class InlineBadYieldComponent < ViewComponent::Base
erb_template <<~ERB
<%= yield :foo %>
ERB
end

class InlineComponentDerivedFromComponentSupportingVariants < Level2Component
erb_template <<~ERB
<div class="inline-template">
<%= yield :parent %>
<%= render_parent %>
</div>
ERB
end
Expand Down Expand Up @@ -143,14 +137,6 @@ class InlineComponentDerivedFromComponentSupportingVariants < Level2Component
assert_selector ".inline-template .level2-component.variant .level1-component"
end

test "yielding unexpected value raises error" do
error = assert_raises(ViewComponent::UnexpectedTemplateYield) do
render_inline(InlineBadYieldComponent.new)
end

assert_equal "An unexpected value ':foo' was yielded inside a component template. Only :parent is allowed.", error.message
end

test "calling template methods multiple times raises an exception" do
error = assert_raises ViewComponent::MultipleInlineTemplatesError do
Class.new(InlineErbComponent) do
Expand Down
8 changes: 0 additions & 8 deletions test/sandbox/test/rendering_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -991,14 +991,6 @@ def test_child_components_fall_back_to_default_variant
assert_selector ".level3-component.base .level2-component.base .level1-component"
end

def test_yielding_unexpected_value_raises_error
error = assert_raises(ViewComponent::UnexpectedTemplateYield) do
render_inline(BadYieldValueComponent.new)
end

assert_equal "An unexpected value ':foo' was yielded inside a component template. Only :parent is allowed.", error.message
end

def test_component_renders_without_trailing_whitespace
template = File.read(Rails.root.join("app/components/trailing_whitespace_component.html.erb"))
assert template =~ /\s+\z/, "Template does not contain any trailing whitespace"
Expand Down

0 comments on commit fa6e672

Please sign in to comment.