Sometimes you are coding a template and you need to refer to the same method chain over and over. For example, you’re coding a template that summarizes activity on recent messages. You iterate through a block of messages, and for each message you want to display some information pertaining to the last comment. You could do it like this:
<div class="active_messages">
<% @active_messages.each do |message| %>
<h1><%= message.title %></h1>
<div class="latest_comment">
<div class="avatar">
<%= avatar_for(message.comments.last.creator) %>
</div>
Latest comment <%= time_ago_in_words(message.comments.last.created_at) %> ago by <%= message.comments.last.creator.full_name %>
</div>
<% end %>
</div>
Everything inside of div.latest_comment deals with the exact same comment, but we have to use a method chain to get the comment each time (message.comments.last).
One solution is to set a local variable inside the iterating block with the knowledge that the variable will be reset after each iteration:
<div class="active_messages">
<% @active_messages.each do |message| %>
<h1><%= message.title %></h1>
<% comment = message.comments.last %>
<div class="latest_comment">
<div class="avatar">
<%= avatar_for(comment.creator) %>
</div>
Latest comment <%= time_ago_in_words(comment.created_at) %> ago by <%= comment.creator.full_name %>
</div>
<% end %>
</div>
One on hand, this is better because the methods called on `comment` are easier to scan. The whole div.latest_comment is more readable without the repeated method chain. On the other hand, setting a local variable is bad style. The local variable assignment creates state without explicitly showing where that state applies. It feels a little too PHP for my taste.
A better approach is to use the `tap` method to scope a variable to a block:
<div class="active_messages">
<% @active_messages.each do |message| %>
<h1><%= message.title %></h1>
<div class="latest_comment">
<% message.comments.last.tap do |comment| %>
<div class="avatar">
<%= avatar_for(comment.creator) %>
</div>
Latest comment <%= time_ago_in_words(comment.created_at) %> ago by <%= comment.creator.full_name %>
<% end %>
</div>
<% end %>
</div>
The `tap` block shows exactly where the scope of the assignment starts and ends. I like how the template explicitly says “now we are going to deal with a comment in the following section, and this is the comment we are working with.”
I just hit on this pattern today while working on a feature and I think it’ll come in handy in the future.