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