You need after add or after remove callbacks to take action on has any belongs to many set modifications

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the rails category.

Last Updated: 2024-04-25

A LawCase had and belonged to many LawDisciplines and vice-versa.

I wanted to touch (bump timestamp of) the law_discipline whenever a law_case was added to that discipline.

My first draft was:

class LawCase
  has_and_belongs_to_many :law_disciplines

  after_commit :touch_associated_records

  private

  def touch_associated_records
    # n < 3, therefore optimizations were overkill
    law_disciplines.each(&:touch)
  end
end

The current code correctly touched the association when an attribute of a LawCase was modified. But this did not touch the associated records when you added or removed a record from the association, e.g. as follows

law_case.law_disciplines << law_discipline

# or (not sure syntax is correct...but you get the idea)
law_case.law_disciplines.delete(law_discipline)

This is because these actions just interact with the has_and_belongs_to_many association.

The solution involved adding after_add and after_remove callbacks:

has_and_belongs_to_many :law_disciplines,
                        after_add: :touch_updated_at,
                        after_remove: :touch_updated_at

# This always get a parameter
def touch_updated_at(law_discipline)
  law_discipline.touch
end