Отслеживание грязных атрибутов, которые не были сохранены в объекте ActiveRecord в рельсах - PullRequest
26 голосов
/ 14 апреля 2011

У меня есть объект, который наследуется от ActiveRecord, но у него есть атрибут, который не сохраняется в БД, например:

 class Foo < ActiveRecord::Base
   attr_accessor :bar
 end

Я хотел бы иметь возможность отслеживать изменения в 'bar',с помощью таких методов, как 'bar_changed?', предоставленных ActiveModel Dirty.Проблема в том, что когда я пытаюсь реализовать Dirty на этом объекте, как описано в документах , я получаю сообщение об ошибке, так как ActiveRecord и ActiveModel определили define_attribute_methods, но с разным количеством параметров,поэтому я получаю сообщение об ошибке при попытке вызвать define_attribute_methods [:bar].

Я пытался создать псевдоним define_attribute_methods перед включением ActiveModel::Dirty, но безуспешно: я получаю ошибку не определенного метода.

Есть идеи, как с этим бороться?Конечно, я мог написать необходимые методы вручную, но мне было интересно, возможно ли это сделать с помощью модулей Rails, расширив функциональность ActiveModel до атрибутов, не обрабатываемых ActiveRecord.

Ответы [ 4 ]

41 голосов
/ 16 мая 2012

Я использую метод 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.

Признаюсь, я еще не проверил его полностью, но до сих пор не столкнулся с какими-либо проблемами.

2 голосов
/ 23 января 2013

Я нашел решение, которое сработало для меня ...

Сохранить этот файл как 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 ...

1 голос
/ 09 января 2019

ActiveRecord имеет метод #attribute ( source ), который после вызова из вашего класса позволит ActiveModel::Dirty создавать такие методы, как bar_was, bar_changed? и manyдругие .

Таким образом, вам придется вызывать attribute :bar внутри любого класса, который простирается от ActiveRecord (или ApplicationRecord для самых последних версий Rails), чтобы создать эти вспомогательные методы на bar.

0 голосов
/ 16 апреля 2011

Напишите метод 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
...