LoginSignup
3
2

More than 3 years have passed since last update.

after_initialzeを使用時には、実行条件をつけてあげよう

Posted at

はじめに

現在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.allUser.where等でもafter_initializeは実行されることになります。

実はnewだけでなく、allwhereでもインスタンスは生成されている。

つまり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

本当にインスタンスを生成しているのか?

余談だが、allwhereの戻り値が本当にインスタンスが生成されているのかを確認したい場合の方へ。

Rubyにはinstance_of?という対象簿のオブジェクトが引数に指定したクラスのインスタンスを生成しているのか、判定するメソッドがある。

pry(main)> users = User.all
pry(main)> users[0].instance_of? User
=> true

普段使わない機能を使う事で新たな気付きがあるので、今後も積極的に使っていきたい

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2