高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

RVM - Ruby enVironment(Version) Manager

本篇文章已刊載於 OpenFoundry 電子報技術專欄,刊登之內容由專業的 OpenFoundry 團隊潤稿,此篇為原文。

前言

我相信很多人學習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之間也都切得很乾淨,不會影響到原來系統設定,對我這種喜歡嚐鮮、試新玩具又怕把系統搞髒搞爛的人來說,真的是一大福音!

參考資料

Comments