Avec Observers officiellement retiré de Rails 4.0 , je suis curieux de savoir ce que les autres développeurs utilisent à leur place. (Autre que l'utilisation de la gemme extraite.) Alors que les observateurs étaient certainement maltraités et pouvaient facilement devenir parfois difficiles à manier, il y avait de nombreux cas d'utilisation en dehors du simple effacement de cache où ils étaient bénéfiques.
Prenons, par exemple, une application qui doit suivre les modifications d'un modèle. Un observateur pourrait facilement surveiller les changements sur le modèle A et enregistrer ces changements avec le modèle B dans la base de données. Si vous souhaitez surveiller les changements sur plusieurs modèles, un seul observateur peut gérer cela.
Dans Rails 4, je suis curieux de savoir quelles stratégies les autres développeurs utilisent à la place des observateurs pour recréer cette fonctionnalité.
Personnellement, je penche vers une sorte d'implémentation de "gros contrôleur", où ces changements sont suivis dans la méthode de création / mise à jour / suppression de chaque contrôleur de modèles. Bien que cela gonfle légèrement le comportement de chaque contrôleur, cela aide à la lisibilité et à la compréhension car tout le code est au même endroit. L'inconvénient est qu'il existe maintenant un code très similaire dispersé dans plusieurs contrôleurs. Extraire ce code dans des méthodes d'assistance est une option, mais il vous reste toujours des appels à ces méthodes éparpillés partout. Pas la fin du monde, mais pas tout à fait dans l'esprit des "maigres contrôleurs" non plus.
Les rappels ActiveRecord sont une autre option possible, bien que je n'aime pas personnellement car elle a tendance à coupler deux modèles différents trop étroitement à mon avis.
Donc, dans le monde Rails 4, sans observateurs, si vous deviez créer un nouvel enregistrement après qu'un autre enregistrement a été créé / mis à jour / détruit, quel modèle de conception utiliseriez-vous? De gros contrôleurs, des rappels ActiveRecord ou autre chose?
Je vous remercie.
Réponses:
Jetez un œil aux préoccupations
Créez un dossier dans votre répertoire de modèles appelé préoccupations. Ajoutez-y un module:
Ensuite, incluez cela dans les modèles dans lesquels vous souhaitez exécuter after_save:
Selon ce que vous faites, cela pourrait vous rapprocher sans observateurs.
la source
Ils sont maintenant dans un plugin .
Puis-je également recommander une alternative qui vous donnera des contrôleurs comme:
la source
ActiveSupport::Notifications
sont orientés vers l'instrumentation, pas les sous / pub génériques.ActiveSupport::Notifications
?Notifications
beaucoup utilisé, mais je dirais que j'aiWisper
une API et des fonctionnalités plus agréables telles que «abonnés globaux», «sur le préfixe» et «mappage d'événements», ce quiNotifications
n'est pas le cas. Une future version deWisper
permettra également la publication asynchrone via SideKiq / Resque / Celluloid. De plus, potentiellement, dans les futures versions de Rails, l'API deNotifications
pourrait changer pour être plus axée sur l'instrumentation.Ma suggestion est de lire l'article de blog de James Golick à http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html (essayez d'ignorer comment impudique le titre sonne).
À l'époque, tout était "gros modèle, contrôleur maigre". Ensuite, les gros modèles sont devenus un casse-tête géant, en particulier lors des tests. Plus récemment, la poussée a été pour les modèles maigres - l'idée étant que chaque classe devrait assumer une responsabilité et que le travail d'un modèle est de conserver vos données dans une base de données. Alors, où finit toute ma logique métier complexe? Dans les classes de logique métier - classes qui représentent des transactions.
Cette approche peut se transformer en bourbier (giggity) lorsque la logique commence à se compliquer. Le concept est cependant valable - au lieu de déclencher des choses implicitement avec des rappels ou des observateurs difficiles à tester et à déboguer, déclenchez les choses explicitement dans une classe qui superpose la logique à votre modèle.
la source
L'utilisation de rappels d'enregistrement actifs inverse simplement la dépendance de votre couplage. Par exemple, si vous avez
modelA
un style et des rails d'CacheObserver
observationmodelA
3, vous pouvez le supprimerCacheObserver
sans problème. Maintenant, dites à la placeA
d'invoquer manuellement laCacheObserver
sauvegarde après, qui serait des rails 4. Vous avez simplement déplacé votre dépendance pour pouvoir la supprimer en toute sécurité,A
mais pasCacheObserver
.Maintenant, depuis ma tour d'ivoire, je préfère que l'observateur dépende du modèle qu'il observe. Est-ce que je m'en soucie suffisamment pour encombrer mes contrôleurs? Pour moi, la réponse est non.
Vraisemblablement, vous avez réfléchi aux raisons pour lesquelles vous voulez / avez besoin de l'observateur, et donc créer un modèle dépendant de son observateur n'est pas une terrible tragédie.
J'ai aussi un dégoût (raisonnablement fondé, je pense) pour toute sorte d'observateur dépendant de l'action d'un contrôleur. Tout à coup, vous devez injecter à votre observateur toute action de contrôleur (ou un autre modèle) qui peut mettre à jour le modèle que vous souhaitez observer. Si vous pouvez garantir que votre application ne modifiera jamais les instances que via des actions de création / mise à jour de contrôleur, plus de puissance pour vous, mais ce n'est pas une hypothèse que je ferais à propos d'une application rails (envisagez des formulaires imbriqués, des associations de mise à jour de la logique métier de modèle, etc.)
la source
Wisper est une excellente solution. Ma préférence personnelle pour les rappels est qu'ils sont déclenchés par les modèles, mais les événements ne sont écoutés que lorsqu'une demande arrive, c'est-à-dire que je ne veux pas que les rappels soient déclenchés pendant que je configure des modèles dans les tests, etc. mais je les veux tiré chaque fois que les contrôleurs sont impliqués. C'est vraiment facile à configurer avec Wisper car vous pouvez lui dire de n'écouter que les événements à l'intérieur d'un bloc.
la source
Dans certains cas, j'utilise simplement l' instrumentation de support actif
la source
Mon alternative à Rails 3 Observers est une implémentation manuelle qui utilise un rappel défini dans le modèle mais parvient à (comme le déclare agmin dans sa réponse ci-dessus) "inverser la dépendance ... couplage".
Mes objets héritent d'une classe de base qui permet d'enregistrer les observateurs:
(Certes, dans l'esprit de la composition plutôt que de l'héritage, le code ci-dessus pourrait être placé dans un module et mélangé dans chaque modèle.)
Un initialiseur enregistre les observateurs:
Chaque modèle peut ensuite définir ses propres événements observables, au-delà des rappels ActiveRecord de base. Par exemple, mon modèle utilisateur expose 2 événements:
Tout observateur qui souhaite recevoir des notifications pour ces événements doit simplement (1) s'inscrire avec le modèle qui expose l'événement et (2) avoir une méthode dont le nom correspond à l'événement. Comme on pouvait s'y attendre, plusieurs observateurs peuvent s'inscrire pour le même événement et (en référence au deuxième paragraphe de la question initiale) un observateur peut surveiller les événements sur plusieurs modèles.
Les classes d'observateurs NotificationSender et ProfilePictureCreator ci-dessous définissent des méthodes pour les événements exposés par divers modèles:
Une mise en garde est que les noms de tous les événements exposés dans tous les modèles doivent être uniques.
la source
Je pense que le problème avec les observateurs étant obsolètes n'est pas que les observateurs étaient mauvais en eux-mêmes mais qu'ils étaient maltraités.
Je vous déconseille d'ajouter trop de logique dans vos rappels ou de simplement déplacer du code pour simuler le comportement d'un observateur alors qu'il existe déjà une solution solide à ce problème, le modèle Observer.
S'il est judicieux d'utiliser des observateurs, alors utilisez certainement des observateurs. Comprenez simplement que vous devrez vous assurer que votre logique d'observateur suit de bonnes pratiques de codage, par exemple SOLID.
Le gem observer est disponible sur rubygems si vous souhaitez le rajouter à votre projet https://github.com/rails/rails-observers
voir ce bref fil, bien que pas une discussion complète complète, je pense que l'argument de base est valable. https://github.com/rails/rails-observers/issues/2
la source
Vous pouvez essayer https://github.com/TiagoCardoso1983/association_observers . Il n'est pas encore testé pour les rails 4 (qui n'a pas encore été lancé), et nécessite plus de collaboration, mais vous pouvez vérifier s'il fait l'affaire pour vous.
la source
Pourquoi ne pas utiliser un PORO à la place?
La logique derrière cela est que vos «actions supplémentaires lors de l'enregistrement» seront probablement une logique métier. Ceci que j'aime garder séparé à la fois des modèles AR (qui devraient être aussi simples que possible) et des contrôleurs (qui sont gênants à tester correctement)
Et appelez-le simplement comme tel:
Vous pouvez même le développer en injectant des objets d'action supplémentaires après l'enregistrement
Et pour donner un exemple des «extras». Vous voudrez peut-être les épeler un peu:
Si vous aimez cette approche, je vous recommande de lire l'article de blog de Bryan Helmkamps 7 Patterns .
EDIT: Je dois également mentionner que la solution ci-dessus permet également d'ajouter une logique de transaction en cas de besoin. Par exemple avec ActiveRecord et une base de données prise en charge:
la source
Il convient de mentionner que le
Observable
module de la bibliothèque standard Ruby ne peut pas être utilisé dans des objets de type enregistrement actif depuis les méthodes d'instancechanged?
etchanged
sera en conflit avec celles deActiveModel::Dirty
.Rapport de bogue pour Rails 2.3.2
la source
J'ai le même problème! Je trouve une solution ActiveModel :: Dirty pour que vous puissiez suivre les changements de votre modèle!
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
la source