The Pug Automatic

Using concat and capture to clean up custom Rails helpers

Written June 18, 2013. Tagged Ruby on Rails, Helpers.

You can use the built-in Rails helpers, such as content_tag or link_to, in your own helpers.

If you need to concatenate them, you could use +:

module MyHelper
def widget
content_tag(:p, class: "widget") do
link_to("Hello", hello_path) + " " + link_to("Bye", goodbye_path)
end
end
end

But this gets fiddly quick.

concat

Instead, you can use concat:

module MyHelper
def widget
content_tag(:p, class: "widget") do
concat link_to("Hello", hello_path)
concat " "
concat link_to("Bye", goodbye_path)
end
end
end

Each concat(content) adds that content to the output buffer, much like <%= content %> will in a template file.

capture

There's a gotcha when you use concat outside a block helper (outside e.g. a content_tag or link_to block).

Say you have this:

module MyHelper
def widget
concat link_to("Hello", hello_path)
concat " "
concat link_to("Bye", goodbye_path)
end
end

You might expect this to produce no output, but you'll see the output once:

<% widget %>

You might expect this to produce the output once, but it will appear twice:

<%= widget %>

Why? Inside block helpers, concat appends only to that element's content; outside block helpers, it appends straight to the page itself, and is then appended to the page again as <%= widget %> writes out the helper's return value.

You can get around this with the capture helper. That's what content_tag uses internally: capture, true to its name, captures the content so it isn't appended straight to the page. Instead, it's built up as a separate string, for your custom helper to return (or have its way with):

module MyHelper
def widget
capture do
concat link_to("Hello", hello_path)
concat " "
concat link_to("Bye", goodbye_path)
end
end
end

If you find yourself writing a lot of complex markup in a helper, you may of course be better served by rendering a partial – perhaps from within your helper. But for those cases with a lot of logic influencing the choice of elements or attributes, where a helper may be the best option, concat and capture can clean things up a bit.