Rails Active Record Callbacks 順序

簡易的列出 Ruby on Rails 全部的 active records callbacks 以及執行順序的筆記

Rails Active Record Callbacks 順序
Photo by Markus Spiske / Unsplash

開發 Ruby on Rails 中,時常會使用到 callback 的功能,簡單來說可以在 model 的生命週期當中插入自己的程式碼,e.g. 建立完 model 之後呼叫 mailer 去發信、或在驗證之前幫資料消毒等。

隨著專案的規模越來越大,callbacks 越長越多,腦袋冒出一些問題,例如:

  • 如果定義順序 A->B 執行的結果會是誰先誰後?
  • 我該如何找到我全部的 callbacks

執行順序

after_create 的順序會是由上到下,可是換成 after_commit 或是 after_rollback 順序會是相反

after_create :after_create_one
after_create :after_create_two
after_commit :after_commit_one
after_commit :after_commit_two

def after_create_one
  Rails.logger.info '********** after_create_one **********'
end

def after_create_two
  Rails.logger.info '********** after_create_two **********'
end

def after_commit_one
  Rails.logger.info '********** after_commit_one **********'
end

def after_commit_two
  Rails.logger.info '********** after_commit_two **********'
end

CleanShot-2023-12-19-at-16.24.39@2x

我們可以在 console 裡面看到

User._create_callbacks.map(&:filter)
=> [:after_create_two, :after_create_one]

User._commit_callbacks.map(&:filter)
=> [:after_commit_one, :after_commit_two]
Model._*_callbacks.map(&:filter)

Model.__callbacks.each_with_object(Hash.new([])) do |(k, callbacks), result|
  next if k == :validate # ignore validations
    callbacks.each do |c|
    # remove autosaving callbacks from result
    next if c.filter.to_s.include?("autosave")
    next if c.filter.to_s.include?("_ensure_no_duplicate_errors")
        result["#{c.kind}_#{c.name}"] += [c.filter]
  end
end

可以幫助我們 debug 快速的找到所有的 callbacks

參考資料

https://effectiva.hr/en/blog/what-active-record-callbacks-are-registered-on-some-model