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.