如何設計出漂亮的 Ruby APIs [演講摘要]

註:這篇是好幾個月前應 InfoQ China 的邀稿所寫,不過看起來是沒有派上用場 (大概是程式碼太多了)。對我來說,寫好的稿子沒公開出來太浪費了,所以就貼出來吧。

本文摘錄了我去年在 RubyConf China 2010 中的演講內容,包含了 Ruby APIs 的十個設計技巧以及範例程式,同時也介紹了 Ruby 的物件模組及元編程(Meta-programming)。完整的範例程式請搭配 投影片 服用。

閱讀全文〈如何設計出漂亮的 Ruby APIs [演講摘要]〉

Ruby Tuesday #15 開放報名

今年的最後一場,帶給大家與 Ruby/Rails 比較間接的主題:

* Felix & Mars 圖形資料庫 (neo4j) 在社群網路的應用 Graph Database in Social Network Application,會有圖形資料庫的簡介和 Ruby 程式範例。
* TaopaiC: Be nice to your Designers : Useful tips For Rails developers,會介紹一些 Rails 的前端技巧。

時間是 2010/12/28 週二晚上 PM7~9

地點依舊在果子咖啡,報名網頁在此

Service-Oriented Design and Implement with Rails3 投影片

這是昨晚在 Ruby Tuesday #14 演講的投影片。這次的題目 SOA 其實我在今年初講 Distributed Ruby and Rails 時也有涵蓋過,只是那時候還沒有給出實際的程式範例。這次搭配充滿彈性的 Rails3 有了 Web service 端和 Client side application 程式範例,相信讀者們可以具體的實作出來。

Ruby Tuesday #14 開放報名

要年底了,好久沒辦 Ruby Tuesday 了,該來辦個幾場熱鬧一下。

這一次的主題是 Service-Oriented Design and Implement with Ruby on Rails 3,將講解基本的 SOA 服務導向架構、SOA 設計有什麼優點,以及如何使用 Ruby on Rails 3 來進行實作。這場演講由小弟我 Solo 獨講,請多多指教。

時間是 2010/12/15(週三)晚上七點到九點。請注意這次是辦在週三,不是週二唷 XD

地點依舊在果子咖啡,報名網頁在此

OSDC.TW 2011 演講徵求

一年一度的台灣 Open Source 開發界盛事 OSDC.TW 又來了,日期是 2011/3/26~3/27。
目前正在徵求演講,詳見公告文章,徵稿日期到 1/15/2011。

這次我會幫忙張羅其中的 Ruby 議程,所以如果你有 Ruby 相關的議程想要分享,或是想講但苦於想不到題目,也可以直接跟我聯繫詢問

至於有沒有 RubyConf Taiwan 2011 呢? 這次就不會跟 OSDC.TW 合辦了,目前的計畫是獨立辦在明年的七月或八月。

深入Rails3: ActiveSupport 的 class_attribute

如果你對 Ruby Object Model 稍加認識,就會知道除了 class variable 和 instance variable 之外,還有一種變數叫做 class instance variable,之前我在研究時有撰文解釋過,讀者可以複習一下。

在 Rails3 ActiveSupport Core Extension 中,就有幾個方法是在處理這件事情,讓我們可以很方便地定義存取方法,讓我們來看看。

cattr_* 系列

Ruby 語言本身就有針對 instance variable 提供 attr_accessor, attr_reader, attr_writer 等方法,這些會建立 @ 開頭的實例變數並提供存取方法。而 ActiveSupport 的這個擴充則是針對 class variable 也提供類似的功能,它會建立 @@ 開頭的類別變數及提供存取方法。


class A
  cattr_accessor :x
end
  
class B < A
end
  
A.x = 1
A.x # => 1
B.x = 2
B.x # => 2
A.x # => 2 跟著改了

注意到整個繼承體系 A, B 都共用了 @@x,所以如果改了 B.x,那也會連動 A.x。很多時候,這不是我們要的,例如在 Rails 中所有 Model 都繼承自 ActiveRecord,於是會共用 class variable,如果要各自 Model 需要有自己的 class 屬性就不合用了。所以說認識 class instance variable 可以說是寫 ActiveRecord Plugin 的必備知識 (甚至也有人說 Ruby 的 class variable 設計錯誤,當初就應該把行為設計成 class instance variable 比較實用)

class_attribute

不像 class variable 整個繼承體系共用類別變數,class instance variable 是不同 class 分別獨立的,也就是類別 A 的 class instance variable 和 B < A 的 class instance variable 是獨立的。


class A
  @x = 1
end

class B < A
end

A.instance_eval { @x } # => 1
B.instance_eval { @x } # => nil 因為跟 A 的 @x 是獨立的,不會繼承下來

這個特性讓我們可以實作出真正實用的行為,也就是 “屬性可以繼承,但是如果有修改,不會影響到 parent class 的值”。這就是 ActiveSupport 的 class_attribute 提供的功能:


class A
  class_attribute :x
end
  
class B < A
end
  
A.x = 1
B.x # => 1 繼承自 A
B.x = 2
B.x # => 2
A.x # => 1 不變

最後的 A.x 還是保持本來的值不受影響。

不過使用上有個細節要注意:如果這個值是會變動的結構(物件),例如 Array 或 Hash,那麼 child class 第一次使用時就不適合用 in-place 類型的方法,例如:


A.x = []
B.x << :foo
A.x # => [:foo] 也跟著改了,不對啊啊啊!!
B.x # => [:foo]

要改成用 setter 類型的方法:


A.x = []
B.x += :foo # 第一次設定必須使用 setter 類型的方法
A.x # => [] 不變
B.x # => [:foo]

B.x << :bar
A.x # => []
B.x # => [:foo,:bar]

會造成這種行為的原因是,ActiveSupport 並不是複製 A.x 給 B.x,而是如果 B.x 沒設定,就去讀 A.x (這點跟下述的 class_inheritable_* 用複製的作法就不同) 。

ActiveSupport cattr_* 還提供了 query 是否為 nil 的方法,也就是 A.x? 和 B.x?

最後,ActiveSupport cattr_* 的行為也適用於實例化時,不會影響到 parent class:


A.x = 1
object = A.new
object.x = 2 
object.x # => 2
A.x # => 1 保持不變

class_inheritable_* 系列

ActiveSupport 還有一套古早的 class_inheritable_* 方法,它的作用跟上述的 class_attribute 是差不多的,只是內部的實作不同。是說 class_attribute 是 Rails3 才新寫的,效能較佳,會留著 class_inheritable_* 主要是因為向下相容性(有很多的 Plugins 使用了這個方法)。

參考資料