Rails plugins are great for many reasons, one being that they provide extra functionality without being an external dependency – they’re packaged right there with your application. Until recently, there was no way do programmatically define a Rails applications’ external gem dependencies and we were left with rolling our own gem dependency solutions.
That all changes with a nice way to define, and tie, gem dependencies to our Rails apps. In our environment.rb we have the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Rails::Initializer.run do |config| # Require the latest version of haml config.gem "haml" # Require a specific version of chronic config.gem "chronic", :version => '0.2.3' # Require a gem from a non-standard repo config.gem "hpricot", :source => "http://code.whytheluckystiff.net" # Require a gem that needs to require a file different than the gem's name # I.e. if you normally load the gem with require 'aws/s3' instead of # require 'aws-s3' then you would need to specify the :lib option config.gem "aws-s3", :lib => "aws/s3" end |
So great – when your app loads up it will automatically find and require each of the gems you’ve listed. But what if you’re on a system that doesn’t have all of these gems installed, or you’ve just deployed to a fresh environment? There’s now a rake task that will install all the referenced config.gems on your target system:
rake gems:install |
Before running this you can see what gem dependencies your app has by running rake gems.
However, this still doesn’t package these gem dependencies with the app, it only refers to system-dependent gems. If you want to pull these gems into your application source you can do so with the new rake gems:unpack task:
1 2 |
# Unpack all gems to vendor/gems rake gems:unpack |
You can also unpack individual gems:
1 2 |
# Unpack only the hpricot gem to vendor/gems rake gems:unpack GEM=hpricot |
This will unpack the gem into the vendor/gems/hpricot-0.5 directory which is automatically searched as part of the config.gem startup.
Your deployment strategy can now choose to automatically install required gems in each target environment or just package gems as part of the application source.
Update: Just added is the ability to build gems that have native extensions:
rake gems:build |
tags: ruby, rubyonrails

The beginning of the end of plugins.
Ryan,
Changeset #9148 means that what you should have there is:
config.gem “haml”
Rick pluralised on the first changeset :) The rake tasks are correct though, just those config’s
The additions of gem dependencies into the rails core is probably one of my favourite additions recently.
Brilliant stuff.
Matt – thanks for the proofread! I’ve updated the code to show the proper singularized form of
config.gem.Brilliant indeed! ...and much needed.
Another great essential functionality. Bravo!!!!
Ryan – not a problem :) Thanks for keeping us up to date with what’s happening on the edge!
great to see how “competing” projects (datamapper for partial sql updates and merb for gem dependencies instead of plugins) are helping rails get better!
Still using Rails 1.2.3 in my project and hopefully will upgrade to Rails 2.x in 2008. Hopefully, we are using geminstaller ( http://geminstaller.rubyforge.org/) With a simple YAML config file, here are the features:
Chad Wooley used bdd and rspec, his test coverage is very good and he has cruisecontrol running for testing his library with all versions of rubygems!
It works for any kind of ruby project.
I think it’s worth pointing out that those config.gem statements in environment.rb appear to be case-sensitive. I was getting an error with ‘bluecloth’ until I changed it to ‘BlueCloth’!
It also appears this doesn’t work for certain gems, where the name of the gem is different than the main file. For example, try the ruby-openid gem. That’s the name of the gem, but the library file is “openid.rb”. So, neither of the following work:
config.gem ‘ruby-openid’ config.gem ‘openid’
When I do ‘rake gems:unpack’ I get the following error: uninitialized constant Gem::CommandManager
The only gem defined in config is tzinfo, and I have v0.9.4 of ruby gems.
Matt – it spews all over the place when I try to run any of the rake tasks. Hopefully this is a temporary condition while things are still in development.
There seems to be a confusion regarding syntax
config.gem “ferret”, :version => ‘0.11.6’
works, whereas
config.gem “ferret”
does not work due to some latest changes in edge
Now possible on Edge:
config.gem “ruby-openid”, :lib => “openid”, :version => “2.0.4”
works fine for me
This is again one of the reason to really LOVE Rails. This is one of the easiest, and fun, ways to resolve dependencies I’ve ever seen making relocating apps to different environments worry free and nearly effortless.
At my company we’ve modified our deploy scripts to handle all gems the application is dependent on that automagically installs them when needed. I love that it’s going to be baked right in! (even if it means letting go of our young deploy scripts)
good stuff, keep it coming ;-)
When I run rake gems:install, I get the following:
(in /u/apps/Manager/releases/20080617202021) Could not find RubyGem haml (= 2.1.0) Could not find RubyGem chronic (= 0.2.3) Could not find RubyGem hpricot (= 0.6) Could not find RubyGem scrapi (= 1.2.0) Could not find RubyGem activemerchant (= 1.3.2) rake aborted! no such file to load—haml
(See full trace by running task with—trace)
So it seems rails is trying to load one of my gems (haml) before it runs the rake task. What am I missing?
pretty and we him. leaves they had a scientist. beechnuts We used I got then did
Nice Site! http://google.com
just testing this out with an app that has the railspdf plugin that requires the pdf-writer gem. I purposefully uninstalled the pdf-writer gem to test. i get an error before the rake tasks can ever figure anything out. That’s because each of these rake tasks still set about loading up the plugins, and in this case this plugin is requiring the pdf-writer gem which then error. any idea how i would go about fixing this—or if it is even desirable to fix?—or if there is some workaround i’m missing?