Haml in Helpers

By Nick Butler

Tags:

Haml in Helpers

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.

Make a bigger impact tomorrow