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)
else
options = app
app, path = options.find { |k, v| k.respond_to?(:call) }
options.delete(app) if app
end
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])
self
end
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
begin
env[PATH_INFO] = env[PATH_INFO].sub(prefix, EMPTY_STRING)
env[PATH_INFO] = EMPTY_STRING if env[PATH_INFO] == SLASH
env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + prefix)
@app.call(env)
ensure
env[PATH_INFO] = old_path_info
env[SCRIPT_NAME] = old_script_name
end
else
@app.call(env)
end
end
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.