跟踪 Rails 中 ActiveRecord 对象中非持久属性的脏信息

发布于 2024-11-02 02:09:00 字数 679 浏览 2 评论 0原文

我有一个继承自 ActiveRecord 的对象,但它有一个未保留在数据库中的属性,例如:

 class Foo < ActiveRecord::Base
   attr_accessor :bar
 end

我希望能够使用 ActiveModel 提供的“bar_changed?”等方法来跟踪“bar”的更改肮脏的。问题是,当我尝试在此对象上实现 Dirty 时,如 docs 中所述,我'我收到错误,因为 ActiveRecord 和 ActiveModel 都定义了 define_attribute_methods,但参数数量不同,因此在尝试调用 define_attribute_methods [:bar] 时收到错误。

在包含 ActiveModel::Dirty 之前,我尝试过使用别名 define_attribute_methods,但没有成功:我收到未定义方法错误。

关于如何处理这个问题有什么想法吗?当然,我可以手动编写所需的方法,但我想知道是否可以使用 Rails 模块,通过将 ActiveModel 功能扩展到 ActiveRecord 未处理的属性来实现。

I have an object that inherits from ActiveRecord, yet it has an attribute that is not persisted in the DB, like:

 class Foo < ActiveRecord::Base
   attr_accessor :bar
 end

I would like to be able to track changes to 'bar', with methods like 'bar_changed?', as provided by ActiveModel Dirty. The problem is that when I try to implement Dirty on this object, as described in the docs, I'm getting an error as both ActiveRecord and ActiveModel have defined define_attribute_methods, but with different number of parameters, so I'm getting an error when trying to invoke define_attribute_methods [:bar].

I have tried aliasing define_attribute_methods before including ActiveModel::Dirty, but with no luck: I get a not defined method error.

Any ideas on how to deal with this? Of course I could write the required methods manually, but I was wondering if it was possible to do using Rails modules, by extending ActiveModel functionality to attributes not handled by ActiveRecord.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

愛上了 2024-11-09 02:09:00

我正在使用 attribute_will_change! 方法,一切似乎运行良好。

它是在 active_model/dirty.rb 中定义的私有方法,但 ActiveRecord 将其混合在所有模型中。

这就是我最终在模型类中实现的内容:

def bar
  @bar ||= init_bar
end
def bar=(value)
  attribute_will_change!('bar') if bar != value
  @bar = value
end
def bar_changed?
  changed.include?('bar')
end

init_bar 方法仅用于初始化属性。您可能需要也可能不需要它。

我不需要指定任何其他方法(例如 define_attribute_methods)或包含任何模块。
您确实必须自己重新实现一些方法,但至少行为将与 ActiveModel 基本一致。

我承认我还没有彻底测试它,但到目前为止我还没有遇到任何问题。

I'm using the attribute_will_change! method and things seem to be working fine.

It's a private method defined in active_model/dirty.rb, but ActiveRecord mixes it in all models.

This is what I ended up implementing in my model class:

def bar
  @bar ||= init_bar
end
def bar=(value)
  attribute_will_change!('bar') if bar != value
  @bar = value
end
def bar_changed?
  changed.include?('bar')
end

The init_bar method is just used to initialise the attribute. You may or may not need it.

I didn't need to specify any other method (such as define_attribute_methods) or include any modules.
You do have to reimplement some of the methods yourself, but at least the behaviour will be mostly consistent with ActiveModel.

I admit I haven't tested it thoroughly yet, but so far I've encountered no issues.

ゝ杯具 2024-11-09 02:09:00

ActiveRecord 具有 #attribute 方法 (source) 一旦从您的类调用,将让 ActiveModel::Dirty 创建方法,例如 bar_wasbar_changed?许多其他

因此,您必须在从 ActiveRecord(对于最新版本的 Rails 为 ApplicationRecord)扩展的任何类中调用 attribute :bar,以便在 bar 上创建这些辅助方法。

编辑:请注意,此方法不应与 attr_accessor :bar 混合

编辑 2:另一个注意事项是使用 attribute 定义的非持久属性(例如 attribute :bar, : string)将在保存时被吹走。如果您需要 attrs 在保存后保留(就像我所做的那样),您实际上可以(小心)与 attr_reader 混合,如下所示:

attr_reader :bar
attribute :bar, :string

def bar=(val)
  super
  @bar = val
end

ActiveRecord has the #attribute method (source) which once invoked from your class will let ActiveModel::Dirty to create methods such as bar_was, bar_changed?, and many others.

Thus you would have to call attribute :bar within any class that extends from ActiveRecord (or ApplicationRecord for most recent versions of Rails) in order to create those helper methods upon bar.

Edit: Note that this approach should not be mixed with attr_accessor :bar

Edit 2: Another note is that unpersisted attributes defined with attribute (eg attribute :bar, :string) will be blown away on save. If you need attrs to hang around after save (as I did), you actually can (carefully) mix with attr_reader, like so:

attr_reader :bar
attribute :bar, :string

def bar=(val)
  super
  @bar = val
end
恋竹姑娘 2024-11-09 02:09:00

我找到了一个适合我的解决方案...

将此文件另存为 lib/active_record/nonpersisted_attribute_methods.rb: https://gist.github.com/4600209

然后你可以这样做:

require 'active_record/nonpersisted_attribute_methods'
class Foo < ActiveRecord::Base
  include ActiveRecord::NonPersistedAttributeMethods
  define_nonpersisted_attribute_methods [:bar]
end

foo = Foo.new
foo.bar = 3
foo.bar_changed? # => true
foo.bar_was # => nil
foo.bar_change # => [nil, 3]
foo.changes[:bar] # => [nil, 3]

但是,当我们这样做时,看起来我们会收到警告:

DEPRECATION WARNING: You're trying to create an attribute `bar'. Writing arbitrary attributes on a model is deprecated. Please just use `attr_writer` etc.

所以我不知道这种方法在 Rails 4 中是否会被破坏或者变得更加困难......

I figured out a solution that worked for me...

Save this file as lib/active_record/nonpersisted_attribute_methods.rb: https://gist.github.com/4600209

Then you can do something like this:

require 'active_record/nonpersisted_attribute_methods'
class Foo < ActiveRecord::Base
  include ActiveRecord::NonPersistedAttributeMethods
  define_nonpersisted_attribute_methods [:bar]
end

foo = Foo.new
foo.bar = 3
foo.bar_changed? # => true
foo.bar_was # => nil
foo.bar_change # => [nil, 3]
foo.changes[:bar] # => [nil, 3]

However, it looks like we get a warning when we do it this way:

DEPRECATION WARNING: You're trying to create an attribute `bar'. Writing arbitrary attributes on a model is deprecated. Please just use `attr_writer` etc.

So I don't know if this approach will break or be harder in Rails 4...

烂柯人 2024-11-09 02:09:00

自己编写 bar= 方法并使用实例变量来跟踪更改。

def bar=(value)
  @bar_changed = true
  @bar = value
end

def bar_changed?
  if @bar_changed
    @bar_changed = false
    return true
  else
    return false
  end
end

Write the bar= method yourself and use an instance variable to track changes.

def bar=(value)
  @bar_changed = true
  @bar = value
end

def bar_changed?
  if @bar_changed
    @bar_changed = false
    return true
  else
    return false
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文