Category Archives: Ruby

RubyConf Taiwan 2012 籌辦祕辛與心得

這篇拖稿超久,堆在草稿大半年了,真是不好意思。跟往年 2010, 2011 一樣,還是要留個籌辦心得紀錄。聽眾版心得文可以看會後記錄

該怎麼描述這次的 RubyConf Taiwan 2012 呢,應該是超乎預期的成功吧。大體來說,除了 wifi 搞砸了之外,其他似乎都讓大家還蠻滿意的,特別是議程和食物 :)

Continue reading RubyConf Taiwan 2012 籌辦祕辛與心得

如何使用 pry 作為 Ruby debugger 進行除錯

很奇怪,ruby-debug 或是 ruby-debug19 常常有版本問題,甚至還有 fork 版本 debugger。都不知道哪個沒問題。

最近發現其實 pry gem 就可以作為 debugger 之用,本來還以為只是漂亮的 irb 取代品,沒想到功能這麼強大。作為 debugger 使用的方法很簡單,只要在程式裡加上:

binding.pry

這樣就會設下中斷點。例如在 rails console 或 rails server 中,就會停下來讓你可以進行檢查和除錯。

可是如果你有用 Rails 伺服器,例如 pow 或 passenger 的話,因為不在同一個 process,所以必須加裝 pry-remote gem。它利用 DRb 技巧讓另一個 process 可以亂入進行除錯,用法改成:

binging.remote_pry

接著在 command line 輸入 pry-remote 就可以進行除錯了。非常簡單好用。

其他 pry plugins 也可以裝一下,包括:1. pry-stack_explorer 輸入 show-stack 的話可以看到 call stack 2. pry-debugger 可以加上 step, next, finish 和 continue 的控制

A brief introduction to Vagrant – 原來 VirtualBox 可以這樣玩 投影片

這是今年在 OSDC.TW 演講的投影片。這次的內容相對簡單,就分享一個好用的工具給大家。會挑這個題目並不是因為自己對 Vagrant/Chef 非常熟很有研究(我們公司的 DevOps 當然比我厲害)。而是從一個 Rubyist 的角度想說 Ruby 社群裡有什麼好東西可以介紹給大家。畢竟 OSDC.TW 並不是以特定技術為主的研討會,來聽的人四面八方,所以從去年開始我就盡量考慮聽眾適合聽的程度,以後大概也會抱持這樣的想法吧。

Defensive Programming 防禦性程式設計

TL;DR 避免 Defensive Programming,愛用 Fail Fast 策略。

什麼是防禦性程式設計,在 Erlang’s Programming Rules and Conventions: Do not program “defensively” 裡這樣說明:

防禦性程式發生在程式設計師不相信系統的輸入(input)資料時。一般來說我們不需要測試輸入的資料來確保功能正常。系統中大部分程式碼應該可以假設輸入的資料是正確的,只有一小部分的程式需要真的檢查資料,這通常發生在資料第一次輸入的時候。只要資料在輸入進系統時被檢查過,那麼就應該可以假設它是正確的。

也就是說,當你不相信輸入時,你就得去檢查它。有經驗的程式設計師應該都非常熟悉這種作法,他的缺點在於這常是多餘的資源浪費,不但浪費效能,也增加程式碼維護的成本。好比說一個方法已經檢查過參數了,該方法又繼續把參數傳遞給別的方法時,可能又要再檢查一次。如果所有方法都要針對參數重複做嚴格的檢查,豈不是非常囉嗦。所以 Erlang’s Programming Rules 建議你在系統裡不要檢查,crash 與否的責任在於呼叫方,在於資料一開始輸入進系統的時候,而不是這個方法。

但是,總是有需要檢查的時候,特別是資料第一次輸入進系統的邊界情況,以應用程式來說,就是指使用者的輸入; 以 Library 來說,就是指 Public API。這時候有兩種策略來對付不乖的參數: 1. 修正或略過資料的錯誤(defensive, compensate) 2.丟出錯誤(offensive, fail fast)

讓我們來看看例子吧。以 Check Null 型的問題來說,有三種寫法:

# defensive programming
# 如果參數不符合條件,就略過或修正它
def resize_image(image)
    if image && image.is_a?(File) # 檢查 image 不是 nil,而且是 File
        # execute code
    else
        # ingore, nothing happened.
    end
end

# non-defensive programming
# 不檢查,錯了就讓他 crash 吧
def resize_image(image)
    # execute code
end

# offensive programming (fail fast)
# 檢查,有錯就馬上失敗讓你知道
def resize_image(image)     
  raise "The image is invalid" unless ( image && image.is_a?(File) )                
  # execute code        
end

Daniel Roop 在 Why Defensive Programming is Rubbish 認為 defensive 策略是一種 Hide the Problem Programming,我覺得講的很好。為什麼呼叫方會傳不對的值進來呢? 這是應該要去 trace 的問題,而不是把問題隱藏起來。長久以往下來,整個系統的不確定性就會越來越多,也就越來越難維護。

比較建議的作法是,如果是系統內可以相信呼叫方,那就不要檢查了。如果是邊界情況需要檢查,就做 offensive programming,也就是 “Fail Fast”,把條件限制當做一種契約,如果呼叫方(caller)不照規矩來,整個方法就會 fail 掉,這樣的好處是可以提早發現問題,並且在問題發生的第一時間就可以修好,讓整個系統朝向可以互相信任的方向成長。

再舉一個更惡名昭彰的 defensive programming 的例子:

# defensive programming
def run
    # execute code      
    rescue  # 救回所有例外
end

此例中,無論發生什麼錯誤,這個方法都會回傳 nil,非常安全不會 crash。但是即使 execute code 的地方打錯字也不會怎樣,徹底把問題隱藏起來了。

可是….

可是我們設計API的時候,特別是像 Ruby 這種動態語言,常常允許參數很有彈性耶? 例如

 # 參數 i 除了可以是數字之外,也可以是字串,例如 "123"
def process_integer(i)
    var = i.to_i # 轉數字
    # process var
end

# 參數 s 除了可以是字串之外,也可以是 Symbol 或數字等
def process_string(s)
    var = s.to_s # 轉字串
    # process var
end

# 參數 a 除了可以是陣列之外,也可以只傳一個元素或nil
def process_array(a)
    arr = Array(a) # 讓 arr 一定是陣列
    arr.each do |x|
        # process x
    end
end

好吧,這種算是”有意 (intentional)”的 API 設計,我們擴大了參數的彈性,而且呼叫方(caller)明確知道可以這樣呼叫(最好搭配API文件)。在 Confident Ruby 這本書裡,提到了很多 Ruby 適用的技巧。

Contract by Design

另一種解決檢查參數前置性條件的根本方法,叫做”契約式設計 Contract by Design”,也就是在語言層面去宣告前置條件。不過,大部分的程式語言都沒有內建,Ruby 是有一些第三方套件(利用Ruby的meta-programming特性來做),不過用的人就不多了。

結論

  1. 在系統邊界內,盡量相信輸入,不做檢查
  2. 如果需要檢查,採用有錯誤就中斷掉的方式(Fail-fast),避免 defensive programming 把問題藏起來

後話

另一個會讓整個系統充滿防禦性風格的情況,可能發生在團隊感情不好的環境。為什麼呢? 因為溝通不良,加上沒有適當的規範,所以寫code的時候只好處處防著別人,為了怕 crash 要麻煩別人(caller),只好額外費工加一大堆多餘的條件檢查和處理,把問題都埋起來好讓方法不會crash…

參考資料

註: 有些人認為 “fail fast” 也算是 defensive programming 的一招。我這裡採用對比的 offensive programming 說法。

機會難得之 Matz 在台見面會

因為昨天才敲定這個行程,所以宣傳慢了。目前還有名額,不限有報名 conference 朋友,機會難得。報名請前往 Registrano

來台參加 RubyConf Taiwan 2012 的 Matz (Ruby 程式語言發明人) 和 Koichi Sasada (YARV作者) 將和大家進行粉絲見面會。本活動自由報名,不限參與 RubyConf Taiwan 的聽眾,免費。

時間: 2012/12/6 18:30 ~ 21:30 (Matz 約 19:00 到場)

議程: 現場 QA,自由聊天

地點: 伯朗咖啡天母店 – 台北市天母東路68號5F (新光三越A棟5F) Google Map

◎公車: 啟智學校:285、646、645、606、616、685 ◎捷運:芝山站出口,轉搭新光三越接駁車

報名請前往 Registrano 系統

RubyConf Taiwan 2012 CFP&CFS

To English readers:

Hi, I’m the organizer of RubyConf Taiwan. Please see Call For Presentations and Call For Sponsors for more information.

Our invited speaker this year includes Yukihiro Matsumoto (Ruby creator), Koichi Sasada (Ruby 1.9 VM YARV author), Akira Matsuda (kaminari author, Rails Contributor, Ruby core team), Koan-Sin Tan (famous open source developer in Taiwan).

今年的 RubyConf Taiwan 已經開始籌辦一陣子了,才想到自己部落格還沒有貼這則消息… XD 日期是 2012/12/7-8,跟去年一樣是週五周六,場地則改在天母會議中心

詳細的公告,請詳見 Ruby Taiwan 部落格的 Call For PresentationsCall For Sponsors。這週末就截止徵稿囉,要投要快。

大家期待的邀請講者部分,名單有:

* Yukihiro Matsumoto (matz): Ruby 程式語言發明人
* Koichi Sasada: Ruby 1.9 VM (YARV)的作者
* Akira Matsuda: kaminari 的作者、日本最活躍的 Rails Contributor、Ruby core team
* Koan-Sin Tan (Freedom): 台灣開放源碼社群的資深前輩,會來跟我們談跟 Ruby 淵源很深的 Smalltalk

最快九月底,最慢下個月就會開放報名,敬請期待。