6
5

More than 5 years have passed since last update.

複数の after_commit / after_rollback は定義と逆順で実行される

Posted at

発端

ActiveRecord で after_create 使って処理を書いてたけど、ActiveJob で実行するように変更する、という状況で、トランザクション後に処理できるよう after_commit に書き換えていた。

すると、module を分けて複数コールバックを定義していた箇所で、テストが失敗していた。debugger で見てみると、以前と逆順でコールバックが実行されているようにみえる。

調査

下記のようなコードを書いて確かめた。

$ rails new callback_order
$ cd callback_order
$ rails g model book title
class CreateBooks < ActiveRecord::Migration
  def change
    create_table :books do |t|
      t.string :title, null: false

      t.timestamps null: false
    end
  end
end
class Book < ActiveRecord::Base
  before_validation do
    puts "before_validation #1"
  end

  before_validation do
    puts "before_validation #2"
  end

  after_validation do
    puts "after_validation #1"
  end

  after_validation do
    puts "after_validation #2"
  end

  before_save do
    puts "before_save #1"
  end

  before_save do
    puts "before_save #2"
  end

  before_create do
    puts "before_create #1"
  end

  before_create do
    puts "before_create #2"
  end

  after_create do
    puts "after_create #1"
  end

  after_create do
    puts "after_create #2"
  end

  after_save do
    puts "after_save #1"
  end

  after_save do
    puts "after_save #2"
  end

  after_commit do
    puts "after_commit #1"
  end

  after_commit do
    puts "after_commit #2"
  end

  after_rollback do
    puts "after_rollback #1"
  end

  after_rollback do
    puts "after_rollback #2"
  end

  before_destroy do
    puts "before_destroy #1"
  end

  before_destroy do
    puts "before_destroy #2"
  end

  after_destroy do
    puts "after_destroy #1"
  end

  after_destroy do
    puts "after_destroy #2"
  end
end
# create and commit
$ rails runner 'Book.create(title: "Martian")'
before_validation #1
before_validation #2
after_validation #1
after_validation #2
before_save #1
before_save #2
before_create #1
before_create #2
after_create #1
after_create #2
after_save #1
after_save #2
after_commit #2
after_commit #1
# create and rollback
$ rails runner 'Book.create'
before_validation #1
before_validation #2
after_validation #1
after_validation #2
before_save #1
before_save #2
before_create #1
before_create #2
after_rollback #2
after_rollback #1
...
# create, destroy and commit
$ rails runner 'Book.create(title: "Martian").destroy'
before_validation #1
before_validation #2
after_validation #1
after_validation #2
before_save #1
before_save #2
before_create #1
before_create #2
after_create #1
after_create #2
after_save #1
after_save #2
after_commit #2
after_commit #1
before_destroy #1
before_destroy #2
after_destroy #1
after_destroy #2
after_commit #2
after_commit #1

こうしてみてみると、after_commitafter_rollback のみ、定義と逆順で実行されるようだ。

issue

issue が上がっていた。

余談

ちなみに通常 after_commit を使ったテストで transactional fixture を使ってる場合は、test_after_commit gem を使う必要がある。

6
5
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
6
5