OO Design in Rails: Où mettre les choses

J’apprécie vraiment Rails (même si je suis généralement sans REST), et j’apprécie que Ruby soit très OO. Cependant, la tendance à créer d’énormes sous-classes ActiveRecord et d’énormes contrôleurs est tout à fait naturelle (même si vous utilisez un contrôleur par ressource). Si vous deviez créer des univers d’objects plus profonds, où placeriez-vous les classes (et les modules, je suppose)? Je pose des questions sur les vues (dans les helpers eux-mêmes?), Les contrôleurs et les modèles.

Lib est correct, et j’ai trouvé des solutions pour le faire recharger dans un environnement de développement , mais j’aimerais savoir s’il existe une meilleure façon de procéder. Je suis vraiment préoccupé par le fait que les classes deviennent trop grandes. Aussi, qu’en est-il des moteurs et comment s’intègrent-ils?

Étant donné que Rails fournit une structure en termes de MVC, il est normal que vous finissiez par utiliser uniquement les conteneurs de modèle, de vue et de contrôleur qui vous sont fournis. L’idiome typique pour les débutants (et même certains programmeurs intermédiaires) est d’introduire toute la logique de l’application dans le modèle (classe de firebase database), le contrôleur ou la vue.

À un moment donné, quelqu’un fait remarquer que le paradigme «fat-model, skinny-controller» et les développeurs intermédiaires précipitent rapidement tout sur leurs contrôleurs et le jettent dans le modèle, qui commence à devenir une nouvelle poubelle pour la logique applicative.

Les maigres contrôleurs sont en fait une bonne idée, mais le corollaire – tout mettre dans le modèle, n’est pas vraiment le meilleur.

Dans Ruby, vous avez quelques bonnes options pour rendre les choses plus modulaires. Une réponse assez populaire consiste à utiliser des modules (généralement cachés dans lib ) qui contiennent des groupes de méthodes, puis à inclure les modules dans les classes appropriées. Cela vous aide dans les cas où vous avez des catégories de fonctionnalités que vous souhaitez réutiliser dans plusieurs classes, mais où la fonctionnalité est toujours liée aux classes.

Rappelez-vous, lorsque vous incluez un module dans une classe, les méthodes deviennent des méthodes d’instance de la classe, de sorte que vous vous retrouvez toujours avec une classe contenant une tonne de méthodes, elles sont simplement organisées en plusieurs fichiers.

Cette solution peut bien fonctionner dans certains cas – dans d’autres cas, vous allez vouloir utiliser des classes dans votre code qui ne sont pas des modèles, des vues ou des contrôleurs.

Un bon moyen d’y penser est le «principe de la responsabilité unique», selon lequel une classe devrait être responsable d’un seul (ou d’un petit nombre) de choses. Vos modèles sont responsables de la persistance des données de votre application dans la firebase database. Vos contrôleurs sont responsables de recevoir une demande et de retourner une réponse viable.

Si vous avez des concepts qui ne correspondent pas parfaitement à ces cases (persistance, gestion des demandes / réponses), vous voudrez probablement réfléchir à la manière dont vous modéliseriez l’idée en question. Vous pouvez stocker des classes non modèles dans des applications / classes ou ailleurs, et append ce répertoire à votre chemin de chargement en procédant comme suit:

 config.load_paths << File.join(Rails.root, "app", "classes") 

Si vous utilisez passager ou JRuby, vous souhaiterez probablement également append votre chemin aux chemins de chargement impatients:

 config.eager_load_paths << File.join(Rails.root, "app", "classes") 

L'essentiel est qu'une fois que vous êtes arrivé à un point dans Rails où vous vous posez cette question, il est temps de renforcer vos côtelettes Ruby et de commencer à modéliser des classes qui ne sont pas uniquement les classes MVC que Rails vous propose par défaut.

Mise à jour: cette réponse s'applique aux Rails 2.x et supérieurs.

Mise à jour : L’utilisation des problèmes a été confirmée comme la nouvelle valeur par défaut dans Rails 4 .

Cela dépend vraiment de la nature du module lui-même. Je place généralement des extensions de contrôleur / modèle dans un dossier / concern dans l’application.

 # concerns/authentication.rb module Authentication ... end # controllers/application_controller.rb class ApplicationController include Authentication end # concerns/configurable.rb module Configurable ... end class Model include Indexable end # controllers/foo_controller.rb class FooController < ApplicationController include Indexable end # controllers/bar_controller.rb class BarController < ApplicationController include Indexable end 

/ lib est mon choix préféré pour les bibliothèques à usage général. J'ai toujours un espace de noms de projet dans lib où je mets toutes les bibliothèques spécifiques aux applications.

 /lib/myapp.rb module MyApp VERSION = ... end /lib/myapp/CacheKey.rb /lib/myapp/somecustomlib.rb 

Les extensions de base Ruby / Rails ont généralement lieu dans les initialiseurs de configuration, de sorte que les bibliothèques ne sont chargées qu’une seule fois sur le boostrap Rails.

 /config/initializer/config.rb /config/initializer/core_ext/ssortingng.rb /config/initializer/core_ext/array.rb 

Pour les fragments de code réutilisables, je crée souvent des (micro) plugins pour pouvoir les réutiliser dans d'autres projets.

Les fichiers d'assistance contiennent généralement des méthodes d'assistance et parfois des classes lorsque l'object est destiné à être utilisé par les assistants (par exemple, les générateurs de formulaires).

Ceci est un aperçu vraiment général. Veuillez fournir plus de détails sur des exemples spécifiques si vous souhaitez obtenir des suggestions plus personnalisées. 🙂

… la tendance à créer d’énormes sous-classes ActiveRecord et d’énormes contrôleurs est assez naturelle …

“énorme” est un mot inquiétant … 😉

Comment vos contrôleurs deviennent-ils énormes? C’est quelque chose que vous devriez regarder: idéalement, les contrôleurs devraient être minces. Si vous choisissez une règle générale, si vous avez régulièrement plus de 5 ou 6 lignes de code par méthode de contrôleur (action), vos contrôleurs sont probablement trop gros. Y a-t-il une duplication qui pourrait passer dans une fonction d’assistance ou un filtre? Existe-t-il une logique métier pouvant être intégrée dans les modèles?

Comment vos modèles deviennent-ils énormes? Devriez-vous chercher des moyens de réduire le nombre de responsabilités dans chaque classe? Y a-t-il des comportements communs que vous pouvez extraire dans les mixins? Ou des domaines de fonctionnalité que vous pouvez déléguer aux classes d’assistance?

EDIT: Essayer de développer un peu, en espérant ne pas déformer quoi que ce soit trop mal …

Helpers: vivent dans app/helpers et sont principalement utilisés pour simplifier les vues. Ils sont soit spécifiques au contrôleur (également disponibles pour toutes les vues pour ce contrôleur), soit généralement disponibles ( module ApplicationHelper dans application_helper.rb).

Filtres: Supposons que vous ayez la même ligne de code dans plusieurs actions (très souvent, récupération d’un object à l’aide de params[:id] ou similaire). Cette duplication peut être abstraite en premier lieu vers une méthode distincte, puis entièrement supprimée en déclarant un filtre dans la définition de classe, par exemple, before_filter :get_object . Voir la section 6 du Guide des Rails ActionController. Laissez la programmation déclarative être votre ami.

Les modèles de refactoring sont un peu plus religieux. Disciples of Uncle Bob suggérera, par exemple, que vous suiviez les cinq commandements de SOLID . Joel et Jeff peuvent recommander une approche plus «pragmatique», même s’ils semblent s’être un peu plus réconciliés par la suite. La recherche d’une ou de plusieurs méthodes au sein d’une classe fonctionnant sur un sous-ensemble clairement défini de ses atsortingbuts constitue un moyen d’identifier les classes susceptibles d’être refaites à partir de votre modèle dérivé d’ActiveRecord.

Les modèles Rails ne doivent pas nécessairement être des sous-classes d’ActiveRecord :: Base. Ou, en d’autres termes, un modèle ne doit pas nécessairement être un analogue d’une table, ou même être lié à quelque chose stocké. Mieux encore, tant que vous nommez votre fichier dans une app/models selon les conventions de Rails (appelez #underscore sur le nom de la classe pour savoir ce que Rails recherchera), Rails le trouvera sans require cela soit nécessaire.

Voici un excellent article de blog sur la refactorisation des gros modèles qui semblent provenir de la philosophie de “contrôleur léger”:

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

Le message de base est “Ne pas extraire les mixins de Fat Models”, utilisez plutôt des classes de service, l’auteur fournit 7 modèles pour le faire