Как определить изменения атрибута из модели?

Я хотел бы создать функцию обратного вызова в рельсах, которая выполняется после сохранения модели.

У меня есть эта модель, утверждающая, что имеет атрибут "статус", который изменяется в зависимости от состояния претензии, возможные значения ожидаются, одобряются, утверждаются, отклоняются

В базе данных есть "состояние" со значением по умолчанию "ожидающий".

Я хочу выполнить определенные задачи после того, как модель будет создана в первый раз или обновлена ​​из одного состояния в другое, в зависимости от того, из какого состояния оно изменяется.

Моя идея - иметь функцию в модели:

    after_save :check_state

    def check_state
      # if status changed from nil to pending (created)
      do this

      # if status changed from pending to approved
      performthistask
     end

Мой вопрос: как проверить предыдущее значение перед изменением в модели?

Ответ 1

Вы должны посмотреть модуль ActiveModel:: Dirty: Вы должны выполнить следующие действия над своей моделью претензии:

claim.status_changed?  # returns true if 'status' attribute has changed
claim.status_was       # returns the previous value of 'status' attribute
claim.status_change    # => ['old value', 'new value'] returns the old and 
                       # new value for 'status' attribute

claim.name = 'Bob'
claim.changed # => ["name"]
claim.changes # => {"name" => ["Bill", "Bob"]}

О! радости рельсов!

Ответ 2

вы можете использовать этот

self.changed

он возвращает массив всех столбцов, которые были изменены в этой записи

вы также можете использовать

self.changes

который возвращает хеш столбцов, которые были изменены, и до и после результатов в виде массивов

Ответ 3

Я рекомендую вам посмотреть один из доступных плагинов конечного автомата:

Любой из них позволит вам устанавливать состояния и переходы между состояниями. Очень полезный и простой способ удовлетворить ваши требования.

Ответ 4

Я видел, как вопрос поднимался во многих местах, поэтому я написал для него крошечный рубигем, чтобы сделать код немного приятнее (и везде избегайте утверждений if/else): https://github.com/ronna-s/on_change. Надеюсь, что это поможет.

Ответ 5

Вам будет намного лучше использовать хорошо протестированное решение, например state_machine gem.

Ответ 6

Для Rails 5. 1+, вы должны использовать метод атрибута активной записи.

saved_change_to_attribute? (attr_name, ** options) '

Изменился ли этот атрибут при последнем сохранении? Этот метод может быть вызван как saved_change_to_name? вместо saved_change_to_attribute?("name"). Ведет себя аналогично attribute_changed? , Этот метод полезен после обратных вызовов, чтобы определить, изменил ли вызов сохранения определенный атрибут.

Опции

from При передаче этот метод будет возвращать false, если исходное значение не равно заданному параметру

to При передаче, этот метод возвращает ложь, если значение не было изменено в заданное значение

Таким образом, ваша модель будет выглядеть следующим образом, если вы хотите вызвать какой-либо метод, основанный на изменении значения атрибута:

class Claim < ApplicationRecord

  after_save: :do_this, if: Proc.new { saved_change_to_status?(from: nil, to: 'pending') }

  after_save: :do_that, if: Proc.new { saved_change_to_status?(from: 'pending', to: 'approved') }


  def do_this
    ..
    ..
  end

  def do_that
    ..
    ..
  end

end

И если вы не хотите проверять изменение значения в обратном вызове, вы можете сделать следующее:

class Claim < ApplicationRecord

  after_save: :do_this, if: saved_change_to_status?


  def do_this
    ..
    ..
  end

end