开发者

Dynamic Rails routing based on database

I'm building a CMS with various modules (blog, calendar, etc.) using Rails 2.3. Each module is handled by a different controller and that works just fine.

The only problem I have is with the root URL. Depending on the configuration chosen by th开发者_开发百科e user, this default URL should show a different module i.e. a different controller, but the only way I have to determine the correct controller is by checking the database for what "default" module is to be shown.

For the moment I'm using a specific "root" controller which checks the database and redirects to the correct controller. However I'd prefer the URL not to be changed, which means I want to invoke the correct controller from the very same request.

I've tried using Rails Metal to fetch this info and manually calling the controller I want but I'm thinking I may be reinventing the wheel (identify the request path to choose the controller, manage session, etc.).

Any idea? Thanks a lot in advance!


This problem can be solved with some Rack middleware:

This code in lib/root_rewriter.rb:

module DefV
  class RootRewriter
    def initialize(app)
      @app = app
    end

    def call(env)
      if env['REQUEST_URI'] == '/' # Root is requested!
        env['REQUEST_URI'] = Page.find_by_root(true).uri # for example /blog/
      end

      @app.call(env)
    end
  end
end

Then in your config/environment.rb at the bottom

require 'root_rewriter'
ActionController::Dispatcher.middleware.insert_after ActiveRecord::QueryCache, DefV::RootRewriter

This middleware will check if the requested page (REQUEST_URI) is '/' and then do a lookup for the actual path (Implementation to this is up to you ;-)). You might do good on caching this info somewhere (Cache.fetch('root_path') { Page.find... })

There are some problems with checking REQUEST_URI, since not all webservers pass this correctly. For the whole implementation detail in Rails see http://api.rubyonrails.org/classes/ActionController/Request.html#M000720 (Click "View source")


In Rails 3.2 this was what I came up with (still a middleware):

class RootRewriter
  def initialize(app)
    @app = app
  end

  def call(env)
    if ['', '/'].include? env['PATH_INFO']
      default_thing = # Do your model lookup here to determine your default item
      env['PATH_INFO'] = # Assemble your new 'internal' path here (a string)
      # I found useful methods to be: ActiveModel::Naming.route_key() and to_param
    end

    @app.call(env)
  end
end

This tells Rails that the path is different from what was requested (the root path) so references to link_to_unless_current and the like still work well.

Load the middleware in like so in an initialiser:

MyApp::Application.config.middleware.use RootRewriter
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜