Who Moved My Gems?
I appreciate the tough decisions the guys at Phusion had to make. Distributing products like Ruby Enterprise Edition and mod_rails is a lot of work. It's got to run on dozens of different operating systems and Linux distributions, every one with it's own package manager and configuration preferences. They did a great job of it. Their install script is the best of it's kind that I have seen, and I applaud their decision to use /opt, they are using that directory for it's intended purpose.
The problem is that the executable files in /opt/ruby-enterprise/bin are not in the default path, and it's a lot of keys to type /opt/ruby-enterprise-1.8.6-20090421/bin/ every time you want to run rake or rails or any other gem based executable. This is not a problem for your production application. Once you have Apache configured to load Passenger it knows where to find all the gems, but in development or deployment you may experience pain.
I have been around and around on this problem. At first I just typed in the whole /opt path, when I remembered too, and used the regular version of gems and Debian's ruby1.8 when I forgot. When I set up some new servers with REE and passenger from the start with no other ruby interpreter on the machine I knew I needed to stop winging it. I could have just run a parallel installation with ruby1.8 and all the gems installed twice, once for Apache and once for the command line, but I found that unappealing.
I did not test the first version of this post well enough. I had thought I solved the problem by changing the PATH environment variable so /opt/ruby-enterprise/bin was the first place searched for executables, but there are situations when that just does not work.
For example: you will need to run rake as a normal user during development, with sudo when you install gems specified in environment.rb, as root if you su – to work through a new installation or a system upgrade, as a non-interactive user when you deploy with capistrano, and with sudo via capistrano. I changed /etc/profile, /etc/environment, /root/.bashrc, ~./bashrc, and aliased sudo but I still couldn't get every situation to work correctly. I need to warn you in particular about using an alias for sudo as I showed in the first version of this post. It removed the sbin directories from the PATH you get with sudo and system commands cannot be found.
One thing I had tried early on was to use /etc/alternatives. It was tedious to run update-alternatives by hand and every new gem could add a new executable and when Phusion released a update to REE I had to do it all over again.
My new approach goes back to /etc/alternatives and, so far so good, it seems to solve all these problems. If you are running Ubuntu and installed REE from Phusion's .deb you will already have a /opt/ruby-enterprise directory. If you are running Debian you can create a link by that name to the current release of REE. Then we can depend on /opt/ruby-enterprise being the current release. When a new version comes out just update the link and you should be ready to go. This is how I create the link for Debian.
$ sudo ln -s /opt/ruby-enterprise-1.8.6-20090421 /opt/ruby-enterprise
Now to automate the chore of creating the /etc/alternative links. I just did a rake task because it simplifies a few things, but it is just a simple loop and it could be done as a real script someday.
namespace :ree_alt do
desc 'find ruby-enterprise-edition executables and link as alternatives'
task :install do
Dir.entries("/opt/ruby-enterprise/bin").each do |executable|
unless [".", ".."].include? executable
`update-alternatives --install /usr/bin/#{executable} #{executable} /opt/ruby-enterprise/bin/#{executable} 100`
end
end
end
desc 'remove all ruby-enterprise based alternatives'
task :remove do
Dir.entries("/opt/ruby-enterprise/bin").each do |executable|
unless [".", ".."].include? executable
`update-alternatives --remove-all #{executable}`
end
end
end
end
As usual there is plenty of room for improvement. When you need to update the alternatives what you have to do is run the remove task and then run the install task, with sudo. Trick is when you run install you will not have rake in your path so you need to go back to the long /opt path name.
$ sudo /opt/ruby-enterprise/bin/rake ree_alt:install
So it's not beautiful, but it does work this time, and it's not too much of a chore.
Deploying with git submodule 1
I’ve been busy upgrading servers to Passenger 2.1.3 and trying out Rails 2.3.2. One nifty feature is rails-templates. You can write a simple template that lets you create new rails projects with all your favorite plugins and gems automatically installed, you can install the CSS and JavaScript you like, and change all the default files to suit your purpose, all with the one rails command line. This probably saves an hour or more per new project and gives you a consistent setup. My rails-template is published at http://zipi.github.com/rails-templates/, I may have gotten a little carried away with it, but I like it.
One thing I learned to do while writing my template is to use git submodule. When you install a plugin from a git repository you can specify it be installed as a submodule. This way the plugins are not included in your new project’s repository. The first lesson was when I cloned a project to my laptop so I could work on the road. The application wouldn’t start up because the plugin directories were empty. Before I could run the application I had to run some more git commands
git submodule init git submodule update
It took longer to learn what I needed to do than it took to do it. Good thing I saw this problem on my laptop, because when I deployed to a staging server I saw it again and knew what to do. I shelled in, cd to the release directory and ran the git submodule commands. Easy enough but I can’t do that every time I push a new version.
So I have a new recipe for my deploy.rb file to be called after update.
desc "Install git submodules"
task :git_submodules do
run "cd #{release_path} && git submodule init"
run "cd #{release_path} && git submodule update"
end
task :after_update_code do
# other tasks here
git_submodules
end
It works. And that is about all I can say about it. It’s slow and silly to clone rspec in order to deploy to a production server where you will never run rspec. I also prefer to have the same specific version of a plugin I use in development to be used in production.
Maybe using git submodule for plugins is not really a good idea after all.
cap deploy:web:disable and Phusion Passenger (final update) 6
To make it easy to copy around I put this mix-in module in lib/maintenance_mode.rb
module MaintenanceMode
protected
def disabled?
maintfile = RAILS_ROOT + "/public/system/maintenance.html"
if FileTest::exist?(maintfile)
send_file maintfile, :type => 'text/html; charset=utf-8', :disposition => 'inline'
end
end
end
And make these the first two lines in class ApplicationController, before any before_filters that might access the database.
include MaintenanceMode before_filter :disabled?
And that’s all it takes to get cap deploy:web:disable to work again. This is the bit of code I put at the end of config/deploy.rb to replace start, stop and restart, and provide some handy Capistrano recipes for working with passenger.
namespace :deploy do
desc "Restart Application"
task :restart, :roles => :app do
run "touch #{current_path}/tmp/restart.txt"
end
desc "Not used with Passenger"
task :start, :roles => :app do
# nothing
end
desc "Not used with Passenger"
task :stop, :roles => :app do
# nothing
end
end
namespace :passenger do
desc "passenger memory stats"
task :memory, :roles => :app do
run "passenger-memory-stats" do |channel, stream, data|
puts data
end
desc "passenger general info"
task :general, :roles => :app do
run "sudo passenger-status"
end
end
I think for my next Ruby on Rails post I’ll show a capistrano recipe for generating a custom maintenance.html page.
![[FSF Associate Member]](http://www.scottmoe.info/files/1102.png)




