Rob van Dijk's Blog

Specify callbacks before specifying the associations

Posted in rubyonrails by Rob van Dijk on 05/02/2010

Just wasted a full day on this… Suppose you have a User model, with certain Users being children and other Users being parents. With the following code I intended to delete the parent when the child being deleted is the last child of the parent:

class ChildParent < ActiveRecord::Base
  belongs_to :child, :class_name => "User"
  belongs_to :parent, :class_name => "User"
end

class User < ActiveRecord::Base

  has_many :parents, :through => :child_parents_as_child
  has_many :child_parents_as_child, :foreign_key => :child_id, :class_name => "ChildParent", :dependent => :destroy
  has_many :children, :through => :child_parents_as_parent
  has_many :child_parents_as_parent, :foreign_key => :parent_id, :class_name => "ChildParent", :dependent => :destroy

  before_destroy :determine_parents_to_be_destroyed
  after_destroy :destroy_parents

  private
  def determine_parents_to_be_destroyed
    @destroy_parents = []
    self.parents.each do |p|
      @destroy_parents << p unless p.children.size > 1
    end
  end
  def destroy_parents
    @destroy_parents.each do |p|
      p.destroy
    end
  end
end

This didn't work - @destroy_parents was never populated. Finally I stumbled on the following sentence, clearly documented in the api for ActiveRecord, yet easily missed: *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won‘t be inherited.

Moving the before_destroy and after_destroy declarations above the associations solved the problem.

Volg

Ontvang elk nieuw bericht direct in je inbox.