Rails Routes: match and mount

While working on the SlugEngine built into this blog site, I came across a note-worthy errata in the book Rails 3 in Action.

Section 18.3.1 states that the following two lines in config/routes.rb are functionally equivalent:

# method one
mount Foo::Bar, :at => 'foo/bar'

# method two
match 'foo/bar', :to => Foo::Bar

These two routes are similar. In fact, mount in turn calls match to complete the route setup. However, the difference (as of Rails 3.1.1) lies in the options that mount passes along to match.

Take a look at the mount method, as defined in action_dispatch/routing/mapper.rb:

def mount(app, options = nil)
  if options
    path = options.delete(:at)
    options = app
    app, path = options.find { |k, v| k.respond_to?(:call) }
    options.delete(app) if app

  raise "A rack application must be specified" unless path

  options[:as] ||= app_name(app)

  match(path, options.merge(:to => app, :anchor => false, :format => false))

  define_generate_prefix(app, options[:as])

Near the end of the method body, it calls match. At this point, whatever options have been established to this point are merged with some defaults, which will overwrite whatever options were declared in config/routes.rb.

Specifically, the mount method overrides and sets the :anchor option to false.

The net effect of setting :anchor => false is that the request path is that the mount point is stripped from the request path and inserted in the script name request parameter. This magic happens in Rack::Mount::Prefix as follows:

def call(env)
  if prefix = env[KEY] || @prefix
    old_path_info = env[PATH_INFO].dup
    old_script_name = env[SCRIPT_NAME].dup

      env[PATH_INFO] = env[PATH_INFO].sub(prefix, EMPTY_STRING)
      env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + prefix)
      env[PATH_INFO] = old_path_info
      env[SCRIPT_NAME] = old_script_name

Most of the time, this probably isn’t a big deal, but it can have an impact on route helpers if used by the mounted application. The route helpers will pick up on the env[SCRIPT_NAME] and assume that any routes they generate should be prefixed by this value. So even root_path would become /foo/bar instead of / in the example above.

The easy solution to this is to use match instead of mount in config/routes.rb.

match 'foo/bar', :to => Foo::Bar

Using this route, root_path returns /, as expected.

Leave a Reply

Your email address will not be published. Required fields are marked *