カテゴリー:
Rails
タグ:
 Rails model initialize モデル 初期化

このエントリーをはてなブックマークに追加
更新日時:
2013年07月22日(月)
作成日時:
2013年07月22日(月)

前の記事 / 次の記事

Railsでモデルの初期化処理を書きたい場合の注意
で書いた記事の続きのような話なのだけど、

結論としては after_initializeafter_find
モデル初期化するために用意されているとしか思えない名前が付いているけれど、
基本的にここにモデルの初期化処理を書いちゃ駄目なんだろうし、書きたくない、
という話。

after_initializ と after_find
はオブジェクトが生成されたら必ず走る、走ってしまう。

それを意識して、不必要にオブジェクトが生成されないようにすれば
いいんじゃないか、って思ったこともある。

Railsでモデルの初期化処理を書きたい場合の注意 に書いたように、
find系メソッドじゃなくて、where系メソッドを使えば、実際にそのオブジェクトが
必要になるまでオブジェクトは生成されない。

でも、例えば

@articles = Articles.where(user: User.find_by_name("俺"))

@articles.each do |article|
  article.content
end

みたいな処理があった時、コレクションをゲットした時点では
after_initialize と after_find は走らないけれど、

@articles.each した時点で毎回オブジェクトが生成されるから、結局走る。

で、この時、コールバックで初期化された値を必ず使うっていうなら
何の問題もないんだけど、そうじゃないケースって結構ある。

例えば、この時Articleモデルが、

class Article < ActiveRecord::Base
  after_initialize :init

  def init
    self.category_id ||= 1
  end
end

みたいになってるけど、実際に欲しいのは article.content だけで、
カテゴリーとかどうでもいい場合とか、不要でも必ず after_initialize が走る。

で、重い処理はそもそも初期化する時に書かないと思うんだけど、
この例みたいに category_id に初期値をセットするだけの処理でも、
件数が増えるとあからさまにパフォーマンスが落ちる。

50件とか100件なら体感的には変わらないし、
ほとんどの場合一度に大量のオブジェクトを生成するようなことはないと思うんだけど、

将来的に何かの都合で一度に大量のレコードを展開したいということになったら
その時のマシンの性能にもよるんだろうけど、きっとこの瞬間破綻するし、
どこかの過程で大量のオブジェクトを生成する処理が混入したらそこでバグる。

そもそもそんなことしなきゃいいって話なのかも知れないけど、
将来的に破綻する危険性を抱えたものをつくるのって嫌だなあと思うので、
after_initialize と after_find は使いたくない、という結論に至った次第です。

じゃあどこで初期化するのかっていうと、
あんまりRailsっぽくないけど、普通にDB側で初期値をセットするか

class CreateArticles < ActiveRecord::Migration
  def change
    create_table :articles do |t|
      t.integer :category_id, :default => 1
      t.text    :content

      t.timestamps
    end

  end
end

とか、

コントローラーで頑張って値をセットするのがいいのかなと。
Rails4のストロングパラメーターみたいなノリで。

ていうかRails的にもっと直撃的な解法が用意されている気がしてならないのだけど、
調べてもちょっと分からなかった。みんなどうやって初期化してるんだろう。

「何お前ダサことやってんの?このメソッド使えば一発だろ!?」
っていうのを知ってる人がいたら教えて下さいw