What is the replacement for ActionController::Base.relative_url_root?
I am porting a 2.x rails app to rails3; we'll call it foo-app. Foo-app is one section of a larger rails app and lives at main_rails_app.com/foo-app. Previously we just set up the following in our foo-app production config to ensure that our foo-app routes worked properly:
ActionController::Base.relative_url_root = "/foo-app"
However, with rails3, I now get:
DEPRECATION WARNING: ActionController::Base.relative_url_root is ineffective. Please stop using it.
I have since changed the config entry to the following:
config.action_controller.relative_url_root = "/foo-app"
This mostly works in that all calls to external resources (javascript/css/images) will use /foo-app. However, none of my routes change appropriately, or put another way, foo-app root_path gives me '/' when I would expect '/foo-app'.
Two questions:
- What is the replacement for ActionController::Base.relative_url_root
- if it is config.action_controller.relative_url_root, then why are my routes not reflecting the relative_url_root value I set?
Solution 1:
You should be able to handle all that within the routes.rb file. Wrap all your current routes in scope; for instance.
scope "/context_root" do
resources :controller
resources :another_controller
match 'welcome/', :to => "welcome#index"
root :to => "welcome#index"
end
You can then verify your routing via the rake routes
they should show your routes accordingly, including your context root(relative_url_root)
Solution 2:
If you deploy via Passenger, use the RackBaseURI
directive: http://www.modrails.com/documentation/Users%20guide%20Apache.html#RackBaseURI
Otherwise, you can wrap the run
statement in your config.ru
with the following block:
map ActionController::Base.config.relative_url_root || "/" do
run FooApp::Application
end
Then you only have to set the environment variable RAILS_RELATIVE_URL_ROOT
to "/foo-app". This will even apply to routes set in gems or plugins.
Warning: do not mix these two solutions.
Solution 3:
I feel like I must be over-complicating this and/or missing something, but this issue has been frustrating me for a while now, and here are my notes.
Summary
There are two separate issues with two points each for dynamic and static routes:
- how to get routing to correctly match an incoming URL
- for routes
- for static files
- how to generate URLs that include the relative_root
- via url helpers
- for static assets
One way to solve all four points:
- Configure Nginx to strip the
relative_root
portion- This solves route matching; just write routes expecting URLs at
/
like development - Also static files are served as in development
- This solves route matching; just write routes expecting URLs at
- Set
RAILS_RELATIVE_URL_ROOT
environment variable- This solves generated static asset helpers
- Use the
ScriptName
middleware below (modify it to use the value from the environment)- This solves generated url helpers, e.g.
users_path
- This solves generated url helpers, e.g.
Wrapping the Rails application in Rack::URLMap in config.ru (Christoph's answer)
# config.ru
map '/relative_root' do
run Myapp::Application
end
- requires incoming URL contain the relative_url_root (Nginx can be configured to remove or retain this; see below)
- Rack appends the relative_url_root to the Rack env
SCRIPT_NAME
rack/urlmap.rb:62 - Rails adds the current request's
SCRIPT_NAME
to url_for options metal/url_for.rb:41 - Rails' url_for prepends the script name when generating paths routing/url_for.rb:133
So that covers URLs generated by the url helpers, e.g. given UserController
, users_path
will be prefixed by the relative url root.
Set SCRIPT_NAME in middleware
# config.ru
class ScriptName
def initialize(app, name)
@app = app
@name = name
end
def call(env)
env['SCRIPT_NAME'] += @name
@app.call(env)
end
end
use ScriptName, '/relative_root'
run Rails.application
- Has same effect as above, but
- Requires that incoming URL NOT contain the relative_url_root
Setting RAILS_RELATIVE_URL_ROOT
- value is saved in
app.config.relative_url_root
configuration.rb:41 - which in turn affects asset paths asset_url_helper.rb:137
- but that's it as far as I see
- notably does not affect url helpers
Setting config.action_controller.relative_url_root
- ?? May affect assets compilation?
- Overrides RAILS_RELATIVE_URL_ROOT env var?
Explicitly defining all routes under /relative_root
(rizzah's answer)
# config/routes.rb
Myapp::Application.routes.draw do
scope '/relative_root' do
...
end
end
- Url helpers will generate correct urls
- Incoming URL must contain the relative url root (sensitive to Nginx configuration, see below), else "no route matches" exceptions
- URLs requesting static assets, e.g.
/relative_root/images/logo.png
will result in "no route matches" exceptions. This may not be an issue if nginx is serving static assets anyway.
Nginx config
Given a config like this:
upstream myapp {
server localhost:3000;
}
server {
...
location /relative_root {
proxy_pass http://myapp/;
}
}
Nginx will strip out the /relative_root
, and the Rails app will not see it. If you need the Rails app so see it, one way is to change the proxy_pass
line:
...
proxy_pass http://myapp/relative_root/;
...