Rails: Vendor Everything Just Got Easier
Here's a common deployment fiasco: You build something. Something great. You use a number of different third party libraries installed on your system. Then, when you deploy the app to production, things break. You investigate. You realize that one of the libraries you're depending on doesn't exist on the server. You install it. Things still don't work. You realize that another one of the third party libraries is an older version. You update it. Things start to work. But now, one of the other applications hosted on that same server stops working, because some feature it was depending on in the older library is not available in the new version.
Eek. Guess it's time to "vendor everything"...
Keeping system-wide libraries like Ruby Gems in sync between development and production environments is of the utmost importance, and failure to do so can lead to many a crying fit or late night nervous breakdown. The same problem, of course, exists when multiple developers are working on the same project. In Rails, the solution is to explicitly list those dependencies in your project and to unpack any Gems you're using into the project's vendor directory so they can be loaded in place of the system-wide Gem versions. This strategy has been termed vendor everything and includes other benefits as well, including the ability to get a version of your app up and running just about anywhere with minimal fuss.
Since this wasn't a native feature of the framework, a few different approaches evolved including Dr Nic's GemsOnRails plugin, GemInstaller, and the FrozenGemsGenerator. However, recent changes in Rails core have made this easy to do right out of the box.
Today, in Rails Edge, you can take advantage of these features by adding Gem requirements directly to your config/environment.rb:
Rails::Initializer.run do |config|
config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
With this in place, you can now list required gems (money, chronic, and hpricot) using the new rake gems task. The output will also indicate whether or not the gem is installed locally. You can install missing gems using rake gems:install and unpack the gems into the vendor/gems directory using rake gems:unpack:
<br />$ rake gems:unpack GEM=money <br />Unpacked gem: '/Users/nap/dev/edgetest/vendor/gems/money-1.7.1'<br />
A simple improvement, sure. But one that is bound to save some of us countless hours and make deployment far more pleasant in general. Once the gems are unpacked, they'll be automatically added to the Rails load path when your server is started, so the right libraries (and the right *versions* of those libraries) will be present for your app. Note that this isn't a 100% solution however; unpacking some gems, particularly those that have native bindings like Hpricot and RMagick, may add some unavoidable complication here. That said, it still helps to mitigate a serious pain point.
In any case, this is just one of the many new features in Rails Edge, which I'd encourage you to check out if you're a Ruby developer. Related features include the ability to specify gem dependencies for plugins and allow gems to be used as plugins. Other recent advances are covered elsewhere by Ryan Daigle, Caboo.se, and the Rails Envy team.
This should all be bottled up into the Rails 2.1 release sometime in the near future, but as always, feel free to freeze your Rails project to edge (rake rails:freeze:edge) if you want to take an early look at what's coming!