Pergunta

Estou configurando um retorno de chamada posterior no meu modelo para enviar uma notificação apenas se o modelo do modelo Publicados O atributo foi alterado de falso para verdadeiro. Uma vez que métodos como mudado? Só são úteis antes que o modelo seja salvo, a maneira como estou atualmente (e sem sucesso) tentando fazer isso é o seguinte:

def before_save(blog)
  @og_published = blog.published?
end

def after_save(blog)
  if @og_published == false and blog.published? == true
    Notification.send(...)
  end
end

Alguém tem alguma sugestão sobre a melhor maneira de lidar com isso, de preferência usando retornos de chamada do Model Observer (para não poluir meu código do controlador)?

Foi útil?

Solução

Na tua after_update filtre no modelo que você posso usar _changed? acessador (pelo menos no Rails 3, não tenho certeza dos trilhos 2). Por exemplo:

class SomeModel < ActiveRecord::Base
  after_update :send_notification_after_change

  def send_notification_after_change
    Notification.send(...) if (self.published_changed? && self.published == true)
  end

end

Apenas funciona.

Outras dicas

Para aqueles que querem saber as mudanças feitas em um after_save ligue de volta:

Rails 5.1 e maior

model.saved_changes

Rails <5.1

model.previous_changes

Veja também: http://api.rubyonrails.org/classes/activeModel/dirty.html#method-i-previous_changes

Para qualquer pessoa que veja isso mais tarde, como atualmente (agosto de 2017) supera o Google: vale a pena mencionar, que esse comportamento será alterado em Rails 5.2, e tem avisos de depreciação a partir de Rails 5.1, como ActiveModel :: Dirty mudou um pouco.

O que eu mudo?

Se você está usando attribute_changed? Método no after_*-Callbacks, você verá um aviso como:

Aviso de depreciação: o comportamento de attribute_changed? Dentro de retornos de chamada após a mudança na próxima versão do Rails. O novo valor de retorno refletirá o comportamento de chamar o método depois save retornou (por exemplo, o oposto do que retorna agora). Para manter o comportamento atual, use saved_change_to_attribute? em vez de. (chamado de algum_callback em /path_to/app/models/user.rb:15)

Como menciona, você pode consertar isso facilmente substituindo a função por saved_change_to_attribute?. Por exemplo, name_changed? torna-se saved_change_to_name?.

Da mesma forma, se você está usando o attribute_change Para obter os valores anteriores, isso também muda e joga o seguinte:

Aviso de depreciação: o comportamento de attribute_change Dentro de retornos de chamada após a mudança na próxima versão do Rails. O novo valor de retorno refletirá o comportamento de chamar o método depois save retornou (por exemplo, o oposto do que retorna agora). Para manter o comportamento atual, use saved_change_to_attribute em vez de. (chamado de algum_callback em /path_to/app/models/user.rb:20)

Novamente, como menciona, o método muda o nome para saved_change_to_attribute que retorna ["old", "new"]. ou uso saved_changes, que retorna todas as mudanças, e elas podem ser acessadas como saved_changes['attribute'].

Caso você possa fazer isso em before_save ao invés de after_save, você poderá usar isso:

self.changed

Ele retorna uma matriz de todas as colunas alteradas neste registro.

você também pode usar:

self.changes

que retorna um hash de colunas que mudaram e antes e depois dos resultados como matrizes

A resposta "selecionada" não funcionou para mim. Estou usando o Rails 3.1 com o modelo Couchrest :: (com base no modelo ativo). o _changed? métodos não retornam true para atributos alterados no after_update gancho, apenas no before_update gancho. Consegui fazer com que funcione usando o (novo?) around_update gancho:

class SomeModel < ActiveRecord::Base
  around_update :send_notification_after_change

  def send_notification_after_change
    should_send_it = self.published_changed? && self.published == true

    yield

    Notification.send(...) if should_send_it
  end

end

Você pode adicionar uma condição ao after_update igual a:

class SomeModel < ActiveRecord::Base
  after_update :send_notification, if: :published_changed?

  ...
end

não há necessidade de adicionar uma condição dentro do send_notification próprio método.

Estou usando isso para extrair um hash com os novos valores de atributo, o que é útil para eu atualizar outros modelos

attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}

o

attr[1].last == false

é necessário quando um novo valor é false, onde a tarefa retorna falsa e "hash" não é devolvida.

Eu acho que há uma maneira mais fácil, sou novo no Rails

Você apenas adiciona um acessador que define o que você muda

class Post < AR::Base
  attr_reader :what_changed

  before_filter :what_changed?

  def what_changed?
    @what_changed = changes || []
  end

  after_filter :action_on_changes

  def action_on_changes
    @what_changed.each do |change|
      p change
    end
  end
end
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top