Skip to content

Instantly share code, notes, and snippets.

@eiel
Last active December 26, 2015 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eiel/7177959 to your computer and use it in GitHub Desktop.
Save eiel/7177959 to your computer and use it in GitHub Desktop.
ActiveSupport::Notifications の説明の下書き。Ruby勉強会の資料にする。

ActiveSupport::Notifications は通知を実現するクラスです。 通知はオブジェクト同士がメッセージのやりとりをするために使います。 繋りをすごく緩くしたい時に便利な機能。

オブジェクト同士の繋り

メソッド呼び出し

クラス A のインスタンスである オブジェクト a が クラス B のインスタンスであるオブジェクト b のメソッドを呼ぶ。

  • ab の参照できる必要がある
  • BA を知らなくてもいい
  • A は メッセージを送る対象が増えると A の変更の必要がある

例: b だけでなく cd にも 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 が主体になる。 bcdは自分で用意できるため問題にならないが、ライブラリにあるようなクラスでは変更ができなくて問題になる。 Aからみたとき、bcd に強い繋りがある状態。 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 の外側で処理を追加できるようになった。 でもでも、bcd からすると 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

ActiveSupport::Notifications を使う

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が入ります。 同じ通知の中で処理したかどうか判断したりするのに使えそうです。

引数 paylodActiveSupport::Notifications.instrument から渡される情報です。普通のメソッドの引数と考えてもよいかもしれません。

より深いところ

以下は使うのに別に知る必要のない部分。

  • 通知処理の実質的な処理は ActiveSupport::Notifications::Instrumenter で実装されています。このインスタンスはスレッドごとに生成されていました。
  • 登録した処理は ActiveSupport::Notifications::Fanout というクラスに管理しています。登録した処理自体は ActiveSupport::Notifications::Fanout::Subscribers に実装されているクラスのいずれかになります。

他にも、ある name で通知した場合に発生する処理を確認するメソッドとかあったりなかったりします。

メモ

  • callback って副作用ありきだと感じた
  • この文章…全体的に何かの受け売りな気がする…参考文献がありそうだ。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment