将保存回调添加到单个ActiveRecord实例,是否可能?

时间:2010-03-22 17:05:54

标签: ruby-on-rails ruby activerecord

是否可以向单个ActiveRecord实例添加回调?作为进一步的约束,这是继续使用库,因此我无法控制该类(除了对其进行修补)。

这或多或少是我想做的事情:

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

5 个答案:

答案 0 :(得分:5)

您可以通过在创建对象后立即向对象添加回调来执行此类操作,就像您所说的那样,修补默认的AR before_save方法:

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

答案 1 :(得分:3)

对于类似这样的事情,你总是可以定义自己的疯狂处理程序:

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

这可以更通用,或者ActiveRecord :: Base扩展,适用于您的问题范围。使用它应该很容易:

something = Something.new

something.before_save do
  Rails.logger.warn("I'm saving!")
end

答案 2 :(得分:1)

我想在我自己的项目中使用这种方法,以便能够在“保存”中注入其他操作。来自我的控制器层的模型的动作。我将Tadman的答案进一步提升,创建了一个可以注入活动模型类的模块:

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

这允许您通过包含模块将完整的实例回调注入任何模型。在这种情况下:

class Message
  include InstanceCallbacks
end

然后你可以做以下事情:

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

答案 3 :(得分:1)

要添加到bobthabuilda的答案 - 而不是在对象元类上定义方法,请使用模块扩展对象:

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

这样,您可以定义多个回调,它们将按照您添加它们的相反顺序执行。

答案 4 :(得分:1)

以下将允许您使用普通的before_save构造,即在类上调用它,只有在这种情况下,您在实例的元类上调用它,以便没有{{{{{{ 1}}会受到影响。 (在Ruby 1.9,Rails 3.13中测试)

Message

测试它:

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)