発端
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_commit
と after_rollback
のみ、定義と逆順で実行されるようだ。
issue
issue が上がっていた。
余談
ちなみに通常 after_commit を使ったテストで transactional fixture を使ってる場合は、test_after_commit gem を使う必要がある。