新的 Rails3 Controller 重構後,變成 ActionController::Base < ActionController::Metal < AbstractController 的繼承體系。除了 ActionMailer 繼承自 AbstractController,讓 API 與 Controller 一致之外,新的 ActionController::Metal 更是充滿玩味。
故事可以從上個禮拜,Yehuda 把 Rails 2 的 Metal 移除了(commit),根據 commit 的說明,Rails 2 的 Metal 在 Rails 3 裡面,可以用 1. 放在 Route 之前的 Rack middleware 或 2. 放在 Route 之後的一個 Rack endpoint,而這個 Rack endpoint,除了自己實作 Rack app,我們也可以用 ActionController::Metal 來實作出自己的客製化 Controller。
Rails2 發明 Metal 原因是,有些事情不需要完整的 Controller 功能,希望能夠越快越好,例如 XML/JSON API 等。而 Rails2 的 Metal 雖然非常快,但是沒什麼彈性,完全不能使用 Controller 的功能,例如用 Layout, Template, Sessions 等,基本上就跟單純的 Rack middleware 沒什麼兩樣。但是在 Rails3 中,可以透過自繼承 ActionController::Metal 做出白紙般的客製 Controller,可以有非常快的速度,如果有需要用到 Controller 的功能,也可以任意選擇組合加入,十分彈性。
例如,我們來實作一個超級精簡的 Static Controller:
# lib/static_controller.rb
class StaticController < ActionController::Metal
include ActionController::Rendering
append_view_path "#{Rails.root}/app/views"
def about
render "about"
end
end
# config/route.rb
match '/about', :to => "static#about", :as => :about
這個範例有接近於 Metal 的速度,並加入了 Controller 的 Template 功能 (你可以參考 ActionController::Base 這是擁有全部功能的版本)。其中 “static#about” 是 StaticController 的 about action 縮寫,而在 Rails3 中,controller action 也都是一個 Rack app。
除了我的這個簡單範例,在 The Powerful New Rails Router 和 Upgrading a Rails 2 App to Rails 3 的影片中,也有分別舉例。
總而言之,如果你在 Rails3 中不需要全部的 Controller 的功能,想要盡量拉高效能,有幾種推薦作法:
* 寫成 Rack Middleware,然後在 config/application.rb 中插入 config.middleware.use YourMiddleWare
* 寫成 Rack App,在 config.route.rb 中將某個對應的網址指到這個 Rack App
* 繼承自 ActionController::Metal 實作一個 Controller,其中的 action 也是一個 Rack App
其中的差異就在於後兩者會在 Rails Route 之後(好處是統一由 route.rb 管理 URL 路徑),如果繼承自 ActionController::Metal 可以有彈性獲得更多 Controller 功能。原則上,我想我會推薦 ActionController::Metal,寫起來最為簡單,一致性跟維護性較高。
另外,還有個小玩意, ActionController::Middleware 是 Controller 層級的 Rack Middleware,讓你可以在放入到某個特定 Controller 之中(也就是只有該 Controller 使用這個 Middleware)。不過呢,這個功能我到現在還沒看到任何實用的例子就是了。
最後,Yehuda 提供了一個 參考數據:
fast: through middleware inserted at 0
slwr: through middleware inserted via @use@
rotr: through endpoint sent via the router
bare: through ActionController::Metal with self.response_body
cntr: through ActionController::Base with self.response_body
text: through ActionController::Base with render :text
tmpl: through ActionController::Base with simple render :template
layt: through ActionController::Base with render :template with layout
real rps
fast 0.004271 2900 Rack 極限
slwr 0.067029 2200 使用 config.middleware.use YourMiddleware
rotr 0.088085 2000 經過 Rails Route 之後
bare 0.103868 1900 使用 ActionController::Metal 的最快速度
cntr 0.355898 1070 使用 ActionController::Base 的最快速度
text 0.557127 825 使用 ActionController::Base 加上 render :text
tmpl 0.639581 765 使用 ActionController::Base 加上 render :template
layt 1.678789 375 使用 ActionController::Base 加上 Template 跟 Layout