Rails3: Railtie 和 Plugins 系統

Rails3 提供了一些新的方法來擴充 Rails,其中最重要的算是了解 Railtie 吧。(我個人對 Generator 比較沒興趣)

Railtie 是 Rails 的核心程式,提供了 hooks 來修改啟動時的載入流程。Rails 的主要元件 (Action Mailer, Action Controller, Action View, Active Record, Active Resource) 都有 Railtie 來各自負責自己的載入流程。這樣設計的好處有 1. 主要元件也是可以抽換的(例如換 ORM) 2. 要在 Rails 裡 hook 變得十分乾淨,只要撰寫 railtie 即可,不像之前只能用 alias_method_chain。

不過,擴充 Rails 不一定會用到 Railtie。會需要實作的情境是你要在 Rails 框架啟動流程中做些互動,例如:

1. 建立 initializers
2. 新增 generator
3. 修改 Rails config.*
4. 訂閱 Rails +ActiveSupport::Notifications+
5. 新增 rake tasks

因此,我歸類出寫 Rails Plugin 的考量有 1. 要不要寫成 Gem 2. 需不需要用到 Railstie:

傳統 vender/plugin

傳統的 plugin 寫法依然適用,Rails 還是會去載入 plugin 目錄下的 init.rb。有趣的是,在 Rails 內部是把 plugin 自動包裝成一個 Rails::Plugin 類別,而這個 Plugin class 也是繼承自 Rails::Railtie 的。

包成 Rubygem

自從有了 Bundler 之後,包成 Gem 可以說是最佳實務了。透過標準的 Gem 格式,可以 1.設定 dependcncies 2. 有版號 version 3. 方便分享及安裝

如何包成 Gem 請參考 Rubygems 套件管理工具 這篇,很簡單的。

沒用到 railtie

Rails3 架構師 wycats 開示:”如果你沒有要 hook 在 Rails lifecycle 之中,不要用 Railtie。就如同一般的 Ruby library,你先 require 你需要的 Rails 元件,然後再修改或擴充即可” (例如 override 或 include something)


# in your_lib.rb
require "active_record" 
require "your_lib/extensions" 
class ActiveRecord::Base
  include YourLib::Extensions
end

用到 railtie

如果你需要 hook 或載入 rake, generator 等,那就需要用到 railtie 了,該怎麼寫呢?

基本的寫法如下:


# my_new_gem/my_new_gem.rb
require 'rails'
class MyCoolRailtie < Rails::Railtie

  # console 時載入
  console do 
    Foo.console_mode!
  end

  # 載入 generator
  generators do
    require 'path/to/generator'
  end

  # 載入 rake tasks
  rake_tasks do
    require 'path/to/railtie.tasks'
  end

  # 建立 config/initializers
  initializer "my_cool_railtie.boot_foo" do
    Foo.boot(Bar)
  end

end

除了上述這些,還有更多細微的 hook 如 config.after _initialize, config.middlewares, before_configuration, before_eager_load, before_initialize, to_prepare 等等族繁不及背載,如果沒仔細研究 Rails 的啟動流程恐怕也搞不清楚所有的差異。

如果你的 gem 不只給 rails 用,可以這樣寫:


# lib/my_new_gem/my_cool_railtie.rb
module MyNewGem
  class MyCoolRailtie < ::Rails::Railtie
    # Railtie code here
  end
end

# lib/my_new_gem.rb
require 'my_new_gem/my_cool_railtie.rb' if defined?(Rails)

請期待下集 "Rails3: Engine 和 Plugins 系統",Rails::Engine 可以讓你包裝出獨立的 App 元件。

參考資料

Plugin Authors: Toward a Better Future
Rails 3 Plugins - Part 1 - The Big Picture
Extending Rails 3 with Railties
Class Rails::Railtie

參與討論

1 則留言

發佈留言

發表迴響