Je configure un rappel after_save dans mon observateur de modèle pour envoyer une notification uniquement si l'attribut publié du modèle est passé de false à true. Comme les méthodes telles que modifié? ne sont utiles que avant l'enregistrement du modèle, voici comment j'essaie (et sans succès) de le faire:
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
Quelqu'un a-t-il des suggestions quant à la meilleure façon de gérer cela, en utilisant de préférence des rappels d'observateurs de modèle (afin de ne pas polluer le code de mon contrôleur)?
Dans votre after_update
filtre sur le modèle que vous pouvez utiliser _changed?
accesseur (au moins dans Rails 3, pas sûr de Rails 2)]. Ainsi, par exemple:
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
Ça fonctionne.
Pour ceux qui veulent connaître les modifications apportées à un after_save
rappeler:
model.saved_changes
model.previous_changes
Voir aussi: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes
À tous ceux qui le verront plus tard, comme il le fait actuellement (août 2017) en tête de Google: il est intéressant de noter que ce comportement sera modifié dans Rails 5.2 , et a des avertissements de dépréciation à partir de Rails 5.1, comme ActiveModel :: Dirty changé un peu.
Qu'est-ce que je change?
Si vous utilisez attribute_changed?
méthode dans le after_*
- callbacks, vous verrez un avertissement comme:
AVERTISSEMENT DE DEPRECATION: Le comportement de
attribute_changed?
à l'intérieur de après les rappels seront modifiés dans la prochaine version de Rails. La nouvelle valeur de retour reflétera le comportement de l'appel de la méthode après le retour desave
(par exemple, l'opposé de ce qu'elle retourne maintenant). Pour conserver le comportement actuel, utilisezsaved_change_to_attribute?
au lieu. (appelé de some_callback à /PATH_TO/app/models/user.rb:15)
Comme il est mentionné, vous pouvez résoudre ce problème facilement en remplaçant la fonction par saved_change_to_attribute?
. Ainsi, par exemple, name_changed?
devient saved_change_to_name?
.
De même, si vous utilisez le attribute_change
pour obtenir les valeurs avant-après, cela change également et génère ce qui suit:
AVERTISSEMENT DE DEPRECATION: Le comportement de
attribute_change
à l'intérieur de après les rappels seront modifiés dans la prochaine version de Rails. La nouvelle valeur de retour reflétera le comportement de l'appel de la méthode après le retour desave
(par exemple, l'opposé de ce qu'elle retourne maintenant). Pour conserver le comportement actuel, utilisezsaved_change_to_attribute
au lieu. (appelé de some_callback à /PATH_TO/app/models/user.rb:20)
De nouveau, comme elle le mentionne, la méthode change de nom et devient saved_change_to_attribute
qui retourne ["old", "new"]
. Ou utiliser saved_changes
, qui renvoie toutes les modifications, accessibles en tant que saved_changes['attribute']
.
Si vous pouvez le faire sur before_save
au lieu de after_save
, vous pourrez utiliser ceci:
self.changed
il retourne un tableau de toutes les colonnes modifiées dans cet enregistrement.
vous pouvez aussi utiliser:
self.changes
qui retourne un hachage de colonnes qui ont changé et avant et après les résultats sous forme de tableaux
La réponse "sélectionnée" n'a pas fonctionné pour moi. J'utilise Rails 3.1 avec CouchRest :: Model (basé sur le modèle actif). Les méthodes _changed?
Ne renvoient pas la valeur true pour les attributs modifiés dans le paramètre after_update
crochet, seulement dans le crochet before_update
. J'ai réussi à le faire fonctionner avec le crochet (nouveau?) around_update
:
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
vous pouvez ajouter une condition à la after_update
ainsi:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
il n'est pas nécessaire d'ajouter une condition dans le send_notification
méthode elle-même.
J'utilise ceci pour extraire un hachage avec les nouvelles valeurs d'attribut, ce qui m'est utile pour mettre à jour d'autres modèles
attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}
Le
attr[1].last == false
est nécessaire lorsque la nouvelle valeur est false
, où l'affectation renvoie false et "hash" non renvoyé.
Je suppose qu'il y a un moyen plus facile, je suis nouveau sur Rails