concat(string, binding) public

The preferred method of outputting text in your views is to use the <%= "text" %> eRuby syntax. The regular puts and print methods do not operate as expected in an eRuby code block. If you absolutely must output text within a non-output code block (i.e., <% %>), you can use the concat method.

Examples

  <%
      concat "hello", binding
      # is the equivalent of <%= "hello" %>

      if (logged_in == true):
        concat "Logged in!", binding
      else
        concat link_to('login', :action => login), binding
      end
      # will either display "Logged in!" or a login link
  %>
Show source
Register or log in to add new notes.
July 21, 2008
14 thanks

helper method to partial

concat can be useful for rendering a block to a partial from a helper:

def block_to_partial(partial_name, options = {}, &block)
  options.merge!(:body => capture(&block))
  concat(render(:partial => partial_name, :locals => options), block.binding)
end

This would be particularly useful if you had some partial to help you out with rounded corners, for example. So, in your helper:

def rounded_corners &block
  block_to_partial("shared/rounded_corners", {}, &block)
end

In your view you could have something like:

<% rounded_corners do -%>
     This text is surrounded by rounded corners
<% end -%>

You would have to create some partial in

app/views/shared/rounded_corners.html.erb

And it would look something like:

<div class='c1'>
  <div class=c2>
    .
    .
    .
    <%= body -%>
  </div>
</div>
August 13, 2008
7 thanks

Re: Helper method taking a block

The same using the ActionView::Helpers::TagHelper#content_tag and ActionView::Helpers::CaptureHelper#capture methods:

def render_tree(collection, &block)
  concat(
    content_tag(:ul,
      collection.collect { |item|
        content_tag(:li, capture(item, &block))
      }.join("\n")
    ),
    block.binding
  )
end

The benefit is that it’s easier to improve with html attributes (just add a hash of options to the content_tag call) and it makes just one call to concat (which probably makes it faster).

August 12, 2008
6 thanks

Helper method taking a block

Following the similar egzample by autonomous, here’s a simpler version when you just need to write a flexible helper method that takes a block.

For example, suppose you have a method that renders a tree:

def render_tree(ary, &block)
  concat("<ul>", block.binding)
  for elem in ary
    concat("<li>", block.binding)
    yield elem
    concat("</li>", block.binding)
  end
  concat("</ul>", block.binding)
end

You can use it in your view, eg:

<% render_tree(@objects) do |elem| -%>
  <%= elem.title -%>
  <%= link_to 'delete', elem -%>
<% end -%>

that would return for egzample:

<ul>
  <li>
    Test title
    <a href="delete">/elems/1</a>
  </li>
</ul>

Testing concat

To test such helper methods, use the following pattern (a utility method added to your Rspec/unit test suite:

def render_for(root, options = {})
  _erbout = ''
  render_tree(root, options) do |node|
    _erbout.concat(node.title)
  end
  _erbout
end

and test like this (RSpec example):

it "should return abc" do
  render_for(object).should == 'abc'
end
August 20, 2008
5 thanks

Iterate and join blocks

Following LacKac’s idea, we can write render_join (useful to render a collection with a small chunks of code, where a render :partial + :spacer_template would be overkill):

def render_join(collection, join_string, &block)
  output = collection.collect do |item| 
    capture(item, &block)
  end.join(join_string)
  concat(output, block.binding)
end

An example of use:

<% render_join(@items, '<br />') do |item| %>
   <p>Item title: <%= item.title %></p>
<% end %>
August 4, 2009 - (>= v2.2.1)
1 thank

Binding parameter deprecated in > 2.2

Supplying the binding argument produces a deprecation warning in 2.2 and newer:

DEPRECATION WARNING: The binding argument of #concat is no longer needed. Please remove it from your views and helpers.

December 3, 2009 - (>= v2.2.1)
1 thank

Capturing blocks after >2.2

After 2.2, you can omit the do …end block and simply use the &block variable directly:

concat(content_tag(:div, :class => "wrapped_content") do
  capture(&block)
end, block.binding)

becomes simply:

concat(content_tag(:div, capture(&block), :class => "wrapped_content"))