By Nick Butler
Tags: Development
Haml is one of my favourite Rails plugins – I really like the way it simplifies views, makes sure they’re syntactically correct, and outputs perfectly formatted HTML. However, this gets ruined when using helpers that output multiline HTML. It’d be great to use Haml generation in helpers so that everything keeps to the same formatting.
This class can help:
class HamlRenderer
def self.haml(&block)
HamlRenderer.new(&block).to_s
end
def initialize(&block)
@block = block
@binding = @block.binding
@object = eval('self', @binding)
@context = Object.new
class << @context
include Haml::Helpers
end
@context.init_haml_helpers
@object.metaclass.send(:class_eval) do
def haml_tag(*args, &block)
@haml_context.send(:haml_tag, *args, &block)
end
def haml_concat(*args, &block)
@haml_context.send(:haml_concat, *args, &block)
end
end
@object.instance_variable_set('@haml_context', @context)
yield @context
@object.instance_variable_set('@haml_context', nil)
@object.metaclass.send(:undef_method, :haml_tag)
@object.metaclass.send(:undef_method, :haml_concat)
end
def method_missing(symbol, *args, &block)
@context.send(symbol, *args, &block)
end
def to_s
@context.send(:haml_buffer).buffer
end
end
Now a helper can output HTML like so:
def table(headers, rows)
HamlRenderer.haml do
haml_tag :table do
haml_tag :tr do
headers.each do |header|
haml_tag :th, header
end
end
rows.each do |row|
haml_tag :tr do
row.each do |column|
haml_tag :td do
haml_concat column
end
end
end
end
end
end
end
This is rather contrived, but as you can see in the HamlRenderer.haml block there are two new functions available. haml_tag will create a new tag, and haml_concat will add some text content. In the end this is likely to be more verbose than constructing an HTML string, but the output will be cleaner and less prone to errors.