portaldacalheta.pt
  • Principal
  • Rise Of Remote
  • Outils Et Tutoriels
  • Équipes Distribuées
  • Mode De Vie
Back-End

Créez des composants de rails élégants avec des objets rubis anciens simples



Votre site Web évolue et vous grandissez rapidement. Ruby / Rails est votre meilleure option de programmation. Votre équipe est plus grande et vous avez déjà quitté le concept 'gros modèles, pilotes minces' ( modèles gras, contrôleurs maigres ) comme style de conception pour vos applications Rails. Cependant, vous ne voulez toujours pas arrêter d'utiliser Rails.

Il n'y a pas de problème. Aujourd'hui, nous allons discuter de la manière d'utiliser les meilleures pratiques de POO pour rendre votre code plus propre, plus isolé et séparé.



Vaut-il la peine de refactoriser votre application?

Commençons par regarder comment vous devez décider si votre application est un bon candidat pour la refactorisation.





Voici une liste de métriques et de questions que je me pose habituellement pour déterminer si mes codes doivent être refactorisés ou non.

  • Unités de test lentes. Les unités de test PORO fonctionnent généralement rapidement, avec des codes bien isolés, de sorte que les tests à exécution lente peuvent souvent être un indicateur d'une mauvaise conception ou de responsabilités sur-couplées.
  • Modèles ou contrôleurs FAT. Un modèle ou un contrôleur avec plus de 200 lignes de code ( ENDROIT ) c'est généralement un bon candidat pour la refactorisation.
  • Base de code excessivement longue. Si vous avez ERB / ​​HTML / HAML avec plus de 30 000 ENDROIT ou code source Ruby (sans GEMs ) avec plus de 50000 ENDROIT , il y a de fortes chances que vous ayez besoin de refactoriser.

Essayez d'utiliser quelque chose comme ceci pour savoir combien de lignes de code source Ruby vous avez:



find app -iname '*.rb' -type f -exec cat {} ;| wc -l

Cette commande recherchera tous les fichiers avec une extension .rb (fichiers ruby) dans le dossier / app, puis imprimera le nombre de lignes. Veuillez noter que ce nombre n'est qu'une estimation, car les lignes de commentaires seront incluses dans ce total.



Une autre option plus précise et informative consiste à utiliser la tâche râteau stats Rails, qui expose un résumé rapide des lignes de code, du nombre de classes, du nombre de méthodes, du ratio méthodes / classes et du ratio de lignes de code par méthode:

*bundle exec rake stats* +----------------------+-------+-----+-------+---------+-----+-------+ | Nombre | Líneas | LOC | Clase | Método | M/C | LOC/M | +----------------------+-------+-----+-------+---------+-----+-------+ | Controladores | 195 | 153 | 6 | 18 | 3 | 6 | | Helpers | 14 | 13 | 0 | 2 | 0 | 4 | | Modelos | 120 | 84 | 5 | 12 | 2 | 5 | | Mailers | 0 | 0 | 0 | 0 | 0 | 0 | | Javascripts | 45 | 12 | 0 | 3 | 0 | 2 | | Bibliotecas | 0 | 0 | 0 | 0 | 0 | 0 | | Controlador specs | 106 | 75 | 0 | 0 | 0 | 0 | | Helper specs | 15 | 4 | 0 | 0 | 0 | 0 | | Modelo specs | 238 | 182 | 0 | 0 | 0 | 0 | | Petición specs | 699 | 489 | 0 | 14 | 0 | 32 | | Routing specs | 35 | 26 | 0 | 0 | 0 | 0 | | Vista specs | 5 | 4 | 0 | 0 | 0 | 0 | +----------------------+-------+-----+-------+---------+-----+-------+ | Total | 1472 |1042 | 11 | 49 | 4 | 19 | +----------------------+-------+-----+-------+---------+-----+-------+ Código LOC: 262 Prueba LOC: 780 Ratio Código a Prueba: 1:3.0
  • Puis-je extraire des modèles récurrents dans ma base de code?

Séparer en action

Commençons par un exemple réel.



Supposons que nous voulions écrire une application qui suit la météo pour les gens qui font du jogging; Sur la page principale, l'utilisateur peut voir les heures entrées.



Chacune des entrées d'heure a la date, la distance, la durée et des informations d'état supplémentaires pertinentes (par exemple, la météo, le type de terrain, etc.), et une vitesse moyenne qui peut être calculée si nécessaire.

Nous avons besoin d'un rapport qui montre la vitesse moyenne et la distance par semaine. Si la vitesse moyenne à l'entrée est supérieure à la vitesse moyenne au total, nous en informerons l'utilisateur par SMS (pour cet exemple, nous utiliserons Nexmo API RESTful pour envoyer le SMS).



La page principale vous permettra de sélectionner la distance, la date et l'heure auxquelles vous faites du jogging, afin de créer une entrée similaire à celle-ci:

Nous avons également une page estadísticas, qui est essentiellement un rapport hebdomadaire qui comprend la vitesse moyenne et la distance parcourue par semaine.

  • Vous pouvez voir l'exemple en ligne ici .

Le code

La structure de répertoires de aplicación ressemble à ceci:

⇒ tree . ├── assets │ └── ... ├── controllers │ ├── application_controller.rb │ ├── entries_controller.rb │ └── statistics_controller.rb ├── helpers │ ├── application_helper.rb │ ├── entries_helper.rb │ └── statistics_helper.rb ├── mailers ├── models │ ├── entry.rb │ └── user.rb └── views ├── devise │ └── ... ├── entries │ ├── _entry.html.erb │ ├── _form.html.erb │ └── index.html.erb ├── layouts │ └── application.html.erb └── statistics └── index.html.erb

Je ne parlerai pas du modèle Usuario car il n'y a rien d'extraordinaire, puisque nous l'utilisons avec Devise pour implémenter l'authentification.

Concernant le modèle Entrada, il contient la logique métier de notre application.

Chaque Entrada appartient à un Usuario.

Nous validons la présence des attributs suivants pour chaque entrée distancia, períodode_tiempo, fecha_hora et estatus.

Chaque fois que nous créons une entrée, nous comparons la vitesse moyenne de l'utilisateur avec la moyenne de tous les utilisateurs du système, et notifions l'utilisateur par SMS, en utilisant Nexmo (Nous ne discuterons pas de la façon dont la bibliothèque Nexmo est utilisée, même si je voulais démontrer un cas où nous utilisons une bibliothèque externe).

  • Échantillon de base

Notez que le Entrada le modèle contient plus que la seule logique métier. Il gère également certaines validations et appels.

Le entries_controller.rb a les actions CRUEL main (bien que sans mise à jour). EntriesController#index récupère les entrées de l'utilisateur actuel et trie les enregistrements par date de création, tandis que EntriesController#create créer une nouvelle entrée. Il n'est pas nécessaire de discuter de l'évidence ou des responsabilités de EntriesController#destroy :

  • Échantillon de base

Alors que statistics_controller.rb est responsable du calcul du rapport hebdomadaire, StatisticsController#index récupère les entrées de l'utilisateur connecté et les regroupe par semaine, en utilisant le #group_by qui est dans la classe Énumérable dans Rails. Essayez ensuite de décorer les résultats en utilisant des méthodes privées.

  • Échantillon de base

Nous ne discutons pas beaucoup des vues ici car le code source est explicite.

Ci-dessous se trouve la vue pour lister les entrées de l'utilisateur connecté (index.html.erb). C'est le modèle qui sera utilisé pour afficher les résultats de l'action d'index (méthode) dans le gestionnaire d'entrée:

  • Échantillon de base

Notez que nous utilisons render @entries Partiels, pour amener le code partagé dans un modèle partiel _entry.html.erb pour que nous puissions garder notre code SEC et réutilisable:

  • Échantillon de base

La même chose s'applique à un _forma partiel. Au lieu d'utiliser le même code avec des actions (nouvelles et modifiées), nous créons un formulaire partiel réutilisable:

  • Échantillon de base

En ce qui concerne la vue de la page du rapport hebdomadaire, statistics/index.html.erb affiche des statistiques et des rapports sur les activités hebdomadaires de l'utilisateur en regroupant certaines entrées:

  • Échantillon de base

Et enfin, le assistant pour les entrées, entries_helper.rb, comprend deux aides readable_time_period et readable_speed ce qui devrait faciliter la lecture des attributs:

  • Échantillon de base

Rien de trop compliqué pour l'instant.

La plupart d'entre vous peuvent affirmer que la refactorisation est contraire au principe BAISER et cela rendra le système plus compliqué.

Alors, cette application a-t-elle vraiment besoin d'être refactorisée?

Absolument non , mais nous ne le considérerons qu'à titre d'exemple.

Après tout, si vous regardez la section suivante et les caractéristiques qui indiquent qu'une application doit être refactorisée, il devient évident que l'application dans notre exemple n'est pas un candidat valide pour la refactorisation.

Le cercle de la vie

Commençons par expliquer le modèle de structure MVC en Rails.

Cela commence généralement par le moteur de recherche en faisant une requête comme https://www.toptal.com/jogging/show/1.

Le serveur Web reçoit la demande et utilise rutas définir quoi controlador porter.

Les contrôleurs effectuent le travail d'analyse des demandes des utilisateurs, des livraisons de données, des cookies, des sessions, etc., puis demandent modelo obtenir les données.

Le modelos Ce sont des classes Ruby qui communiquent avec la base de données, enregistrent et valident les données, exécutent la logique métier et font le gros du travail. Les vues sont ce que l'utilisateur peut voir: HTML, CSS, XML, Javascript, JSON.

Si nous voulons afficher la séquence d'une demande de cycle de vie Rails, cela ressemblerait à ceci:

Rails découplant le cycle de vie MVC

Ce que je veux réaliser, c'est ajouter plus d'abstraction, en utilisant des PORO et faire du modèle quelque chose de similaire à ce qui suit, pour les actions create/update:

Diagramme de rails créer un formulaire

Et quelque chose de similaire pour les actions list/show :

Requête de liste de diagramme de rails

En ajoutant des abstractions PORO, nous assurons une séparation complète, entre les responsabilités FAUCILLE , quelque chose que Rails ne maîtrise pas totalement.

Des lignes directrices

Pour réaliser le nouveau design, j'utiliserai les directives ci-dessous, mais gardez à l'esprit que ce ne sont pas des règles que vous devez suivre à la lettre. Considérez-les comme des lignes directrices flexibles qui facilitent la refactorisation.

  • Les modèles ActiveRecord peuvent contenir des associations et des constantes, mais rien d'autre. Cela signifie qu'il n'y aura pas d'appels (utilisez des objets de service et ajoutez-y les appels) et pas de validations (utilisez Objets de formulaire pour inclure les noms et les validations du modèle).

  • Conservez les contrôleurs sous forme de couches minces et appelez toujours les objets Service. Certains d'entre vous peuvent se demander pourquoi utiliser des contrôleurs, si nous voulons continuer à appeler des objets de service pour contenir la logique? Eh bien, les contrôleurs sont un bon endroit pour avoir le routage HTTP, analyse des paramètres, authentification, négociation de contenu, appel du service ou de l'éditeur correct, détection des exceptions, mise en forme des réponses et renvoi du statut de code HTTP correct.

  • Les services doivent appeler des objets Requete , et ne stockez pas l'état. Utilisez des méthodes d'instance sans classe. Il doit y avoir très peu de méthodes publiques enregistrées avec FAUCILLE .
  • Requêtes doit être fait avec des objets requete . Méthodes objet Requete doit retourner un objet, un hacher ou tableau , mais pas une association ActiveRecord.

  • Évitez d'utiliser Aides , mieux utiliser les décorateurs. Parce que? Une difficulté commune avec aides dans Rails, c'est qu'ils peuvent être transformés en un tas de non- OO , qui partagent un nom d'espace et se chevauchent. Mais c'est bien pire, le fait non que vous ne puissiez utiliser aucun polymorphisme avec le aides Rails - en fournissant différentes implémentations pour différents contextes ou types, et en contournant ou en sous-classifiant les helpers. Je pense que les types de assistant dans Rails, ils doivent généralement être utilisés pour des méthodes utilitaires, pas pour des cas d'utilisation spécifiques; comment mettre en forme les attributs de modèle pour toute logique de présentation. Gardez-les légers et faciles à suivre. Décorateurs / Délégués mieux. ** Pourquoi? Après tout, les soucis semblent faire partie des rails, et ils peuvent sécher ( Sécher ) un code lorsqu'il est partagé entre plusieurs modèles. Cependant, le plus gros problème est que les préoccupations ne rendent pas l'objet modèle plus cohérent. Seul le code est mieux organisé. En d'autres termes, il n'y a pas de véritable changement dans l'API du modèle.

  • Essayez d'extraire Objets de valeur des modèles pour garder votre code plus propre et regrouper les attributs associés.

  • Passez toujours une variable d'instance pour chaque vue.

Refactorizar

Avant de commencer, je veux discuter d'autre chose. Lorsque la refactorisation commence, vous vous demandez généralement: 'Est-ce un bon refactoring?'

Si vous sentez que vous faites plus de séparation ou d'isolement entre les responsabilités (même si cela signifie ajouter plus de code et de nouveaux fichiers), c'est une bonne chose. Après tout, détacher une application est une très bonne pratique et nous permet de réaliser plus facilement un test unitaire approprié.

Je ne vais pas discuter de choses, comme déplacer la logique des contrôleurs vers les modèles, car je suppose que vous faites cela et que vous êtes à l'aise avec les Rails (généralement Skinny Controller et le modèle FAT).

Afin de garder cet article concis, je ne vais pas discuter de la façon de tester, mais cela ne signifie pas que vous ne devriez pas tester.

Au contraire, vous devriez commencez toujours par un test pour s'assurer que tout va bien, avant d'aller de l'avant. C'est quelque chose obligatoire, surtout lors du refactoring.

Nous pouvons ensuite implémenter des modifications et nous assurer que les tests passent par les parties pertinentes du code.

Extraire des objets de valeur

Premièrement, qu'est-ce qu'un objet de valeur?

Martin Fowler Explique:

L'objet de valeur est un petit objet, tel qu'un objet argent ou plage de dates. Leur principale caractéristique est qu'ils suivent la sémantique des valeurs, plutôt que la sémantique référentielle.

Parfois, vous pouvez vous retrouver dans une situation où un concept mérite sa propre abstraction et où son égalité ne repose pas sur des valeurs, mais sur une identité. Des exemples de ceci peuvent être: la date, l'URI et le chemin de Ruby. L'extraction d'un objet précieux (ou d'un modèle de domaine) est d'une grande commodité.

Pourquoi s'embêter?

L'un des grands avantages d'un objet de valeur est qu'il aide à obtenir l'expressivité de votre code. Votre code aura tendance à être plus clair, ou du moins cela peut l'être si vous avez de bonnes pratiques de dénomination. Puisque l'objet de valeur est une abstraction, il conduit à des codes plus clairs et moins d'erreurs.

Un autre avantage est le immutabilité . L'immuabilité des objets est très importante. Lorsque nous stockons certains ensembles de données, qui peuvent être utilisés dans un objet de valeur, je n'aime généralement pas que les données soient manipulées.

Quand est-ce utile?

Il n'y a pas de réponse parfaite à cette question. Faites ce qui est le mieux pour vous et ce qui a le plus de sens dans une situation donnée.

Au-delà de cela, il y a quelques lignes directrices que j'utilise pour m'aider à prendre cette décision.

Si vous pensez qu'un groupe de méthodes est lié, eh bien, les objets de valeur sont plus chers. Cette expressivité signifie qu'un objet de valeur doit représenter un ensemble de données distinctif, qui peut être déduit par votre développeur moyen simplement en regardant le nom de l'objet.

Comment se fait ceci?

Les objets de valeur doivent suivre certaines règles:

  • Les objets de valeur doivent avoir plusieurs attributs.
  • Les attributs doivent être immuables, tout au long du cycle de vie de l'objet.
  • L'égalité est déterminée par les attributs de l'objet.

Dans notre exemple, je vais créer un objet de valeur EntryStatus, pour abstraire les attributs Entry#status_weather et Entry#status_landform à sa propre classe, qui ressemble à ceci:

  • Échantillon de base

Remarque: Il ne s'agit que d'un PORO (Plain Old Ruby Object), il n'hérite pas de ActiveRecord::Base. Nous avons défini des méthodes de lecture pour nos attributs et nous les attribuons au démarrage. De plus, nous utilisons un mélange comparable pour faire correspondre les objets à l'aide de la méthode ().

Nous pouvons modifier le modèle Entry pour utiliser l'objet de valeur que nous avons créé:

  • Échantillon de base

Nous pouvons également modifier la méthode EntryController#create pour utiliser le nouvel objet de valeur en conséquence:

  • Échantillon de base

Extraire des objets de service

Qu'est-ce qu'un objet Service?

Le travail d'un objet Service est de contenir le code pendant un espace particulier de la logique métier. Contrairement au style 'Gros modèle' , où un petit nombre d'objets contient de très nombreuses méthodes, pour toute la logique nécessaire, l'utilisation d'objets de service entraîne la création de nombreuses classes, chacune ayant un objectif unique.

Parce que? Quels sont les bénéfices?

  • Démonter. Les objets de service vous aident à obtenir une meilleure isolation entre les objets.

  • Visibilité. Les objets de service (s'ils sont correctement nommés) montrent ce que fait une application. Je peux parcourir le répertoire des services pour voir les fonctionnalités offertes par une application.

  • Nettoyez les modèles et les pilotes. Les contrôleurs tournent sur demande ( paramètres , session, cookies) dans les arguments, il les transmet au service et les redirige ou les laisse en fonction de la réponse du service. Alors que les modèles ne traitent que des associations et de la persistance. L'extraction de code des contrôleurs / modèles vers des objets de service prendrait en charge FAUCILLE et cela séparerait davantage le code. La responsabilité du modèle n'aurait qu'à traiter les associations et enregistrer / supprimer des enregistrements, tandis que l'objet Service n'aurait qu'une seule responsabilité ( FAUCILLE ). Cela conduit à une meilleure conception et à de meilleures unités de test.
  • SEC et acceptez le changement. Je garde les articles de service aussi simples et petits que possible. Je compose des objets de service avec d'autres objets de service et je les réutilise.

  • Nettoyez et accélérez votre suite de tests. Les services sont rapides et faciles à tester, car ce sont de petits objets Ruby avec un point d'entrée (la méthode appelée). Les services complexes sont complétés par d'autres services, vous pouvez donc facilement séparer vos tests. En outre, l'utilisation d'objets de service facilite la récupération des objets associés sans charger l'ensemble de l'environnement des rails.

  • Échangeable de n'importe où. Les objets de service seront appelés, sûrement, à partir des contrôleurs ainsi que des objets de service, des tâches DelayedJob / Rescue / Sidekiq, des tâches Rake, de la console, etc.

D'un autre côté, rien n'est parfait. Un inconvénient des objets Service est qu'ils peuvent être exagérés pour chaque petite action. Dans ces cas, vous pouvez finir par compliquer et non simplifier votre code.

Quand devez-vous extraire des objets de service?

Il n'y a pas non plus de règle fixe ici.

En règle générale, les objets Service sont les meilleurs pour les systèmes de taille moyenne à grande: ceux avec une quantité décente de logique, au-delà des opérations CRUD standard.

Ainsi, lorsque vous pensez qu'un morceau de code n'appartient pas au répertoire, à l'endroit où vous alliez l'ajouter, c'est une bonne idée de le reconsidérer et de voir s'il vaudrait mieux qu'il aille vers un objet de service.

Voici quelques conseils pour savoir quand utiliser les objets Service:

  • L'action est complexe.
  • L'action atteint plusieurs modèles.
  • L'action interagit avec un service externe.
  • L'action n'est pas une préoccupation première du modèle souligné.
  • Il existe plusieurs façons de faire l'action.

Comment devez-vous concevoir des objets de service?

La conception de la classe pour un objet de service est relativement simple, car vous n'avez pas besoin gemmes tu ne devrais pas apprendre un DLS nouveau, mais si vous pouvez, plus ou moins, compter sur les compétences en conception de logiciels que vous possédez déjà.

En général, j'utilise les directives et conventions suivantes pour concevoir l'objet de service:

  • Ne stockez pas l'état de l'objet.
  • Utilisez des méthodes d'instance, pas des méthodes de classe.
  • Il devrait y avoir très peu de méthodes publiques (de préférence une qui prend en charge *FAUCILLE .
  • Les méthodes doivent renvoyer des résultats d'objets riches et non booléens.
  • Les services vont dans le répertoire app/services . Je vous conseille d'utiliser des sous-répertoires, pour des domaines de logique métier forts. Par exemple, le fichier app/services/report/generate_weekly.rb définira Report::GenerateWeekly tandis que app/services/report/publish_monthly.rb définira Report::PublishMonthly.
  • Les services commencent par un verbe (et ne se terminent pas par un service): ApproveTransaction, SendTestNewsletter, ImportUsersFromCsv.
  • Les services répondent à la méthode appelée. J'ai trouvé que l'utilisation d'un autre verbe le rend un peu redondant: ApproveTransaction.approve () ne lit pas bien. De plus, la méthode appelée est la méthode de facto de lambda , procs et objets de méthode.

Si vous regardez StatisticsController#index, vous remarquerez un groupe de méthodes (weeks_to_date_from, weeks_to_date_to, avg_distance, etc.) regroupées dans le contrôleur. Ce n'est pas bon. Tenez compte des ramifications, si vous souhaitez générer le rapport hebdomadaire en dehors de statistics_controller.

Dans notre cas, nous allons créer Report::GenerateWeekly et extrayez le rapport logique de StatisticsController:

  • Échantillon de base

Donc StatisticsController#index maintenant ça a l'air plus propre:

  • Échantillon de base

En appliquant le modèle d'objet Service, nous regroupons le code autour d'une action complexe et spécifique et favorisons la création de méthodes plus petites et plus claires.

Tâche: envisagez d'utiliser Objet précieux pour le WeeklyReport au lieu de Struct .

Extraire des objets Requete Contrôleurs

Qu'est-ce qu'un objet Requete ?

Un objet Requete est un PORE, qui représente une base de données de requêtes. Il peut être réutilisé à différents endroits de l'application, tout en masquant la logique de la requête. Il fournit également une bonne unité isolée pour les tests.

Vous devez extraire les requêtes SQL / NoSQL complexes dans leurs propres classes.

Chaque objet Requete est responsable de renvoyer un ensemble de résultats en fonction des critères / règles métier.

Dans cet exemple, nous n'avons aucune requête ( requete ) complexe, alors utilisez object Requete ce ne serait pas efficace. Cependant, à des fins de démonstration, nous allons extraire la requête dans Report::GenerateWeekly#call et nous créerons generate_entries_query.rb:

  • Échantillon de base

Et dans Report::GenerateWeekly#call, remplaçons:

def call @user.entries.group_by(&:week).map do |week, entries| WeeklyReport.new( ... ) end end

avec:

def call weekly_grouped_entries = GroupEntriesQuery.new(@user).call weekly_grouped_entries.map do |week, entries| WeeklyReport.new( ... ) end end

Le motif de l'objet requete (query) permet de garder la logique de votre modèle strictement liée au comportement de classe, tout en gardant vos contrôleurs maigres. Parce qu'ils ne sont rien d'autre que classes de rubis anciennes , les objets requete ils n'ont pas besoin d'hériter de ActiveRecord::Base et ne devraient être responsables que de l'exécution des requêtes.

Extraire Créer une entrée dans un objet de service

Nous allons maintenant extraire la logique de création d'une nouvelle entrée dans un nouvel objet de service. Utilisons la convention et créons CreateEntry:

  • Échantillon de base

Et maintenant notre EntriesController#create est comme suit:

def create begin CreateEntry.new(current_user, entry_params).call flash[:notice] = 'Entry was successfully created.' rescue Exception => e flash[:error] = e.message end redirect_to root_path end

Plus de validations sur un objet de forme

Maintenant, les choses commencent à devenir plus intéressantes.

N'oubliez pas que dans nos directives, nous avons convenu que nous voulions que les modèles aient des associations et des constantes, mais rien d'autre (pas de validations ni d'appels). Commençons donc par supprimer les légendes et utilisons à la place un objet Shape.

Un objet Shape est un PORO (Plain Old Ruby Object). Prenez les commandes de l'objet contrôleur / service lorsque vous avez besoin de parler à la base de données.

Pourquoi utiliser des objets Shape?

Lorsque vous avez besoin de refactoriser votre application, il est toujours judicieux de garder à l'esprit la principale responsabilité ( FAUCILLE ).

FAUCILLE vous aide à prendre de meilleures décisions en matière de conception, concernant la responsabilité qu'une classe devrait avoir.

Votre modèle de table de base de données (un modèle ActiveRecord dans le contexte de Rails), par exemple, représente un enregistrement de base de données unique dans le code, il n'y a donc aucune raison pour que vous soyez préoccupé par ce que fait votre utilisateur.

C'est là que l'objet Shape entre en jeu.

Un objet Shape est chargé de représenter une forme dans votre application. Ainsi, chaque champ d'entrée peut être traité comme un attribut de la classe. Vous pouvez valider que ces attributs répondent à certaines règles de validation, et vous pouvez transmettre les données 'propres' là où elles devraient aller (par exemple, votre modèle de base de données ou peut-être votre générateur de recherche de requêtes).

Quand devez-vous utiliser un objet Shape?

  • Lorsque vous souhaitez extraire des validations de modèles Rails.
  • Lorsque plusieurs modèles peuvent être mis à jour par une seule méthode de livraison, vous devez créer un objet Shape.

Cela vous permet de mettre toute votre logique de formulaire (conventions de dénomination, validations et autres) au même endroit.

Comment créer un objet Shape?

  • Créez une classe Ruby simple.
  • Comprend ActiveModel::Model (dans Rails 3, vous devez inclure le nom, la conversion et la validation à la place).
  • Commencez à utiliser votre nouvelle classe de formes, comme s'il s'agissait d'un modèle ActiveRecord normal, où la plus grande différence est que vous ne pouvez pas continuer avec les données stockées dans cet objet.

Veuillez noter que vous pouvez utiliser le gemme réforme , mais en continuant avec PORO, nous allons créer entry_form.rb qui ressemble à ceci:

  • Échantillon de base

Et nous modifierons CreateEntry pour commencer à utiliser l'objet Format EntryForm:

class CreateEntry ...... ...... def call @entry_form = ::EntryForm.new(@params) if @entry_form.valid? .... else .... end end end

Remarque: Certains d'entre vous diront qu'il n'est pas nécessaire d'accéder à l'objet Shape depuis l'objet Service et que nous pouvons appeler l'objet Shape directement depuis le contrôleur, ce qui est un argument valide. Cependant, je préfère avoir un flux clair, c'est pourquoi j'appelle toujours l'objet Form à partir de l'objet Service.

Déplacez les appels vers l'objet de service.

Comme nous l'avons convenu précédemment, nous ne voulons pas que nos modèles contiennent des validations et des appels. Nous avons extrait les validations à l'aide d'objets Shape. Mais nous utilisons toujours des appels (after_create dans le modèle Entry compare_speed_and_notify_user).

Pourquoi voulons-nous supprimer les appels des modèles?

Développeurs de rails ils commencent généralement à remarquer une douleur avec les appels, pendant les tests. Si vous ne testez pas vos modèles ActiveRecord, vous commencerez à remarquer la douleur plus tard, à mesure que votre application se développe et que plus de logique est nécessaire pour appeler ou éviter les appels.

después_* les appels sont principalement utilisés en relation avec l'enregistrement ou la poursuite de l'objet.

Une fois l'objet enregistré, l'objectif de l'objet (par exemple la responsabilité) a été rempli. Donc, si nous voyons toujours des appels appelés, après que l'objet a été enregistré, ce sont probablement des appels qui cherchent à sortir de la zone de responsabilité de l'objet, et c'est là que nous rencontrons des problèmes.

didacticiel de l'API Web de base asp.net

Dans notre cas, nous envoyons un SMS à l'utilisateur, qui n'est pas lié au domaine d'entrée.

Un moyen simple de résoudre le problème consiste à déplacer l'appel vers l'objet de service associé. Après tout, l'envoi d'un SMS à l'utilisateur correspondant est lié à l'objet de service CreateEntry et non le modèle Entry en tant que tel.

En faisant cela, nous n'avons plus à arrêter, le compare_speed_and_notify_user dans nos tests. Nous en avons fait une chose simple, en créant une entrée sans avoir besoin d'envoyer un SMS et nous suivons une bonne conception orientée objet, en veillant à ce que nos classes aient une responsabilité unique ( FAUCILLE ).

Alors maintenant CreateEntry c'est quelque chose de similaire à ceci:

  • Échantillon de base

Utilisez des décorateurs plutôt que des aides

Bien que nous puissions facilement utiliser la collection Draper de mannequins et décorateurs, je reste avec PORO, pour cet article, comme je l'ai fait jusqu'à présent.

Ce dont j'ai besoin, c'est d'une classe qui appelle des méthodes sur l'objet décoré.

Je peux utiliser method_missing pour l'implémenter, mais j'utiliserai la bibliothèque Ruby standard, SimpleDelegator. Le code suivant montre comment utiliser SimpleDelegator pour implémenter notre décorateur de base:

% app/decorators/base_decorator.rb require 'delegate' class BaseDecorator

Pourquoi la méthode _h?

Cette méthode agit comme un proxy pour le contexte de vue. Par défaut, le contexte de vue est une instance d'une classe de vue, ceci étant ActionView::Base. Vous pouvez accéder au aides de vues comme suit:

_h.content_tag :div, 'my-div', class: 'my-class'

Pour le rendre plus pratique, nous ajoutons une méthode decorado a ApplicationHelper:

module ApplicationHelper # ..... def decorate(object, klass = nil) klass ||= '#{object.class}Decorator'.constantize decorator = klass.new(object, self) yield decorator if block_given? decorator end # ..... end

Maintenant, nous pouvons déplacer le aides EntriesHelper aux décorateurs:

# app/decorators/entry_decorator.rb class EntryDecorator

Et nous pouvons utiliser readable_time_period et readable_speed comme suit:

# app/views/entries/_entry.html.erb - + - +

Structure après refactorisation

Nous nous sommes retrouvés avec plus de fichiers, mais ce n'est pas forcément une mauvaise chose (et rappelez-vous ceci, depuis le début, nous étions conscients que cet exemple était à des fins de démonstration et non était forcément un bon cas d'utilisation pour le refactoring):

app ├── assets │ └── ... ├── controllers │ ├── application_controller.rb │ ├── entries_controller.rb │ └── statistics_controller.rb ├── decorators │ ├── base_decorator.rb │ └── entry_decorator.rb ├── forms │ └── entry_form.rb ├── helpers │ └── application_helper.rb ├── mailers ├── models │ ├── entry.rb │ ├── entry_status.rb │ └── user.rb ├── queries │ └── group_entries_query.rb ├── services │ ├── create_entry.rb │ └── report │ └── generate_weekly.rb └── views ├── devise │ └── .. ├── entries │ ├── _entry.html.erb │ ├── _form.html.erb │ └── index.html.erb ├── layouts │ └── application.html.erb └── statistics └── index.html.erb

conclusion

Bien que nous nous concentrions sur les rails dans cet article de blog, RoR (Ruby on Rails) n'est pas une dépendance sur des objets de service ou d'autres PORO. Vous pouvez utiliser cette approche avec n'importe quel cadre web , application mobile ou console.

Lors de l'utilisation MVC En tant qu'architecture, tout colle ensemble et vous ralentit car la plupart des changements ont un impact sur d'autres parties de l'application. Cela vous oblige également à réfléchir à l'endroit où placer la logique métier - doit-elle aller dans le modèle, le contrôleur ou la vue?

En utilisant un simple PORO, nous avons déplacé la logique métier vers des modèles ou des services, qui n'héritent pas de ActiveRecord, ce qui est déjà un gain, sans oublier que nous avons un code plus clair, qui prend en charge FAUCILLE et des tests unitaires plus rapides.

Une architecture propre essaie de placer les boîtes d'utilisation au centre / en haut de votre structure afin que vous puissiez facilement voir ce que fait votre application. Elle facilite également l'adoption des changements, car elle est plus modulaire et isolée. J'espère avoir montré comment Objets rubis anciens simples et plus d'abstractions, il sépare les préoccupations, simplifie les tests et aide à produire un code propre et maintenable.

En relation: Quels sont les avantages de Ruby on Rails? Après deux décennies de programmation. Utiliser des rails

Tutoriel de texte SVG: annotation de texte sur le Web

La Technologie

Tutoriel de texte SVG: annotation de texte sur le Web
Esthétique et perception - Comment aborder l'imagerie de l'expérience utilisateur

Esthétique et perception - Comment aborder l'imagerie de l'expérience utilisateur

Conception Ux

Articles Populaires
Création d'une API REST Node.js / TypeScript, partie 2: modèles, middleware et services
Création d'une API REST Node.js / TypeScript, partie 2: modèles, middleware et services
Un didacticiel sur la radio définie par logiciel: images de la Station spatiale internationale et écoute de jambons avec un RTL-SDR
Un didacticiel sur la radio définie par logiciel: images de la Station spatiale internationale et écoute de jambons avec un RTL-SDR
Les drones commerciaux révolutionnent les opérations commerciales
Les drones commerciaux révolutionnent les opérations commerciales
Faire des affaires dans l'Union européenne
Faire des affaires dans l'Union européenne
AI vs BI: différences et synergies
AI vs BI: différences et synergies
 
Stratège de contenu produit
Stratège de contenu produit
Risque vs récompense: un guide pour comprendre les conteneurs logiciels
Risque vs récompense: un guide pour comprendre les conteneurs logiciels
Explorer SMACSS: architecture évolutive et modulaire pour CSS
Explorer SMACSS: architecture évolutive et modulaire pour CSS
Si vous n'utilisez pas de données UX, ce n'est pas de la conception UX
Si vous n'utilisez pas de données UX, ce n'est pas de la conception UX
Simplification de l'utilisation des API RESTful et de la persistance des données sur iOS avec Mantle et Realm
Simplification de l'utilisation des API RESTful et de la persistance des données sur iOS avec Mantle et Realm
Articles Populaires
  • quelle est la différence entre s corp et c corp
  • numéros de carte de crédit de travail 2017 avec de l'argent
  • si les utilisateurs n'accèdent qu'à certains champs d'une table, comment pouvez-vous améliorer les performances ?
  • comment investit Warren Buffet
  • fuite de numéros de carte de crédit avec cvv
Catégories
  • Rise Of Remote
  • Outils Et Tutoriels
  • Équipes Distribuées
  • Mode De Vie
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt