Skip to content

Instantly share code, notes, and snippets.

@henrik
Last active May 25, 2025 01:17
Show Gist options
  • Save henrik/d053195e891b08e13c35338a82927f0a to your computer and use it in GitHub Desktop.
Save henrik/d053195e891b08e13c35338a82927f0a to your computer and use it in GitHub Desktop.
Rails ViewComponent convenience, e.g. `c.heading("Hi")`
Before:
<%= render Admin::HeadingComponent.new("Hi") %>
After:
<%= c.heading "Hi" %>
module Admin::BaseHelper
def c
@component_renderer ||= ComponentRenderer.new(self, namespace: Admin)
end
end
# E.g. in app/lib.
class ComponentRenderer
def initialize(view_context, namespace: Object)
@view_context = view_context
@namespace = namespace
end
def method_missing(name, ...)
const_name = build_const_name(name)
return super unless @namespace.const_defined?(const_name)
# For performance (TODO: measure), we define a method the first time. It won't be missing on the next call on the same (presumed memoized) instance.
klass = @namespace.const_get(const_name)
define_singleton_method(name) do |*args, **kwargs, &block|
@view_context.render(klass.new(*args, **kwargs), &block)
end
public_send(name, ...)
end
def respond_to_missing?(name, _include_private = false)
const_name = build_const_name(name)
@namespace.const_defined?(const_name) || super
end
private
def build_const_name(name) = name.to_s.camelize + "Component"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment