Controller Inheritance 7

Posted by Koz Monday, April 06, 2009 23:20:00 GMT

Just a brief interlude from the File Management series while I sort out some time to do some benchmarking.

A common pattern I see in submissions and client-applications is repetitive declarations in controllers. There’s a neat and simple solution for this, but given how often Things like this:

  class TodoController < ApplicationController
    before_filter :login_required
    before_filter :handle_iphone_support
    before_filter :fetch_todo
    around_filter :performance_logging

    def index
    ...
    end
  end
For a single controller this set of declarations wouldn’t be a problem, but the problem is when all those declarations end up duplicated in several different controllers. Thankfully every controller inherits from ApplicationController so we can do something like this:
  class ApplicationController < ActionController::Base
    before_filter :login_required
    before_filter :handle_iphone_support
    around_filter :performance_logging

  end

  class TodoController < ApplicationController
    before_filter :fetch_todo

    def index
    ...
    end
  end

This is easy, and obvious and most people do that. However what do you do when you have several controllers which don’t need the login_required call? One option is to give up on inheritance and go back to copying-and-pasting the filter declarations. Another option is to selectively opt-out of the parent controller’s filters.

  class ApplicationController < ActionController::Base
    before_filter :login_required
    before_filter :handle_iphone_support
    around_filter :performance_logging
  end

  class SignupController < ApplicationController
    skip_before_filter :login_required
  end

However if you have several controllers which don’t require logins, you’ll find yourself duplicating the skip_before_filter declarations around. Otherwise filters and declarations which aren’t completely universal still. An approach which solves this nicely is to introduce an abstract parent controller for all your authenticated controllers.

  class ApplicationController < ActionController::Base
    before_filter :handle_iphone_support
    around_filter :performance_logging
  end

  class AuthenticatedController < ApplicationController
    before_filter :login_required
  end 

  class TodoController < AuthenticatedController
    before_filter :fetch_todo

    def index
    ...
    end
  end

This technique doesn’t just work with filter declarations most other declarations such as caching, session and csrf options work as expected too. In addition you can introduce several parent classes as needed such as a PublicController with page caching declarations, an AdminController for your admin panels etc. Inheritance isn’t the solution to every case of duplicate declarations, yet it’s a simple technique that can simplify most uses.