Easy Tabs for Your Rails Application
If you’re like me, most of the Rails applications you’ve written use tabbed navigation. And if you’re like me, you find that writing the code to handle tabs becomes increasingly more boring with each new application. So I wrote tabulous. Tabulous aims to solve this problem once and for all with a quick and easy way to set up and manage your tabs.
Getting Started
The tutorial will get you off to a good start. Have fun!
Design Decisions
Consolidation
Whenever I implemented tab code in Rails I always ended up adding logic to my views and peppering my controllers with tab-related code.
Although it worked, it never felt quite right to have my tab-related code splattered all over my views and controllers in tiny little droplets like some sort of new Pollock-inspired software design pattern. So I decided to consolidate tab code as much as possible. It all lives in app/tabs/tabulous.rb. The only exceptions are the calls you have to make in your layout(s) to <%=tabs%> and <%=subtabs%> and any CSS you write for the tabs.
Ruby Table
Perhaps the most unusual design decision I made was to part ways with existing Ruby idioms and create my own idiom which I call a “Ruby table”. It looks like this:
config.tabs do [ #--------------------------------------------------------------------------------------------------------------------------------------------# # TAB NAME | DISPLAY TEXT | PATH | VISIBLE? | ENABLED? # #--------------------------------------------------------------------------------------------------------------------------------------------# [ :home_tab , 'Home' , root_path , true , true ], [ :people_tab , 'People' , people_path , true , true ], [ :preferences_tab , 'Preferences' , preferences_path , true , true ], [ :services_tab , 'Services' , services_path , true , true ], [ :jobs_tab , 'Jobs' , jobs_path , true , true ], [ :schedules_tab , 'Schedules' , "/schedules" , can? :view, Schedule , true ], [ :unavailable_dates_subtab , 'Availability' , "/unavailable_dates" , can? :manage, UnavailableDate , true ], #--------------------------------------------------------------------------------------------------------------------------------------------# # TAB NAME | DISPLAY TEXT | PATH | VISIBLE? | ENABLED? # #--------------------------------------------------------------------------------------------------------------------------------------------# ] end
Now Ruby has an idiom which I absolutely love: sending arguments to a method using a hash. Here is a method call with and without the idiom:
display 'some message', 'red', 40, 5 display 'some message', :color => 'red', :max_length => 40, :indentation => 5
Let’s see what happens when we use hashes to represent the above tab data:
config.tabs do { :home_tab => { :path => root_path }, :people_tab => { :path => people_path }, :preferences_tab => { :path => preferences_path }, :services_tab => { :path => services_path }, :jobs_tab => { :path => jobs_path }, :schedules_tab => { :path => "/schedules", :visible => can? :view, Schedule }, :unavailable_dates_subtab => { :text => 'Availability', :path => "/unavailable_dates", :visible => can? :manage, UnavailableDate } } end
The hash version has two benefits:
- It can rely on intelligent defaults so the code is terser.
- Rubyists are quite used to hashes, even nested hashes.
The Ruby table also has some compelling benefits which is why I ultimately went with it. One is explicitness. The hash version relies on intelligent defaults which means that the programmer needs to be familiar with how these defaults are determined. The hash version also does not show the programmer all of the available options. The Ruby table explicitly shows all options and all values. No guesswork.
Secondly, the Ruby table is easier to scan than the nested hash. Since code is read more often than it is written, I opted for the solution that was easier to read even though it’s more verbose to write.
The Ruby table idiom is not without its drawbacks, however. It forces you into the drudgery of constantly realigning the table columns. That’s why tabulous comes with a tabs:format rake task that does that for you.

Is there a way to change the markup? To be precise, I want the id of the nav element be changed to something with my own rather than “#tabs” or “#subtabs”.
Hi ramanr,
You can monkey_patch Tabulous.render_tabs and Tabulous.render_subtabs in (see lib/tabulous/tabulous.rb). Sorry that I don’t offer a better interface for that directly.
thanks for your response. I did see I can monkey patch your code and change the markup. As raised an issue in github, I wanted to find out is there any other way to get it done.