before_save, after_save は中々便利な機能で、このような何かをフックに勝手に処理が走ってくれる、という仕組みは小規模なアプリでは大変楽にプログラムが書ける。save すれば必ず処理を走らせてくれるのだからうっかり忘れることが無い。
しかし、ある程度大きなプログラムになってくるとこれが厄介者となってくる。save したときに必ずhookを走らせるというのは、どんな時もhookが走るのだから整合性を保つうえでは楽な仕組みではあるが、例えばパフォーマンスに目を向けたときには厄介なものとなる。バッチで10万件のデータの save を行う、みたいな場合にヘビーなhookが自動で走ってしまうと問題が起きてしまう。そもそもその save は例えば特定のフラグを立てるなど、簡単な更新処理のはずなのに、 validation に引っかかってしまって保存できずにバッチ処理の処理もれが発生する、などが起きるとまずい。
validateion だけなら save :validate => false したり、update_attribute を使えば回避できるが、validate => false は association を伝搬しないし、update_attribute は一つのカラムしか更新できない(おまけにもうobsolete)。他にも before_save をスキップしたいときというのはいくらでもある。本来は入力画面から保存したときだけは知ればよいはずのhook を簡単で確実、という理由で before_save にしたばっかりに、どこで save しても hook が走るようになってしまい困る、ということが何度か起きていた。アプリの規模が大きくなるとhookを1.動かしたい場所、2.動かしても構わない場所、3.動かしては困る場所のうち3がどうしても増えてくる。そうなると結局細かくhookを実行するかどうかの制御をしないといけなくなる。before_save はそれが実行されることが若干わかりづらいために、特に複数人開発などではだんだんと混乱してくる。
そのために before_save はあまり使わないようにしている。代わりに、先のポストに書いた、機能ごとのモジュール内でhookを定義するようにしている。こうするとたとえば入力画面で保存したときは必ずhookが走ることが保証される。モデルそのものに hook を定義することはしない。
0 件のコメント:
コメントを投稿