Skip to content

Commit 909437c

Browse files
committed
Add after_compile hook
1 parent 6a33362 commit 909437c

File tree

4 files changed

+68
-2
lines changed

4 files changed

+68
-2
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.0.rc6
14+
15+
* Add `compile.view_component` ActiveSupport::Notifications instrumentation, enabling gems to safely extend the compiler without monkey-patching.
16+
17+
*Jose Solás*
18+
1319
* Setup Trusted Publishing to RubyGems to improve software supply chain safety.
1420

1521
*Hans Lemuet*

lib/view_component/base.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,18 @@ def __vc_compiled?
588588
__vc_compiler.compiled?
589589
end
590590

591+
# Called after a component class has been compiled.
592+
#
593+
# Extensions can override this instance method to run logic after
594+
# compilation (e.g., generate helpers, register metadata, etc.).
595+
#
596+
# By default, this is a no-op. The compiler will invoke this method on an
597+
# uninitialized instance using `allocate` to avoid requiring initializer
598+
# arguments.
599+
def after_compile
600+
# no-op by default
601+
end
602+
591603
# @private
592604
def __vc_ensure_compiled
593605
__vc_compile unless __vc_compiled?

lib/view_component/compiler.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ def compile(raise_errors: false, force: false)
2424
return if @component == ViewComponent::Base
2525

2626
@lock.synchronize do
27-
# this check is duplicated so that concurrent compile calls can still
28-
# early exit
27+
# This check is duplicated so that concurrent compile calls can still
28+
# early exit before we instrument.
2929
return if compiled? && !force
3030

3131
gather_templates
@@ -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: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,45 @@ 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+
def self.calls = @@calls
203+
def after_compile; @@calls += 1; end
204+
erb_template ""
205+
end
206+
207+
self.class.const_set("TempHookComponent", klass)
208+
ViewComponent::CompileCache.invalidate_class!(klass)
209+
210+
ViewComponent::Compiler.new(klass).compile(force: true)
211+
assert_equal 1, klass.calls
212+
ensure
213+
self.class.send(:remove_const, "TempHookComponent") if self.class.const_defined?("TempHookComponent")
214+
ViewComponent::CompileCache.invalidate_class!(klass) if defined?(klass)
215+
end
216+
217+
def test_after_compile_not_called_on_cached_compile
218+
klass = Class.new(ViewComponent::Base) do
219+
@@calls = 0
220+
def self.calls = @@calls
221+
def after_compile; @@calls += 1; end
222+
erb_template ""
223+
end
224+
225+
self.class.const_set("TempHookCachedComponent", klass)
226+
ViewComponent::CompileCache.invalidate_class!(klass)
227+
228+
compiler = ViewComponent::Compiler.new(klass)
229+
compiler.compile(force: true)
230+
assert_equal 1, klass.calls
231+
232+
# compile again without force -> should not call hook again
233+
compiler.compile
234+
assert_equal 1, klass.calls
235+
ensure
236+
self.class.send(:remove_const, "TempHookCachedComponent") if self.class.const_defined?("TempHookCachedComponent")
237+
ViewComponent::CompileCache.invalidate_class!(klass) if defined?(klass)
238+
end
198239
end

0 commit comments

Comments
 (0)