Rails controller like filters for your Backbone Router

Backbone is amazing and so small that you have no excuse not to look at the source code as you’re using it. The hardest things I’ve encounter with Backbone are reconciling it with my experience using the Rails MVC structure and reminding myself that a 1,500 line library (that’s with comments!) is sure not to have all the features I’ve become used to in Rails.

To many the following semi-equivalence is obvious but sometimes it doesn’t take residence in the front of your mind until it’s just said (or typed in this case). Models are more or less equivalent and are easy to transition to. Rails views are most like the templates used in Backbone views. It’s the responsibility found in the Rails controllers that has caused me the most confusion. There isn’t a clear one to one relationship as Backbone views contain some of the responsibility found in Rails controllers while the rest is found in the Backbone router.

I encountered a case where I wanted to run some code before the majority of my router methods and optionally not perform the rest of the method under certain conditions. Not a difficult thing to wrap in a single method call plus return value and conditional logic, but I like and am used to keeping code DRY. If this were Rails, I would simply use a before_filter and set the except option for the routes I didn’t want it called on. Since Backbone is delightfully small and agile, I decided to simply create my own filter plugin to accomplish this.

The result is Backbone.RouterFilters. When defining your router, you can include either or both of the beforeFilter or afterFilter properties. Then just throw in the filterMethod you want run before or after a routing action and optionally define the list of routes to include or exclude in the routes property if you don’t want it to apply to all routes.

Example

var ExampleRouter = Backbone.Router.extend({  
  beforeFilter: {
    filterMethod: function() {
      /* something that returns a truthy or falsy value */

      if (Math.floor(100 * Math.random()) % 2 === 0) {
        return true;
      } else {
        return false;
      }
    },
    routes: {
      only: ['filterThisRoute', /filterRoute/]
    }
  },

  afterFilter: {
    filterMethod: function() {
      /* anything you'd want to run after each router method */
      (this.routeCounter && this.routeCounter++) || this.routeCounter = 1;
      console.log('I have routed ' + routeCounter + ' times.');
    }
    /* NOTE: By not defining a routes property, this afterFilter will run on ALL routes */
  },

  routes: {
    'filterThisRoute': 'filterThisRouteHandler',
    'filterRoute1'   : 'filterRoute1Handler',
    'filterRoute2'   : 'filterRoute2Handler',
    'foofilterRoute' : 'foofilterRouteHandler',
    'unfilteredRoute': 'unfilteredRouteHandler'
  },

  filterThisRouteHandler: function() {},
  filterRoute1Handler   : function() {},
  filterRoute2Handler   : function() {},
  foofilterRouteHandler : function() {},
  unfilteredRouteHandler: function() {}
});

The code, a usage guide, this example code, and some tests can be found on Github: https://github.com/mojotech/backbone.routerFilters

Check it out and let me know if you find it useful.

—Aaron Rosenberg