Getting output from Rails template helpers, plus blocks

I had something I was doing repetitively in an ERb template today and I figured I’d DRY it up by making a helper. Specifically, I was trying to output some given HTML unless a condition was met that should generate a hidden field instead—this case calls for handcrafted HTML generation.

I was doing things like:

<% ignore_these_fields = ['foo', 'bar'] %>

<% unless ignore_these_fields.include?('abc') %>
  <input type="text" name="abc" value="<%= @vals['abc'] %>" />
<% else %>
  <input type="hidden" name="abc" value="<%= @vals['abc'] %>" />
<% end %>

<% unless ignore_these_fields.include?('foo') %>
  <input type="radio" name="foo" value="<%= @vals['foo'] %>" /> One
  <input type="radio" name="foo" value="<%= @vals['foo'] %>" /> Two
<% else %>
  <input type="hidden" name="foo" value="<%= @vals['foo'] %>" />
<% end %>

<% unless ignore_these_fields.include?('bar') %>
  <select name="bar">
    <option <% 'selected' if @vals['bar']=='a'%>>blah1</option>
    <option <% 'selected' if @vals['bar']=='b'%>>blah2</option>
    <option <% 'selected' if @vals['bar']=='c'%>>blah3</option>
  </select>
<% else %>
  <input type="hidden" name="foo" value="<%= @vals['bar'] %>" />
<% end %>

Here, fairly different HTML form fields get rendered for each type of fields, and in some cases we do not want to show certain fields. So I wanted to do this instead: write a helper that would take a block and render its contents verbatim, otherwise create a hidden field.

<% ignore_these_fields = ['foo', 'bar'] %>

<% render_unless_ignored(ignore_these_fields, 'abc') do %>
  <input type="text" name="abc" value="<%= @vals['abc'] %>" />
<% end %>

<% render_unless_ignored(ignore_these_fields, 'foo') do %>
  <input type="radio" name="foo" value="<%= @vals['foo'] %>" /> One
  <input type="radio" name="foo" value="<%= @vals['foo'] %>" /> Two
<% end %>

<% render_unless_ignored(ignore_these_fields, 'bar') do %>
  <select name="bar">
    <option <% 'selected' if @vals['bar']=='a'%>>blah1</option>
    <option <% 'selected' if @vals['bar']=='b'%>>blah2</option>
    <option <% 'selected' if @vals['bar']=='c'%>>blah3</option>
  </select>
<% end %>

That’s a bit DRYer. But here came the tricky part: the helper. I had done what I thought was right:

def render_unless_ignored (ignoring_array, key)
  unless ignoring_array.include?(key)
    yield
  else
    %Q{<input type="hidden" name="#{CGI::escapeHTML key}" value="#{CGI::escapeHTML @vals[key]}" />}.html_safe
  end
end

The yielded content was showing up but the string result was not! The ERb template was not outputting it. Then a colleague pointed out that in this case we need to tell the ERb template to render the string with concat():

def render_unless_ignored (ignoring_array, key)
  unless ignoring_array.include?(key)
    yield
  else
    concat %Q{<input type="hidden" name="#{CGI::escapeHTML key}" value="#{CGI::escapeHTML @vals[key]}" />}.html_safe
  end
end

And THAT worked!