Ruby Version Managers

Up to now we were managing multiple versions of Ruby through ConEmu tasks. Even though we can use different Ruby versions this way, such approach has few drawbacks. Testing scripts or running existing applications that relly on specific Ruby version require opening new or changing to tab that already has target Ruby activated. Adding new Ruby version is somewhat complex and soon we might finish with too many different tabs in ConEmu so it would be hard to remember which tab corresponds to particular Ruby version. Especially if we decide to install other Rubies besides MRI, like JRuby or IronRuby.

At the moment we have two versions of Ruby on our system. The first one is official 2.2.1-p85 and the second one is the latest development version. Since we have used installer for the 2.2.1 and have chosen to add it to the path, in each Command Prompt we open it will be the only one available unless we add the path to the development version to the system path. So we have to choose either to use Ruby in ConEmu's tabs or to constantly alter system path in order to change Ruby version we work with. This certainly is not the most comfortable way and, fortunately, there is a much better solution.

Uru

Uru is new, lightweight, portable Ruby version manager which can be used to easily switch Ruby versions in the current Command Prompt. In order to install it you have to download archive with latest version from Bitbucket and unpack it in some folder that is in the PATH. In order to keep things isolated let's create new directory C:\tools and add it to the path as is explained in the first chapter - Windows Command Line. Open command prompt, go to target directory and execute following command:

C:\tools>uru_rt.exe admin install
---> Installing uru into C:\tools

This will "install" Uru by creating two scipts in the same folder: uru.bat and uru.ps1. Starting Uru without any option will display help screen with all available commands:

C:\>uru
Usage: uru [options] CMD ARG ...

where [options] are:
  -debug=false: enable debug mode

where CMD is one of:
 admin   administer uru installation
   gem   run a gem command with all registered rubies
    ls   list all registered ruby installations
  ruby   run a ruby command with all registered rubies
   TAG   switch to use ruby version TAG, 'auto', or 'nil'

for help on a particular command, type 'uru help CMD'

If you now check which Ruby versions are registered with Uru you will see that it still doesn't know anything about existing Rubies on our system.

C:\>uru ls
---> No rubies registered with uru

As you can see from the help screen Uru has admin command which is used to administer installation. Executing following command will display information about available admin subcommands.

C:\>uru help admin
  Description: administer uru installation
  Usage: admin SUBCMD ARGS
  Example: uru admin add C:\ruby200\bin

where SUBCMD is one of:
     add   register an existing ruby installation
           usage: admin add DIR [--tag TAG] | --recurse DIR [--dirtag] | system
           eg: uru admin add C:\ruby200\bin

  gemset   administer gemset installations
           aliases: gemset, gs
           usage: admin gemset init NAME... | rm
           eg: uru admin gemset init 211@gemset

 install   install uru
           aliases: install, in
           usage: admin install
           eg: uru admin install

 refresh   refresh all registered rubies
           usage: admin refresh [--retag]
           eg: uru admin refresh

   retag   retag CURRENT tag value to NEW
           aliases: retag, tag
           usage: admin retag CURRENT NEW
           eg: uru admin retag 200p197 200p197-x64

      rm   deregister a ruby installation
           aliases: rm, del
           usage: admin rm TAG | --all
           eg: uru admin rm 193p193

So in order to register Ruby version with Uru we have to execute uru admin add with a directory of Ruby's bin folder as an argument. Let's register our first Ruby with Uru:

C:\>uru admin add C:\Ruby\22\bin
---> Registered ruby at `C:\Ruby\22\bin` as `221p85`

By executing same command but this time with directory where our current development Ruby version is we will register second Ruby version.

C:\>uru admin add C:\Ruby\Dev\bin
---> Registered ruby at `C:\Ruby\dev\bin` as `230dev`

Listing Rubies known to Uru will now show both our versions.

C:\>uru ls
 => 221p85      : ruby 2.2.1p85 (2015-02-26 revision 49769) [i386-mingw32]
    230dev      : ruby 2.3.0dev (2015-03-18 master 50002) [i386-mingw32]

Now we can easily switch currently used Ruby version.

C:\>uru 221
---> Now using ruby 2.2.1-p85 tagged as `221p85`

Just to be sure check which Ruby is active now

C:\>ruby -v
ruby 2.2.1p85  (2015-02-26 revision 49769) [i386-mingw32]

Similarly we can switch Ruby to latest development version:

C:\>uru 230
---> Now using ruby 2.3.0-dev tagged as `230dev`

C:\>ruby -v
ruby 2.3.0dev (2015-03-18 master 50002) [i386-mingw32]

But how we will switch Ruby version if we have multiple patchlevels of same version installed (same build numbers i.e 2.2.1 with different number after 'p', i.e. p85)? If we invoke Uru with only base version info and we have multiple patchlevels installed we will get following output:

C:\>uru 221
---> these rubies match your `221` tag:

 [1] 221p85     : ruby 2.2.1p85 (2015-02-26) [i386-mingw32]
                   Home: C:\Ruby\221\bin
 [2] 221p95     : ruby 2.2.1p95 (2015-03-24) [i386-mingw32]
                   Home: C:\Ruby\221-p95\bin

select [1]-[2] to use that specific ruby (0 to exit) [0]:

You can see that Uru is smart enough to display all versions that match version information given in the switch command. Now you can easily choose version or quit by entering 0.

If you want to avoid this additional step you can use version tag which Uru displays after installing new version, in output of uru ls command or in the menu that appears if conflicting versions are found when version switch command is executed. In the above example these tags are 221p85 and 221p95. So executing:

uru 221p85

or

uru 221p95

will immediately change active Ruby version. There's much more you can do with Uru. Define gemsets on project level, change Ruby tag value, etc. but that's beyond the scope of this book.

Pik

Another Ruby version manager available on Windows is Pik. Pik is quite old and not maintained any more but still can be used for managing installed Rubies. Since Pik is quite old and all samples are written for old Rubies and are not updated for newest versions.

Pik is implemented as Ruby gem so it is installed as any other Ruby gem. Open new Command Prompt window (not ConEmu) and execute following command:

C:\>gem install pik
----------------------------------------------------------------------------
*  If you're upgrading from a version <= 0.1.1, you'll want to delete the pik.bat
   file from all of your ruby versions. Gem uninstall should do the trick.
*  Install pik to a location that's in your path, but someplace other than your
   ruby\bin dir If you're upgrading from a more recent version, pik_install will
   overwrite the older files as needed.
    >path
      PATH=C:\tools\;C:\ruby\186-p368-mingw32\bin;C:\WINDOWS\system32;C:\WINDOWS
    >pik_install C:\tools
*  If this is a first-time install, add all the versions of ruby that you want to
   use with pik
    >pik add
    Adding:  186: ruby 1.8.6 (2009-03-31 patchlevel 368) [i386-mingw32]
     Located at:  c:/ruby/186-p368-mingw32/bin
    >pik add C:\ruby\IronRuby-091\bin
    Adding:  091: IronRuby 0.9.1.0 on .NET 2.0.0.0
     Located at:  C:/ruby/IronRuby-091/bin
    >pik add C:\ruby\jruby-1.4.0RC1\bin
    Adding:  140: jruby 1.4.0RC1 (ruby 1.8.7 patchlevel 174) (2009-09-30 80c263b)
 (Java HotSpot(TM) Client VM 1.6.0_14) [x86-java]
     Located at:  C:/ruby/jruby-1.4.0RC1/bin
----------------------------------------------------------------------------
Successfully installed pik-0.2.8
1 gem installed
Installing ri documentation for pik-0.2.8...
Installing RDoc documentation for pik-0.2.8...

Command is executed in the new Command Prompt where the only Ruby available, at the moment, is 2.2.1, therefore gem will be installed in it. Let's check that:

C:\>gem list

*** LOCAL GEMS ***

bigdecimal (1.1.0)
io-console (0.3)
json (1.5.5)
minitest (2.5.1)
pik (0.2.8)
rake (0.9.2.2)
rdoc (3.9.5)

As you can see Pik is available in the list of gems currently available to the default Ruby. If you wonder where other gems come from when we didn't install them they are bundled with Ruby and are available with Ruby installation.

It is time to finalize Pik installation. Comments displayed after gem is installed are self-explanatory but let's analyze them. We are not upgrading Pik so we can skip the first one. The second one states that we must use pik_install command in order to install Pik, and we should pass it, as argument, some directory that is in the path. It is recommended to avoid our ruby\bin directory.

Currently our path contains directories added during Windows installation, directory where Git is installed and, finally, bin directory of our Ruby 2.2.1 version. Since I like to keep things clear we will create new directory which we will use when we launch pik_install command. We'll follow Pik's suggestion and install it in previously created C:\tools directory.

C:\>pik_install C:\tools
Thank you for using pik.

mkdir -p C:\tools
mkdir -p C:\Users\bosko/.pik
Installing to C:\tools

cp c:/Ruby/192/lib/ruby/gems/1.9.1/gems/pik-0.2.8/tools/pik_runner.exe C:\tools
cp c:/Ruby/192/lib/ruby/gems/1.9.1/gems/pik-0.2.8/tools/pik.bat C:\tools
cp c:/Ruby/192/lib/ruby/gems/1.9.1/gems/pik-0.2.8/tools/pik.ps1 C:\tools

creating C:\Users\bosko/.pik/.pikrc

pik is installed

if you want to use pik with git bash, add the following line to your ~/.bashrc:
  [[ -s $USERPROFILE/.pik/.pikrc ]] && source $USERPROFILE/.pik/.pikrc

As a good behaved tool, Pik displayed everything it did during installation. First it created directory where it installed it's files, C:\tools in our case. If directory already exists Pik will do nothing. Next, it creates .pik directory in C:\Users\bosko. But why does Pik chose this directory?

On the Unix-like systems all user specific files and directories are kept in user's home directory which is usually located at the /home/<user_name> path and this value is stored in HOME system variable. On Windows, value that is used is stored in the USERPROFILE variable and on my system its value is C:\Users\bosko. Pik stores its data within .pik directory in the user's home folder. That it a reason why it created .pik folder in it.

Further, it installed necessary scripts in the C:\tools folder by copying them from gem folder. At the end, Pik created .pikrc file in the .pik folder. Don't be surprised by the name of the folder and the path. Naming convention on Unix-like systems is to hide them by prefixing names with a dot. Actually files and directories which have names that start with a dot are not actually hidden but are not displayed in any tool unless specifically requested. Pik follows this convention so it uses .pik and .pikrc for names of its folder and file within it.

Notice last comment printed out by pik_install script. It tells us that if we want to use Pik from Git bash shell we should add line:

[[ -s $USERPROFILE/.pik/.pikrc ]] && source $USERPROFILE/.pik/.pikrc

to ~/.bashrc file. Again tilda character is used on Unix-like systems to denote user's home folder. As you might already guess it is C:\Users\bosko on my system. Let's retain for a while on the .pikrc file. In the version 0.2.8 it contains following code:

#!/bin/sh
pik_path=/c/tools

function pik  {
  $pik_path/pik_runner.exe pik.sh $@
  [[ -s $USERPROFILE/.pik/pik.sh ]] && source $USERPROFILE/.pik/pik.sh
}

The first line is the shebang line we mentioned earlier. Value /bin/sh means that the rest of the script should be executed in the shell. After that, there is a definition for pik_path variable and then a code that looks like, and is, function definition. As comment printed out at the end of execution of pik_install script indicates, that this script is used for Git bash shell. Since Git uses Linux-like shell called Bash, it inherits almost all functionalities from Linux system. In Windows Command Prompt invoking pik command means executing pik.bat, batch file, stored in the C:\tools directory. On the other hand Git bash shell doesn't know how to start pik command. Open new ConEmu window and type in the Git bash tab:

$ pik
sh.exe: pik: command not found

Therefore in order to use Pik in this shell we must execute it in such a way that pik function becomes available in it. You can do it by executing following statement:

source $HOME/.pik/.pikrc

which tells Bash shell to apply changes from .pikrc configuration file. After that you can try Pik in the Git's Bash shell too:

$ pik -V
pik 0.2.8

If you do not want to “source” this configuration file every time you open Git Bash you should create .bashrc file in your home folder and put

[[ -s $USERPROFILE/.pik/.pikrc ]] && source $USERPROFILE/.pik/.pikrc

in it. Go ahead and try it. Close current Git tab in ConEmu, create .bashrc file and then open Git tab again. You will see that Pik command is available.

Up to now we haven't actually tried Pik at all. We have only checked its availability in the Git Bash shell and its version. It is time to execute Pik for the first time:

c:\>pik
** Adding:  22: ruby p85 (2015-02-26 revision 49769) [i386-mingw32]
 Located at:  C:\Ruby\193\bin
Usage: pik command [options]

To get help with a command

  pik help (command)

To list all commands and descriptions:

  pik help commands

    -V, --version                    Pik version
    -d, --debug                      Outputs debug information

Amazing! Pik somehow found our installed Ruby and added it to its list of available Rubies. The question is how did Pik know what Ruby we have. The answer is, again, Windows registry. If you open Registry Editor and navigate to HKEY_CURRENT_USER\Software\RubyInstaller key you will see that it has MRI sub-key, and that's the place where it finds all necessary data about installed Ruby versions. If you are curious what data is stored there click on the 2.2.1 sub-key within MRI and you will see that it holds information about platform Ruby is built on, installation date, full path to the directory where Ruby is installed as well as a patch level. And that's all Pik has to know to add Ruby version to itself.

Now go back to .pik folder in your home directory. You will see additional file created there, named config.yml. Extension .yml denotes it is YAML file. YAML (YAML Ain't Markup Language) is common format for serializing data in Ruby world. We will not dwell on it. If you are not already familiar with YAML you can check the official YAML Web Site. Let's go further and open config.yml in your favorite editor.

---
"22: ruby 2.2.1p85 (2015-02-26 revision 49769) [i386-mingw32]":
  :path: !ruby/object:Pathname
    path: C:/Ruby/193/bin
--- {}

As you can see Pik has stored all data it needs about our Ruby in that configuration file. We should start exploring Pik features at this point. Fire up ConEmu and in the default tab check the list of all Ruby versions Pik is aware of by issuing pik list or pik ls command:

C:\>pik ls
* 22: ruby 2.2.1p85 (2015-02-26 revision 49769) [i386-mingw32]

As expected, at the moment, Pik is aware only of our installed Ruby 2.2.1 version and it is marked as a default one, in the list that is printed out, by the asterisk (*) sign at the beginning of the line.

More then just listing installed Ruby versions, Pik is capable of showing all Ruby versions that can be installed through it.

c:\>pik ls -r
---
DevKit:
  3.4.5r3: http://rubyforge.org/frs/download.php/66888/devkit-3.4.5r3-20091110.7z
IronRuby:
  0.3.0: http://rubyforge.org/frs/download.php/53552/ironruby-0.3.0.zip
  0.5.0: http://rubyforge.org/frs/download.php/57126/ironruby-0.5.0.zip
  0.6.0: http://rubyforge.org/frs/download.php/59717/ironruby-0.6.0.zip
  0.9.0: http://rubyforge.org/frs/download.php/61382/ironruby-0.9.0.zip
  0.9.1: http://rubyforge.org/frs/download.php/64504/ironruby-0.9.1.zip
  0.9.2: http://rubyforge.org/frs/download.php/66606/ironruby-0.9.2.zip
  "1.0": http://rubyforge.org/frs/download.php/70179/ironruby-1.0.zip
  1.0-rc1: http://rubyforge.org/frs/download.php/67955/ironruby-1.0-rc1.zip
  1.0-rc2: http://rubyforge.org/frs/download.php/69180/ironruby-1.0-rc2.zip
  1.0.0rc: http://rubyforge.org/frs/download.php/69853/ironruby-1.0.0rc.zip
  1.0rc3: http://rubyforge.org/frs/download.php/69665/ironruby-1.0rc3.zip
  1.0v4: http://rubyforge.org/frs/download.php/70181/ironruby-1.0v4.zip
JRuby:
  1.6.8: http://jruby.org.s3.amazonaws.com/downloads/1.6.8/jruby-bin-1.6.8.zip
  1.7.4: http://jruby.org.s3.amazonaws.com/downloads/1.7.4/jruby-bin-1.7.4.zip
Ruby:
  1.8.7-p302: http://rubyforge.org/frs/download.php/72087/ruby-1.8.7-p302-i386-mingw32.7z
  1.8.7-p330: http://rubyforge.org/frs/download.php/73720/ruby-1.8.7-p330-i386-mingw32.7z
  1.8.7-p334: http://rubyforge.org/frs/download.php/74296/ruby-1.8.7-p334-i386-mingw32.7z
  1.8.7-p352: http://rubyforge.org/frs/download.php/75108/ruby-1.8.7-p352-i386-mingw32.7z
  1.8.7-p357: http://rubyforge.org/frs/download.php/75680/ruby-1.8.7-p357-i386-mingw32.7z
  1.8.7-p358: http://rubyforge.org/frs/download.php/75852/ruby-1.8.7-p358-i386-mingw32.7z
  1.8.7-p370: http://rubyforge.org/frs/download.php/76278/ruby-1.8.7-p370-i386-mingw32.7z
  1.8.7-p371: http://rubyforge.org/frs/download.php/76525/ruby-1.8.7-p371-i386-mingw32.7z
  1.9.1-p429: http://rubyforge.org/frs/download.php/71496/ruby-1.9.1-p429-i386-mingw32.7z
  1.9.1-p430: http://rubyforge.org/frs/download.php/72076/ruby-1.9.1-p430-i386-mingw32.7z
  1.9.2-p0: http://rubyforge.org/frs/download.php/72160/ruby-1.9.2-p0-i386-mingw32.7z
  1.9.2-p136: http://rubyforge.org/frs/download.php/73723/ruby-1.9.2-p136-i386-mingw32.7z
  1.9.2-p180: http://rubyforge.org/frs/download.php/74299/ruby-1.9.2-p180-i386-mingw32.7z
  1.9.2-p290: http://rubyforge.org/frs/download.php/75128/ruby-1.9.2-p290-i386-mingw32.7z
  1.9.3-p0: http://rubyforge.org/frs/download.php/75466/ruby-1.9.3-p0-i386-mingw32.7z
  1.9.3-p125: http://rubyforge.org/frs/download.php/75849/ruby-1.9.3-p125-i386-mingw32.7z
  1.9.3-p194: http://rubyforge.org/frs/download.php/76055/ruby-1.9.3-p194-i386-mingw32.7z
  1.9.3-p286: http://rubyforge.org/frs/download.php/76528/ruby-1.9.3-p286-i386-mingw32.7z
  1.9.3-p327: http://rubyforge.org/frs/download.php/76558/ruby-1.9.3-p327-i386-mingw32.7z
  1.9.3-p362: http://rubyforge.org/frs/download.php/76643/ruby-1.9.3-p362-i386-mingw32.7z
  1.9.3-p374: http://rubyforge.org/frs/download.php/76707/ruby-1.9.3-p374-i386-mingw32.7z
  1.9.3-p385: http://rubyforge.org/frs/download.php/76753/ruby-1.9.3-p385-i386-mingw32.7z
  1.9.3-p392: http://rubyforge.org/frs/download.php/76799/ruby-1.9.3-p392-i386-mingw32.7z
  1.9.3-p429: http://rubyforge.org/frs/download.php/76953/ruby-1.9.3-p429-i386-mingw32.7z
  2.0.0-p0: http://rubyforge.org/frs/download.php/76807/ruby-2.0.0-p0-i386-mingw32.7z
  2.0.0-p195: http://rubyforge.org/frs/download.php/76957/ruby-2.0.0-p195-i386-mingw32.7z

Omitting DevKit for the moment we can see that Pik can install various versions of IronRuby, JRuby and MRI. We'll use this Pik's feature and install IronRuby. This version is not so important for usage throughout the book, but is installed just to show one of many Pik's features. Before we start IronRuby installation we'll configure Pik to install new Ruby versions where we want to – in C:\Ruby.

pik config installs=C:\Ruby

Now we are ready to start IronRuby installation

c:\>pik install ironruby
** Downloading:  http://rubyforge.org/frs/download.php/70181/ironruby-1.0v4.zip
   to:  C:\Users\bosko\.pik\downloads\ironruby-1.0v4.zip

ironruby-1.0v4.zip: 100% |ooooooooooooooooooooo|   2.9MB/  2.9MB Time: 00:00:06

You need the 7zip utility to extract this file.
Would you like me to download it? (yes/no)  |yes|

** Downloading:  http://downloads.sourceforge.net/sevenzip/7za465.zip
   to:  C:\Users\bosko\.pik\downloads\7za465.zip

7za465.zip: 100% |ooooooooooooooooooooooooooooo| 352.5KB/352.5KB Time: 00:00:01

** Extracting:  C:\Users\bosko\.pik\downloads\ironruby-1.0v4.zip
   to:  C:\ruby\IronRuby-10v4
done

** Adding:  100: IronRuby 1.0.0.0 on .NET 4.0.30319.18052
 Located at:  C:\ruby\IronRuby-10v4\bin

This is simple and elegant way to install additional Ruby versions on your Windows system. Checking available Rubies now gives:

c:\>pik ls
  100: IronRuby 1.0.0.0 on .NET 4.0.30319.18052
* 22: ruby 2.2.1p85 (2015-02-26 revision 49769) [i386-mingw32]

You must be aware of the fact that installing Rubies through Pik doesn't run full installer, if it is available. Rather Pik downloads Zip or 7Zip archives, unpacks them and adds to its configuration file. This means that various configuration options that exist in installers like altering system path, files associations, menu shortcuts, etc. will not be set.

Additionally Pik has command for displaying more information about Ruby setup that is active in the current shell – pik info.

c:\>pik info
pik 0.2.8

ruby:
interpreter:  "ruby"
version:      "2.2.1"
date:         "2015-02-26"
platform:     "i386-mingw32"
patchlevel:   "545"
full_version: "ruby 2.2.1p85 (2015-02-26 revision 49769) [i386-mingw32]"

homes:
gem:          "C:\Users\bosko\.gem\ruby\1.9.1"
ruby:         "C:\Ruby\22"

binaries:
ruby:         "C:\Ruby\22\bin"
irb:          "C:\Ruby\22\bin\irb.bat"
gem:          "C:\Ruby\22\bin\gem.bat"
rake:         "C:\Ruby\22\bin\rake.bat"

environment:
GEM_HOME:     ""
HOME:         "C:/Users/bosko"
IRBRC:        ""
RUBYOPT:      ""

file associations:
.rb:           "C:\Ruby\22\bin\ruby.exe" "%1" %*
.rbw:          "C:\Ruby\22\bin\rubyw.exe" "%1" %*

Output of Pik's info command shows active interpreter version, full path where existing Ruby gems are and future ones will be installed, path to Ruby binaries, environment variables and file associations.

Until now we have added two Ruby versions to Pik. First was added automatically when we first ran Pik and the second one was installed through Pik itself. In the chapter Building Ruby we have built latest development version, copied it to C:\Ruby\22dev directory, but Pik is not aware of it. We will use new Pik command to add this version. Logically command is called add and it accepts path to the directory where ruby.exe is located.

C:\>pik add C:\Ruby\193dev\bin
** Adding:  193: ruby 1.9.3dev (2013-09-09 trunk 42897) [i386-mingw32]
 Located at:  C:\Ruby\193dev\bin

In a similar way we can add any Ruby version available on the system that is not installed through RubyInstaller installer. If we download any of Ruby 1.8 archives from RubyInstaller's download page all we have to do is to unpack archive to some directory and use pik add command to add it to the list of versions that Pik can handle. Installing Ruby 1.8.7p330 is left for your exercise.

At the beginning of this chapter we said Pik's main purpose is to manage various Ruby versions. At any time you can change active Ruby version using Pik's switch command

c:\>pik sw 22
c:\>ruby -v
ruby 22: ruby 2.2.1p85 (2015-02-26 revision 49769) [i386-mingw32]