Subscriberを読む
はじめに
この記事はActiveSupportソースコードリーディング Advent Calendar 2021の1日目の記事です。
このアドベントカレンダーは私がActiveSupportについて理解を深めるために作ったものです。各記事ではActiveSupportの各モジュールについてコードを読んでいき、気づいたことをまとめていきます。
なお、コードの状態としては現時点での最新リリースであるv7.0.0.alpha2
タグを利用します。
Subscriber
今日はSubscriber
を取り上げます。このクラスはActiveSupport::Notifications
クラスを購読(subscribe)するためのクラスです。
使い方
使い方を知るためにドキュメントを読みます。
ActiveSupport::Subscriber is an object set to consume ActiveSupport::Notifications. The subscriber dispatches notifications to a registered object based on its given namespace.
An example would be an Active Record subscriber responsible for collecting statistics about queries:
module ActiveRecord
class StatsSubscriber < ActiveSupport::Subscriber
attach_to :active_record
def sql(event)
Statsd.timing("sql.#{event.payload[:name]}", event.duration)
end
end
end
After configured, whenever a “sql.active_record” notification is published, it will properly dispatch the event (ActiveSupport::Notifications::Event) to the sql method.
We can detach a subscriber as well:
ActiveRecord::StatsSubscriber.detach_from(:active_record)
なるほど、attach_to
メソッドで購読できるのですね。その際、インスタンスメソッドを定義するとそのメソッド名とattach_to
の引数をドットでつないだイベントを購読できるらしい。
コードを読む
attach_to
attach_to
はクラスメソッドのようなので、見てみます。
def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false)
@namespace = namespace
@subscriber = subscriber
@notifier = notifier
@inherit_all = inherit_all
subscribers << subscriber
# Add event subscribers for all existing methods on the class.
fetch_public_methods(subscriber, inherit_all).each do |event|
add_event_subscriber(event)
end
end
やはり、fetch_public_methods
というのが見えるので、これで定義されたインスタンスメソッドを取ってきてそれぞれをadd_event_subscriber
で登録するんですね。
目を引くのは、subscriber = new
となっている引数定義のところ、引数にnew
を渡すのは初めてみました。
detach_from
ドキュメントにも書いてあったように、購読をやめるにはdetach_from
を使うようです。
def detach_from(namespace, notifier = ActiveSupport::Notifications)
@namespace = namespace
@subscriber = find_attached_subscriber
@notifier = notifier
return unless subscriber
subscribers.delete(subscriber)
# Remove event subscribers of all existing methods on the class.
fetch_public_methods(subscriber, true).each do |event|
remove_event_subscriber(event)
end
# Reset notifier so that event subscribers will not add for new methods added to the class.
@notifier = nil
end
なるほど、やっていることはattach_to
の真逆らしい、それはそうですね。
ちなみに、ここで使われている@namespace
などは全てクラスインスタンス変数になっています。子クラスには引き継がれない値なのがポイントです。
add_event_subscriber
実際の登録処理も見てみましょう。
def add_event_subscriber(event) # :doc:
return if invalid_event?(event)
pattern = prepare_pattern(event)
# Don't add multiple subscribers (e.g. if methods are redefined).
return if pattern_subscribed?(pattern)
subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
end
最後のnotifier.subscribe(pattern, subscriber)
のところで、notifier
はattr_reader
で定義されているものなので、何も指定しなければActiveSupport::Notifications
が入っています。subscriber
はこのクラス自身のインスタンスが入っています。
Notifications
に続く
ということで、コードを読んでいたら別のクラスに処理が移ってしまいました。次回はActiveSupport::Notifications
のコードを読んでいきます。
Discussion