RVM - Ruby enVironment(Version) Manager

前言

我相信很多人學習Ruby是因為Ruby on Rails的緣故,但 Rails 在改版的速度很快,而且有時候改版的幅度不小,例如從以前的 2.x 版本跳到 3.x 版本,甚至有些小版號的改版幅度也不小。像是不久前 release 出來的 Rails 3.1.0,就跟 3.0.x 的架構就差滿多的。就算不談 Rails,光是主流的 Ruby 目前也有 1.8.x 跟 1.9.x 的分支,在功能上都有些差異。

這些新推出來的新玩具有的很好玩,不裝起來試一下就會覺得手很癢。你當然可以在你電腦裡安裝這些不同版本新玩具,但畢竟在自己工作的機器上試是有風險的,萬一把環境弄壞了還得花時間想辦法回復。以往我們可能會使用類似VirtualBox之類的軟體來模擬作業環境,玩壞了隨時都可以很快的還原或重建一個新的,不過即使這樣還是有點麻煩。

這時候你就需要RVM(Ruby enVironment(或 Version) Manager)了,有了它,你可以安心的在你的電腦裡同時安裝多個不同版本的 Ruby 而不會打架,例如標準的 MRI(Matz's Ruby Interpreter),或是 REE(Ruby Enterprise Edition),甚至是 JRuby、MacRuby 都沒問題,在 RVM 裡都可以都可以隨你高興的切換。RVM 裡每個版本的 Ruby 的 gem 也都是分開裝的,甚至在同一個 Ruby 版本底下也可以建立不同的 gemset,彼此獨立互不影響。如果哪天覺得膩了、不想玩了,可以用 rvm remove 指令移掉指定的版本;萬一哪天整個不想要玩了,因為 RVM 是把檔案安裝在 /home 的個人帳號資料夾底下,所以也不會去影響到系統的設定,不要的時候就整個 ~/.rvm 資料夾砍掉就行了,不會影響原來系統的設定。也就是因為 RVM 是安裝在你的個人帳號底下,所以你在安裝過程中是不需要 root 權限的。

安裝

以下提到的各項安裝指令,會以 Ubuntu 10.10 做為示範。

安裝 RVM 會需要兩項工具 curlgit,如果沒有的話請用 apt-get install curl 以及 apt-get install git 指令把這兩個裝起來(安裝這兩個工具你可能需要有系統的 root 權限)。這兩項工具安裝完成後,請打開你的終端機,貼上這行:

bash <<(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

它就會開始去下載 RVM 的安裝程式回來並且自動開始進行安裝。完成後還需要做一下簡單的設定,看你用的 shell 是哪一套而要修改不同的檔案,假設你用的是 ubuntu 預設的 bash shell 的話,你可以直接在終端機裡貼下面這行:

echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile

更詳細的安裝說明可以參考官網的安裝說明

使用

接著我們來看一些在 RVM 裡常用的指令,rvm list known 會列出目前有哪些可以安裝的列表:

> rvm list known

# MRI Rubies
[ruby-]1.8.6[-p420]
[ruby-]1.8.6-head
[ruby-]1.8.7[-p352]
[ruby-]1.8.7-head
[ruby-]1.9.1-p378
[ruby-]1.9.1[-p431]
[ruby-]1.9.1-head
[ruby-]1.9.2-p180
[ruby-]1.9.2[-p290]
[ruby-]1.9.2-head
[ruby-]1.9.3-preview1
[ruby-]1.9.3[-rc1]
[ruby-]1.9.3-head
ruby-head

# GoRuby
goruby

# JRuby
jruby-1.2.0
jruby-1.3.1
jruby-1.4.0
jruby-1.6.1
jruby-1.6.2
jruby-1.6.3
jruby[-1.6.4]
jruby-head

# Rubinius
rbx-1.0.1
rbx-1.1.1
rbx-1.2.3
rbx-1.2.4
rbx[-head]
rbx-2.0.0pre

# Ruby Enterprise Edition
ree-1.8.6
ree[-1.8.7][-2011.03]
ree-1.8.6-head
ree-1.8.7-head

# Kiji
kiji

# MagLev
maglev[-26852]
maglev-head

# Mac OS X Snow Leopard Only
macruby[-0.10]
macruby-nightly
macruby-head

# IronRuby -- Not implemented yet.
ironruby-0.9.3
ironruby-1.0-rc2
ironruby-head

長長的一大串,幾乎目前常見的 Ruby Interpreter 都有了。列表裡的中括號表示那些是可以省略的,所以如果用 rvm install 來安裝的時候輸入:

> rvm install 1.8.7

會自動找 [ruby-]1.8.7[-p352] 這個版本的 Ruby 來安裝。前面我們提過可以一次安裝多個不同的版本,所以你也可以再裝個 1.9.2 的版本:

> rvm install 1.9.2

如果你對最新的 Ruby 1.9.3 版本有興趣,雖然目前還沒正式 release,也可以試著裝一份來試一下它的新功能。再來,我們看看目前已經安裝哪些版本:

> rvm list

rvm rubies

        ruby-1.9.2-p290 [ i686 ]
        ruby-1.8.7-p352 [ i686 ]

可以看到我電腦上目前裝了 2 個版本的 Ruby(1.8.7 跟 1.9.2),這時候我們來看看 Ruby 的版本:

> ruby -v
ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]

看到 patchlevel 只有 299,表示這個不是我們剛才安裝的版本,這是系統裡原來就已經裝好的。如果要切換到我們剛才安裝的 Ruby 1.9.2 版本:

> rvm use 1.9.2
Using /home/eddie/.rvm/gems/ruby-1.9.2-p290

想少打幾個字的話,use 也可以省略:

> rvm 1.9.2

再來看一下 Ruby 的版本:

> ruby -v
ruby 1.9.2p290 (2011-07-09 revision 32553) [i686-linux]

已經切換到 Ruby 1.9.2 了! 不過有個小問題是,RVM 會在下次終端機視窗重開的時候回到預設值(就是系統內建的 Ruby 版本),所以如果你希望每次開終端機視窗的時候都會切到 Ruby 1.9.2 的話:

> rvm 1.9.2 --default

這樣你每次開終端機視窗就會自動幫你切換到 1.9.2 版了。如果你想切回到原來系統內建的版本:

> rvm system

不要了,想砍掉它?

> rvm remove 1.9.2

就可以把指定的版本移除了,相當便利。如果你整個 RVM 都不想要了,只要把個人帳號底下的 .rvm 資料夾整個移除,再把 .bash_profile 的 RVM 相關設定改回來,就會整個清潔溜溜了。

運作原理

你也許會好奇為什麼 RVM 可以這麼神奇的切換 Ruby 的環境,我們來看一下系統的預設 PATH:

> echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/eddie/.gem/ruby/1.8/bin:/usr/local/lib/python2.6/dist-packages/django/bin:/home/eddie/.rvm/bin:/home/eddie/.rvm/bin

> which ruby
/usr/bin/ruby

> ruby -v
ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]

(以上內容是我自己電腦裡的設定,可能跟各位的會有些不同)

再來用 RVM 切換到 1.9.2:

> rvm 1.9.2
Using /home/eddie/.rvm/gems/ruby-1.9.2-p290

> echo $PATH
/home/eddie/.rvm/gems/ruby-1.9.2-p290/bin:/home/eddie/.rvm/gems/ruby-1.9.2-p290@global/bin:/home/eddie/.rvm/rubies/ruby-1.9.2-p290/bin:/home/eddie/.rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/eddie/.gem/ruby/1.8/bin:/usr/local/lib/python2.6/dist-packages/django/bin

> which ruby
/home/eddie/.rvm/rubies/ruby-1.9.2-p290/bin/ruby

> ruby -v
ruby 1.9.2p290 (2011-07-09 revision 32553) [i686-linux]

事實上,RVM 是把不同版本的 Ruby 安裝在你的個人帳號底下的 .rvm 資料夾,在你切換不同版本的 Ruby 的時候,會幫你把系統預設的 PATH 的最前面加上這個 .rvm 的資料夾,當你在終端機底下輸入 ruby 指令的時候,系統原本的 /usr/bin/ruby 因為在 PATH 的比較後面,所以感覺會暫時失效而使用 RVM 版本的 Ruby。你可以試著用 rvm info 來看看 RVM 幫你做了哪些設定。

Gem

在 RVM 裡,不同版本的 Ruby 的 gem 是各別獨立的,來做個簡單的實驗,例如我們先切換到 1.9.2 底下:

> gem list

*** LOCAL GEMS ***

bundler (1.0.0.rc.5)
json (1.6.1 ruby)
rake (0.9.2 ruby)
rdoc (3.10 ruby)

再來我們切換到 1.8.7,然後安裝 Rails:

> rvm 1.8.7
> gem install rails

然後你就可以看到一大票的安裝訊息,然後我們看看 gem 的安裝情形:

> gem list

*** LOCAL GEMS ***

actionmailer (3.1.1)
actionpack (3.1.1)
activemodel (3.1.1)
activerecord (3.1.1)
.. 略 ..
rails (3.1.1)
railties (3.1.1)
rake (0.9.2 ruby)
rdoc (3.10 ruby)
sprockets (2.0.2)
thor (0.14.6)
tilt (1.3.3)
treetop (1.4.10)
tzinfo (0.3.30)

安裝了最新版本的 Rails 3.1.1,這時候切回去 Ruby 1.9.2 再看原來的 gem list 的話,還是原來的樣子,表示 gem 是跟著不同版本的 Ruby 獨立安裝的。

要注意的是我們在 RVM 安裝的任何 Ruby 版本都跟系統的 Ruby 無關,即使版號相同也是不同的 Ruby,gem 也不會安裝到系統的 Ruby 裡,所以可以放心的玩。基本上你只要發現你在安裝 gem 的時候會需要 root 權限的時候,通常你用的就是系統版本的 Ruby 了。

Gemset

Ruby/Rails 的世界在進步很快,套件的版本會一直在變,所以很常看到這個狀況:

> gem list
.. 略 ..
rails (3.1.1, 3.1.0, 3.0.9, 3.0.7)
.. 略 ..

同一個套件就裝了二、三個以上的版本,看了實在有些討厭,而且也不知道會不會遇到版本衝突的問題。如果你跟我一樣有這方面的潔癖,你可以選擇把不用的版本移除掉,不過我會建議你用不同的 gemset 來管理。例如我想同時在 1.9.2 版本的 Ruby 底下同時安裝 Rails 2.3.9 跟 3.1.1 版本,來看看怎麼做。

先切換到 1.9.2 之後,建立一個準備要來裝 Rails 2.3.9 版的 gemset:

> rvm gemset create rails-2.3.9
'rails-2.3.9' gemset created (/home/eddie/.rvm/gems/ruby-1.9.2-p290@rails-2.3.9).

這邊不一定要跟我一樣用 rails-2.3.9,gemset 的名字你可以取自己喜歡的。再來建一個給 Rails 3.1.1 版的:

> rvm gemset create rails-3.1.1
'rails-3.1.1' gemset created (/home/eddie/.rvm/gems/ruby-1.9.2-p290@rails-3.1.1).

看一下目前的已經建立的 gemset:

> rvm gemset list

gemsets for ruby-1.9.2-p290 (found in /home/eddie/.rvm/gems/ruby-1.9.2-p290)
        global
        rails-2.3.9
        rails-3.1.1

你看到 2 個我們剛剛建好的 gemset,那個 global 是整個 Ruby 預設的 gemset,如果沒特別指定的話,gem 都是安裝到 global 裡面。再來我們切換到 rails-2.3.9:

  > rvm gemset use rails-2.3.9

來安裝一下 Rails 2.3.9 版

> gem install rails -v='2.3.9' --no-rdoc --no-ri
Fetching: activesupport-2.3.9.gem (100%)
Fetching: activerecord-2.3.9.gem (100%)
.. 略 ..
Successfully installed activeresource-2.3.9
Successfully installed rails-2.3.9
7 gems installed

安裝完成,再來切換到 rails-3.1.1:

> rvm gemset use rails-3.1.1

再來做一樣的動作,只是版號改成 3.1.1。兩個 gemset 都裝好之後來看看結果:

> rvm gemset use rails-2.3.9
> rails -v
Rails 2.3.9

> rvm gemset use rails-3.1.1
> rails -v
Rails 3.1.1

如果你要從別的版本的 Ruby 直接切換到指定的 gemset:

> rvm 1.9.2@rails-3.1.1

你可以隨你高興的建立、切換 gemset,每個 gemset 都是獨立的。如果玩膩了或玩爛了,想要把 rails-2.3.9 這個 gemset 清空的話:

> rvm gemset empty rails-2.3.9
WARN: Are you SURE you wish to remove the installed gems for gemset 'ruby-1.9.2-p290@global' (/home/eddie/.rvm/gems/ruby-1.9.2-p290@global)?
(anything other than 'yes' will cancel) > yes

清空只會把安裝的 gem 砍掉,gemset 的名字還會在;如果想把整個 gemset 刪掉:

> rvm gemset delete rails-2.3.9
WARN: Are you SURE you wish to remove the entire gemset directory 'rails-2.3.9' (/home/eddie/.rvm/gems/ruby-1.9.2-p290@rails-2.3.9)?
(anything other than 'yes' will cancel) > yes

各別專案設定

有時候我們會遇到專案 A 用的是 Ruby 1.8.7,專案 B 用的是 Ruby 1.9.2,雖然 RVM 可以很快的切換,但偶爾忘了換回來可能也會造成一些困擾。這時候你可以在該專案的資料夾底下放一個 .rvmrc 的檔案,只要簡單的一行設定:

rvm use 1.9.2@rails-3.1.1

如果後面沒特別指定的話,預設會使用 global 的 gemset。在你第一次進到該資料夾的時候會出現一個提示訊息:

> cd project1
==============================================================================
= NOTICE                                                                     =
==============================================================================
= RVM has encountered a new or modified .rvmrc file in the current directory =
= This is a shell script and therefore may contain any shell commands.       =
=                                                                            =
= Examine the contents of this file carefully to be sure the contents are    =
= safe before trusting it! ( Choose v[iew] below to view the contents )      =
==============================================================================
Do you wish to trust this .rvmrc file? (/tmp/project1/.rvmrc)
y[es], n[o], v[iew], c[ancel]> yes
Using /home/eddie/.rvm/gems/ruby-1.9.2-p290 with gemset rails-3.1.1

這樣當你下次再用 cd 指令進到該資料夾的時候,就會自動幫你切換成你指定的 Ruby + gemset 版本。

你可能會遇到的麻煩..

RVM 很方便、很好用,但之前在OSSF 工作坊開 Ruby 的課的時候也曾經吃過悶虧。RVM 在安裝的時候,有些特別的 package 的安裝需要另外處理,像 OpenSSL 就是其中一例。就算用系統的 apt-get install 來安裝也沒用,RVM 也是找不到而出現 LoadError。需要用以下面這個方式來安裝:

> rvm pkg install openssl
> rvm remove 1.9.2
> rvm install 1.9.2 --with-openssl-dir=$rvm_path/usr

除了 OpenSSL 之外,Readline 也有類似的狀況,如果你是用 RVM 在 Ubuntu 上練習寫 Rails 的時候特別容易遇到,這點還請多留意。更多詳細內容可參考這裡

結論

RVM 可以快速的在各個版本之間切換,每個版本跟 gemset 之間也都切得很乾淨,不會影響到原來系統設定,對我這種喜歡嚐鮮、試新玩具又怕把系統搞髒搞爛的人來說,真的是一大福音!

參考資料

工商服務

實體課程:Ruby on Rails 實戰課程
線上課程:五倍學院線上課程