回调更改的ActiveRecord属性?

时间:2009-10-03 15:08:33

标签: ruby-on-rails events activerecord attributes

我知道ActiveRecord :: Dirty及相关方法,但我没有看到可以订阅属性更改事件的方法。类似的东西:

class Person < ActiveRecord::Base
  def attribute_changed(attribute_name, old_value, new_value)
  end

  #or

  attribute_changed do |attribute_name, old_value, new_value|
  end
end

是否有Rails标准或插件?我觉得它必定存在于某处,而我却错过了它。

6 个答案:

答案 0 :(得分:18)

cwninja的回答应该可以解决问题,但还有更多的内容。

首先,使用write_attribute方法完成基本属性处理,因此您应该使用它。

Rails也有一个内置的回调结构,虽然它不允许传递有点烦人的参数,但它可能很好用。

使用自定义回调你可以这样做:

class Person < ActiveRecord::Base

  def write_attribute(attr_name, value)
    attribute_changed(attr_name, read_attribute(attr_name), value)
    super
  end

  private

    def attribute_changed(attr, old_val, new_val)
      logger.info "Attribute Changed: #{attr} from #{old_val} to #{new_val}"
    end

 end

如果您想尝试使用Rails回调(如果您可能有多个回调和/或子类,则特别有用),您可以执行以下操作:

class Person < ActiveRecord::Base
  define_callbacks :attribute_changed

  attribute_changed :notify_of_attribute_change

  def write_attribute(attr_name, value)
    returning(super) do
      @last_changed_attr = attr_name
      run_callbacks(:attribute_changed)
    end
  end

  private

    def notify_of_attribute_change
      attr = @last_changed_attr
      old_val, new_val = send("#{attr}_change")
      logger.info "Attribute Changed: #{attr} from #{old_val} to #{new_val}"
    end

end

答案 1 :(得分:14)

对于Rails 3:

class MyModel < ActiveRecord::Base
  after_update :my_listener, :if => :my_attribute_changed?

  def my_listener
    puts "Attribute 'my_attribute' has changed"
  end
end

评论:https://stackoverflow.com/a/1709956/316700

我找不到关于此的官方文档。

答案 2 :(得分:8)

对于Rails 4

def attribute=(value)
  super(value)
  your_callback(self.attribute)
end

如果你想覆盖属性的值,你应该使用write_attribute(:attribute, your_callback(self.attribute))而不是attribute=,否则你将循环遍历它,直到你得到一个堆栈太深的异常。

答案 3 :(得分:5)

尝试:

def attribute_changed(attribute_name, old_value, new_value)
end

def attribute=(attribute_name, value)
  returning(super) do
    attribute_changed(attribute_name, attribute_was(attribute_name), attribute(attribute_name))
  end
end

现在就发明了这个,但它应该有效。

答案 4 :(得分:3)

您始终可以访问私有方法changed_attributes并使用before_save检查其中的密钥并按照您的意愿执行操作。

答案 5 :(得分:2)

我发现最简单的方法:

after_save :my_method, :if => Proc.new{ self.my_attribute_changed? }

def my_method
  ... do stuff...
end