Skip to content

12b Flash Refactoring

Dave Strus edited this page Jul 15, 2015 · 1 revision

I don't know about you, but I hate having all that flash message logic in the layout.

app/views/layouts/application.html.erb

            <% flash.each do |name, msg| -%>
              <% if msg.is_a? Array -%>
                <div class="<%= name %>">
                  <ul>
                    <% msg.each do |message| -%>
                      <li><%= message %></li>
                    <% end -%>
                  </ul>
                </div>
              <% else -%>
                <%= content_tag :div, msg, class: name %>
              <% end -%>
            <% end -%>

Wouldn't it be nice if we could replace all that with this:

            <%= flash_messages(flash) unless flash.empty? %>

We can make that happen if we build out that flash_messages helper. I'm going to work from the inside out, and first define a method to replace just this part:

                  <ul>
                    <% msg.each do |message| -%>
                      <li><%= message %></li>
                    <% end -%>
                  </ul>

We'll create the li elements with a content_tag and nest it inside another content_tag for the ul.

app/helpers/application_helper.rb

  def flash_list(messages)
    content_tag :ul do
      messages.map do |message|
        content_tag(:li, message)
      end.join.html_safe
    end
  end

We'll use Enumerator#map to collect all of the li elements in an array. At the end, we'll join them into a string.

When we return the resulting string, we need to prevent Rails from escaping the HTML inside. That's where html_safe comes in. When we use content_tag by itself, this isn't necessary, but because our string was built from an array, and not directly from content_tag, we need html_safe.

Now let's replace that giant if statement.

              <% if msg.is_a? Array -%>
                <div class="<%= name %>">
                  <ul>
                    <% msg.each do |message| -%>
                      <li><%= message %></li>
                    <% end -%>
                  </ul>
                </div>
              <% else -%>
                <%= content_tag :div, msg, class: name %>
              <% end -%>

We'll create another helper method.

app/helpers/application_helper.rb

  def flash_message_output(content)
    if content.is_a? Array
      flash_list content
    else
      content
    end
  end

If the content supplied to flash_message_output, we'll call the flash_list method we wrote a moment ago. Otherwise, we just return the content itself.

Now let's write the actual flash_messages method to output the entire div. From this method, we'll call the flash_message_output method we just wrote.

app/helpers/application_helper.rb

  def flash_messages(flash)
    flash.map do |name, msg|
      content_tag :div, class: "alert #{name}" do
        flash_message_output msg
      end
    end.join.html_safe
  end

Now that single line in our layout does the work of that whole big mess. I like this version much better!

If you're into it, commit it!

$ git add .
$ git commit -m "Extract flash into helper methods."