Bundler and Rails 3 environments

I usually write very long blog posts. Not this time! This is just a small tip, the first of hopefully many learnings I’ll make as I am working with my first project in Rails 3.

Bundler (Github | Website) is the new way to manage gem dependencies in Rails and other Ruby applications. If you haven’t heard about it, there is (of course) a great introduction at Railcasts. It’s pretty easy and straight-forward to use, especially if you are used to the config.gem format of Rails.

However, one very common use case in Bundler’s Gemfile that is not explicitly documented anywhere I’ve seen, is how to configure gems to be installed in both development and test environments, but not in staging and production environments. You have probably seen the examples of how you can assign a gem to a specific environment using the :group option or method. So you’d probably write something like this:

group :test do
  gem "rspec-rails", ">= 2.0.0.beta.1"
end

group :development do
  gem "ruby-debug"
end

Continue reading

Hosting a Spree webshop on Heroku

Spree is the webshop solution for Ruby on Rails, and Heroku is the place to host your Rails-applications – especially if you like free hosting :)

Finding myself in the need of a webshop, I decided to try to deploy Spree to Heroku. It wasn’t too hard, but it did take a few tricks to get Spree running on a server that doesn’t allow you to write to the file system.

I can’t say I have too much hands-on experience with Spree yet, but it seems to be have borrowed its extension concept from Radiant CMS, which happens to be one of my areas of expertise. So I put together an extension for Spree that will easily allow you to deploy the e-commerce system to Heroku.

Just follow the instructions in the README on Github.

Manage and rollback Heroku deployments Capistrano-style

If you are a Rails-developer, and you don’t know about Heroku, now would definitely be a good time. Heroku is a platform for hosting web-based Ruby applications in “the cloud”, in this case Amazon EC2, making you able to scale your application in an effortless and cost-effective manner without worrying about the hardware behind. Heroku differs from competition by offering by far the fastest and easiest way to deploy your Rails-application. You simply create your application from Heroku’s web interface or using their API, and then push your git-repository to Heroku’s git-server. Heroku takes care of the rest, and you can get back to coding.

I use Heroku for Capistrano, once that has been setup with the right recipes and the server has been setup to corporate and serve our web application properly. But that can also take some time, especially on a brand new server.

Continue reading

Radiant CMS on Dreamhost with Phusion Passenger

Believe it or not, but my mom has quite a lot of websites. I must have built 8-10 websites for my dear mother during the past 10 years, and currently she has three active ones. Most of them has been built in static HTML, a more recent one is a Rails application and one is no more than a Word document exported as a webpage. Now, a mother loves every excuse to get in touch with her son, but I have grown a bit weary of making minor text changes to one of her websites every few weeks.

Which is why I decided make life easier for myself and for my mom. I have never been fond of content management systems, especially not complex ones, but during the past year I have developed quite a lot of websites using Ruby on Rails based Radiant CMS at work, and I find it very simple to use and easy to extend.

Rails applications has never been easy to deploy, especially not on a shared host, so I have been following the development of Phusion Passenger, a.k.a. mod_rails, for the past months with a growing interest. I was quite happy when Dreamhost recently announced that they now offered to run Rails applications using Passenger, and I was eager to try it out – which is what I did today.

Now, I didn’t expect to be hard to get Radiant up and running on Dreamhost with Passenger for a seasoned Rails developer and Dreamhost user such as myself, but I found it in fact to be ridiculously easy – which is why I had to write this long introduction about my mom, as there is really not much to say. Anyhow I want to take the opportunity to explain exactly the configuration I selected and how I got it running.

I am running Radiant version 0.6.7 (the newest as I am writing this, always go for the newest) with two vital extensions: Gallery, for facilitating easy upload and display of images, and MultiSite, for being able to host all three of my mother’s websites within a single Radiant instance. Also, I of course wanted to deploy from a Subversion repository using Capistrono, as nothing is sweeter than writing cap deploy and get all the heavy deployment lifting done automatically.

The basics of running Passenger on Dreamhost is explained in the Dreamhost wiki: You create or edit a domain and check the Ruby on Rails Passenger option. You must also set the web directory to be path/to/rails/application/current/public, as Capistrano deploys to the current directory, and Passenger must be pointed to the public directory of the current version.

Let’s create a new Radiant CMS Rails application and add our extensions. I don’t use the Radiant gem, as gems can be troublesome to manage on Dreamhost, I simply use a fresh checkout of the version I want:

svn export http://svn.radiantcms.org/radiant/tags/rel_0-6-7/radiant/ radiant
cd radiant
git clone git://github.com/pilu/radiant-gallery.git vendor/extensions/gallery
svn export http://svn.radiantcms.org/radiant/tags/rel_0-6-7/extensions/multi_site/ vendor/extensions/multi_site

There is one minor snag which will undoubtedly be fixed in the near future. The MultiSite extension is not compatible with Radiant 0.6.7, and two things need to be done to get it up and running: First, the HAML plugin bundled with the extension must be deleted, as it conflicts with the one now bundled with Radiant:

rm -rf vendor/extensions/multi_site/vendor/plugins/haml/

Second, three lines must be outcommented in vendor/extensions/multi_site/lib/multi_site/page_controller_extensions.rb so that the first few lines looks like this:

module MultiSite::PageControllerExtensions
  def self.included(base)
    base.class_eval {
      alias_method_chain :index, :root
      alias_method_chain :clear_model_cache, :site
      alias_method_chain :continue_url, :site
      # %w{remove clear_cache}.each do |m|
      #  alias_method_chain m.to_sym, :back
      # end
    }
  end
  ...
end

Both extensions need to copy some files to the public directory of the application before we continue:

rake radiant:extensions:update_all

Now, let’s check our Radiant application into a Subversion repository and deploy it Capistrano. I cooked up a Capistrano recipe based on a mix of older recipes. Run capify . and copy and modify this code into config/deploy.rb:

set :user, 'my_dreamhost_user'
set :application, "my-passenger-domain.com"
set :repository, "my_svn_repository_url"
set :svn_username, "my_svn_username"

# =============================================================================
# You shouldn't have to modify the rest of these
# =============================================================================

role :web, application
role :app, application
role :db,  application, :primary => true

set :deploy_to, "/home/#{user}/#{application}"
# set :svn, "/path/to/svn"       # defaults to searching the PATH
set :use_sudo, false
# set :restart_via, :run
set(:svn_password) { Capistrano::CLI.password_prompt }

# saves space by only keeping last 3 when running cleanup
set :keep_releases, 3

# issues svn export instead of checkout
set :checkout, "export"

# keeps a local checkout of the repository on the server to get faster deployments
set :deploy_via, :remote_cache

ssh_options[:paranoid] = false

# Shared dirs between releases
set :shared_dirs, ['galleries']

# =============================================================================
# OVERRIDE TASKS
# =============================================================================
namespace :deploy do

  desc "Restart Passenger"
  task :restart, :roles => :app do
    run "touch #{current_path}/tmp/restart.txt"
  end

  desc <<-DESC
    Deploy and run pending migrations. This will work similarly to the
    `deploy' task, but will also run any pending migrations (via the
    `deploy:migrate' task) prior to updating the symlink. Note that the
    update in this case it is not atomic, and transactions are not used,
    because migrations are not guaranteed to be reversible.
  DESC
  task :migrations do
    set :migrate_target, :latest
    update_code
    migrate
    migrate_extensions # Added to support Radiant Extensions
    symlink
    restart
  end

  desc "Migrates Radiant Extensions"
  task :migrate_extensions do
    rake = fetch(:rake, "rake")
    rails_env = fetch(:rails_env, "production")
    migrate_env = fetch(:migrate_env, "")
    migrate_target = fetch(:migrate_target, :latest)

    directory = case migrate_target.to_sym
      when :current then current_path
      when :latest  then current_release
      else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
      end

    run "cd #{directory}; #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate:extensions"
  end

  desc "Tasks to execute after initial setup"
  task :after_setup do
    # create shared dirs
    shared_dirs.each do |dir|
      run "mkdir -p #{shared_path}/public/#{dir}"
    end
  end

  desc "Tasks to execute after code update"
  task :after_update_code do
    # relink shared dirs
    shared_dirs.each do |dir|
  	  run "rm -rf #{release_path}/public/#{dir}"
      run "ln -nfs #{shared_path}/public/#{dir} #{release_path}/public/#{dir}"
    end
  end
end

This deployment script does a few clever things. All of it is borrowed from various places, but I simply don't remember who to give credit for the different bits anymore, so - just don't give me any other credit than explaining it. It overrides the migrations task to also invoke the new migrate_extensions task, making sure that all extensions also have their migrations run. It ensures that any uploaded images into public/galleries is shared between releases using a symlink, as uploaded images would otherwise disappear between releases. Finally, it restarts the Phusion Passenger instance by creating or changing a file called tmp/restart.txt, which is Passenger's solution for allowing easy restart on shared hosts with limited access rights. Clever.

Also, don't forgot to delete unwanted database.yml files in the config directory. Deploying on Dreamhost, you want to rename database.mysql.yml to database.yml and delete the others. Remember to create a MySQL database with an appropriate user using the Dreamhost Panel, and to write host: mysql.mydomain.com instead of socket: /tmp/mysql.sock for the production part of the file.

Okay, we are ready to deploy to deploy our application:

cap deploy:setup
cap deploy

Now, since both Radiant itself and the Gallery has interactive installation scripts, we need to ssh into the Dreamhost server and run these scripts manually:

ssh my_dreamhost_user@my-passenger-domain.com
cd my-passenger-domain.com/current
rake production db:bootstrap
rake production radiant:extensions:gallery:install

Log out of the server and do another Capistrano deployment, this time also running migrations:

cap deploy:migrations

And that's it. You should now have your very own Radiant application running on Dreamhost, and with the MultiSite extension you can even manage multiple sites from the same physical Rails instance. All you need to do is mirror domains to the application and create new sites within the administration panel.

If you have any problems following these steps, please let me know. I might be able to help you out.

UPDATE: You don't have to use Subversion and Capistrano to deploy Radiant on DH, especially not if you aren't going to use any extensions and will only deploy once. Take a look at the Radiant wiki page on deploying to Dreamhost to see which steps you can skip to just upload once using SFTP.

RailsConf: The DHH keynote – Rails 2.0 update

This article summarizes some important points from David Heinemeier Hansson’s keynote at RailsConf Europe 2007 in Berlin. Last year, DHH brought us the whole REST way of thinking which I immediately named normalization, but that term didn’t really catch on.

David Heinemeier Hansson
© 2007 Pinar Ozger

This year, DHH noted that he up until now had started all his previous keynotes about Rails celebrating how “freakin’ fantastic we are”. For this keynote, he thought – with Rails being well within in its third year in existence – that it was time to perhaps be happy with what we have.

He explained that new things tend to go through certain phases: First they ignore you, then they laugh at you, then they fight you, and then you win – … and then what? “Ruby on Rails went through these phases way too fast”, DHH said, “I was having a lot of fun going through these phases; I’m kind of sad it is over.”

For the last couple of years, David and the Rails community has pushing new concepts; Convention over configuration, AJAX and REST, but these focal points were adopted very fast, so there is no big cause to fight for right now. That leaves Rails 2.0 in a really weird spot, since it won’t have any groundbreaking new concepts.

This is not a bad thing, though, DHH stressed. “Even though [the lack of a great cause] is an off-switch for me, it’s not about me anymore”, he said. “Rails are moving from big revolutions to tiny evolutions” – a statement that was underlined when David went on to demonstrate new features in Rails; features that were definitely more tiny evolutions than big revolutions.

But before we get to the Rails 2.0 code examples of the keynote, let me just mention something that was news to me: The new policy for how to get a patch into Rails, also known as Report #12. The idea is basically that you, once you have written a patch and submitted it to the Rails trac, should get three other Rails developers to check out your patch, verify that your tests run, and then add a “+1″ in a comment in trac. Once you have your three “+1″‘s, you tag your patch with the keyword “verified”, and the patch will appear in Report #12: Verified Patches. The core team should then – theoretically – be able to very fast to accept the patch into Rails.

Moving on to the, in my opinion, most interesting part of the presentation: What’s new in Rails 2.0 – show me the code! Migrations has been updated to support a new syntax known as “sexy migrations”, which basically allows even shorter and more readable migrations by grouping fields of the same type:

create_table :blogs do |t|
	t.string :title, :subtitle
	t.text :body, :description
	t.integer :category_id
	t.timestamps # Adds created_at and updated_at fields
end

Notice how another evolutionary improvement has been sneaked in: Tired of always adding created_at and updated_at timestamps to most tables, a timestamps method has been added as a shortcut. Also this new syntax, including the timestamps, is now standard in the model and scaffold generators.

A small, but to me significant improvement has also found its way to Rails 2.0: You can now create the needed databases with a rake command. By running this command, all referenced databases in your database.yml will be created:

rake db:create:all

Another interesting, and perhaps generally quite invisible improvement, is that session data are now stored in a encrypted cookie instead on each server, which makes it easy to partition applications across multiple servers without having to put session data in the database. This requires, of course, the session data to be quite compact (storing id’s, not entire objects), as a cookie can contain 4 KB of data the most.

My favourite point of DHH’s keynote was his solution to the common pattern of a resource, which you both need to display to the common user (usually index and show) and to the admin user (usually index, new, create, edit, update and destroy). The normal RESTful solution to this leaves with a problem, because we are only allowed one index action, and we need two; one for the common user and one for the admin user. Also, the admin actions will usually have a different layout than the common user actions, and they require some kind of authorization.

The solution is to used RESTful namespaced routes, referring to two different controllers with the same name, but in different namespaces. To use DHH’s eternal blog example:

./script/generate controller posts # => /controllers/posts_controller.rb
./script/generate controller admin::posts # => /controllers/admin/posts_controller.rb

Now we have two different controllers working in the same model; Post, but offering different actions, layouts, authorization and so on. This nice thing about this solution, is that it is very easy to understand and fits well into the Rails structure. The catch is that we have two controllers with the same name, so we need to explicitly express the namespace when referring to the admin controller:

form_for([:admin, post]) do |f|
	...
end

DHH also pointed out that HTTP authentication is a perfect way of doing authentication for administration pages (which I’d already realized). You don’t really need nice-looking form-authentication and remember me checkboxes for that, and it is so very easy to implement with Rails 2.0:

# In a controller or module shared between admin controllers
before_filter :ensure_administrator
def ensure_administrator
	authenticate_or_request_with_http_basic('Blog admin') do |username, password|
		username == 'dhh' && password == '123'
	end
end

A very visible change in Rails 2.0 is the naming of views. The .rhtml, .rxml etc. file endings has been discarded to replaced by the form [name].[mime-type].[renderer]. This leaves the road open for file names such as:

  • index.html.erb
  • index.xml.builder
  • index.html.liquid
  • index.iphone.erb

The last example needs a little additional configuration, since iphone is not a “real” mime-type, but we can easily make Rails treat it like such:

# In mime_types.rb:
Mime::Type.register "application/x-iphone"

# In application.rb:
before_filter :adjust_format_for_iphone
def adjust_format_for_iphone
	if request.env["HTTP_USER:AGENT"] ~= /iPhone/
		request.format = :iphone
	end
end

# In specific controllers
respond_to do |format|
	...
	format.iphone do
		render :text => 'Hello iPhone', :content_type => Mime::Html
	end
end

Notice how content_type must manually be set to Mime::Html, as the iPhone browser – as well as any other browser – doesn’t recognize our fake x-iphone mime-type, and Rails will always default to respond with the request mime-type.

My second favourite point was about the “new” debugging support in Rails 2.0. As I understood it, the well-known ruby-debugger library has been baked into Rails 2.0, so that we write debugger instead of breakpoint, and need to start the server with a --debugger prefix (./script/server --debugger) to enable these breakpoints. This debugger also support more advanced commands such as “cont” (continue), “list” (view current code) and “up” (step out).

David Heinemeier Hansson rounded off his keynote by announcing the release of a Rails 2.0 Preview Release (how Microsoft-ish does that sound?) either during or shortly after the conference.

You can download my full notes from the keynote here.

UPDATE: My other notes from RailsConf Europe 2007 are now available.

RailsConf: Shared Host Deployment

Geoffry Grosenbach is cool. And I’ve struggled with the same issues as he has, albeit I’ve been able to build on his work in my own deployment setup. So of course I had to attend his talk on deploying to shared hosts, although most of what he said wasn’t new to me. Geoff’s entertaining and well-designed presentation focused on three areas: How to design a site to run a shared host, how to maintain the site, and how to troubleshoot problems.

Design
First of all, Geoff suggested that only really small sites should be deployed to a shared host. By small, it is not as much disk space, as how much memory and how many CPU cycles the site requires running smoothly. You are restricted on all these three factors on most shared hosts, but you also have the benefit of running on multiple servers – usually the database server will quite powerful, you will have a separate mail server, etc.

Another important thing in designing the application is caching. Geoff explained, that by using the caches_page macro, an html file will automatically be created upon the first hit, and after that; the web server will simply serve that static page without even invoking Ruby, Rails, FastCGI or anything else. The html file is stored in a directory structure that corresponds to the mapping of the action, so if you have for instance the action /da-DK/blogs/show/1, it will store that particular blog for that particular language. So that sounds like quite a big possibility to improve on speed. In order to expire the cached pages, a cache_sweeper needs to be implemented.

Maintaince
Geoff underlined that you need to make sure you don’t have too many zombie FastCGI processes running on your shared host, since you are restricted on number of processes. Maybe Mongrel will be the holy grail giving a nice, reliable way to run sites on a shared host, Geoff said, and this was somewhat confirmed by the “Deploying Rails” presentation by Ezra Zygmuntowicz the following day, stating that the future of Rails deployment definately belongs to Mongrel.

For monitoring our Rails applications, Geoff suggested Montastic.com or dwatch, which he has written a great tutorial on using.

Troubleshooting
Finally, Geoff gave some advice on how to debug and fix your problems when your site is not working on the server. The hash-bang (or is it she-bang?) lines has to actually point to place Ruby exists on the server, and it is a good idea to set export RAILS_ENV="production" in the ~/.bashrc or ~/.bash_profile to indicate that this the production site, instead of hardcoding it in environment.rb or relying on the shared host to set it.

./script/about, ./script/server –e production and ./script/console production are all good indicators of whether or not the actual site are running on the shared host, but also, the various logs produced can be good indicators of where the problem is, especially apache_log, production.log and fast_cgi.log.

Conclusion
Deploying Rails can be frustrating, and deploying it to a shared host with no control and lots restrictions can be a nightmare, so, as Geoff started out by saying, his talk was not a tutorial or a lesson, it was therapy.

But really the morale here is: Don’t use shared hosts for Rails, and if you do, make sure it’s only very small applications, or only for test purposes. As Ezra said at his talk on deployment: “Rails is a pig”. Once you want people to actually use your site, you should go with a Virtual Private Server (VPS) – or of course a dedicated setup if you can afford it – since you will get a certain amount of memory guaranteed, and you will have less users pr. machine. Ezra adds that you always go for a VPS host that uses XEN, since this platform uses almost no resources on its own.

Deploying Rails with Edge and Engines to Dreamhost using Capistrano

It’s not that I’m the first to describe the process of deploying a Rails application, in fact; it’s rather well-documented, also when it comes specifically to Dreamhost. It’s just that I have a strange combination of circumstances that none of these articles cover, and also; I know almost nothing about Unix-based systems – I don’t even have a Mac. I am one the of many Microsoft-developers who are starting to see the light, and because of that, I need to start entirely from scratch when it comes to understanding the whole Linux/Apache/open-source universe.

(Take me straight to the solution, please)

My strange combination of circumstances
The Rails application I’ve had to learn to deploy the hard way has this configuration:

  • Dependent on Rails 1.1.2
  • Uses Login and User Engine.
  • Build for MySQL (so no problem there)
  • Must run with FastCGI, or it’s too slow
  • Developed with RadRails on Windows XP (yes, this makes a difference)
  • Resides in a Subversion repository

If you can live with Rails 1.0.0, if you don’t use Rails Engines, if you don’t need FastCGI and if you know about Linux, you are probably better off using shorter and simpler tutorials, because these where my specific problems, and what I am going to dvelve into here.

Linux basics
Although it is possible to deploy Rails to a Windows server (and presumably not much harder if you can use Apache instead of IIS), Dreamhost, and all other commercial hosts supporting Ruby on Rails, are using Linux servers. You can develop your entire Rails application completely unknowningly about Linux, but when it gets to deployment you have to learn the basics about this strange operating system:

  • You don’t use Remote Desktop, you use SSH. Remember DOS? When you connect with SSH, you get a text prompt simular to that, and you need to know some basic commands. I recommend using PuTTY for SSH.
  • Use pwd to print you current directory.
  • Use ls to print the contents of the current directory.
  • Use cd (e.g. cd /home/user/) to move into a directory.
  • Use vim (e.g. vim environment.rb) to quickly view and edit files directly on the server. Press your INSERT button to enable editing, and press ESCAPE, then write :wq to save and exit the file.

Now, in a perfect world (see below) you actually shouldn’t have to know this, because Capistrano would do all the dirty work for you, but let’s get real: It’s just not that easy.

In a perfect world
Here is a quick recap of what you would be all needed to do, if the world was perfect or you are extremely lucky and everything works as expected

  1. Now, I expect you have already created MySQL database on Dreamhost and entered the connection details into config/database.yml – otherwise; start out with that.
  2. Create a new fully hosted domain in the Dreamhost panel, and make sure it is configured like this:
    Dreamhost Rails setup
    • FastCGI Support enabled
    • The user has shell access of the type /bin/bash/ (configurable from the Manage Users page)
    • The web directory points to [yourdomain.com]/current/public (not just [yourdomain.com]/public, since we want to use Capistrano)
    • (The www option is up to you)
  3. SSH to [yourdomain.com] and remove the current directory (containing the public directory) Dreamhost has automatically created. (Capistrano needs to create this as a symbolic link later.):
    # On your Dreamhost server
    [nimitz]$ pwd
    /home/cape
    [nimitz]$ cd myrailsapp.com/
    [nimitz]$ ls
    current
    [nimitz]$ rm -r current/
    [nimitz]$ ls
    [nimitz]$
    • Start PuTTY, enter [yourdomain.com] as Host Name and click Open.
    • Login with the user you specified when creating the domain
    • Change to the directory of your Rails application (cd [yourdomain.com])
    • Remove the directories (rm -r current)
  4. Download Geoffrey Grosenbach’s Capistrano script, add it to the /config/ directory of your Rails application, and edit it to reflect your setup:
    # In config/deploy.rb
    set :user, 'cape'
    set :application, 'myrailsapp.com'
    set :repository, 'http://svn1.cvsdude.com/...'
    set :svn_username, "casper"
    set(:svn_password) { Capistrano::CLI.password_prompt }
    • :user must match the user you specified when creating the domain
    • :application must be the domain you created
    • :repository must be the url of your Subversion repository for the application, from where Capistrano will grab the code to deploy.
    • :svn_username only have to be present, if your subversion username is different from your Dreamhost shell username.
    • set(:svn_password) { Capistrano::CLI.password_prompt } tells Capistrano to prompt you for your subversion password. This might not be nescessary, if your Dreamhost user is already in a trust relationship with the Subversion server.
  5. Edit public/.htaccess and change the line RewriteRule ^(.*)$ dispatch.cgi [QSA,L] to RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]. This tells Apache which file it should execute when a request comes in.
  6. Edit public/dispatch.fcgi and change the first line from #!c:/ruby/bin/ruby (or something simularly Windowish) to #!/usr/bin/env ruby. This line is called a shebang location and tells Linux where to find the program that are able to execute this file, in our case; this is ruby.
  7. Edit config/enviroment.rb and incomment the line: ENV['RAILS_ENV'] ||= 'production'. Dreamhost says they set this enviroment variable automatically the nice way when using FastCGI – in my experience, they don’t. This means you have to incomment the line every time you release, and outcomment it again the develop in the development environment – in my final deploy.rb I have a much cleaner solution to this problem, so stay tuned.
  8. Open up a Command Window, move into the directory of your Rails Application and run the command rake remote:exec ACTION=setup. (You will need to type your password for the user you specified when creating the domain)
  9. Run rake deploy. If you are really, really lucky, this will actually do the job for you, and you can browse to your application. If you are not, go on …

Finding out what’s wrong
I’m going to assume that the steps above sort of worked for you, such that Capistrano was actually able to setup the server and copy the files to the correct directory, but it probably failed by the end of the rake deploy command, because it didn’t have the permissions to restart the web server. So now when you go to [yourdomain.com] in your browser, you don’t get a “Page not found”, you get a “Rails application failed to start properly” after a looong wait.

Since the “Rails application failed to start” message is not very information, I am going to start out by giving you a few tips about how to diagnose your failing Rails application. You should be able to distinguish between to scenarios:

  1. Your Rails application is not able to run, probably because it can’t connect to the database or can’t locate some files
  2. The application can run, but the FastCGI link doesn’t work, so pages cannot be served

It’s easy to test for the first scenario; just SSH to the server, cd to the directory of your app (cd /home/user/[yourdomain.com]/current/), and run the command ruby script/console production. This attempts to start your Rails app in production mode, and any error messages will be right there on the screen for you.

The second scenario is in fact also pretty easy to test, at least under the assumption that Apache is able to redirect to “dispatch.fgi” (but it wasn’t, you probably wouldn’t get the “Rails application failed to start properly” error). By running ruby public/dispatch.fgi you actually invoke the same file as the web server, which will often give you handy information.

Once you application is running, you can also get useful debugging information from the log files in the logs directory, but if your Rails app is actually writing to these, you probably have something that are pretty close to work.

Fixing deploy.rb
As I have hinted, I had to make a couple of changes to Geoff’s “shovel” deploy.rb in order to make it run smoothly:

  1. To be able to restart the FastCGI-processes in the :restart task, I had to add ruby to the line that restarts, because the reaper-script that restarts is not executable on Dreamhost. So my :restart task now looks like this:
    # In config/deploy.rb
    task :restart, :roles => :app do
      run "ruby #{current_path}/script/process/reaper --dispatcher=dispatch.fcgi"
    end
  2. To make Apache able to actually execute dispatch.fcgi our deploy.rb also needs to run a command makes dispatch.fcgi executable. On Unix-based systems, any file can be marked as executable, and the OS will then read the shebang location when the file is invoked. By inserting the task below, dispatch.fcgi will be made executable each time you make a deployment. By naming the task after_symlink, it will be executed after the symbolic link has been moved to point to your new files – first by then, the current_path variable is available.
    # In config/deploy.rb
    task :after_symlink, :roles => [:web, :app] do
      # Make dispatcher executable
      run "chmod a+x #{current_path}/public/dispatch.fcgi"
    end

Rails in the /vendor directory
It’s supposed to be so easy to use a different version of Rails than the one installed on your host. Just run rake rails:freeze:gems – or even better: Set /vendor/rails to point to http://dev.rubyonrails.org/svn/rails/tags/rel_X-X-X/ (depending on your prefered version) using the svn:externals feature of Subversion. However, strange things can happen to your application, once all the core files of Rails are suddenly supposed to be read from the /vendor/rails directory. I had to make three crucial changes to avoid various “required file not found” or “NoMethodError” messages after I “froze” Rails:

  1. Move the inclusion of files in config/environenment.rb down to the bottom of this file. For instance; I have require 'taggable' in my environment.rb, and before I “froze” Rails it I just had this line at the top without any problems, but after, it suddenly couldn’t find the taggable stuff, and it only worked when I moved that line down after the whole Rails::Initializer.run do |config| chunk.
  2. Force public/dispatch.fcgi (the file that runs the show) to use the “frozen” version of Rails, by replacing the line require 'fcgi_handler' in dispatch.fcgi with this line:
    require File.dirname(__FILE__) + "/../vendor/rails/railties/lib/fcgi_handler"
  3. Unfortunately, we also need to edit a file inside the Rails framework itself, putting in the absolute location of a required file instead of just the name of the file, as described in this post. However, if you use svn:externals like I do, you can’t just alter a file in vendor/rails, since you don’t have any commit rights. Furthermore, the location of the line we need to add is specific to our setup on the host, so it would be baaad to hardcode it. That’s why I came up with a solution letting the deployment script replace this line on the server using the current_path variable available. You could do this with ruby, but I asked one of my hardcore Linux geek friends to write a one-liner that I could just run, and he chose to let Perl make the replace directly in the file for us. This also happens in the after_symlink task:
    # In config/deploy.rb
    task :after_symlink, :roles => [:web, :app] do
      # Ensure Rails in the vendor dir is used by replacing a line in fcgi_handler.rb
      run "perl -i -pe "s/require 'dispatcher'/require '#{current_path.gsub(///, '\/')}\/vendor\/rails\/railties\/lib\/dispatcher'/" #{current_path}/vendor/rails/railties/lib/fcgi_handler.rb"
      ...
    end

Rails Engines
Rails Engines is a brilliant way of creating “super-plugins” that acts like small sub-applications inside your application, but where everything can be extended or overriden – even views! If you don’t use engines, this small section is irrelevant for you, but I’d recommend you taking a look a them – I love them.

There is really only one thing you need to be aware of when you deploy a Rails application to Dreamhost that uses engines and has Rails in the vendor directory, and that is to put this code at the very top of your environment.rb:

# In config/environment.rb
module Engines
  CONFIG = {:edge => true}
end

One other thing, not really related to deployment: As I am writing this, the LoginEngine has a problem loading two files in its init_engine.rb, so if you get errors in this file, try replacing the two original require statements with this:

# In vendor/plugins/login_engine/init_engine.rb
require 'login_engine/authenticated_user'
require 'login_engine/authenticated_system'


Wrapping it up: The complete solution
Today, I am able to run rake deploy and it actually works. My deployment script for Capistrano, based on Geoff’s shovel, can be downloaded here. Notice that this script, besides the tasks described above, also automatically replaces #ENV['RAILS_ENV'] ||= 'production' with ENV['RAILS_ENV'] ||= 'production' (incomments it) in config/environment.rb to force production environment on the Dreamhost server.

Here is a complete list of files I’ve uploaded for your reference:

I had to rename some of the files in order to let them be downloadable, but the link listed above has the correct name and the directory the file belongs to.

Please note: You can’t just dump these four files into your own Rails application and expect things to work. You have to carefully extract the pieces from each file you think you need in order to get your application up and running.

Good luck!


Recommended ressources