'How trigger a callback after a nullify in rails

model 1:

class Programmer  
    has_many :tasks, dependent: :nullify  
end

model 2:

class Task  
    belongs_to :programmer  
end

Programmer has many tasks. When I destroy a programmer, his tasks should be nullified (programmer_id = nil), but also his tasks should be setup to open (status = 'open'), independent of it current status.

I'd like to when destroy programmer, besides nullify programmer_id, set status attribute in task to 'open', but nullify not trigger callbacks

I don't know how to make this because :nullify don't trigger callback, only :destroy



Solution 1:[1]

As you say, dependent: :nullify doesn't trigger callbacks. I would probably define a method a little higher up the stack (in a Service or some such) that knows how to delete a programmer and then set the status on all the previously associated tasks.

If you really want to do it automatically, though, another option is to use the ActiveSupport instrumentation API to hook all SQL execution and do a comparison there:

ActiveSupport::Notifications.subscribe "sql.active_record" do |*args|
  data = args.extract_options!
  if data[:sql] == "UPDATE \"tasks\" SET \"programmer_id\" = NULL WHERE \"tasks\".\"programmer_id\" = $1" 
    Task.where(programmer_id: nil, status: 'assigned').update_all(status: 'open')
  end
end

Solution 2:[2]

I'd suggest a more visible approach so that others reading the code know what is happening:

class Programmer  
  has_many :tasks

  before_destroy :nullify_tasks

  private

  def nullify_tasks
    tasks.update programmer: null
  end
end

This will trigger callbacks on each of the updated tasks. dependent: :nullify actually just adds a callback like this anyway but does something more like tasks.update_all programmer: null (thus skipping the callbacks).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tom Copeland
Solution 2 Brendon Muir