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?
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)
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.
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
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
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/;
...
Related
I feel like this should not be all that difficult, but I cannot find any solutions that seem to work with Rails4
My setup
I have a proxy server (Kong) that directs to various services behind it based on path.
myapp.com/app1/ is redirected to http://app1_address:PORT/ (notice /app1 is stripped)
same for myapp.com/app2
app2 is a Rails 4 application and it works just fine when browsing to specific, but its relative routing is completely off. For example, link_to or url_for links to controllers or actions all redirect to the wrong links.
For example, I have a logout link that has a path of /logout on app2, but redirecting the user to /logout is incorrect. They need to be routed to /app2/logout. How can I configure the Rails app to add a prefix to all routes?
I have tried:
config.action_controller.relative_url_root = '/app2'
As well as this:
config.relative_url_root = '/app2'
And this in config.ru
map <MyAppName>::Application.config.relative_url_root || "/" do
run Rails.application
end
Any ideas for how to make this work?
You should use a scope in your routes:
Rails.application.routes.draw do
scope ENV["PROXY_PATH"] do
# your routes..
end
end
This way you'll have to set the ENV variable:
$ export PROXY_PATH=app2
$ bin/rails s
I would like to use a different public folder from a parent directory called client which contains the entire AngularJS app. Essentially I want to tell Rails to load AngularJS app and the only job that Rails has to do is serve JSON.
Is that possible in Ruby on Rails?
As others have mentioned, it may or may not be a great idea to override the existing paths['public'] folder. But you can do the following safely in somewhere like application.rb:
Rails.application.config.middleware.insert_after(
ActionDispatch::Static,
ActionDispatch::Static,
Rails.root.join("client").to_s,
Rails.application.config.static_cache_control
)
The public folder is exposed to the web server through the Rack middleware ActionDispatch::Static. There's nothing else special about it, and the above code simply adds another instance of the middleware that points to the directory client. So in the above case, the browser would be able to access everything in public as well as client.
Just had to do it myself for an Angular app.
Put this in your application.rb:
config.serve_static_files = true
paths['public'] = File.join 'client', 'app'
Or if you still use asset pipeline (config.assets.enabled = true):
paths['public/javascripts'] = File.join 'client', 'app', 'scripts'
paths['public/stylesheets'] = File.join 'client', 'app', 'styles'
Would be interesting to know if there are any consequences with the second bit as my front-end is served completely separately thus I keep asset pipeline switched off and use grunt instead.
You can define another path like
# config/application.rb
paths['my_website'] = 'website'
Then you can use this path in your routes like
# routes.rb
get '/my_website', to: redirect('my_website/index.html')
I have a setup where nginx serves a rails application inside a specific subfolder
eg. http://myserver/railsapp/ and everything inside gets proxied to rails, if the first subfolder is different, it servers static files from another folder.
I haven't been able to find how to specify this behaviour in rails in an intelligent way. I mean, what I want is to specify an option like Rails.server_prefix = /railsapp so that all the routes get prepended automagically, both on the incoming requests and on the generated links.
You probably want to use the router's scope method with the :path argument:
Rails.application.routes do
scope(:path => '/railsapp') do
# the rest of your routes go here
end
end
See the docs for more info.
So far I have in config/routes.rb:
match 'doc/:path' => 'doc#show'
And in app/controllers/doc_controller.rb:
class DocController < ApplicationController
layout false
def show
render File.join( RAILS_ROOT, 'doc', params[:path] )
end
end
This works find for index.html and other .html files. But it doesn't serve up .css and .js files. It also doesn't serve nested files and directories such as /doc/metrics/output/index.html
How can I get Rails to serve up all static files in /doc but without simply putting a link to them in /public (so that I can autheticate the user in the controller first)?
I would recommend not serving the files through Rails at all. Serve them through your server (Nginx, Apache). You can use the X-Accel-Redirect and the X-Sendfile headers to tell Nginx and Apache to send the static file instead. The benefit of this approach is that you can still authenticate a user before allowing them access to the file. Here's the Nginx tutorial:
http://ablogaboutcode.com/2010/11/19/serving-static-files-passenger-nginx/
Another option is to setup your routes like this:
match 'doc' => 'doc#show'
And pass your path as a parameter so you don't have to do nested URL matching in your routes, or handle special cases (.css, .js, .html, ...)
/doc?path=/path/to/my/document.css
It appears that as of Rails 3 plugin routes are now loaded after application routes, which means that my default route blocks plugins such as admin_data.
Currently I avoid this problem by using the following ugly hack in routes.rb, which works for plugins with their routes defined in config/routes.rb:
# Load plugin routes
$LOAD_PATH.each do |path|
path = File.dirname(path)
file = File.join path, 'config', 'routes.rb'
if File.exists? file
require file[0..-4]
end
end
# Default root
match ':a', :to => 'foo#bar'
What is the proper way of setting my default route to a lower priority than that of plugins?
I believe engines rather than plugins are the preferred way of adding app-esque functionality like this in Rails 3. You could try out the more modern engines-based rails_admin instead of admin_data if that's the only plugin you're using like this