つながりをゆるふわにしよう ActiveSupport::Notifications 2013-11-02 広島Ruby勉強会 #035
Jul 09, 2015
つながりをゆるふわにしよう ActiveSupport::Notifications2013-11-02 広島Ruby勉強会 #035
Rails のコードリーディング
•コード読んでるので毎月ピックアップ • http://railsdoc.eiel.info/ • 今回は ActiveSupport::Notifications
ActiveSupport::Notifications
•通知を実現するためのクラス •オブジェクト間を緩い繋りに
オブジェクトの繋り
•オブジェクトはメッセージで繋る •メッセージを送る方法 •メソッドの呼び出し
オブジェクトの繋り
•直接メソッド呼び出し •繋りが強い •ちょっと回り道をする •繋りを緩くできる
どうして緩くしたいか
•変更箇所を減らしたい •本質と関係ないものを分離
どうして通知が欲しいかみていく
•普通の呼び出し •処理を後から登録 •通知を使う
普通の呼び出し
•強い繋り • 1対1の繋り
普通の呼び出し
a b
作る
メソッドを呼ぶ
問題点
•後から処理を追加するのに •コードの変更が必要
例
class A def hoge # hogeする
end end !a = A.new a.hoge
a を hoge するときに b.hoge して欲しい
例class B def hoge # B hoge する
end end !class A def hoge # hoge する
B.new.hoge end end
さらに追加したくなった
例class B def hoge # B hoge する
end end !class A def hoge # hoge する
B.new.hoge B.new.hoge end end
Aを変更しなきゃいけない
Aが変えたくない場合がある
• Aがライブラリやフレームワーク •完成しているので変えられない • Aが自分が作ったBを知ってるわけがない
処理を後から登録する
•少し弱くなった繋り • 1対多の繋り
処理を後から登録する
•ルールを決める •オブジェクトを登録できるように •決めたメソッドを呼ぶ
b
処理を後から登録する
a
b
メソッドを呼ぶ b
b
オブジェクトを登録
0個からn個
例class A def hoge # hoge する
objects.each do |object| object.hoge end end ! def objects @objects ||= [] end end !a = A.new a.hoge
b.hoge を追加したい
例
a = A.new a.objects << B.new a.hoge
Aの外側で Aに処理を追加できる
問題点
• b を追加するのに a を知ってる必要がある •オブジェクトへの参照が必要
通知を使う
•文字列で繋りを作る •だいぶ ゆるふわ •多対多の繋り
通知を使う
•通知を呼び出す側 •相手のことを意識する必要がない •呼ばれる側 •相手のことを意識する必要がない
a
処理を後から登録する
b
メソッドを呼ぶb
b
処理をお願いする 通知システム
a
b登録しておく
通知システムを経由することで a と b の直接的な繋りを無くせる
代わりに 通知システムに 強い繋りが
具体例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
具体例
# 通知 hoge があったときにすることを登録
Notifications.regist(‘hoge’, B.new) !# 通知 hoge を発生させる
Notifications.notify(‘hoge’)
ActiveSupport::Notifications
• Rails で通知を使うためのクラス • Rails 3 で追加 • Developmentモードで • HTMLの生成時間と • SQLの実行時間
ActiveSupport::Notifications
•やりすぎ注意 •処理の流れを明確にしたいところでは使わないように
• Rails の中にも通知ポイントがある
使い方
•通知の仕方 • ActiveSupport::Notifications.instrument • 処理の登録 • ActiveSupport::Notifications.subscribe
通知の仕方
•後で処理を追加したくなりそうだなー。 • ActiveSupport::Notifications.instrument(name, payload)
• name •処理を登録する時に使う名前 • payload • 登録した処理に渡す追加情報
処理の登録
• ActiveSupport::Notifications.subscribe(pattern,&block)
• pattern • 通知のした際の name •正規表現が使える • block • したい処理
pattern
• pattern には正規表現が • /hoge/ であれば • instrument の name が • hoge.mogu • mogu.hoge • でも実行される
block
• block の中で使える情報 • name • start • finish • id • payload
name
• Notifications.instrument した時の引数 • pattern で指定しているので 正確な名前が欲しい時に
例require ‘active_support/notifications’ !include ActiveSupport !Notifications.subscribe(/hoge/) do |name| puts name end !Notifications.instrument(“hoge.goro”) Notifications.instrument(“goro.hoge”) # >> hoge.goro # >> goro.hoge
start finish
• instrument側の 処理の時間を測定できる
•始めの時間と終わりの時間 • instrumentする際にブロックを使う
例require ‘active_support/notifications’ !include ActiveSupport Notifications.subscribe(/hoge/) do |name, start, finish| p [start, finish] end !Notifications.instrument(“hoge”) do sleep(1) end # >> [2013-11-01 15:54:13 +0900, 2013-11-01 15:54:14 +0900]
id
•通知システム内の通知者のID •スレッドごとに変化する • (別に知らなくて良い気がする)
例require ‘active_support/notifications’ require ‘thread’ !include ActiveSupport Notifications.subscribe(‘hoge’) do |name, s, f, id| p [name, id] end !Notifications.instrument(‘hoge’) Notifications.instrument(‘hoge’) Thread.new { Notifications.instrument(‘hoge’) }.run
例 結果
["hoge", "8c71812749ca24e5a40e"] ["hoge", "8c71812749ca24e5a40e"] ["hoge", "c1050d011fe625537e41"]
payload
•メソッドのように引数を使いたい
例require ‘active_support/notifications’ !include ActiveSupport Notifications.subscribe(‘hoge’) do |n, s, f, i, payload| p payload end !Notifications.instrument(‘hoge’, :hoge) Notifications.instrument(‘hoge’,{hoge: 1}) # >> :hoge # >> {:hoge => 1}
より深いどうでもいいところ
• ActiveSupport::Notifications::Instrumenter • 通知処理を実質的にしているクラス •スレッド毎に存在
より深いどうでもいいところ
• ActiveSupport::Notifications::Fanout • 登録した処理を管理しているところ • ActiveSupport::Notifications::Fanout::Subscribers • 処理はこのモジュールの中にあるクラスのいずれかにラップされる
• finish がいるかどうかとかで区分される •呼ばれる予定処理を確認したりもできる
ご清聴ありがとうございました