Ruby Dev's like me don't like verbosity no matter how small, syntactic sugar is one of the underlying attractions of Ruby.
Rails ViewComponents are a superb addition to Rails especially when paired with utility style CSS like Tailwind.
However, repeatedly writing these render
's gnaws at my OCD.
Constants in a view template, :shock:, the overloaded render
method, all those parens!
render(SomeAwesomeComponent.new) do |component|
Render(AlertComponent.new(type: 'success') do |component|
render(ButtonComponent.new(classes: 'justify-end ml-2 items-start')
end
end
What if I told you, you could create a helper, obvious right?
more awesome, more lowercase, less parens, easier on the eyes
component :some_awesome_component do |awesome|
component :alert, type: 'success' do |alert|
component :button, classes: 'justify-end ml-2 items-start'
end
end
I chose to use component
but if you want to go super succinct, you could even do
vc :some_awesome_component do |awesome|
vc :alert, type: 'success' do |alert|
vc :button, classes: 'justify-end ml-2 items-start'
end
end
This could be yours for the low low price of... reading below.
def component(name, context: nil, **args, &block)
return render_component_in(context, name, **args, &block) if context
render component_class_for(name).new(args), &block
end
def render_component_in(context, name, **args, &block)
component_class_for(name).new(args).render_in(context, &block)
end
private
def component_class_for(path)
name, namespace = path.to_s.split('/').reverse
file_name = "#{name}_component"
component_name = file_name.classify
namespace ||= namespace(file_name)
return "#{namespace.capitalize}::#{component_name}".constantize unless namespace == 'components'
component_name.constantize
end
What's more if you follow some design pattern like atomic, and structure your components as such, you can create a separate namespace
method to discover the component path allowing you to communicate your design system like
organism :some_awesome_component do |awesome|
molecule :alert, type: 'success' do |alert|
atom :button, classes: 'justify-end ml-2 items-start'
end
end
You can find the RailsByte here https://railsbytes.com/templates/xjNsDY
Top comments (2)
Hello @scottbarrow , Great article, although I dont think the rails bytes code works when passing blocks. However, the article's code
render component_class_for(name).new(args), &block
does the trick .cheers
You can keep your Tailwind classes, but I'm totally using these helpers. Great work.