This is a duplicate of an article I ended up moving to the Rails Wiki here. The Rails Wiki version now acts as the master copy and you should read it in preference to this page, but I’ve kept the text below intact as wiki.rubyonrails.com
seems to fall over quite often! If you can’t get at the master copy, perhaps the text below will be of some use.
Almost every Rails application I’ve ever examined in detail assumes that it will be run from a web server’s document root location. This means only one application can be installed. If you want multiple Rails applications to be used together to make up a bigger Web site, a popular option is to use multiple (virtual) hosts – but only if you are able and willing to set up and administer them. Once you’ve got it all going, running a single domain with multiple Rails applications can make day to day administration life easier and helps with things like shared cookies and SSL certificates, but how is it done?
The solution I describe below works for me under LightTPD on a Linux based host to serve the RISC OS Open web site at the time of writing (presented as an example, not an advert!). Apache users should have no trouble either, though you’ll have to adapt the configuration details given herein. I have shell access to the server, though this is only really necessary to set up symbolic links; you may have more restrictive access to your host, but so long as you can create symlinks and edit (or at worst upload and overwrite) files you should be fine.
Each Rails application is stored in a directory somewhere in your host filesystem that is not part of the web server document root. This is where the symbolic links come in – create a symlink within the web server document structure that connects to the Rails application’s public
folder. This helps avoid any potential security problems arising from having failed to adequately hide private parts of the Rails application from the web server, which might be visible to the wider world if you just dumped the entire application in the document tree. There are other ways to approach this problem of course, many documented elsewhere, but personally I find a symlink the simplest and cleanest of the lot.
Doing this means we have two distinct paths to consider:
config
, app
, public
etc. folders). This is typically expressed as an absolute Unix path (e.g /home/username/rails/appname
).public
folder, such as stylesheets or images (e.g. /funky_stuff/appname/images/appname_logo.png
).From here on, I’ll refer to these as the Unix path to the application or the Web path to the application respectively.
To ensure that the application goes to the correct Web page when it uses ERb-included JavaScript, stylesheet etc. links, we’ll need to set up the Rails “asset host” in the application configuration. That’s quite simple. To ensure that routing works properly within the subdirectory of the Web server documents, we will also need to modify the application routes. This can be a bit fiddly, especially with RESTful routes in Rails 1.2.
If you’re using cookies for session management rather than your database, you’ll need to tell Rails to use a custom cookie name, otherwise cookies from different Rails applications will all try to use the same name and overwrite each other. If your users only concentrate on one part of your site at a time they might not notice, but the minute they start to jump around it’ll soon become obvious that state is being lost as they move from one application to another.
The rest of the application configuration is done just as for any other installation; there is nothing special to do in database.yml
, for example.
The Rails application needs to access the Web path in a couple of places, so it makes sense to set a global configuration variable containing the relevant details. Do this in config/environment.rb
, near the start of the file – e.g. just after the boot
file is loaded. Choose a variable that isn’t already in use; grep
will tell you if the name you’ve chosen is used elsewhere in the application. PATH_PREFIX
is usually free and happens to neatly match a parameter used in Rails routes, which makes the code easier to understand when it comes to modify the routes later.
We could hard-code this variable:
PATH_PREFIX = '/funky_stuff/appname'
Alternatively, you may choose to define an environment variable through your web server and use that instead:
PATH_PREFIX = ENV['RAILS_RELATIVE_URL_ROOT']
This way, if you have lots of Rails applications you can set up a common include script in the Web server configuration to pass the Web path down to each application, without having to modify each application with different in environment.rb
. I’ll describe this in detail later for LightTPD. I recommend this approach if your host and server setup supports it.
A Rails asset host is a place from which static assets – that is, things like images or stylesheets – may be fetched. This is usually intended for larger scale Rails applications where static assets can get served from a dedicated server that may have specialised features to serve static content more rapidly and to a larger number of users than a typical general purpose Web server. Really, though, all it really does is let us tell Rails where it should get things from when authors make ERb calls like “stylesheet_link_tag” in view files.
This, then, is the key to telling Rails to fetch such entities from a different location than the usual default references made relative to the document root. In config/environment.rb
(or, if you prefer, one of the environment-specific files such as config/environments/production.rb
), add something like this within the Rails::Initializer.run
block:
config.action_controller.asset_host = PATH_PREFIX
It’s generally recommended that you try to store session information in the database unless there’s some strong reason why that’s not possible on your site. Doing this is described elsewhere but, briefly, you should add something like this to config/environment.rb
within the Rails::Initializer.run
block:
# Create the database session table with "rake db:sessions:create" config.action_controller.session_store = :active_record_store
Even if you do this, though, you should probably set a session cookie name. You might want to drop back to cookies for testing or give the application to someone who can only use cookies in future. Setting the name now may help avoid confusing behaviour later. To do this, add the following /after/ the Rails::Initializer.run
block:
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_key] = 'some_cookie_name'
If you’re configuring several applications it helps to be consistent with the names. For example, you may choose to always use the form “appname_session_id
”, or you could use an environment variable passed in by the Web server, as described for PATH_PREFIX
above.
So, we’ve made sure that the Rails application’s public components are visible directly through the Web server’s document tree. We’ve told Rails to fetch static assets from the right location for this public content and set a new session cookie name to ensure that two or more Rails applications using cookies for session management don’t trample on one another’s data. The next step is to set up the routes.
In simplest terms, we want to add the PATH_PREFIX
variable’s value (or whatever variable you defined earlier) to the front of each and every route. In Rails 1.1, this usually just means adding PATH_PREFIX
to the front of the first parameter to map.connect
or similar calls, though we have to be a bit careful with trailing forwards slashes. In config/routes.rb
:
map.connect '', :controller => 'foo;, :action => 'bar' ...becomes... map.connect PATH_PREFIX + '/', :controller => 'foo', :action => 'bar' map.connect 'one/two/:id', :controller => 'foo;, :action => 'bar' ...becomes... map.connect PATH_PREFIX + 'one/two/:id', :controller => 'foo;, :action => 'bar'
…I.e., add PATH_PREFIX
to routes and leave no trailing slash, except for the route – if there is one – that mapped an empty path (the root location).
In Rails 1.2, RESTful routes make life a bit complex. Generally though Rails comes to the rescue, providing a :path_prefix
parameter we can add in to complex routes as follows:
map.resources :foo, :path_prefix => PATH_PREFIX map.resources :foo, :path_prefix => PATH_PREFIX, :thing => { :one => :two } do |fooitem| fooitem.resources :bar end map.resources :foo, :path_prefix => PATH_PREFIX do |fooitem| fooitem.resources :bar do |baritem| baritem.resources :one, :two end end
To a point the use of the prefix parameter makes it easy to see what is going on, but it’s not so clear once we start nesting blocks. In general the path prefix parameter seems to apply to all declared routes by that block, though I’ve seen some odd behaviour with this. I don’t fully understand the mechanism, so it might be a failure to correctly modify the results or could be a Rails bug (if you know the answer, feel free to tidy up this part of the Wiki entry!).
One final important touch – if you run tests from the command line, PATH_PREFIX
will probably not be defined. The routes will be read but the script will fail because of the missing variable. To patch around this, add the following line right at the start of routes.rb
:
PATH_PREFIX ||= ''
This defines the prefix to be an empty string if and only if it is not already defined, allowing tests to run.
The following describes configuration for LightTPD using FastCGI.
In your main configuration file (e.g. lighttpd.conf
) ensure that mod_fastcgi
is in your modules list:
server.modules = ( ..., "mod_fastcgi", ... )
Ensure dispatch.fcgi
is included in the list of filenames used for index files:
index-file.names = ( "index.html", "dispatch.fcgi" )
To reduce duplication in the main configuration file, it is wise to use a configuration file fragment with the Rails dispatch details inside. We will call this from the main file. For each application, do something like this:
$HTTP["url"] =~ "^/hub(/.*)*" { var.appname = "hub" include "lighttpd-rails.conf" }
The first part matches the URL at which the application can be found – in this case, /hub...
is matched. The var.appname
part sets a variable that will be read by the configuration file fragment, which is then included. Chain such declarations together using else if
, for example:
$HTTP["url"] =~ "^/hub(/.*)*" { var.appname = "hub" include "lighttpd-rails.conf" } else $HTTP["url"] =~ "^/content(/.*)*" { var.appname = "content" include "lighttpd-rails.conf" } else $HTTP["url"] =~ "^/news(/.*)*" { var.appname = "news" include "lighttpd-rails.conf" }
The configuration file fragment does two main jobs:
dispatch.fcgi
script, so that “Not found” pages get routed through Rails. When a web browser makes a request for a page that is part of your application but which doesn’t physically exist (since it’s running some action in some controller, not fetching a static document), LightTPD sees that the page is missing and invokes its Not Found handler. By running this through the Rails dispatcher, we get Rails pages sent back to the browser instead of Not Found messages. It sounds a strange approach but it’s widely used and a surprisingly efficient mechanism, which also underpins certain cache strategies that Rails applications can use.For the FastCGI instance, various bits of information are needed:
localhost
, that is, we keep all communications within the same server machine rather than, say, trying to load balance across multiple machines (way beyond the scope of this document!).Since the configuration uses localhost
for the FCGI process, it’ll use a Unix domain socket so we provide a filesystem location for its representation. It is a good idea to collect these together in a directory that’s reserved just for the sockets, to avoid confusion with other “real” files. With that in mind, the configuration file fragment might look like this:
# Inclusion fragment for LightTPD configuration files. Sets up # environment for FCGI Rails support. Set "var.appname" first. server.error-handler-404 = "/" + appname + "/dispatch.fcgi" fastcgi.server = ( ".fcgi" => ( "localhost" => ( "min-procs" => 1, "max-procs" => 1, "socket" => "/home/username/rails/fcgi_sockets/" + appname + "_fcgi", "bin-path" => "/home/username/rails/" + appname + "/public/dispatch.fcgi", "bin-environment" => ( "RAILS_ENV" => "production", "RAILS_RELATIVE_URL_ROOT" => "/" + appname, "SERVER_DOCUMENT_ROOT" => "/home/username/www" ) )))
In this example, server.error-handler-404
is set to the path, from document root, of the dispatch.fcgi
script as you’d fetch it from a web browser (the application’s Web path). Since in our examples so far we’ve used symlinks to point to the applications’ public
folders based off the web server document root, those symlinks named after each application, the "/" + appname + "/dispatch.fcgi"
construct makes sense. It’s easy enough to adapt for your uses provided you are consistent about using the application name in the path, as the thing which makes that path unique (remember, though, that by “application name” we really just mean any name you choose for the directories in which the application itself lives).
The min-procs
and max-procs
values depend to a degree on the way that your service provider handles FCGI processes and sometimes on whether or not your application might send multiple concurrent requests that it wants handling simultaneously – AJAX-heavy applications can fall into this category. In this example we only use one single dispatcher process, so both are set to 1. It’s as good a value as any to use if you’re not sure what else to put in!
The socket
location is something that’s up to you to choose; in this example we’ve put it in a directory fcgi_sockets
that we’ve created in our user space at /home/username
– this is a Unix pathname, not a Web site path. The choice of appname + "_fcgi"
as a leafname is pretty much arbitrary and you can use whatever you like provided it’s a unique name, most easily achieved by including appname
somewhere in the path.
In the case of Rails applications configured as described herein, the bin-path
value works out as being the Unix path equivalent of the thing you set in server.error-handler-404
. So, it’s the full Unix path to the dispatch.fcgi
script rather than the Web path to the script.
Finally we come to bin-environment
, where we can do some really handy things. Set up the RAILS_ENV
variable to make applications run in production, development or even test mode. Set RAILS_RELATIVE_URL_ROOT
to something that makes sense for your routes.rb
files, since we used this system variable to avoid hard-coding application locations. Now you can see how the web server tells the Rails application its location in the wider Web site so it can set up its routes correctly without any manual intervention. If you choose to move (in effect, rename) the application you won’t need to update its routing file.
A useful trick which isn’t shown in this example is to set RAILS_ASSET_ID
to an empty string. Doing so prevents the addition of ?...
– query strings with a series of numbers in them – to the end of any URL to static assets (images, stylesheets, that kind of thing). Rails seems to do this as a deliberate cache defeat mechanism rather than using HTTP headers for such. If you want to stop your web browser fetching the items over and over – generally a good idea! – you use the asset ID environment variable. If defined, its contents are added to any asset URL after the ?
, unless it is an empty string, in which case the entire query string is omitted altogether, leaving you with clean – and therefore more conventional – asset URLs.
In the list of variables, I’ve included SERVER_DOCUMENT_ROOT
– it is not essential but I find it useful to have sometimes and I’ve kept it in as part of the example. If you use the Hub single sign-on mechanism, you can set essential Hub environment variables in the same part of the configuration file fragment – and so-on; whatever system variables you find useful for applications may be passed in, including setting GEM_PATH
so that your applications can draw from more than one Gem repository.
Stylesheets can include other items, such as background images or other imported stylesheets. It’s extremely rare for a Rails application to dynamically generate its own stylesheets through the views mechanism; the vast majority of application authors simply hard code URLs into the stylesheet(s). Of course, this fails because the application isn’t running in the document root. I’ve not found a solution better than simply updating the hard-coded links to the correct location by hand on a case by case basis.
While stylesheets can be forgiven for hard-coded links, the same is not so true of Rails views. Some applications, though, do contain hard-coded links to routes or assets, perhaps because the author didn’t quite see the point of adding in a relatively long ERb Rails call to generate the link when it seemed easier to just drop the link in using static HTML. It’s best to update these (again, by hand usually) to link_to
calls or similar, so that they automatically start working thanks to the prefix information set up in the application configuration. If you make such fixes, it’s wise to submit patches back, since you have done what amounts to a bug fix (don’t forget to test everything first!).
If you want your applications to share a common visual theme, there are various approaches you can take. One is a simple shared template scheme. Fragments of template are included by modifying each application’s default view. For example, we might create standard header, footer and sidebar RHTML snippets in a common location. Inside each application, we create folder app/views/shared
and within this, add symbolic links to the template fragment files. Inside the application default layout (e.g. app/views/layouts/default.html
or app/views/layouts/application.rhtml
), the fragments are included at appropriate places using lines such as:
<%= render 'shared/fixed_header' %> <%= render 'shared/fixed_sidebar' %> <%= render 'shared/fixed_footer' %>
…each line appearing individually at wherever within the containing document is appropriate, given the containing document’s structure and the data within fragments being included.
There is usually still plenty of manual work to do, such as modifying application CSS files to match a common central theme or, if brave, modifying all RHTML files so that the application uses class and ID names defined by a common central stylesheet. At least a change of fixed header, footer or sidebar content will always be reflected across all applications using this mechanism (although flushing of application-specific cache mechanisms may be needed).
Of course, this whole idea may not suit you. There are many possible approaches – the use of symbolic links to shared, fixed content fragments is just one.
Your site might have lots of applications providing dynamic content, but it could also have some more static pages too. You may want to be able to edit such pages or edit template fragments – if you use them – using your web site itself, inside your web browser, rather than editing files in (say) a text editor and uploading theme to your host by hand.
With a content management system (CMS) such as Radiant you can easily edit static pages inside your web browser. You might want users to be able to modify shared template fragments, as described above, within the CMS too. If the CMS stores its data inside a database, you will have to extend it to support the idea of exporting content to files as well as the database whenever that content is modified. The CMS thus exports to the common template fragment file locations automatically, symbolic links effectively transferring those changes to any other applications. Users of the CMS don’t need to know the details.
A version of Radiant modified in this way is used on the RISC OS Open web site and can be downloaded from the site’s sources section as a demonstration of this kind of modification.
Should your applications require user account mechanisms of any kind, you might want to use a single sign-on mechanism so that users only need to create one account in a central location and sign on there, rather than creating individual accounts in each application on your site. This requires a relatively deep level of integration and is beyond the scope of this document. There are plenty of resources on the Web describing a wide range of approaches to single sign-on. For Ruby On Rails in particular, if sharing applications within a single domain, you may wish to consider Hub.
Once you get used to the basic set of changes:
PATH_PREFIX
and the session cookie name change to environment.rb
PATH_PREFIX
into routes.rb
…then you can get going quite quickly. Simple applications like Gullery usually take an hour or three including integration with a single sign-on mechanism, while more complex tasks like Beast can take a day or two, because of more complex user models and stylesheets. If you do it right, you can have no duplication across applications – everything sharing a single layout template with a master stylesheet and local application-specific variations – without an inordinate amount of effort. It’s possible because Rails provides a consistent top to bottom framework used by just about any Rails application you care to choose, allowing you to build deeply integrated, visually consistent and really quite complex web sites with remarkably little effort.