Execute um callback apenas se um atributo foi alterado no Rails

94

Tenho a seguinte associação em meu aplicativo:

# Page 
belongs_to :status

Quero executar um retorno de chamada sempre que o status_idde a pagefor alterado.

Então, se page.status_idfor de 4 para 5, eu quero conseguir pegar isso.

Como fazer isso?

Hommer Smith
fonte

Respostas:

198

Rails 5.1+

class Page < ActiveRecord::Base
  before_save :do_something, if: :will_save_change_to_status_id?

  private

  def do_something
    # ...
  end
end

O commit que alterou ActiveRecord :: Dirty está aqui: https://github.com/rails/rails/commit/16ae3db5a5c6a08383b974ae6c96faac5b4a3c81

Aqui está uma postagem do blog sobre essas mudanças: https://www.ombulabs.com/blog/rails/upgrades/active-record-5-1-api-changes.html

Aqui está o resumo que fiz para mim mesmo nas mudanças em ActiveRecord :: Dirty in Rails 5.1+:

ActiveRecord :: Dirty

https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Dirty.html

Antes de salvar (ALTERAÇÃO OPCIONAL)

Depois de modificar um objeto e antes de salvá-lo no banco de dados ou dentro do before_savefiltro:

  • changes agora deveria ser changes_to_save
  • changed? agora deveria ser has_changes_to_save?
  • changed agora deveria ser changed_attribute_names_to_save
  • <attribute>_change agora deveria ser <attribute>_change_to_be_saved
  • <attribute>_changed? agora deveria ser will_save_change_to_<attribute>?
  • <attribute>_was agora deveria ser <attribute>_in_database

Depois de salvar (BREAKING CHANGE)

Depois de modificar um objeto e depois de salvá-lo no banco de dados ou dentro do after_savefiltro:

  • saved_changes(substitui previous_changes)
  • saved_changes?
  • saved_change_to_<attribute>
  • saved_change_to_<attribute>?
  • <attribute>_before_last_save

Rails <= 5,0

class Page < ActiveRecord::Base
  before_save :do_something, if: :status_id_changed?

  private

  def do_something
    # ...
  end
end

Isso utiliza o fato de que o before_saveretorno de chamada pode ser executado condicionalmente com base no valor de retorno de uma chamada de método. O status_id_changed?método vem de ActiveModel :: Dirty , que nos permite verificar se um atributo específico foi alterado simplesmente anexando_changed? ao nome do atributo.

Quando o do_somethingmétodo deve ser chamado, depende de suas necessidades. Pode ser before_saveou after_savequalquer um dos ActiveRecord :: Callbacks definidos .

pdobb
fonte
4
Esta solução está obsoleta nas versões mais recentes.
Mateus Luiz
6
Atualizado com Rails 5.1+ info.
pdobb
16

O attribute_changed?está obsoleto no Rails 5.1, agora é só usar will_save_change_to_attribute?.

Para obter mais informações, consulte este problema .

Mateus luiz
fonte