Add save callback to one ActiveRecord instance, is this possible?

Can I add a callback to a single instance of ActiveRecord? As an additional limitation, this should go through the library, so I have no control over the class (with the exception of the monkey patch).

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 
+4
source share
5 answers

You could do something like this by adding a callback to the object immediately after its creation and, as you said, neutralized 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 
+5
source

For something like this, you can always define your 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 can be done with a more general or ActiveRecord :: Base extension, no matter what your problem. Using it should be easy:

 something = Something.new something.before_save do Rails.logger.warn("I'm saving!") end 
+3
source

I wanted to use this approach in my own project in order to be able to put additional actions into the “save” model from my controller level. I took Tadman to the stage further and created a module that can be introduced into active classes of models:

 module InstanceCallbacks extend ActiveSupport::Concern CALLBACKS = [:before_validation, :after_validation, :before_save, :before_create, :after_create, :after_save, :after_commit] included do CALLBACKS.each do |callback| class_eval <<-RUBY, __FILE__, __LINE__ #{callback} :run_#{callback}_instance_callbacks def run_#{callback}_instance_callbacks return unless @instance_#{callback}_callbacks @instance_#{callback}_callbacks.each do |callback| callback.call end end def #{callback}(&callback) @instance_#{callback}_callbacks ||= [] @instance_#{callback}_callbacks << callback end RUBY end end end 

This allows you to enter a complete set of instance callbacks into any model by simply turning on the module. In this case:

 class Message include InstanceCallbacks end 

And then you can do things like:

 m = Message.new m.after_save do puts "In after_save callback" end m.save! 
+1
source

To add the answer to bobthabuilda - instead of defining a method on a metaclass of objects, extend the object using the module:

 def do_something_ballsy callback = Module.new do def before_save(msg) puts "Message #{msg} is saved." # Calls before_save defined in the model super end end msg = Message.new msg.extend(callback) end 

This way you can define several callbacks and they will be executed in the order in which you added them.

+1
source

Below you can use the usual before_save construct, i.e. Call it in a class, only in this case you call it in the instance metaclass so that other Message instances do not influence. (Tested in Ruby 1.9, Rails 3.13)

 msg = Message.new class << msg before_save -> { puts "Message #{self} is saved" } # Here, `self` is the msg instance end Message.before_save # Calling this with no args will ensure that it gets added to the callbacks chain (but only for your instance) 

Test it this way:

 msg.save # will run the before_save callback above Message.new.save # will NOT run the before_save callback above 
+1
source

Source: https://habr.com/ru/post/1304787/


All Articles