SimpleSidebar – If you have sidebars, you need this plugin.

Plugins, Ruby on Rails Add comments

Why you need it

Like many other websites out there, Obsidian Portal arranges its content into multiple columns. We have the main content in the center column, and ancillary content in the right sidebar. It’s a fairly standard organization scheme and it works well for us.

Normally, this sort of organization is handled in Rails via the use of layouts. One layout may contain the sidebars for your homepage, another one holds the sidebars for the user’s profile page, and so on. In the beginning, this works.

However, once your application reaches a certain level of complexity, this approach begins to show its true inflexibility. In many cases, you will have some sidebars that are common to every page, such as a login or search bar. You can split these into a partial, but you still have to include that partial in every layout. Not a big deal, but frustrating. Soon after this, you’ll run into cases where you need different sidebars based on the particular action for a controller. Perhaps there are some standard info sidebars for showing and listing, but for editing and creating you want a sidebar that has some editing tips. Now, if you’ve been using the convention for layouts, you probably have a layout for each controller that is named the same as the controller. That way, Rails will automatically select that layout. To support the different sidebars for different actions, you’ll have to clutter that layout up complicated if/then conditionals. Very quickly the template contains more Ruby than HTML.

Trust me, we went this route initially, and it became a nightmare. Here is one of our layouts, prior to SimpleSidebar:

< %= render :partial => “layouts/header” %>

< %= render :partial => ‘account/login’ -%>

< % if controller.action_name == 'show' %>

Description< % if session[:user] && permit?("game_master of :campaign") %> (< %= link_to 'Edit', {:action => ‘edit’, :id => @campaign._id} %>)< % end %>
< %=h @campaign.description %>

Party< % if session[:user] && permit?("game_master of :campaign") %> (< %= link_to 'Edit', {:action => ‘edit_members’, :id => @campaign._id} %>)< % end %>

GM: < %= link_to @campaign.game_master.login.capitalize, :controller => “account”, :action => “profile”, :login => @campaign.game_master.login %>

    < % players_with_pcs = [] %>
    < % for pc in @campaign.player_characters %>

  • < %=h pc.author.login %> (
    < %= link_to h(pc.name), :controller => ‘game_contents’, :action => ‘show’, :id => pc._id %>
    )
    < % players_with_pcs << pc.author %>
  • < % end %>
    < % for player in @campaign.players %>
    < % if !players_with_pcs.include?(player) %>

  • < %=h player.login %>
  • < % end %>
    < % end %>


< % end %>

< % if (controller.action_name == 'list') or (controller.action_name == 'search') %>
< %= render(:partial => ‘search/search_bar’, :locals => {:search_type => ‘Campaigns’, :search_controller => ‘campaigns’, :search_action => ‘search’}) %>

< % end %>

< % if flash[:error] %>
< %= flash[:error] %>

< % end %>
< % if flash[:warning] %>

< %= flash[:warning] %>

< % end %>
< % if flash[:notice] %>

< %= flash[:notice] %>

< % end %>
< %= yield %>

< %= render :partial => “layouts/footer” %>

Ignoring the fact that I’m terrible at writing templates, you can see there’s a fair amount of if/then conditional code sprinkled in. The Login sidebar is supposed to be there always, the Description and Party sidebars are only supposed to appear in the show action, and the Search bar is only supposed to be there in the list or search action. Ugh! And, this is only one of several layouts for several controllers. Each one had this sort of conditional garbage all over the place.

With SimpleSidebar, much of this conditional code moves into your controllers. Much like before and after filters, you define sidebars for a controller (or a particular action) and can give conditions on when they appear.

For example, using the previous example, with SimpleSidebar, I added the following to my controller:

sidebar :login, :unless => :logged_in?
sidebar :welcome, :if => :logged_in?

sidebar :campaign_description, :o nly => :show
sidebar :campaign_party_info, :o nly => :show
sidebar :campaign_search, :o nly => [:list, :search]

This allowed me to reduce my layout to the following:

< %= render :partial => “layouts/header” %>

< %= render_sidebars %>

< % if flash[:error] %>
< %= flash[:error] %>

< % end %>
< % if flash[:warning] %>

< %= flash[:warning] %>

< % end %>
< % if flash[:notice] %>

< %= flash[:notice] %>

< % end %>
< %= yield %>

< %= render :partial => “layouts/footer” %>

A little nicer, huh? If you look closely, there is nothing in there specific to any particular controller or action. Once I converted all my controllers over to SimpleSidebar, I noticed that all my layouts were identical, meaning I was able to delete most of them and just keep a single standard layout.

How to use it

SimpleSidebar is incredibly easy. I was up and running in about ten minutes (including the vendor drop). I was finished refactoring all my ugly templates in about 1.5 hours. Not bad for a major refactor.

Note: These instructions are for revision 13 of the plugin. Beware if you’re using a different version (although it probably won’t change that much).

1. Get the plugin

As always, it’s very easy to install plugins. Just fire up your plugin installer

script/plugin install svn://rubyforge.org/var/svn/simplesidebar

Likewise, as always, we here at AisleTen suggest you skip the plugin installer and do it manually using vendor drops. Just be sure to execute the install.rb when you’re done.

2. Update your controllers

This is where most of the magic of SimpleSidebar occurs. Rather than dealing with sidebars and their conditional display in the view layer, SimpleSidebar moves it to the controller layer. For example, your controller might look like this:

def MyController < ApplicationController
sidebar :login, :unless => :logged_in?
sidebar :welcome, :if => :logged_in?
sidebar :search
sidebar :edit_help, :o nly => [:edit, :new]


end

If you’re familiar with filters, then this probably looks very familiar. When an action is executed in this controller, the following will be run:

  1. If the logged_in? method returns false, then the login sidebar is appended to the list of sidebars to display.
  2. If the logged_in? method returns true, then the welcome sidebar is appended to the list of sidebars to display.
  3. The search sidebar is appended to the list of sidebars to display.
  4. If the current action is edit or new, then the edit_help sidebar is appended to the list of sidebars to display.

3. Add the partials.

If you’ve been good, you already have all your sidebars as partials. If not, it’s time to refactor as such. The partials need to be named the same as the sidebars listed in your controllers. Using our previous example, we will need four partials:

  • _login.rhtml
  • _welcome.rhtml
  • _search.rhtml
  • _edit_help.rhtml

These partials must be placed in the app/views/sidebars directory.

4. Update your layout.

Inside your layout, the code becomes very simple. All you have to do is add <%= render_sidebars %> wherever you want the sidebars displayed.

I highly recommend extracting this to your layouts. That way, rather than calling render_sidebars in each template you render, you just put it into the layout and know it gets called every time the layout is rendered around your template.

5. Delete all the layouts no longer needed.

Assuming you’re like me, you’ll quickly find that many of your layouts are no longer needed. Feel free to consolidate them as appropriate.

Advanced Features

SimpleSidebar also contains some advanced features that I have yet to experiment with, but I’ll still give a quick overview.

Components

If I understand it correctly, components are sidebars that also have an associated controller. This is a very exciting feature, as it could allow you to execute multiple controllers for a single request.

A good example of where this is appropriate is for dynamic sidebars, such as the “Recently Updated Campaigns” sidebar you see on the Obsidian Portal homepage. I need to execute a database query (find) in order to get this. Currently, I’m just doing it inside the partial, which is not really what you’re supposed to do. When I get a chance, I’ll see if I can refactor this and use a component to move the database querying into a controller and make the partial a dumb template.

Sidebar ordering and sorting

In the previous examples, sidebars were ordered simply by their place in the controller. However, if you need more advanced functionality, SimpleSidebar supports ordering and sorting.

Moving sidebars is accomplished like this:

sidebar_move :login, :top
sidebar_move :search, :bottom
sidebar_move :search, :up
sidebar_move :login, :down

Easy enough…

Sorting looks a little more complicated. If I figure it out (or ever have a need for it), then I’ll update how it’s done. Until then, just look in the plugin source for sidebar_sort.

Issues

The only issue I can raise against SimpleSidebar (and it’s a long shot) is that by design it mixes controller and view functionality. I think sidebars are an aspect of the view, but with SimpleSidebar you deal with them in the controller.

In all seriousness, though, this is like whining over the color of the inside of the glove box in a luxury car. In no way does it take away from the sheer awesomeness of the item in question.

Final thoughts

After my brief introduction, I am completely in love with SimpleSidebar. It has quickly made its way into my bag of permanent plugins for all Rails projects. In any future project, it will be the one I install right after acts_as_authenticated. Yes, it’s that awesome.

12 Responses to “SimpleSidebar – If you have sidebars, you need this plugin.”

  1. Eric Allam Says:

    Great work DRYing up code that desperatly needed it! It looks like you’ve found a pretty elegent solution, but part of me sees this as a little to much overlap between the controller and the view layer. I haven’t run into this problem thus I don’t really know of a way to better seperate concerns. Adding another layer between the controller and the view might be the answer. It seems like you want to display certain snippets according to some state. Maybe a StatePattern between the controller and view, Oh well, its not that big of a deal anyways. Sweet plugin man!

  2. Micah Says:

    @Eric,

    Just to be 100% above-board, I should say that I didn’t write the plugin. That honor goes to someone else. I’m not sure, but I think his name is Mathew Abonyi.

    Also, I agree that it seems like too much overlap between the view and controller layer, but once I used it and saw how much code I was able to cut out, I gave up caring.

  3. Nirmal Says:

    I dont have such a situation as of now. But anyways the plugin is nice.

  4. Mitchell Says:

    Great right up I actually found the plugin about five minutes before I found this post and thanks cause it answered some of the questions I had in order to get the plugin functioning correctly on my app. Any way thank for the great articles and tutorials they have helped me a lot enough to bookmark the site as a reference.

  5. Micah Says:

    Thanks for the comment and bookmark, Mitchell. Don’t forget to add us to your feeds. That’s the best way to know when we put up new articles.

  6. bing Says:

    I have a couple of questions with this plugin:
    1)how to tune the look of sidebar links?
    2)how to make nested sidebars? (close or expand maybe)
    Thank you very much for any help!

  7. Midnight Oil » Blog Archive » New SD.rb Talks Posted: Simple Sidebar Plugin & Ajax CSS Star Rating with ActsAsRateable Says:

    [...] http://blog.aisleten.com/2007/06/03/simplesidebar-if-you-have-sidebars-you-need-this-plugin/ [...]

  8. Divide and Conquer » Blog Archive » Web design and SimpleSidebar Says:

    [...] about layouts in Ruby on Rails. As suggested in the Midnight Oil blog (Beds are burning?) article SimpleSidebar – If you have sidebars, you need this plugin I now use SimpleSidebar in The Project. At first it didn’t work at all, but updating [...]

  9. planetmcd Says:

    Nice write up. Thanks.

    One note, I would argue that the job of the controller is to return the proper view. In the case of this plugin and non standard layouts, the controller determines which to use based on the request criteria. Other frameworks handle the overlap between the View and the Controller with a Presenter (and you can emulate that in rails), but within Rails’s paradigm, this does conform to MVC.

  10. Neil Says:

    Has anyone tried this plugin with the new file extensions in Rails 2.0? Partials appended with .html.erb don’t seem to work for me – I had to revert to rhtml.

  11. Micah Says:

    Neil,

    We’re using the plugin with Rails 2.0, but we haven’t renamed all the partials. That’s because we wrote the majority of the code for 1.2.3 and just recently updated.

  12. Ryan Says:

    Neil,

    We used this blog post to create a rake task to both check for any deprecations and also to rename all views from .rhtml to .html.erb.

    http://www.slashdotdash.net/articles/2007/12/03/rails-2-upgrade-notes

    Also, I’ve submitted a patch to simple sidebar for adding Rails 2.0 support:

    http://code.google.com/p/mabs29/issues/detail?id=4

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in