ruby メタプロ method_missing使用例 - delayed_job編 -
gemをもくもくとよんでみた結果得られた物を書こうと思う。
今回はmethod_missing。
method_missingとは
rubyのメタプロ的書き方の一つ。
継承チェーンを一番上まで辿ってもそんなメソッド無いよ!って時に呼ばれる輩。
ググれば色々と出てきますが、要するに実行時に初めて存在するメソッドを呼ぶ時に便利。
delayed_jobでの使用例
delayed_jobって?
非同期にmethodを実行する為のgem。
github.com
実行方法は、Readmeに書いてある通り、レシーバーとメッソッドの間にdelayを挟むだけ。
これで、@user.activate!(@device)ごとシリアライズしてデータベースにキューを保存、daemon化しているプロセスが定期的にキューを拾って実行、とやっている。
# without delayed_job @user.activate!(@device) # with delayed_job @user.delay.activate!(@device)
method_missing使用例
まず、疑問に感じるのが、delayってどこでよんでいるのだ?と@user.delayがなんでactivateを呼べるのか?というところ
delayが呼べる理由
ここは単純だが、ObjectとModuleにincludeさせている箇所がある。
lib/delayed_job.rb
Object.send(:include, Delayed::MessageSending) Module.send(:include, Delayed::MessageSending::ClassMethods)
これもメタプロっぽいがincludeで継承チェーンに突っ込んでいる。(ObjectとModuleなので殆どのクラスは継承する事になる)
Delayed::MessageSendingにdelayがいるのではれて皆がdelayを呼べるようになる。
lib/delayed/message_sending.rb
(snip) module MessageSending def delay(options = {}) DelayProxy.new(PerformableMethod, self, options) end (snip)
activateを呼べる理由
ここでmethod_missingを使っている。
さっきのdelayの中で呼ばれていたDelayProxyを追うと
class DelayProxy < Delayed::Compatibility.proxy_object_class def initialize(payload_class, target, options) @payload_class = payload_class @target = target @options = options end def method_missing(method, *args) Job.enqueue({:payload_object => @payload_class.new(@target, method.to_sym, args)}.merge(@options)) end end
となっていて、.new以外は全部method_missingに送っているのである。
もうちょっと読み進めると、.newでレシーバーとメソッドとオプションをセットしていて
enqueueする時にそれらをセットしようとしている事が分かる。
こうやって、呼び出したいメソッドをごっそりとシリアライズしてdbにキューとしてしまっていた。