Using Ruby Blocks for Custom Rails Tags
Ben Kittrell
06/20/2006 01:44PM
I've wanted to persuade my Ruby code to get a little bit more intimate with some HTML. Thanks to Ruby's block mechanism, this is quite trivial.
Ok, here's the problem. I had a helper method that iterated through a collection of menu items and printed them successively to the screen. The issue is that now I want to wrap certain HTML around the links it generates, however this HTML could change rather regularly.
You've seen this work if you've ever used a for loop.
<% that.each do |this| -%>
<p><a href="#"><%= this %></a></p>
<% end -%>
Beautiful. Now the cool part is we can make helper method that works in a similar fashion.
First you need the helper method, which can be added to application_helper.rb, or your controller's own helper.
def menu_linksmenus = { 'links' => 'Links',
'home' => 'Home',
'about' => 'About Me',
'blog' => 'Blog' }for menu in menus.keys
yield(link_to(menus[menu], :controller => menu))
endend
First I create a hash containing the controller name of the menu link as the key, and the link title as the value. This is just for an example to convey this concept. In my case this data comes from the database. If you haven't used blocks before the yield method can be confusing. Basically, later on we're going to pass some code into this method, and the yield call says run that code NOW! It also takes an argument of the link that I'm generating, which will make more sense in a bit.
Right now you might be saying, "well why don't you just put the for loop in the rhtml template". Well I have my reasons so BACK OFF. No seriously the links I'm generating are very dynamic and have a lot more code associated with them. Plus I can get a lot of reuse out of it. Not only that, but this can be used in a number of different applications.
So anyway, the other part of this is the rhtml.
<% menu_links do |link| -%>
<div class="menu"><%= link %></div>
<% end -%>
Awesome! The link variable represents the argument that was passed to the yield call. Now we can wrap anything we want around the link, in unadulterated HTML.
Let's jazz it up a bit. What if we want to change the div class based on whether it's a link the current page? Easy Peasey.
def menu_linksmenus = { 'links' => 'Links',
'home' => 'Home',
'about' => 'About Me',
'blog' => 'Blog' }for menu in menus.keys
selected = (current_page? :controller => menu) ? "_selected" : ""
yield(link_to(menus[menu], :controller => menu), selected)
endend
First we check to see if the link we're generating is the current page, and if so we pass the string "_selected" to the yield call.
<% menu_links do |link, sel| -%>
<div class="menu<%= sel %>"><%= link %></div>
<% end -%>
Now we pick up the new argument and append it to the CSS class name. Viola!
As I said before, there are a number of applications that this can be used for, and it's a great excuse to learn about one of Ruby's coolest features.
Comments
Yes, this sure is a great way to break the ice with Ruby blocks. Thanks for the write up. I was doing something similar today and could've used this then!
Nice tutorial, but how could i display submenuitems?
Post a Comment