ActiveSupport::Notifications は通知を実現するクラスです。 通知はオブジェクト同士がメッセージのやりとりをするために使います。 繋りをすごく緩くしたい時に便利な機能。
クラス A
のインスタンスである オブジェクト a
が クラス B
のインスタンスであるオブジェクト b
のメソッドを呼ぶ。
a
はb
の参照できる必要があるB
はA
を知らなくてもいいA
は メッセージを送る対象が増えるとA
の変更の必要がある
例: b
だけでなく c
と d
にも hoge
メッセージを送りたい。
# クラスAのインスタンスメソッド内など
class A
def hoge
# メッセージを送るオブジェクトを用意しないといけない
b = B.new
b.hoge
c = B.new
c.hoge
d = B.new
d.hoge
end
end
class B
def hoge
puts 'hoge'
end
end
a = A.new
a.hoge
クラス A
が主体になる。
b
やc
、d
は自分で用意できるため問題にならないが、ライブラリにあるようなクラスでは変更ができなくて問題になる。
A
からみたとき、b
や c
や d
に強い繋りがある状態。
A
は変更せずに、B
側から調整したい場合はオブザーバパターンが使える。
オブジェクト a
にメッセージを送るオブジェクト(または処理)を登録する方法。
似たものにデリゲートとかフックとかある。
デリゲートパターンはメッセージを送るオブジェクトがひとつしかない。
フックという名前の場合は無名関数を登録する時によく使われる。
ハンドラという場合もある気がする。今回はコールバックという名前にする。
例
class A
def callbacks
@callbacks || []
end
def hoge
# b c d の存在はわかる必要はない
@callbacks.each do |callback|
callback.hoge
end
end
end
class B
def hoge
puts 'hoge'
end
end
a = A.new
# a.hoge した時にメッセージを送信したいオブジェクトを渡す
b = B.new
a.callbacks << b
c = B.new
a.callbacks << c
d = B.new
a.callbacks << d
a.hoge
A
の外側で処理を追加できるようになった。
でもでも、b
や c
や d
からすると a
を知らないと登録できない。
B
からみると A
に強い繋りがある。
この繋りを通知を使うことで互いに繋りを緩くできる。
通知は文字列をキーにして、キーに対し処理を登録しておく。 通知側はキーへ通知したいことを伝えると登録していたキーに通知がされる。
メソッドで繋りがあったオブジェクト同士が文字列で繋るようになる。
class Notifications
@@regists = Hash.new([])
def self.regist(key,object)
@@regists[key] << object
end
def self.notify(key)
@@regists[key].each do |object|
object.hoge
end
end
end
class B
def hoge
puts 'hoge'
end
end
b = B.new
# 処理を登録
Notifiactions.regist('hoge', b)
class A
def hoge
# 通知する
Notifications.notify('hoge')
end
end
A.new.hoge
Rails で通知を使うためのクラス。
Rails3 で追加された機能。 DevelopmentモードでSQLの実行ログ出力するために使われていたりするらしい。 繋りが緩いので機能のONにしたりOFFにしたりしやすい。 使いすぎると、関連がわからなくなるため本質とはずれた処理を追加するのに使われる。
Rails の中にはいくつか通知を行うポイントが用意されているので、カスタマイズに使える。
- 通知の仕方
- ActiveSupport::Notificationsinstrument
- 処理の登録
- ActiveSupport::Notifications.subscribe
通知を行うには ActiveSupport::Notifications.instrument(name, payload)
を使います。
引数 name
が通知するキーになります。payload は処理する側に情報を渡すためのものです。
通知をした時に処理が登録されていなければ、何も起きませんので、処理を登録しておく必要があります。
処理を追加するには ActiveSupport::Notifications.subscribe(pattern,&block)
を使います。
block には4つの引数が入ってきます。
- name
- start
- finish
- id
- payload
引数 name
にはどんな名前で通知されたかが代入されています。
subscribeの引数の pattern
には正規表現が使えて通知を受ける側は曖昧にできます。
/hoge/
で subscribe
しておくと、"hoge.mogu"
や "mogu.hoge"
でも通知を受けとることができます。
そのため、実際に通知されて処理するときに、その正確な名前が欲しい場合に使えます。
引数 start
は通知をした処理の始まりの時刻が入ります。
引数には finish
の終わりの時刻が入ります。
始まりはともかく、終わりは何かというと、通知する際に使う、ActiveSupport::Notifications.instrument
はブロックを渡すことができて、ブロックに入る時刻とブロックから抜けるときの時刻が入ります。
登録した処理はブロックを抜けた後に実行されます。
引数 id
は通知機能が管理しているユニークなIDが入ります。
同じ通知の中で処理したかどうか判断したりするのに使えそうです。
引数 paylod
は ActiveSupport::Notifications.instrument
から渡される情報です。普通のメソッドの引数と考えてもよいかもしれません。
以下は使うのに別に知る必要のない部分。
- 通知処理の実質的な処理は
ActiveSupport::Notifications::Instrumenter
で実装されています。このインスタンスはスレッドごとに生成されていました。 - 登録した処理は
ActiveSupport::Notifications::Fanout
というクラスに管理しています。登録した処理自体はActiveSupport::Notifications::Fanout::Subscribers
に実装されているクラスのいずれかになります。
他にも、ある name
で通知した場合に発生する処理を確認するメソッドとかあったりなかったりします。
- callback って副作用ありきだと感じた
- この文章…全体的に何かの受け売りな気がする…参考文献がありそうだ。