ruby-on-rails - tutorial - ruby on rails windows




Rails Observer Alternatives pour 4.0 (8)

Avec les observateurs officiellement retirés 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 ont certainement été abusés et pourraient facilement devenir parfois maladroits, il y avait beaucoup de cas d'utilisation en dehors de la simple mise en cache où ils étaient bénéfiques.

Prenez, par exemple, une application qui doit suivre les modifications apportées à 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 me tourne vers une sorte d'implémentation de "contrôleur de graisse", où ces changements sont suivis dans la méthode create / update / delete de chaque contrôleur de modèle. Bien que cela dégrade légèrement le comportement de chaque contrôleur, cela aide à la lisibilité et à la compréhension car tout le code se trouve au même endroit. L'inconvénient est qu'il y a maintenant du code qui est très similaire sur plusieurs contrôleurs. L'extraction de ce code dans les méthodes d'aide est une option, mais il vous reste encore des appels à ces méthodes dispersés partout. Pas la fin du monde, mais pas tout à fait dans l'esprit des "contrôleurs skinny" non plus.

Les callbacks d'ActiveRecord sont une autre option possible, bien que je n'aime pas personnellement car elle tend à coupler deux modèles différents trop étroitement ensemble à mon avis.

Donc, dans le monde de Rails 4, no-Observers, si vous deviez créer un nouvel enregistrement après qu'un autre enregistrement ait été créé / mis à jour / détruit, quel motif utiliseriez-vous? Les contrôleurs Fat, les callbacks ActiveRecord, ou autre chose entièrement?

Je vous remercie.


Dans certains cas, j'utilise simplement Active Support Instrumentation

ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
  # do your stuff here
end

ActiveSupport::Notifications.subscribe "my.custom.event" do |*args|
  data = args.extract_options! # {:this=>:data}
end


J'ai le même problème! Je trouve une solution ActiveModel :: Dirty pour que vous puissiez suivre vos changements de modèle!

include ActiveModel::Dirty
before_save :notify_categories if :data_changed? 


def notify_categories
  self.categories.map!{|c| c.update_results(self.data)}
end

http://api.rubyonrails.org/classes/ActiveModel/Dirty.html


Je pense que le fait que les Observateurs soient désapprouvés 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 simplement de déplacer du code pour simuler le comportement d'un observateur alors qu'il existe déjà une solution saine à ce problème, le pattern Observer.

Si cela a du sens d'utiliser des observateurs, alors utilisez des observateurs. Comprenez simplement que vous devrez vous assurer que votre logique d'observateur suit les pratiques de codage sonore, par exemple SOLID.

La gemme d'observateur est disponible sur rubygems si vous voulez l'ajouter à 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


L'utilisation de rappels d'enregistrements actifs inverse simplement la dépendance de votre couplage. Par exemple, si vous avez modelA et que CacheObserver observe le style modelA rails 3, vous pouvez supprimer CacheObserver sans problème. Maintenant, disons plutôt que A doit appeler manuellement le CacheObserver après la sauvegarde, ce qui serait des rails 4. Vous avez simplement déplacé votre dépendance pour que vous puissiez supprimer en toute sécurité A mais pas CacheObserver .

Maintenant, de ma tour d'ivoire, je préfère que l'observateur dépende du modèle qu'il observe. Est-ce que je m'inquiète assez pour encombrer mes contrôleurs? Pour moi, la réponse est non.

Vous avez probablement réfléchi à la raison pour laquelle 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 également un dégoût (raisonnablement fondé, je pense) pour tout type d'observateur dépendant d'une action du contrôleur. Soudain, vous devez injecter votre observateur dans toute action du contrôleur (ou un autre modèle) qui peut mettre à jour le modèle que vous voulez observer. Si vous pouvez garantir que votre application ne modifiera les instances que via des actions de création / mise à jour de contrôleur, plus de puissance, mais ce n'est pas une hypothèse que je ferais à propos d'une application rails.


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 immodest le titre sonne).

Retour dans la journée tout était "gros modèle, contrôleur maigre". Ensuite, les gros modèles sont devenus un véritable casse-tête, surtout pendant les tests. Plus récemment, la poussée a été pour les modèles skinny - l'idée étant que chaque classe devrait gérer une responsabilité et le travail d'un modèle est de persister vos données dans une base de données. Alors, d'où vient toute ma logique métier complexe? Dans les classes de logique métier - classes représentant des transactions.

Cette approche peut se transformer en un bourbier (giggity) lorsque la logique commence à se compliquer. Le concept est solide - au lieu de déclencher implicitement des choses avec des rappels ou des observateurs difficiles à tester et à déboguer, déclenchez les choses explicitement dans une classe qui couche la logique au-dessus de votre modèle.


Pourquoi ne pas utiliser un PORO à la place?

La logique derrière cela est que vos «actions supplémentaires sur sauver» vont probablement être la logique métier. Ce que j'aime garder séparé des deux modèles AR (qui devrait être aussi simple que possible) et des contrôleurs (qui sont gênants pour tester correctement)

class LoggedUpdater

  def self.save!(record)
    record.save!
    #log the change here
  end

end

Et appelez simplement comme tel:

LoggedUpdater.save!(user)

Vous pouvez même développer en injectant des objets action post-save supplémentaires

LoggedUpdater.save(user, [EmailLogger.new, MongoLogger.new])

Et pour donner un exemple des «extras». Vous pourriez vouloir les spiffy un peu si:

class EmailLogger
  def call(msg)
    #send email with msg
  end
end

Si vous aimez cette approche, je vous recommande de lire le blog de Bryan Helmkamps 7 Patterns .

EDIT: Je devrais également mentionner que la solution ci-dessus permet d'ajouter la logique de transaction aussi bien que nécessaire. Par exemple, avec ActiveRecord et une base de données supportée:

class LoggedUpdater

  def self.save!([records])
    ActiveRecord::Base.transaction do
      records.each(&:save!)
      #log the changes here
    end
  end

end





ruby-on-rails-4