Brad Wilson - The .NET Guy

Technologist. Agile Evangelist. Poker Player. Amateur Neologist. Metalhead.

My Links

Post Categories

Article Categories

Archives

Blog Stats

Stuff

Writing Your Own Front Controller for Rails

I've been spending the weekend catching up with Rails, and one of the projects I'm working on necessitated that I write my own front controller.

Of course, most people use the Routes system that's built into Rails, which is good enough for the vast majority of applications. However, my URLs are data driven, so maintaining the routes file (and restarting the Rails server) every time I needed to modify the URL space wasn't going to work for me.

I spent the majority of my time finding the right thing to override to get into the request chain. The simplest way seemed to be to override the recognize method in ActionController::Routing::RouteSet, like this: (code from /config/environment.rb)

module ActionController
  module Routing
    class RouteSet
      def recognize(request)
        FrontController.new
      end
    end
  end
end

You can delete all the code from /config/routes.rb, since this avoids the entire route system and forwards all requests to FrontController.

Then I defined FrontController like so:

class FrontController < ApplicationController
  def process(request, response, method = :perform_action, *arguments)
    super(request, response, :index)
  end

  def index
    @req_host = request.env["HTTP_HOST"]
    @req_url = request.env["PATH_INFO"]

    find_controller.constantize.new.process(request, response)
  rescue NameError
    render :file => "public/404.html"
  end

  def find_controller
    # write your code to crack your URL and figure out your controller class
    # return the controller class name as a string
    return "MySpecialController"
  end
end

The find_controller method is where you'll do whatever work you need to crack the URL and figure out what controller you're going to use. The controllers you chain to are defined just like any traditional Rails controller is, including support for rendering views. The call to "constantize" turns the string into an instance of the Class object for the controller class; a new instance of the controller is made, and we call process to chain up the results.

Any modifications you make to request and response will be passed along to the new controller, so you should take the opportunity inside of find_controller to set params[:action] to the name of the action method that you'd like dispatched on your controller class. In my case, I was parsing a whole variety of potential URL spaces (hostnames and paths) to a variety of controller types. The type of the controller depends partially on the prefix of the URL space, and the action may or may not be derived from other portions of the URL.

Obviously, this system has some down-sides. You have to do the URL cracking yourself, and the handy redirect_to() calls with the hash parameters can't reconstruct your URLs for you. However, in my case, it would have been simply impossible to set up static routes to cover everything, so I'm okay with this limitation. I'm using a new base class for my controllers which gives them alternatives to things like redirect_to and link_to.

posted on Sunday, July 09, 2006 5:51 PM