感謝 Shanghai on Rails 社群再度邀請我前往上海,參加今年的 RubyConf China 2010 大會,日期是 2010/6/26。

我的演講的題目和上個月我在 RubyConf Taiwan 一樣是 Designing Beautiful Ruby APIs,所以可以先預習我之前的投影片。雖然題目一樣,不過內容應該會再好好編排過,預期再加入一些 DSL 素材範例。

說到上海,好像應該去世博會走一走(?)

(本篇的對象是 Library 開發者,如果您是單純的 Library 使用者,可以先參考 RubyGems 簡介)

套句流行的話,RubyForge Must Die!! 大概是今年二月底的事情 (外電報導簡中翻譯),新的 gemcutter.org 取代了 rubyforge.org,目前已經成為 Ruby 社群的官方 Gem 儲存庫:http://rubygems.org,包括 gems.rubyforge.org 也是指向 rubygems.org。

RubyForge.org 最大的問題之一,就是他不是用 Ruby 寫的(無誤),而新的 rubygems.org,使用了 Rails 當做前台,以及 Sinatra 當做 Gem server,背後技術包括 delayed::job 跟 Amazon S3 來處理所有上傳的 Gem package,讓發佈 Gem 的速度也大大提昇。整個站甚至也 open source 出來在 http://github.com/qrush/gemcutter。問題之二,就是 RubyForge 的介面實在不怎麼樣,功能也雜亂,而新 rubygems.org 有著全新的簡潔介面,提供具有親和力的資訊。問題之三,就是 API 了,新的 rubygems.org 配合新版的 rubygems library (1.3.6以上,如果你還沒升級,請打 sudo gem update –system),讓發佈 Gem 變得非常簡單。

還有個插曲,之前 github.com 有一度可以支援當做 Gem server,不過自從 gemcutter 計畫展開之後,他們就決定取消這個功能,請見 http://hasmygembuiltyet.org

如何打包及發佈 Gem

說到發佈 Gem 啊,我一直以為是有點麻煩的事情,最早我曾經用過 Hoe,也看過 jeweler 這個工具。不過一直到前一陣子看了 Yehuda 的 Using .gemspecs as Intended 這篇文章之後,才發現其實很簡單,根本不需要用到其餘的工具啊,那到底要怎麼做呢?

首先,我們要把你的 library 打包成一個 Gem package,假設我們的 library 叫 foobar 好了:

步驟一: 撰寫 foobar.gemspec 檔,這是一個描述 Gem package 的 metadata 檔案。以下是一個基本夠用的範例。如同 Yehuda 所說的,你其實不需要用其他的工具來產生這個 gemspec 檔案。


 Gem::Specification.new do |s|
  s.name        = "foobar"
  s.version     = "1.1.1"
  s.date        = "2010-05-14"
  s.authors     = ["Wen-Tien Chang"]
  s.email       = ["ihower@gmail.com"]
  s.homepage    = "http://example.org"
  s.summary     = "blah"
  s.description = "blah blah"

  # s.add_dependency('log4r', '>= 1.0.5')
  # s.add_dependency('log4r', '~> 1.1.0') # 表示 1.1.y 都可以
  # s.add_dependency('log4r', '~> 1.0') # 表示 1.x.y 都可以
  # s.add_runtime_dependency # add_dependency 的別名
  # s.add_development_dependency # 只有在 gem install xxx --development 才會安裝

  s.files = Dir.glob("{lib}/**/*") + %w(LICENSE README) # 只有列在這裡的檔案會打包到 Gem package 裡面。
  # s.executables = [] # 放在 bin 下的執行檔有哪些
end

更多完整的規格請參考 http://docs.rubygems.org/read/chapter/20

步驟二:執行 gem build foobar.gemspec 便會包裝出單一 Gem package 套件檔 foobar-1.1.1.gem

這時候,你就可以透過 gem install foobar-1.1.1.gem 來安裝到自己的電腦了。如果跑 gem server 起來,別人也可以透過你的 gem server 安裝這個套件 (gem install GEMNAME –source http://your_gem_server_host:8808)。

接下來,怎麼發佈 Gem 到 http://rubygems.org 讓全世界的開發者都可以安裝你的大作呢? 在 Rubygems 1.3.6 之後已經內建有 gem push 功能:

步驟一:在 http://rubygems.org/ 註冊一個帳號,拿到 API key,加到 ~/.gem/credentials 裡。
步驟二:執行 gem push foobar-1.1.1.gem 就會發佈出去了

Ruby Library 最佳實務

一個 Ruby library 的組成,大致都是這樣的:

* README
* lib 目錄和會被使用者 require 的檔案,例如 lib/foobar.rb
* test 目錄 (optional)
* example (optional)
* bin 目錄 (有執行檔的話)
* LICENSE

如果你沒什麼概念,可以看看 Ruby Best Practice 一書的第八章 Skillful Project Maintenance,作者講解了一個 Ruby Library Project 的組成,還有介紹到如何用 RDoc 跟 Rakefile。如果你比較初學,建議一看。

接著 Gem Packaging: Best Practices 這一篇非常值得一看,介紹一些最佳實務,像是:

1. 不要在你的 library 裡依賴 rubygems。例如 require ‘rubygems’、rescue Gem::Load、gem “foo” 等等都不要用
2. 因為 lib 這一層目錄會進 $LOAD_PATH,所以不要放不是要給終端使用者 require 的檔案在 lib 下 (而且命名也不要太 general,不然可能會跟其他 library 撞到名字),其他檔案用 module namespace 的方式放到 lib 的子目錄下。例如你看 http://github.com/nex3/haml 的 lib 下就只放了 haml.rb 跟 sass.rb。
3. 承上,在 foobar.rb 中如果要 require 其他檔案,不需要寫 File.dirname(__FILE__) ,直接 require “foo/bar” 就會 require lib/foo/bar.rb 這個檔案了。

如果你想繼續多了解一些故事,可以看看:

* Gemcutter(rubygems.org) 作者在 RubyConf 2009 的演講:Gemcutter: The Next Step in Gem Hosting 以及他的投影片 http://next.heroku.com/
* Yuhada 在 RubyConf 2009 的演講: Polishing Rubygems

最近其實收到不少人來信問 Rails 問題,但是有些問題(特別是入門問題)用 E-mail 往返實在太沒效率了(一來我看不到你的程式碼,二來我也不了解你的程度),如果當面溝通跟示範,想必能夠更快更全面地解決你的問題。對我來說,當面聊也比較有趣,能有更多的交流學習。

完整的公告詳見 Ruby Wednesday 定期聚會,我每週三晚上會出現在生態綠,如果有意外的話(例如:那天我沒空),請注意我的 Twitter

Next Page »