Rails 3 проверяет, изменился ли атрибут

Необходимо проверить, изменился ли блок атрибутов перед обновлением в Rails 3.

street1, street2, city, state, zipcode

Я знаю, что могу использовать что-то вроде

if @user.street1 != params[:user][:street1]
  then do something....
end

Но эта часть кода будет ДЕЙСТВИТЕЛЬНО длинной. Есть ли более чистый способ?

Ответ 1

Откажитесь от ActiveModel:: Dirty (доступно по умолчанию для всех моделей). Документация действительно хорошая, но она позволяет вам делать такие вещи, как:

@user.street1_changed? # => true/false

Ответ 2

Вот как я решил проблему проверки изменений в нескольких атрибутах.

attrs = ["street1", "street2", "city", "state", "zipcode"]

if (@user.changed & attrs).any?
  then do something....
end

Метод changed возвращает массив атрибутов, измененных для этого объекта.

Оба @user.changed и attrs являются массивами, поэтому я могу получить пересечение (см. метод ary & other ary). Результатом пересечения является массив. Вызывая any? в массиве, я получаю true, если есть хотя бы одно пересечение.

Также очень полезно, метод changed_attributes возвращает хэш атрибутов с их исходными значениями и changes возвращает хэш атрибутов с их исходными и новыми значениями (в массиве).

Вы можете проверить APIDock, какие версии поддерживали эти методы.

http://apidock.com/rails/ActiveModel/Dirty

Ответ 3

ActiveModel::Dirty не работал у меня, потому что @model.update_attributes() спрятал изменения. Так вот как я обнаружил изменения в методе update в контроллере:

def update
  @model = Model.find(params[:id])
  detect_changes

  if @model.update_attributes(params[:model])
    do_stuff if attr_changed?
  end
end

private

def detect_changes
  @changed = []
  @changed << :attr if @model.attr != params[:model][:attr]
end

def attr_changed?
  @changed.include :attr
end

Если вы пытаетесь обнаружить множество изменений атрибутов, это может стать беспорядочным. Вероятно, не следует делать это в контроллере, но meh.