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 code block, you can use the concat method.

  <% concat "hello", binding %>

is equivalent to using:

  <%= "hello" %>
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
6 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)
0 thanks

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)
0 thanks

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"))