[Rails] Railtie
初探 Railtie
Railtie 為 Rails 與其他 component 提供了整合的接口, Rails::Application 繼承 Engine, Engine 又繼承 Railtie
Rails 透過 Railtie 讀取 configurations, 建立 application 以及連接各個不同的 component (如: Active Record
, Action View
, Action Controller
, Action Mailer
), 使他們可以建立自己的 Initializers 與 Configuration
來翻 Source code 吧!
Railtie 是抽象的, 無法 instantiate (實例化)
從 source code 可以看到, Rails::Railtie
, Rails::Engine
, Rails::Application
這三種去 call new 方法的話, 皆會回傳錯誤
ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application)
def initialize
if self.class.abstract_railtie?
raise "#{self.class.name} is abstract, you cannot instantiate it directly."
end
end
def abstract_railtie?
ABSTRACT_RAILTIES.include?(name)
end
class << self
private :new
delegate :config, to: :instance
且 Railtie 中 new 方法是 private 的, 所以使用的話不會是 Rails::Railtie.new
而是這樣使用
class MyNewRailtie < Rails::Railtie
initializer "new_initialization_behavior" do
puts "Hello!"
end
end
建立 subClass, 將 Rails::Railtie 當成 baseClass 繼承. 在 subClass 內使用需要的 Rails::Railtie 方法
Railtie 裡的 initializer 跟 config/initializers 裡的 initialize 不一樣 Railtie 的 initializer 是一個特殊的 hook, 需要給他一個名字, 然後將要處理的邏輯寫在 block 裡
class MyRailtie < Rails::Railtie
initializer "my_railtie.configure_rails_initialization" do
# some initialization behavior
end
end
會用到 Railtie 的情境
建立 initializers
activerecord/lib/active_record/railtie.rb
裡面有一段是要設時區的 initializer
initializer "active_record.initialize_timezone" do
ActiveSupport.on_load(:active_record) do
self.default_timezone = :utc
end
end
def initializers
@initializers ||= Collection.new
end
def initializer(name, opts = {}, &blk)
raise ArgumentError, "A block must be passed when defining an initializer" unless blk
opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
initializers << Initializer.new(name, nil, opts, &blk)
end
會在方法內 new 一個 instance, 塞進 @initializers 內
設定 generator
如何建立一個 generator 可以參考 Rails - generators
新增 config.* keys 到 environment
使用 ActiveSupport::Notifications 設定 subscriber
新增 Rake tasks
像我們常用的 rake db: 系列指令, 是在 activerecord/lib/active_record/railtie.rb 36~50 line 進行掛載的
load "active_record/railties/databases.rake"
這行會 load activerecord/lib/active_record/railties/databases.rake 檔案內的所有 task
Conclusion
平常在開發的時候比較少會去使用到 Railtie 如果想要在 Rails 啟動期間或啟動後進行 interact, 這時候就需要 Railtie
如果沒有要 hook 在 Rails lifecycle 中, 就不要使用 Railtie 建議使用 standard Ruby library, require 需要的部分再 override