NOTE: My thanks go out to the guys at RailsConf who attended my "Birds of a Feather" talk about DRYing up views using wrapper helper classes. They gave me the idea to abstract my ideas one level further into its own class and now we have GiftWrap -- I hope you guys can make good use of it!
use_partial method rather than the before_yield and after_yield methods. Read the README file to see examples of how to use this new functionality.
Blocks and Procs
I have always been very impressed by the way the Rails form_for helper method works. I loved that you never had to remember the </form> closing tag or use the silly (and very deprecated) end_form_tag helper method. Perhaps what I was most drawn to about the method was how the block acted as a "closure" -- not just in the programming sense, but also in a semantic sense. It struck me as very beautiful that this method could create a mini-environment of rules and abilities, allow anything to happen within this environment and then close that context until the next time.
And that, of course, is the beauty of using blocks and procs in Ruby.
So why do these blocks in the view stand out to me when their counterparts in models and controllers simply aren't that big of a deal? I think part of the reason is that you don't see blocks in the view very often. The content_tag helper takes a block, but the method itself isn't really that helpful. Looping methods like each take a block, of course, but again that's nothing to blog about. Form_for seems to be a fairly unique method within the Rails framework.
I have found, however, that using blocks within the view to wrap and contextualize markup can be extremely helpful in DRYing up views and making markup more modular.
Nice and Easy Blocks with GiftWrap
Creating a method that works like form_for is a little tricky. First you have to create a helper class with a series of methods for generating the wrapper markup and anything else you need. Then you need to create the helper method itself, which initializes your class, uses the confusing concat method to write your wrapper markup to the ERB buffer, and then yields the class instance to your block.
Thankfully, I was able to abstract all the common elements into the GiftWrap class. By subclassing GiftWrap and following a couple simple conventions, you will easily be able to create wrapper classes that can bring clarity and DRYness to your views.
Example: The Help Popup
The site that I am currently building requires a large number of "question mark" buttons -- if the user is unsure of what a certain section of the site does, a quick press of help button gives them a simple popup box with an explanation. The markup for these buttons/popups looked like this:
<p class="help_popup_link" id="feature_help_popup_link">
<a href="#" onclick="$(this).up('.help_popup_link').next().toggle(); return false;">Help</a>
</p>
<div class="help_popup" id="feature_help_popup" style="display: none;">
<p class="close_button">
<a href="#" onclick="$(this).up('.help_popup').hide(); return false;">Close</a>
</p>
<!-- any amount of markup should be insertable here -->
</div>
These popups will all share similar markup, but their content can vary wildly. I quickly ruled out using partials to DRY my view -- I need too much flexibility in what goes inside this wrapper markup.
I chose instead to use a wrapper class that inherits from GiftWrap.
class HelpPopup < GiftWrap before_yield :help_button, :open_block, :close_button after_yield :close_block def initialize(id) @id = id end private def help_button %Q{ <p class="help_popup_link" id="#{@id}_help_popup_link"> #{link_to_function 'Help', "$(this).up('.help_popup_link').next().toggle()"} </p> } end def close_button %Q{ <p class="close_button"> #{link_to_function 'Close', "$(this).up('.help_popup').hide()"} </p> } end # Called before yielding a block to this instance def open_block %Q{<div class="help_popup" id="#{@id}_help_popup" style="display: none;">} end # Called after yielding a block to this instance def close_block "</div>" end end
As you probably have guessed, the methods passed into the before_yield method will be called in order before the block is run and the returned values will be added to the markup. The after_yield method acts the same.
When GiftWrap is subclassed, a helper method is dynamically generated as the underscored name of the new class. In this example the helper method will be named help_popup because the class is named HelpPopup.
The implementation now looks like this in the view...
<% help_popup :feature do %> <p>This is explanation text within the popup box.</p> <p>More text here.</p> <% end %>
This generates this markup...
<p class="help_popup_link" id="feature_help_popup_link">
<a href="#" onclick="$(this).up('.help_popup_link').next().toggle(); return false;">Help</a>
</p>
<div class="help_popup" id="feature_help_popup" style="display: none;">
<p class="close_button">
<a href="#" onclick="$(this).up('.help_popup').hide(); return false;">Close</a>
</p>
<p>This is explanation text within the popup box.</p>
<p>More text here.</p>
</div>
Example: Boxes with Rounded Corners
Another quality of this site is that it uses boxes with rounded corners extensively. Using a GiftWrap subclass I can remove all the repetitive wrapper markup needed for a 'sliding doors' approach, but that's not all! I also want to use a standard set of markup and CSS classes for my subheaders and "more" links, so it would be great to encapsulate that within this block as well. Here's how we can do it...
class RoundCornerBox < GiftWrap before_yield :header, :open_divs after_yield :close_divs # A new instance of RoundCornerBox will be instantiated each time the # dynamic round_corner_box is called in the view. The arguments passed into # the helper method are passed on directly to the initialize method. def initialize(name, options={}) @name = name @header = options[:header] end # # # These methods can be used within your wrapper block. # # # def subheader(text) "<h4 class='subheader'>#{text}</h4>" end def more_button(text, url) "<p class='more_button'>#{link_to text, url}</p>" end private # # # These methods will only be used internally for creating the wrapper markup. # # # def header @header ? "<h3 class='round_corner_box_header'>#{@header}</h3>" : '' end def open_divs %Q{ <div class="round_corner_box_top"> <div class="round_corner_box_bottom" id="#{@name}_round_corner_box"> } end def close_divs "</div>\n</div>" end end
Now here's how we can implement those public methods...
<% round_corner_box :news, :header => "Today's News" do |b| %>
<%= b.subheader "Top Story" %>
<p>Today was a very exciting day in the news...</p>
<%= b.more_button "Full Story", top_story_path %>
<%= b.subheader "Lesser Story" %>
<p>This story is definitely less important than the top story...</p>
<%= b.more_button "Full Story", bottom_story_path %>
<% end %>
With the GiftWrap class, you now can easily harness the power and flexibility of using wrapper classes in your view to DRY up your code. So go ahead, get GiftWrap now!
If you have ideas for how to improve or extend GiftWrap, please feel free to fork the Git repository or leave comments.

