Comment utiliser les préoccupations dans Rails 4

Le générateur de projet Rails 4 par défaut crée désormais le répertoire “préoccupations” sous les contrôleurs et les modèles. J’ai trouvé quelques explications sur la façon d’utiliser les problèmes de routage, mais rien sur les contrôleurs ou les modèles.

Je suis presque certain que cela a à voir avec la tendance actuelle de la DCI dans la communauté et je voudrais essayer.

La question est la suivante: comment dois-je utiliser cette fonctionnalité, existe-t-il une convention sur la façon de définir la hiérarchie de nommage / classe afin de la faire fonctionner? Comment puis-je inclure une préoccupation dans un modèle ou un contrôleur?

Alors je l’ai découvert moi-même. C’est en fait un concept assez simple mais puissant. Cela concerne la réutilisation du code comme dans l’exemple ci-dessous. Fondamentalement, l’idée est d’extraire des morceaux de code communs et / ou spécifiques au contexte afin de nettoyer les modèles et d’éviter qu’ils deviennent trop gros et désordonnés.

Par exemple, je vais mettre un motif bien connu, le pattern taggable:

# app/models/product.rb class Product include Taggable ... end # app/models/concerns/taggable.rb # notice that the file name has to match the module name # (applying Rails conventions for autoloading) module Taggable extend ActiveSupport::Concern included do has_many :taggings, as: :taggable has_many :tags, through: :taggings class_atsortingbute :tag_limit end def tags_ssortingng tags.map(&:name).join(', ') end def tags_ssortingng=(tag_ssortingng) tag_names = tag_ssortingng.to_s.split(', ') tag_names.each do |tag_name| tags.build(name: tag_name) end end # methods defined here are going to extend the class, not the instance of it module ClassMethods def tag_limit(value) self.tag_limit_value = value end end end 

Ainsi, à la suite de l’exemple de produit, vous pouvez append Taggable à n’importe quelle classe désirée et partager ses fonctionnalités.

C’est assez bien expliqué par DHP :

Dans Rails 4, nous invitons les programmeurs à utiliser les répertoires app / models / concern et app / controllers / préoccupations par défaut qui font automatiquement partie du chemin de chargement. Avec l’encapsuleur ActiveSupport :: Concern, il suffit juste de prendre en charge ce mécanisme de factorisation léger.

J’ai lu des articles sur l’utilisation des préoccupations des modèles pour peaufiner les gros modèles, ainsi que pour sécher vos codes de modèle. Voici une explication avec des exemples:

1) SÉCHAGE des codes de modèle

Considérons un modèle d’article, un modèle d’événement et un modèle de commentaire. Un article ou un événement contient de nombreux commentaires. Un commentaire appartient à un article ou à un événement.

Traditionnellement, les modèles peuvent ressembler à ceci:

Modèle de commentaire:

 class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true end 

Modèle d'article:

 class Article < ActiveRecord::Base has_many :comments, as: :commentable def find_first_comment comments.first(created_at DESC) end def self.least_commented #return the article with least number of comments end end 

Modèle d'événement

 class Event < ActiveRecord::Base has_many :comments, as: :commentable def find_first_comment comments.first(created_at DESC) end def self.least_commented #returns the event with least number of comments end end 

Comme nous pouvons le constater, il existe une partie importante du code commun à Event et Article. En utilisant les préoccupations, nous pouvons extraire ce code commun dans un module distinct Commentable.

Pour cela, créez un fichier commentable.rb dans app / models / concern.

 module Commentable extend ActiveSupport::Concern included do has_many :comments, as: :commentable end # for the given article/event returns the first comment def find_first_comment comments.first(created_at DESC) end module ClassMethods def least_commented #returns the article/event which has the least number of comments end end end 

Et maintenant, vos modèles ressemblent à ceci:

Modèle de commentaire:

 class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true end 

Modèle d'article:

 class Article < ActiveRecord::Base include Commentable end 

Modèle d'événement:

 class Event < ActiveRecord::Base include Commentable end 

2) Modèles de graisse pour la peau.

Considérons un modèle d'événement. Un événement a de nombreux participants et commentaires.

En règle générale, le modèle d'événement peut ressembler à ceci

 class Event < ActiveRecord::Base has_many :comments has_many :attenders def find_first_comment # for the given article/event returns the first comment end def find_comments_with_word(word) # for the given event returns an array of comments which contain the given word end def self.least_commented # finds the event which has the least number of comments end def self.most_attended # returns the event with most number of attendes end def has_attendee(attendee_id) # returns true if the event has the mentioned attendee end end 

Des modèles avec de nombreuses associations et ont tendance à accumuler de plus en plus de code et à devenir ingérable. Les préoccupations fournissent un moyen de peaufiner les gros modules en les rendant plus modulaires et faciles à comprendre.

Le modèle ci-dessus peut être modifié en utilisant les préoccupations suivantes: Créez un fichier attendable.rb et commentable.rb dans le dossier app / models / attendable.rb / event

attendable.rb

 module Attendable extend ActiveSupport::Concern included do has_many :attenders end def has_attender(attender_id) # returns true if the event has the mentioned attendee end module ClassMethods def most_attended # returns the event with most number of attendes end end end 

commentable.rb

 module Commentable extend ActiveSupport::Concern included do has_many :comments end def find_first_comment # for the given article/event returns the first comment end def find_comments_with_word(word) # for the given event returns an array of comments which contain the given word end module ClassMethods def least_commented # finds the event which has the least number of comments end end end 

Et maintenant, en utilisant les préoccupations, votre modèle d'événement réduit à

 class Event < ActiveRecord::Base include Commentable include Attendable end 

* En utilisant des préoccupations, il est conseillé d’utiliser le regroupement basé sur le «domaine» plutôt que le regroupement «technique». Le regroupement basé sur le domaine est comme «Commentable», «Photoable», «Attendable». Le regroupement technique signifiera "ValidationMethods", "FinderMethods", etc.

Cela vaut la peine de mentionner que l’utilisation de préoccupations est considérée comme une mauvaise idée par beaucoup.

  1. comme ce gars
  2. et celui-là

Certaines raisons:

  1. Il y a de la magie noire dans les coulisses – Concern est la méthode include patches, il y a tout un système de gestion des dépendances – beaucoup trop de complexité pour quelque chose de sortingvial bon vieux modèle de mixage Ruby.
  2. Vos cours ne sont pas moins secs. Si vous incorporez 50 méthodes publiques dans différents modules, votre classe a toujours 50 méthodes publiques, c’est juste que vous cachez cette odeur de code, en quelque sorte mettez vos déchets dans les tiroirs.
  3. Codebase est en fait plus difficile à naviguer avec toutes ces préoccupations.
  4. Etes-vous sûr que tous les membres de votre équipe ont la même compréhension de ce qui devrait vraiment remplacer la préoccupation?

Les préoccupations sont un moyen facile de se tirer dans la jambe, faites attention à eux.

Cet article m’a aidé à comprendre les préoccupations.

 # app/models/trader.rb class Trader include Shared::Schedule end # app/models/concerns/shared/schedule.rb module Shared::Schedule extend ActiveSupport::Concern ... end 

J’ai senti la plupart des exemples ici démontrer la puissance du module plutôt que la façon dont ActiveSupport::Concern ajoute de la valeur au module .

Exemple 1: modules plus lisibles.

Donc, sans souci, comment sera un module typique.

 module M def self.included(base) base.extend ClassMethods base.class_eval do scope :disabled, -> { where(disabled: true) } end end def instance_method ... end module ClassMethods ... end end 

Après refactoring avec ActiveSupport::Concern .

 require 'active_support/concern' module M extend ActiveSupport::Concern included do scope :disabled, -> { where(disabled: true) } end class_methods do ... end def instance_method ... end end 

Vous voyez que les méthodes d’instance, les méthodes de classe et les blocs inclus sont moins compliqués. Les préoccupations les injecteront de manière appropriée pour vous. C’est un avantage d’utiliser ActiveSupport::Concern .


Exemple 2: Gérez les dépendances des modules correctement.

 module Foo def self.included(base) base.class_eval do def self.method_injected_by_foo_to_host_klass ... end end end end module Bar def self.included(base) base.method_injected_by_foo_to_host_klass end end class Host include Foo # We need to include this dependency for Bar include Bar # Bar is the module that Host really needs end 

Dans cet exemple, Bar est le module dont Host réellement besoin. Mais comme Bar a une dépendance avec Foo la classe Host doit include Foo (mais attendez-vous à ce que Host veuille savoir à propos de Foo ? Peut-on l’éviter?).

Donc, Bar ajoute la dépendance partout où il va. Et l’ ordre d’inclusion compte aussi ici. Cela ajoute beaucoup de complexité / dépendance à une base de code énorme.

Après refactoring avec ActiveSupport::Concern

 require 'active_support/concern' module Foo extend ActiveSupport::Concern included do def self.method_injected_by_foo_to_host_klass ... end end end module Bar extend ActiveSupport::Concern include Foo included do self.method_injected_by_foo_to_host_klass end end class Host include Bar # It works, now Bar takes care of its dependencies end 

Maintenant, ça a l’air simple.

Si vous pensez pourquoi nous ne pouvons pas append la dépendance Foo dans le module Bar lui-même? Cela ne fonctionnera pas car method_injected_by_foo_to_host_klass doit être injecté en classe, y compris le module Bar non sur la Bar elle-même.

Source: Rails ActiveSupport :: Concern

En ce qui concerne make fichier nomfichier.rb

Par exemple, je veux dans mon application où l’atsortingbut create_by existe mise à jour la valeur par 1 et 0 pour updated_by

 module TestConcern extend ActiveSupport::Concern def checkatsortingbutes if self.has_atsortingbute?(:created_by) self.update_atsortingbutes(created_by: 1) end if self.has_atsortingbute?(:updated_by) self.update_atsortingbutes(updated_by: 0) end end end 

après cela, incluez dans votre modèle comme ceci:

 class Role < ActiveRecord::Base include TestConcern end