Many Rubyists find the need from time to time to run multiple versions of Ruby. If you are developing open-source code, it's a good idea to try to maintain compatibility with all three of the main versions of Ruby current in use, 1.8.6, 1.8.7 and 1.9
There have been some tools for this for a while now. A lot of you probably already know about multiruby, and many may be aware of a new gem called rvm for Ruby Version Manager.
These two are useful for different purposes. Multiruby excels for testing ruby code against different versions, while rvm is great for quickly switching rubies so that you can play with one or the other. I think of RVM as a set of hand tools, and multiruby as a power tool workshop
They complement each other, but I've had a few bumps getting them to work together, hence this article.
Multiruby
One tool which helps do this is the multiruby suite of tools which are part of Ryan Davis' ZenTest gem. There are three tools in this suite:
- multiruby_setup
- which allows you to install and maintain a collection of Ruby versions. The various versions are installed in a subdirectory of .multiruby in your home directory
- multiruby
- which runs each of the installed ruby commands with the same arguments.
- multigem
- which uses multiruby to run the gem command in order to install gems in the right place for each of the ruby versions under that .multiruby directory.
For my
multiruby_path = `which multiruby`.chomp if multiruby_path.length > 0 && Spec::Rake::SpecTask.instance_methods.include?("ruby_cmd") namespace :multi do desc "Run all specs with multiruby and ActiveSupport" Spec::Rake::SpecTask.new(:with_active_support) do |t| t.spec_opts = ['--options', "spec/spec.opts"] t.spec_files = FileList['spec/**/*_spec.rb'] t.ruby_cmd = "#{multiruby_path}" t.verbose = true t.ruby_opts << "-r #{File.join(File.dirname(__FILE__), *%w[gem_loader load_active_support])}" end desc "Run all specs multiruby and the tzinfo gem" Spec::Rake::SpecTask.new(:with_tzinfo_gem) do |t| t.spec_opts = ['--options', "spec/spec.opts"] t.spec_files = FileList['spec/**/*_spec.rb'] t.ruby_cmd = "#{multiruby_path}" t.verbose = true t.ruby_opts << "-r #{File.join(File.dirname(__FILE__), *%w[gem_loader load_tzinfo_gem])}" end end desc "run all specs under multiruby with ActiveSupport and also with the tzinfo gem" task :multi => [:"spec:multi:with_active_support", :"spec:multi:with_tzinfo_gem"] end
I've got three tasks here because RiCal works with either the tzinfo gem OR activesupport from Rails, and I want to test each combination of gems and ruby versions.
RVM
Like multiruby, rvm lets you set up and use multiple versions of ruby. As I said above the difference here is that while multiruby runs them all together, rvm is for when you want to pick one to use for a while.
The rvm command is used to:
- install a ruby implementation specifying one of ruby for MRI ruby, ree for Ruby Enterprise Edition a version of MRI patched for use with passenger (a/k/a mod-rails) or jruby surprisingly enough for JRuby and optionally the specific version and even patch level.
- pick which ruby to use by using "rvm use which", where which is one of the above or default for the standard ruby installation for your system.
as well as other management functions.
The rvm gem is actually a thin ruby wrapper around some bash scripts. The way rvm works is to set up shell environment variables when you use "rvm use" so you get the right ruby executables and environment, and there lies the rub.
Who's got the Gem
Now did I tell you that I decided to add rvm to my arsenal right after I upgraded my MacBook to run Snow Leopard?
Because of this I had to rebuild a lot of my ruby development tool chain. I decided just to 'fault in' things that I found to be missing when I found that they were missing. A lot of those things were gems. So I'd run my various ruby projects, and when I found a missing gem, I'd install it.
So I ran my normal spec tasks against RiCal, and installed the missing gems. When I got those working, I ran the multiruby taks, and found that the tzinfo gem was missing. This wasn't a surprise since multiruby (like rvm) maintains a separate set of gems for each implementation. It was just a matter of "multigem install tzinfo" and move on to the next step. Wrong!
Multiruby reported that it had installed the tzinfo gem for each of the installed multiruby implementations, but when I ran the rake task again, no joy, same thing. Running "multigem list" revealed that there were no gems for any of the multiruby installs!
After a bit of head-scratching, I realized that rvm was setting GEM_HOME so that the gem command would know where to look for and install gems, and this was confusing multigem, which simply runs the gem command with ruby which ends up installing gems relative to the implementations installation directory. But GEM_HOME overrides this, so multigem was just reinstalling the gem three times in whatever directory rvm wanted them.
The Workaround
What's working for me is to use the bash command "unset GEM_HOME" before running multigem. This removes the variable entirely, and multigem goes back to working "normally." It's not ideal but it works.
Trackbacks
Use the following link to trackback from your own site:
http://talklikeaduck.denhaven2.com/trackbacks?article_id=572





Rick,
Great article. I would like to help you with your uneasy feeling about unsetting GEM_HOME by removing the necessity to do so.
Instead of using ‘unset’ which is very destructive using rvm it is very easily ‘switch’ to your default ruby or any other ruby while still maintaining all of the current shell’s settings.
Let’s explore a brief illustration of this:
We can spawn a subshell running a different ruby by prepending an rvm selector:
What is the ruby of our original shell after running the above sub shell command?
Not changed at all! And we haven’t had to mess with GEM_HOME, yay!
So we have 1.8.6p383 selected, what if we want to run something against the system’s default ruby? Prepend another selector in a subshell!
What is the ruby of our original shell after running the above sub shell?
Still the 1.8.6p383 we had selected to use.
I will add these examples to rvm’s website.
If anyone has questions, feedback or suggestions on rvm, please hop in #rvm on irc!
Thank you!
~Wayne