ActiveSupport.onload と ActiveSupport.run_load_hooks のコードを読んでみました。
両メソッドともにActionSupport::LazyLoadHooksのモジュールで定義されているメソッドになります。on_loadを呼び出すと@load_hooks[name]にProcが格納されます。run_load_hooksを呼び出すと@loaded[name]にbase(Procの呼び出し引数 or instance_evalのコンテキスト)を格納するとともに、@load_hooks[name]のブロックをbaseのコンテキストで呼び出します。
module ActiveSupport
module LazyLoadHooks
def self.extended(base) # :nodoc:
base.class_eval do
@load_hooks = Hash.new { |h, k| h[k] = [] }
@loaded = Hash.new { |h, k| h[k] = [] }
end
end
def on_load(name, options = {}, &block)
@loaded[name].each do |base|
execute_hook(base, options, block)
end
@load_hooks[name] << [block, options]
end
def execute_hook(base, options, block)
if options[:yield]
block.call(base)
else
base.instance_eval(&block)
end
end
def run_load_hooks(name, base = Object)
@loaded[name] << base
@load_hooks[name].each do |hook, options|
execute_hook(base, options, hook)
end
end
end
ActionMailerの例だと、ActionMailer::Baseの最後の行でActiveSupport.run_load_hooksを呼び出しています。これはActionMailer::Baseのmix-inやメソッドの定義などが全て終わったタイミングで、hookの処理を呼び出すためです。
module ActionMailer
class Base < AbstractController::Base
include DeliveryMethods
...
ActiveSupport.run_load_hooks(:action_mailer, self)
end
end
RailtieなどでRailsのモジュールを拡張する場合にも使われ、以下のように対象のクラスがロードされたタイミングでクラスを拡張できます。メソッドなどがロードされた状態なのでmethod missingにもならないし、もちろんクラス自体も存在します。
require 'http_action_mailer/delivery_method'
module HttpActionMailer
class Railtie < Rails::Railtie
initializer 'http_action_mailer.add_delivery_method', before: 'action_mailer.set_configs' do
ActiveSupport.on_load :action_mailer do
ActionMailer::Base.add_delivery_method(:http, HttpActionMailer::DeliveryMethod)
end
end
end
end