after_createとafter_update

はじめに

業務で機能の追加や変更でafter_createとafter_updateに苦しめられたのと全然わからなかったので、調べたことを整理します。

コールバックとは

after_createとafter_updateはコールバックの機能です。

そもそもコールバックの意味がわからなかったので調べました。

コールバックとは、オブジェクトのライフサイクル期間における特定の瞬間に呼び出されるメソッドのことです。コールバックを利用することで、Active Recordオブジェクトが作成/保存/更新/削除/検証/データベースからの読み込み、などのイベント発生時に常に実行されるコードを書くことができます。

引用元:Active Record コールバック - Railsガイド

なるほど、オブジェクトがデータベースとやり取りする時に呼び出されるメソッドで作成や更新などの任意のイベントのタイミングで、そのメソッドを発動させることができるみたいですね。

例えばこんな感じです。

 class Test < ApplicationRecord
  after_create :hoge

  private
  def hoge
    puts "create"
  end
end

この場合だとafter_create時にhogeメソッドが呼ばれます。

after_create

コールバックはわかりました。

じゃあ、after_createはいつ呼ばれるのかが問題です。

afterだからオブジェクトがデータベースに保存された後に呼ばれるのかなと思いましたが違いました。

発動されるタイミングはオブジェクトがデータベースにINSERTされた後、COMMITされる前です。(SQL全然わからない)

ちなみにINSERTはオブジェクトをデータベースにレコードとして追加するコマンドです。

COMMITはトランザクション(データベースの更新処理の確定。create,update,deleteで行われる)の確定するコマンドです。つまり、このコマンドが発動すれば永続的にデータベースにレコードを作成して保存したり、更新したり、削除することができます。

脱線したので元に戻すと、データベースにオブジェクトを追加したけど、まだ保存されていない(オブジェクトがデータベースにレコードとして永続化されていない)タイミングでafter_createが発動するよということです。

Image from Gyazo

実際の動きを見てみます。

(0.2ms)  BEGIN
  ↳ app/controllers/test_controller.rb:19:in `create'
  Course Load (0.4ms)  SELECT `courses`.* FROM `courses` WHERE `courses`.`id` = 2 LIMIT 1
  ↳ app/controllers/test_controller.rb:19:in `create'
  Test Create (6.9ms)  INSERT INTO `test` (`name`, `created_at`, `updated_at`, `course_id`, `user_id`) VALUES ('', '2020-01-19 12:34:25.427633', '2020-01-19 12:34:25.427633', 2, 2)
  ↳ app/controllers/test_controller.rb:19:in `create'
after_create is called
   (1.4ms)  COMMIT

INSERTした後、COMMITする前に呼ばれていますね。

after_update

after_updateもafter_createと呼ばれるタイミングは同じです。

ですが、こちらは発動されるタイミングはオブジェクトがデータベースにUPDATEされた後、COMMITされる前(レコードの更新が確定する前)です。

実際の動きを見てみます。

 (0.2ms)  BEGIN
  ↳ app/controllers/test_controller.rb:10
  Level Load (7.9ms)  SELECT  `levels`.* FROM `levels` WHERE `levels`.`id` = 9 LIMIT 1
  ↳ app/controllers/test_controller.rb:10
  User Update (0.4ms)  UPDATE `users` SET `experience` = 32, `updated_at` = '2020-01-19 13:03:46' WHERE `users`.`id` = 1
  ↳ app/controllers/test_controller.rb:10
after_update is called
   (0.5ms)  COMMIT

こちらもUPDATEした後、COMMITする前に呼ばれていますね。

まとめ

after_createとafter_updateはINSERT後、COMMITする前に発動。

after_createの動きがわからなくて、テストコードでエラーが起きても原因がわからなかったし、after_updateで無限にafter_updateのコールバックが続くしで大変な目に合いました。なのでコールバックは詳しく知っておかないといけないなと痛感しました。

その他のコールバックについても補足していくかも

参考

railsguides.jp

blog.toshimaru.net

qiita.com