Skip to content

Commit b143082

Browse files
authored
Merge pull request #2375 from ViewComponent/2344-around-render
Add around_render lifecycle method for wrapping component rendering
2 parents 119abc5 + 58cf95b commit b143082

File tree

7 files changed

+64
-2
lines changed

7 files changed

+64
-2
lines changed

docs/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ nav_order: 6
1010

1111
## main
1212

13+
* Add `around_render` lifecyle method for wrapping component rendering in custom instrumentation, etc.
14+
15+
*Joel Hawksley*, *Blake Williams*
16+
1317
## 4.0.0.rc1
1418

1519
Almost six years after releasing [v1.0.0](https://github.com/ViewComponent/view_component/releases/tag/v1.0.0), we're proud to ship the first release candidate of ViewComponent 4. This release marks a shift towards a Long Term Support model for the project, having reached significant feature maturity. While contributions are always welcome, we're unlikely to accept further breaking changes or major feature additions.

docs/guide/lifecycle.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,21 @@ class ExampleComponent < ViewComponent::Base
2121
end
2222
end
2323
```
24+
25+
## `#around_render`
26+
27+
Since 4.0.0.rc2
28+
{: .label }
29+
30+
Define an `around_render` method to be called around the rendering of a component:
31+
32+
```ruby
33+
# app/components/example_component.rb
34+
class ExampleComponent < ViewComponent::Base
35+
def around_render
36+
MyIntrumenter.instrument do
37+
yield
38+
end
39+
end
40+
end
41+
```

docs/how-it-works.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ First, ViewComponent calls the `before_render` method, which gives ViewComponent
4242

4343
Second, ViewComponent calls `render?`, returning early with an empty string if `render?` returns false.
4444

45-
ViewComponent then renders the component template.
45+
ViewComponent then renders the component template, wrapped in a call to the `around_render` method.

lib/view_component/base.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ def render_in(view_context, &block)
143143
@output_buffer.with_buffer do
144144
@view_context.instance_variable_set(:@virtual_path, virtual_path)
145145

146-
rendered_template = render_template_for(@__vc_requested_details).to_s
146+
rendered_template =
147+
around_render do
148+
render_template_for(@__vc_requested_details).to_s
149+
end
147150

148151
# Avoid allocating new string when output_preamble and output_postamble are blank
149152
value = if output_preamble.blank? && output_postamble.blank?
@@ -228,6 +231,14 @@ def before_render
228231
# noop
229232
end
230233

234+
# Called around rendering the component. Override to wrap the rendering of a
235+
# component in custom instrumentation, etc.
236+
#
237+
# @return [void]
238+
def around_render
239+
yield
240+
end
241+
231242
# Override to determine whether the ViewComponent should render.
232243
#
233244
# @return [Boolean]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class AroundRenderComponent < ViewComponent::Base
2+
def call
3+
"Hi!".html_safe
4+
end
5+
6+
def around_render
7+
Instrumenter.tick do
8+
yield
9+
end
10+
end
11+
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class Instrumenter
2+
cattr_accessor :count, instance_reader: false, instance_writer: false, instance_accessor: false, default: 0
3+
4+
def self.tick
5+
Instrumenter.count = Instrumenter.count + 1
6+
yield
7+
end
8+
end

test/sandbox/test/rendering_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,4 +1311,14 @@ def test_supports_components_with_unnamed_splatted_arguments
13111311
assert_selector("*[data-name='#{orders.first.name}']", text: orders.first.name)
13121312
assert_selector("*[data-name='#{orders.last.name}']", text: orders.last.name)
13131313
end
1314+
1315+
def test_around_render
1316+
assert_equal(0, Instrumenter.count)
1317+
1318+
render_inline(AroundRenderComponent.new)
1319+
1320+
assert_equal(1, Instrumenter.count)
1321+
1322+
assert_text("Hi!")
1323+
end
13141324
end

0 commit comments

Comments
 (0)