高見龍

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

CoffeeScript @ 20th Ruby Tuesday

感謝ihower的邀請,這是今天我在Ruby Tuesday上用到的投影片。雖然主題跟在PHPConf Taiwan的一樣,但因為聽眾們大多對Ruby已經很熟悉,而且CoffeeScript在新版的Rails已經是內建的功能了,沒辦法用同一招繼續騙吃騙喝(同一招不能對聖鬥士用兩次啊),所以有再特別為Rubyist們調整了內容。 image View on Speaker Deck | View on Slideshare
Download PDF | Download Screencast

因為Speaker Deck沒辦法全螢幕播放,所以另外也上傳了一份到Slideshare。不過傳到Slideshare的字型好像會跑掉,變超醜..

部份內容是screencast影片,所以沒辦法在slideshare線上看到,有興趣看的人可以另外下載。依舊沒有太多有趣的哏,希望大家會喜歡。

幫你的類別增加功能

CoffeeScript很有趣也很好寫,雖然它提供了許多的語法糖衣(Syntactic sugar),讓你用短短的幾行程式碼就能換來原來你在JavaScript要做的事。但說白了它本質上還是JavaScript,JavaScript做不到的事,用CoffeeScript一樣也做不到。

不過有些很常做的工作,例字串變換大小寫,雖然JavaScript的字串本身已經有toUpperCase()以及toLowerCase()的功能,但我已經寫習慣Ruby的upcasedowncase,要記那麼多個名字實在很辛苦,每次都得去google一才下知道要怎麼寫;又如果我想把某個字串重複10次,在Ruby有*可以用,但在JavaScript似乎沒有原生的function可以用.. 有沒有辦法也自己做一些讓自己順手的小工具,讓自己寫程式的時候順利一些呢?

如果你曾經寫過Python或Ruby,就會知道其實它們的類別裡的方法是可以很簡單的被打開再新增或覆寫(open class),在JavaScript/CoffeeScript也有類似的做法,在CoffeeScript就是使用::來幫原來已經定義好的類別再增加功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String::repeat = (n) ->
  Array(n + 1).join @

String::downcase = ->
  @toLowerCase()

String::upcase = ->
  @toUpperCase()

String::find = (str) ->
  @indexOf str

String::has = (str) ->
  (@indexOf str) > 0

編譯出來的JavaScript長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
String.prototype.repeat = function(n) {
  return Array(n + 1).join(this);
};
String.prototype.downcase = function() {
  return this.toLowerCase();
};
String.prototype.upcase = function() {
  return this.toUpperCase();
};
String.prototype.find = function(str) {
  return this.indexOf(str);
};
String.prototype.has = function(str) {
  return (this.indexOf(str)) > 0;
};

其實那個::,用的就是prototype啦,這樣一來,你就可以寫出這樣的程式:

1
2
3
4
console.log "a".repeat 10                     # aaaaaaaaaa
console.log "this is a book".upper()          # THIS IS A BOOK
console.log "this is a book".find "book"      # 10
console.log "this is a book".has "book"       # true

程式看起來乾淨多了,也習慣多了,心情也會比較好一些。

PS: 不知道會不會有善心人士會把Python或Ruby一些好用的功能也port一份過來? 還是早就已經有了.. :)

CoffeeScript 裡的全域變數

在CoffeeScript裡,即使是一個空的.coffee檔,它也會被編譯成這樣:

1
2
3
(function() {

}).call(this);

你在.coffee寫的任何變數或function,都是被宣告為區域變數包在裡面,只會在裡面有作用而已。這其實是好事,因為這樣一來與外界隔離,你寫的東西不會去污染到別人寫的,相對的別人寫的東西也不會去弄髒你寫的。但假設因為某些不可抗力,硬是要把變數或function弄成global好讓所有頁面都可以直接使用,也不是沒辦法,你可以直接把變數或function掛在window物件底下:

1
2
window.logout = ->
  alert "You've already logout!" if confirm "Are you sure to logout?"

或是這樣做:

1
2
3
root = exports ? this
root.logout = ->
  alert "You've already logout!" if confirm "Are you sure to logout?"

第二種寫法看起來比較麻煩,但比較通用。exports是在Node.js裡定義的物件,上面第1行的意思是指會先檢查exports是不是已經有定義了,如果這段程式是在Node.js裡執行的話,因為exports是存在的,所以會定義一個root變數並指向它;如果是在一般的網頁上執行,因為沒有exports,所以會定義一個root指向this,也就是window物件 ,所以這樣的寫法的好處就是在網頁或是Node.js都行得通。

這樣一來,你在頁面上的按鈕就可以這樣寫:

1
<input type="button" value="logout" id="Logout" onclick="logout();" />

但是,如果可以的話,建議還是儘量避免用這種global變數的方式來寫程式,現在的JavaScript越寫越複雜,沒人知道你寫的東西會不會剛好就去衝到誰家寫的library,或是衝到你自己或同事寫的程式碼 :)

CoffeeScript -> 與 => 的差別

不知道各位在用CoffeeScript的 -> (dash rocket)在寫function的時候,有沒有發現有另一個長得跟它有點像,但比較胖一點的 => (fat arrow),在CoffeeScript的source code裡有一段這樣的簡短說明:

CoffeeScript has two different symbols for functions. -> is for ordinary functions, and => is for functions bound to the current value of this.

在CoffeeScript裡,用 -> 會產生一個Anonymous function,例如:

1
2
3
4
skinny = (name) ->
  "hello, I'm skinny #{name}"

console.log(skinny "eddie")

編譯出來的結果是:

1
2
3
4
5
var skinny;
skinny = function(name) {
  return "hello, I'm skinny " + name;
};
console.log(skinny("eddie"));

執行結果是:

hello, I'm skinny eddie

=> 也是會產生Anonymous function:

1
2
3
4
fatty = (name) =>
  "hello, I'm fatty #{name}"

console.log(fatty "eddie")

執行結果是:

hello, I'm fatty eddie

就以程式的執行結果來看,用 -> 跟用 => 似乎沒什麼兩樣,但看一下用 => 編譯出來的Javascript程式碼:

1
2
3
4
5
var fatty;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
fatty = __bind(function(name) {
  return "hello, I'm fatty " + name;
}, this);

編出了看起來有點複雜的東東西,這串程式碼裡面看起來比較複雜的大概就是那個fn.apply(),我們先來看看那是做什麼的。

function.call() v.s function.apply()

直接來看段程式碼:

1
2
3
4
5
6
var who = "Eddie";
function doctor(){
  alert(who);
}

doctor();

這光用我們的人腦執行就猜得到結果是Eddie,如果我們再改一下:

1
2
3
4
5
6
var who = "Eddie";
function doctor(){
  alert(this.who);
}

doctor();

多了個this,但執行的結果還是一樣。這裡的this.who其實指的就是整個global的那個who,所以印出來結果也一樣。再來加一點變化:

1
2
3
4
5
6
7
8
9
var who = "Eddie";
var time_lord = { who: "the real doctor" }
function doctor(){
  alert(this);
  alert(this.who);
}

doctor();
doctor.call(time_lord);

其實this指的對像會隨著不同的情境而有所變化。例如我們平常可能會說:「媽! 我在這裡」,如果你是在公司說這句話,「這裡」表示的是公司;如果你是在家裡說這句話,「這裡」表示的是家裡。

再回來看程式碼,在上面程式碼的第8行的呼叫指的this是整個global,在這邊就是window,而第9行指的this則是透過call()方法傳進去的那個物件,程式碼所在的情境變了,所以當第5行要印出this.who的時候,會印出the real doctor

其實通常沒特別指定的變數或function呼叫,前面的this是可以省略的,所以上面的第8行的doctor()也可以改寫成:

1
doctor.call(this);

執行結果是一樣的。再來繼續再加點變化,call()還可以傳更多的參數進去:

1
2
3
4
5
6
7
8
9
10
var who = "Eddie";
var time_lord = { who: "the real doctor" }

function doctor(real_name){
  alert(this.who);
  alert("His real name is : " + real_name);
}

doctor("Aquarianboy");
doctor.call(time_lord, "No one knows!");

另外,在JavaScript裡,還有一個跟call()有點像的function叫做apply(),這兩個的功能差不多,最大的差別是apply()傳的第二個參數是陣列,而call()的參數則是一個一個傳進去,並用逗號分開:

1
2
3
4
5
6
7
8
9
10
var who = "Eddie";
var time_lord = { who: "the real doctor" }

function doctor(real_name){
  alert(this.who);
  alert("His real name is : " + real_name);
}

doctor("Aquarianboy");
doctor.apply(time_lord, ["No one knows!"]);

大概知道在JavaScript裡call()apply()的意思之後,再回來看我們剛剛那段CoffeeScript編譯出來的程式碼:

1
2
3
4
5
var fatty;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
fatty = __bind(function(name) {
  return "hello, I'm fatty " + name;
}, this);

function一層包一層,return再return,不過大意就是把this當做參數傳進fatty這個function裡的意思。

什麼時候會用到

有點離題了..再回來看看CoffeeScript,原來,=>在定義function的同時,還會把this也同時給綁到這個function,雖然this會隨著所在的情境而有所改變,但=>this給綁進來之後,可以確保在指向this的時候不會指錯人。

為什麼要這樣做? 什麼時候會用到它? 大多是被拿來當做event callback的時候會用得上,來看個範例:

1
2
3
4
5
6
7
8
class Student
  constructor: (@username) ->
  say_hello: =>
    alert "Hello, my name is #{@username}"

$ ->
  eddie = new Student("Eddie")
  $("#student_1").click(eddie.say_hello)

我把eddie.say_hello傳給click做為callback,意思就是當頁面上某個id叫做student_1的元素被點擊之後,就會去執行它,而執行結果是:

Hello, my name is Eddie

但是如果你把第3行程式碼的=>換成->的話,執行結果會變成:

Hello, my name is undefined

為什麼結果是undefined? 前面提到,this會隨著出現在不同的地方而會有不同的意思,如果你是用->的話,它的this是指向你剛剛點擊的那顆按鈕,而當然那顆按鈕上面不會有username這個屬性,所以印出undefined;如果是用=>的話,它會透過fn.apply()this給包進來,所以say_hello裡的this,指的就是它自己這個物件,也就是Student類別產生出來的instance,印出來的結果就是你要的了。

細節可以再看看它編譯出來的JavaScript code,大概就可以知道->=>各別做什麼不同的事。

以上,供大家參考,如果有哪邊寫錯再請跟我說 :)

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

參考資料