Designing Beautiful Ruby APIs 投影片

Update: 整理了演講摘要
Update: 被 The Ruby Show #126 介紹了,Audio 在第 8:55~9:32 秒,Video 在第 8:00~8:35 秒。

Updated: RubyConf China 的錄影

這是我在 RubyConf Taiwan 2010 所做的演講:

因為時間不夠的關係,整個 Part2: Ruby Object Model and Meta-programming 其實在現場並沒有講到。會有 Part2 也真是意外,本來並沒有想要準備這些內容,可是因為隨著 Designing Beautiful Ruby APIs 越來越進階,有一些設計方式我發覺如果沒有 Part2 的基礎,就進展不下去,沒辦法解釋。所以我竟然花了一半的時間在準備上台不會有時間講到的 slides… XDXD

Rails3 如何寫 Helper 可以傳 Block 參數

Rails3 beta2 開始確定新的 <%= %> 的用法了,像是 form_for, div_for 等要改成 <%= %> 而不是我們習慣已久的 <% %>,主要是因為這樣統一了 <%= %> 就表示內容會輸出的慣例。目前是兩種寫法都可以,只是本來的寫法會有警告訊息 DEPRECATION WARNING: <% %> style block helpers are deprecated. Please use <%= %>,應該到 Rails 3.1 就會正式移除。用法可以參考 ERB Blocks in Rails 3 一文。

而我這篇接下來要示範的是,如果我們想要自己寫一個 helper,可以接受 block 參數要怎麼寫? 例如在 ERB 中,我們希望這樣的程式可以輸出 header blah footer 字串:


# Rails2
<% my_helper do %>
    blah
<% end %>

在 Rails2 的話,如果有包 Block 無論如何都必須用 <% 而不能用 <%=,不然一定會有錯誤訊息。而這個 helper 最常寫成這樣:


# Rails2
def my_helper
  concat("header")
  yield
  concat("footer")
end

或是:


# Rails2
def my_helper(&block)
   tmp = with_output_buffer(&block) 
   concat("header #{tmp} footer")
end

在 Rails2 中,我們得用 concat 表示輸出到 ERB (這算是一個密招,沒人教還真的不會)。但是在 Rails3,則被改成比較直覺的:


# Rails3
<%= my_helper do %>
  blah
<% end %>

以及


# Rails3
def my_helper(&block)
  tmp = with_output_buffer(&block) 
  "header #{tmp} footer"
end

在 Rails3 就不需要用 concat 了,helper 的回傳值透過 <%= 就會輸出在 ERB 上。

什麼是 User Story?

User stories 是一種非常好用且容易上手的需求文件,它是一種極簡主義,只要求寫下最有價值不要忘記的東西,而且夠讓我們足以估計時程以及與客戶溝通。

嚴謹、漂亮、詳細的文件有兩大危險,一是製作厲害的文件本身變成了一個目標,而不是製作軟體。二是語言本身是模糊的,”詳細”的文件會造成”確定性的幻覺”,以為規格都確定了無需要溝通,最後做出根本不符合預期的軟體。敏捷開發鼓勵我們將時間多花在開發者、客戶、使用者之間頻繁的溝通,而不是製作文件。

什麼是 User Story?

User Story(使用者敘述)是一段簡單的功能敘述,以客戶或使用者的觀點撰寫下有價值的功能(functionality/feature)。與其說它是規格文件(documentation),不如說它代表(represent)客戶的一個需求而已,因為實做細節將延後至開發時才會確定。

幾個 User Stories 的範例如下:

  • 使用者可以在網站上張貼履歷
  • 使用者可以搜尋有哪些工作
  • 公司可以張貼新工作
  • 使用者可以限制誰可以看到他的履歷

初學的範本可以是:作為一個 (某個角色) 使用者,我可以做 (某個功能) 事情,如此可以有 (某個商業價值) 的好處。

As a (role of user), I want (some feature) so that (some business value).

閱讀全文〈什麼是 User Story?〉

Ruby on Rails Performance 最佳實務

這是我 2/26 在中研院 OSSF 工作坊和 3/23 在 Ruby Tuesday 所演講的題目之二。

關於 Performance,有四個最好不要做的事情:

  • 過早最佳化
  • 亂猜哪裡慢
  • 到處做快取
  • 與 Framework 過不去,硬要 Hack 它

然後有五條關於 Performance 的須知:

  • 演算法的改良永遠勝過硬擠程式碼
  • 一般來說,程式碼維護性的重要性勝過效能
  • 只最佳化最需要的部份 (80/20法則)
  • 測量再測量,最佳化前、最佳化後都要測量
  • 必須與程式碼彈性做平衡。有時候做了最佳化之後(例如快取),會犧牲掉一些彈性。

其他就看投影片吧。這份投影片主要還是關注在 Web application layer,也就是 Ruby 程式語言和 Ruby on Rails 上。有點小可惜的是篇幅和準備時間所限,關於 Front-end performance、NoSQL: Key-value stores 和 HTTP Caching 這三個主題是我覺得可以多講一點的東西。

有了效能,也許你會想進一步知道如何做 Scaling? 不同於 Performance 探討單一台 application server 可以服務多少 requests,Scaling 要探討的問題是如何擴展架構到多台伺服器上來服務更多流量。當然 Performance 會是做 Scaling 的一個重要的因素,不過 Scaling 需要考量的地方又更多了,包括:

  • Asynchrony Processing (Message queue)
  • Partition Component using SOA
  • HTTP Reverse Proxy Caching
  • Distributed Filesystem/Database

其中 Asynchrony Processing 和 SOA 的部份,我在 Distributes Ruby and Rails 的演講有分享過。

xdite 也有針對 Scaling Rails Site 這個主題寫了一系列的 Scaling Rails Site: Reading Material 來介紹: #1, #2, #3, #4, #5

Ruby on Rails Security 最佳實務

這是我 2/26 在中研院 OSSF 工作坊和 3/23 在 Ruby Tuesday 所演講的題目之一。

Ruby on Rails 要談的 Security 網站安全,主要在 Web application layer 的範圍(這也是最容易被攻擊的部分),像是 XSS、CSRF、Session Hijacking 跟 Fixation、SQL Injection 等等都是所有 Web 應用程式必須處理的問題。也有一些是 Rails 為了程式方便性而製造出來的問題,像是 Mass assignment、Unscoped finds、Controller Exposing methods 等等。

會講的內容看投影片就可以了,這裡我想特別提一下我的開場跟一個觀念:

我的開場引用了 PHP Security Guide: Overview 介紹什麼是 Security,我很喜歡:

  1. 安全性是相對的,不是一個功能。碰過太多客戶要求 “網站要絕對安全,不能被 HACK”,這實在太強人所難了。安全性就跟溫度一樣,相對熱、相對冷。安全性也是相對安全、相對不安全的,而不是像功能說有或沒有。
  2. 承上,要越安全,就給花越多成本去做。不過呢,不需要多花什麼錢,就可以很簡單做到足夠地安全。如果要非常非常安全,就會非常非常貴了。所以請考量你的預算。
  3. 必須要與使用性(usability)做平衡。很多時候,安全性跟使用性會是衝突的,想要越多安全性,就給犧牲掉一些使用上的方便性。
  4. 安全性必須是網站程式設計過程中的一部分,而不是最後才加上去。

而想提的寫程式觀念是我看 Security on Rails 一書裡面介紹的 Fail Close,底下第一段是用 Fail open 觀念寫的程式,第二段是用 Fail close。


# fail open way, it’s bad
def show
  @invoice = Invoice.find(params[:id])
  unless @user.validate_code( @invoice.code )
    redirect_to :action => 'not_authorized'
  end
end


# fail close way
def show
  @invoice = Invoice.find(params[:id])
  if @user.validate_code( @invoice.code )
    redirect_to :action => 'authorized
  else
    redirect_to :action => 'not_authorized'
   end
end

其中的端倪就是,在撰寫驗證安全性程式碼的時候,請用 Fail close 的觀念:”如果條件成功,才允許進行,不然就不允許”。而不是 Fail open:”如果條件不成功,才不允許,不然就允許。”。這個簡單的撰碼觀念,可以減少我們寫出漏洞程式碼的機會。