28

ActiveRecord から継承するオブジェクトがありますが、次のように DB に永続化されていない属性があります。

 class Foo < ActiveRecord::Base
   attr_accessor :bar
 end

ActiveModel Dirty が提供するように、「bar_changed?」などのメソッドを使用して、「bar」への変更を追跡できるようにしたいと考えています。問題は、 docsで説明されているように、このオブジェクトに Dirty を実装しようとすると、 ActiveRecord と ActiveModel の両方が定義されているdefine_attribute_methodsためエラーが発生しますが、パラメーターの数が異なるため、試行時にエラーが発生することですを呼び出すdefine_attribute_methods [:bar]

define_attribute_methodsを含める前にエイリアシングを試みActiveModel::Dirtyましたが、うまくいきませんでした: メソッドが定義されていないというエラーが発生します。

これに対処する方法についてのアイデアはありますか?もちろん、必要なメソッドを手動で記述することもできますが、Rails モジュールを使用して、ActiveModel の機能を ActiveRecord で処理されない属性に拡張することで、それが可能になるかどうか疑問に思っていました。

4

4 に答える 4

43

私はこの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 とほぼ一致します。

まだ完全にテストしていないことは認めますが、これまでのところ問題は発生していません。

于 2012-05-16T12:46:17.467 に答える
2

私は自分に合った解決策を見つけました...

このファイルを次の名前で保存します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 で壊れるか、難しくなるかどうかはわかりません...

于 2013-01-23T00:25:08.327 に答える
0

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
于 2011-04-16T00:56:37.927 に答える