For my Ruby on Rails pet project Is it Science Fiction? I’ve reached that point when I wanted to show tabs. You know, for the menu, on top of the page. I quickly wrote something like:
<ul class="tabs"> <li><%= link_to 'Home', root_url %></li> <li><%= link_to 'Recommend', new_item_url %></li> <li><%= link_to 'Ranking', items_url %></li> <% if session[:user_name] -%> <li><%= link_to 'Log out', session_url, :method => :delete %></li> <li><%= link_to 'My Profile', edit_profile_url %></li> <% else %> <li><%= link_to 'Log in', new_session_url %></li> <% end %> </ul>
and styled it like the Listamatic Unraveled CSS tabs. And everything was good, except that the current tab wasn’t highlighted. Highlighting it is a matter of setting the correct class for it.
For that I created a new version of link_to that adds the “current” class to a link if it points to the current page. That is not very hard actually, but you have to consider that some tabs, like Ranking, are not only highlighted when that page is show, but when any subpage (like a ranked item) is shown as well. After trying many solutions (in an effort to find the simplest one) I’ve settled for link_to2 and the tabs now look like this:
<ul class="tabs"> <li><%= link_to2 'Home', root_url %></li> <li><%= link_to2 'Recommend', new_item_url %></li> <li><%= link_to2 'Ranking', items_url, {:extra_current => {:controller => :items, :action => :show}} %></li> <% if session[:user_name] -%> <li><%= link_to 'Log out', session_url, :method => :delete %></li> <li><%= link_to2 'My Profile', edit_profile_url %></li> <% else %> <li><%= link_to2 'Log in', new_session_url %></li> <% end %> </ul>
Look at the Ranking tab, it has an extra_current that adds other pages to be treated as current. The code to do this is the following (I’ve put it in application_helper.rb):
module ApplicationHelper def link_to2(*args, &block) if block_given? options = args.first || {} html_options = args.second || {} else name = args.first options = args.second || {} html_options = args.third || {} end if current_page?(options) or as_array(html_options[:extra_current]).any? {|o| current_page2? o} html_options[:class] = add_class(html_options[:class], "current") end html_options.delete(:extra_current) if block_given? link_to(options, html_options, &block) else link_to(name, options, html_options, &block) end end private def add_class(classes, new_class) ((classes or "").split(" ") << new_class).join(" ") end def as_array(o) if o == nil [] elsif not o.is_a? Array [o] else o end end # current_page? of {:controller => :blah, :action => :bleh} when the routes also require an id raises a route error. current_page2? doesn't. def current_page2?(p) current_page? p rescue return false end end
Feel free to pick the code and use it in any way you want. I’m thinking of turning it into a gem, but I need a better name than link_to2, any ideas?
Leave a Reply