Skip to content

Commit 8ed08c8

Browse files
committed
Add after_compile hook
1 parent 4bbea1c commit 8ed08c8

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

docs/CHANGELOG.md

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

1111
## main
1212

13+
## 4.0.3
14+
15+
* Add `after_compile` hook to enable extensions to run logic after component compilation. Extensions can override this instance method to add custom post-compilation behavior.
16+
17+
*Jose Solás*
18+
1319
## 4.0.2
1420

1521
* Share the view context in tests to prevent out-of-order rendering issues for certain advanced use-cases, eg. testing instances of Rails' `FormBuilder`.

lib/view_component/base.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,18 @@ def __vc_compiled?
611611
__vc_compiler.compiled?
612612
end
613613

614+
# Called after a component class has been compiled.
615+
#
616+
# Extensions can override this instance method to run logic after
617+
# compilation (e.g., generate helpers, register metadata, etc.).
618+
#
619+
# By default, this is a no-op. The compiler will invoke this method on an
620+
# uninitialized instance using `allocate` to avoid requiring initializer
621+
# arguments.
622+
def after_compile
623+
# no-op by default
624+
end
625+
614626
# @private
615627
def __vc_ensure_compiled
616628
__vc_compile unless __vc_compiled?

lib/view_component/compiler.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ def compile(raise_errors: false, force: false)
5252
@component.__vc_build_i18n_backend
5353

5454
CompileCache.register(@component)
55+
56+
# Invoke instance-level after_compile hook without calling initialize.
57+
begin
58+
@component.allocate.after_compile
59+
rescue NoMethodError
60+
# no-op
61+
end
5562
end
5663
end
5764

test/sandbox/test/base_test.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,59 @@ def test_uses_module_configuration
195195
assert_equal false, TestAlreadyConfigurableModule::SomeComponent.instrumentation_enabled
196196
assert_equal false, TestAlreadyConfiguredModule::SomeComponent.instrumentation_enabled
197197
end
198+
199+
def test_after_compile_hook_called_on_compile
200+
klass = Class.new(ViewComponent::Base) do
201+
@@calls = 0
202+
203+
def self.calls
204+
@@calls
205+
end
206+
207+
def after_compile
208+
@@calls += 1
209+
end
210+
211+
erb_template ""
212+
end
213+
214+
self.class.const_set(:TempHookComponent, klass)
215+
ViewComponent::CompileCache.invalidate_class!(klass)
216+
217+
ViewComponent::Compiler.new(klass).compile(force: true)
218+
assert_equal 1, klass.calls
219+
ensure
220+
self.class.send(:remove_const, :TempHookComponent) if self.class.const_defined?(:TempHookComponent)
221+
ViewComponent::CompileCache.invalidate_class!(klass) if defined?(klass)
222+
end
223+
224+
def test_after_compile_not_called_on_cached_compile
225+
klass = Class.new(ViewComponent::Base) do
226+
@@calls = 0
227+
228+
def self.calls
229+
@@calls
230+
end
231+
232+
def after_compile
233+
@@calls += 1
234+
end
235+
236+
erb_template ""
237+
end
238+
239+
self.class.const_set(:TempHookCachedComponent, klass)
240+
ViewComponent::CompileCache.invalidate_class!(klass)
241+
242+
compiler = ViewComponent::Compiler.new(klass)
243+
compiler.compile(force: true)
244+
assert_equal 1, klass.calls
245+
246+
# compile again without force -> should not call hook again
247+
compiler.compile
248+
assert_equal 1, klass.calls
249+
ensure
250+
self.class.send(:remove_const, :TempHookCachedComponent) if self.class.const_defined?(:TempHookCachedComponent)
251+
ViewComponent::CompileCache.invalidate_class!(klass) if defined?(klass)
252+
end
198253
end

0 commit comments

Comments
 (0)