はじめに
現在Railsを使って開発中のアプリケーションで、after_initialize
を使用する機会があった。
正直コールバックは苦手意識があって、避けてきたのもあるのとafter_initialize
に関しては使い所がイマイチわからなかったため、無知の状態であった。
いざ使ってみると便利だが、少し気をつけたいポイントもあるので、本記事に記述する
after_initialize
とは
その名のと通り、設定したクラスのオブジェクトがインスタンス化された後に実行される処理のことである。
以下にかんたんな使用例を示す。
Userクラスのオブジェクトがインスタンス化された後に、name属性に予め値をセットしている。
class User < ApplicationRecord
after_initialize do
self.name = "default name"
end
end
Railsガイドには以下のように説明がされている。
after_initializeコールバックは、Active Recordオブジェクトが1つインスタンス化されるたびに呼び出されます。インスタンス化は、直接newを実行する他にデータベースからレコードが読み込まれるときにも行われます。これを利用すれば、Active Recordのinitializeメソッドを直接オーバーライドせずに済みます。
ここで重要なのが、直接newを実行する他にデータベースからレコードが読み込まれるときにも行われます。
という部分になる。
これは本記事のタイトルの部分にもつながる重要な部分である。
なぜ実行条件をつけるのか?
今記事の目的となる部分です。Railsガイドの説明を読み解くと、User.new
だけではなくUser.all
やUser.where
等でもafter_initialize
は実行されることになります。
実はnew
だけでなく、all
や where
でもインスタンスは生成されている。
つまりafter_initialize
を設定していると取得件数分だけ処理が実行されることになる。
DBに10000件ユーザーのデータ入っていたとして、User.all
を実行すると、after_initialze
が同じ件数だけ実行される。
これは処理速やパフォーマンスに影響が出かねない。
また既存データ取得時にself.name = "default name"
のようにデータに触る処理を書いてしまうと、既存データが予期しない値が入ってしまうことも考えられえるため、非常に危ない。
Railsには,FW側で便利なメソッドが揃っているため、その力をお借りして実行条件を付与しよう
私の場合は、新規データ作成時にのみ実行をしたかったためnew_record?
という、インスタンス化したオブジェクトが新規か既存データかを判定してくれるメソッドを使うことで、実行条件を付与した
class User < ApplicationRecord
after_initialize :set_default_name, if: :new_record?
def set_default_name
self.name = "default name"
end
end
本当にインスタンスを生成しているのか?
余談だが、all
やwhere
の戻り値が本当にインスタンスが生成されているのかを確認したい場合の方へ。
Rubyにはinstance_of?
という対象簿のオブジェクトが引数に指定したクラスのインスタンスを生成しているのか、判定するメソッドがある。
pry(main)> users = User.all
pry(main)> users[0].instance_of? User
=> true
普段使わない機能を使う事で新たな気付きがあるので、今後も積極的に使っていきたい