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?