Skip to main content

Poor person's guide to managing Ruby versions

Understanding the guts of Ruby Version Management by rolling your own

I've been tinkering with a fresh install of Ubuntu 12.10, setting up a nice clean development environment. One of the first things to do, of course, is implement some sort of Ruby version management. RVM and rbenv seem to be the clear winners in this arena, though there are a lot of tools out there that do a similar job.
Writing your own version management for your Rubies isn't actually all that difficult. At it's core, we need need two things:
  • A way to segregate the executables of the various versions
  • A way to call the versions at will
Segregating versions is trivial - working with files and folders, we can put the various versions into named directories.
Actually executing our different versions is not all that difficult either. One way would be to create aliases with version numbers and explicitly call those when we want to use them. The more popular way, however, is to manipulate our PATH variable. The PATH variable tells our operating system where to look for executables when we, for example, type a command into the terminal. The first match it hits is the one it uses.
Using this idea, it isn't all that difficult to set up our own poor man's Ruby version management.

But Why?

Why would anyone want to do this when there are already tested and stable versions out there?
Simple - because we can! Really, though, this is an experiment, a little tinkering that will further your own understanding and even if you don't actually use this for your own version management, it will give you a better appreciation of what's going on behind the scenes of your favourite Ruby environment management system.

Step 1 - Get and compile your rubies

To pull off this experiment, we are going to need two distinct versions of Ruby. I'm going to use a 1.9 and a 1.8. The advantage is it is easy to test which we have running with some simple Ruby string commands because of the changes between the versions.
Download Ruby 1.9.3-p327 and Ruby 1.8.7-p371. Now extract the archives:
$ tar -zxvf ruby-1.9.3-p327.tar.gz
$ tar -zxvf ruby-1.8.7-p371.tar.gz

Step 2 - Set up some directories

I'm going to create a directory called ".pmrbm" in which to stash my Ruby versions. Inside that directory, I'll create an explicit 1.8.7-p371 and 1.9.3-p327 directory:
$ mkdir -p $HOME/.pmrbm/1.8.7-p371
$ mkdir $HOME/.pmrbm/1.9.3-p327

Step 3 - Compile

Compiling Ruby from source might sound scary if you've never done it before, but really it's a walk in the park. We should have two directories in our downloads folder (from the unarchiving we did earlier) - one for the 1.8.7 and one for the 1.9.3 versions. As we compile our versions, we will pass in the path into which we want the compiled Ruby to end up with the "--prefix" command:
$ cd ruby-1.8.7-p371
$ ./configure --prefix=$HOME/.pmrbm/1.8.7-p371
$ make
$ make install
And you should have Ruby 1.8.7-p371 installed. Now we do the same for our 1.9.3-p327 version, but obviously substituting our 1.9.3 specific paths in:
$ cd ruby-1.9.3-p327
$ ./configure --prefix=$HOME/.pmrbm/1.9.3-p327
$ make
$ make install
We've now got two versions of Ruby compiled on our machine.

Win

How on earth are we going to use our Ruby versions? Well, if you are paranoid, you could cd into the various directories and confirm that Ruby is installed and running in those directories. I'll skip that step and simple start modifying my PATH. My system had a fresh install of Ubuntu on it, with no system Ruby installed, so at this point, even though I've compiled two different versions of Ruby, if I try to interrogate the version of Ruby on my system, I will get an error:
$ ruby -v
$ The program 'ruby' can be found in the following packages:
...
Obviously, I'm not going to install Ruby from apt-get, because that would defeat the object of this exercise. Instead, I'll type the following:
$ export PATH="$HOME/.pmrbm/1.8.7-p371/bin:$PATH"
And now when I can get the Ruby version (note I'm in my home directory, but you can be in any directory you want, the PATH will make sure that the OS can find your 1.8.7 version of Ruby:
$ ruby -v
$ ruby 1.8.7
If I run some code in irb, I can confirm that I'm running 1.8.7, because of the way it handles single string characters:
$ irb
> "cat[1]"
=> 97
Great, but what about 1.9.3? Easy as pie - simply change your PATH:
$ export PATH="$HOME/.pmrbm/1.9.3-p327/bin:$PATH"
Again, check the version and confirm with a little bit of single string manipulation:
$ ruby -v
$ ruby 1.9.3p327
$ irb
> "cat[1]"
=> "a"
Note that I was pretty sloppy there with the PATH. For example, if you were to examine your PATH variable at this point, you should see something like(your path will obviously vary based on your system setup):
$ echo $PATH
/home/rubyflewtoo/.pmrbm/1.9.3-p327/bin:/home/rubyflewtoo/.pmrbm/1.8.7-p371/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin/usr/bin:/sbin
See, both the ruby versions are present in the path, though at the moment, ruby is reporting it is version 1.9.3 because the first path that the OS finds Ruby in is 1.9.3-p327/bin, so it simply ignores the 1.8.7 path later in our environment variable. You can, if you feel like playing with this, move the 1.9.3 directory, and test again to see that the OS would then pick up the 1.8.7 version. So
$ mv ~/.pmrbm/1.9.3-p327 ~/.pmrbm/1.9.3-p327.back
$ export PATH=$PATH
$ ruby -v
ruby 1.8.7
Obviously, a neater solution would be to remove the portions of the PATH that are not needed rather than continually pre-pending to it

What now?

Now you should trash all of the above and go use one of the existing stable and actively maintained solutions. A few options:

Comments

  1. You might also want to read the source of chruby (it's only ~80 lines). Also, it's a better idea to install Rubies into /opt/rubies/, and point GEM_HOME to ~/.gem/$ruby/$version.

    ReplyDelete
  2. Thanks - I'm looking through the chruby source at the moment :)

    ReplyDelete

Post a Comment

Popular posts from this blog

Getting started with Ruby on Rails 3.2 and MiniTest - a Tutorial

For fun, I thought I would start a new Ruby on Rails project and use MiniTest instead of Test::Unit. Why? Well MiniTest is Ruby 1.9s testing framework dejour, and I suspect we will see more and more new projects adopt it. It has a built in mocking framework and RSpec like contextual syntax. You can probably get away with fewer gems in your Gemfile because of that. Getting started is always the hardest part - let's jump in with a new rails project rails new tddforme --skip-test-unit Standard stuff. MiniTest sits nicely next to Test::Unit, so you can leave it in if you prefer. I've left it out just to keep things neat and tidy for now. Now we update the old Gemfile: group :development, :test do gem "minitest" end and of course, bundle it all up.....from the command line: $ bundle Note that if you start experiencing strange errors when we get in to the generators later on, make sure you read about rails not finding a JavaScript runtime . Fire up

Getting started with Docker

Docker, in the beginning, can be overwhelming. Tutorials often focus on creating a complex interaction between Dockerfiles, docker-compose, entrypoint scripts and networking. It can take hours to bring up a simple Rails application in Docker and I found that put me off the first few times I tried to play with it. I think a rapid feedback loop is essential for playing with a piece of technology. If you've never used Docker before, then this is the perfect post for you. I'll start you off on your docker journey and with a few simple commands, you'll be in a Docker container, running ruby interactively. You'll need to install Docker. On a Mac, I prefer to install Docker Desktop through homebrew: brew cask install docker If you're running Linux or Windows, read the official docs for install instructions. On your Mac, you should now have a Docker icon in your menu bar. Click on it and make sure it says "Docker desktop is running". Now open a terminal and ty

Rails 3.2, MiniTest Spec and Capybara

What do you do when you love your spec testing with Capybara but you want to veer off the beaten path of Rspec and forge ahead into MiniTest waters? Follow along, and you'll have not one, but two working solutions. The setup Quickly now, let's throw together an app to test this out. I'm on rails 3.2.9. $ rails new minicap Edit the Gemfile to include a test and development block group :development, :test do gem 'capybara' gem 'database_cleaner' end Note the inclusion of database_cleaner as per the capybara documentation And bundle: $ bundle We will, of course, need something to test against, so for the sake of it, lets throw together a scaffold, migrate our database and prepare our test database all in one big lump. If you are unclear on any of this, go read the guides . $ rails g scaffold Book name:string author:string $ rake db:migrate $ rake db:test:prepare Make it minitest To make rails use minitest , we simply add a require