Last active
March 19, 2025 21:07
-
-
Save bradgessler/2e59b4b8e2fba751299723dfdfd62083 to your computer and use it in GitHub Desktop.
Phlex handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# config/initializers/phlex_template_handler.rb | |
require "action_view" | |
require "phlex" | |
# Intercept unknown "capitalized" method calls (e.g., PageView(...)) in templates, | |
# look up a Phlex component class, instantiate it, and render it. | |
# Crucially, we re-bind the user’s block so that `self` is the component, not the ActionView context. | |
module PhlexDynamicMethodCalls | |
def method_missing(name, *args, **kwargs, &block) | |
# Only intercept method calls starting with an uppercase letter (e.g. "PageView", "MyComponent", etc.) | |
if name[0] == name[0].upcase && Object.const_defined?(name) | |
klass = Object.const_get(name) | |
# If the constant is a Phlex component | |
if klass < Phlex::HTML | |
component = klass.new(*args, **kwargs) | |
# We need to ensure the user's block is evaluated in the context of `component`, | |
# rather than in the ActionView context. By default, `render_in` will capture | |
# the block using Rails helpers, which is not what we want. | |
# | |
# Instead, we wrap the block in a small "re-bound" block that calls | |
# `component.instance_exec(*yielded_args, &original_block)`. | |
# That way, inside the block, `self == component`, so methods like `h1`, `p`, etc. work. | |
if block | |
return component.render_in(self) do |*yielded_args| | |
# Evaluate the original block inside the component's context: | |
component.instance_exec(*yielded_args, &block) | |
end | |
else | |
# No block given | |
return component.render_in(self) | |
end | |
end | |
end | |
# Otherwise, pass it up the chain | |
super | |
end | |
def respond_to_missing?(name, include_private = false) | |
if name[0] == name[0].upcase && | |
Object.const_defined?(name) && | |
Object.const_get(name) < Phlex::HTML | |
true | |
else | |
super | |
end | |
end | |
end | |
class PhlexMethodMissingHandler | |
def self.call(template, source) | |
# We return Ruby code that extends the template's context with our method_missing module, | |
# then runs the original source code from the .html.rb file. | |
<<~RUBY | |
extend ::PhlexDynamicMethodCalls | |
#{source} | |
RUBY | |
end | |
end | |
# Tell Rails that ".html.rb" templates should use this custom handler: | |
ActionView::Template.register_template_handler :rb, PhlexMethodMissingHandler |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PageView( | |
title: "This is a page" | |
){ | |
erb <<~ERB | |
<h1><%= @title %></h1> | |
<p>Why would somebody want to put ERB in their Phlex view? Who knows</p> | |
ERB | |
div(class: "prose prose-md"){ | |
markdown <<~MD | |
* This | |
* could | |
* be | |
* cool | |
MD | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment