views:

59

answers:

2

Is it possible to add a callback to a single ActiveRecord instance? As a further constraint this is to go on a library so I don't have control over the class (except to monkey-patch it).

This is more or less what I want to do:

def do_something_creazy
  message = Message.new
  message.on_save_call :do_even_more_crazy_stuff
end

def do_even_more_crazy_stuff(message)
  puts "Message #{message} has been saved! Hallelujah!"
end
+3  A: 

You could do something like that by adding a callback to the object right after creating it and like you said, monkey-patching the default AR before_save method:

def do_something_ballsy
    msg = Message.new
    def msg.before_save(msg)
        puts "Message #{msg} is saved."
        # Calls before_save defined in the model
        super
    end
end
bobthabuilda
That modifies that and only that object, right?
J. Pablo Fernández
+1 for `do_something_ballsy`
macek
@Pablo - That's correct.
bobthabuilda
What about if there's already a before_save? Should I somehow save it and call it from my own before_save?
J. Pablo Fernández
Edited my post for this one. Check it out.
bobthabuilda
A: 

For something like this you can always define your own crazy handlers:

class Something < ActiveRecord::Base
  before_save :run_before_save_callbacks

  def before_save(&block)
    @before_save_callbacks ||= [ ]
    @before_save_callbacks << block
  end

protected
  def run_before_save_callbacks
    return unless @before_save_callbacks

    @before_save_callbacks.each do |callback|
      callback.call
    end
  end
end

This could be made more generic, or an ActiveRecord::Base extension, whatever suits your problem scope. Using it should be easy:

something = Something.new

something.before_save do
  Rails.logger.warn("I'm saving!")
end
tadman
Note that he doesn't access to change the class directly.
Tilendor
He did say "except to monkey patch it" which is precisely what this does if you apply it to ActiveRecord::Base directly.
tadman