Le problème des gems non maintenues, ou pourquoi nous avons créé notre propre décorateur pour Rails

Lors de la sortie de Rails 5.0 l'été dernier, nous avons bien évidemment commencé à mettre à jour nos applications vers cette nouvelle version. Non pas que nous souhaitions absolument être “à la pointe”, mais le cycle de support officiel de Rails ne prenant en compte que la version courante et la version précédente, il vaut mieux ne pas prendre trop de retard sur les montées en version de Rails, d'autant plus que cela ne se passe pas toujours aussi facilement qu'on l'imagine, a fortiori avec une base de code importante.

À la sortie d'une version majeure de Rails, il se passe un temps variable avant que les principales gems utilisées dans nos applications se mettent également à niveau pour incorporer les nouveautés/changements liés aux éléments constituant l'ensemble de Rails (ActiveSupport, ActiveRecord, ActionPack, etc.), ou que ces gems soient au moins compatible avec la nouvelle version.

La plupart des gems actives débutent ce processus dès l'annonce des premières versions bêta afin de pouvoir être rapidement disponibles dès la sortie officielle de Rails, mais pour celles dont le développement est au ralenti ou pour lesquelles il n'y a plus de mainteneur officiel, c'est plus délicat. Et c'est exactement ce qui s'est passé avec la gem Draper que nous utilisions pour décorer nos modèles Rails.

Draper est un outil permettant d'implémenter le design pattern presenter/decorator, et de déléguer la logique d'organisation des données d'un modèle ActiveRecord séparément, et ainsi d'alléger à la fois les modèles mais également les vues de logique purement liées à la présentation. C'est une gem qui fait référence depuis 2011 (après la sortie de Rails 3.0). Un railscast lui avait même été consacré.

La gem Draper bloquée en version 3.0.0-pre sur Rubygems depuis juillet 2016

Draper a pourtant été mise à jour dès le mois de juillet 2016, un mois après la sortie officielle de Rails 5.0. Cependant, de nombreux bugs sont apparus lors de nos essais avec la version 3.0.0.pre1 sur notre branche rails5 en cours de migration. Six mois plus tard, sans correctif officiel ni de sortie de nouvelle version programmés, nous nous sommes rabattus sur l'inclusion dans notre Gemfile d'une source non officielle pour permettre notre montée en version de Rails.

# Gemfile

ruby "2.4.0"

gem "rails", "~> 5.0.0"
gem "draper", github: "hgani/draper"

Ticket GitHub du projet Draper demandant si le projet est abandonné

Le temps passant encore et la gem n'étant toujours pas mise à niveau, nous nous sommes penchés sur l'utilisation que nous faisions de cette gem et surtout son fonctionnement, en vue de trouver une alternative. Nous avions rapidement trouvé la gem ActiveDecorator de Akira Matsuda, qui paraissait convenir parfaitement à notre besoin, jusqu'à ce que l'on se rende compte que nos décorateurs existants faisaient également appel à des helpers personnalisés, et qui n'est manifestement pas possible dans cette gem, ainsi qu'un souci avec les modèles utilisant le principe de Single Table Inheritance. On peut aussi citer le choix de décorer systématiquement toutes les variables accessibles à la vue comme discutable, ou gênant pour certains.

Dans le processus de recherche des bugs que nous avons rencontré, la lecture des codes sources de Draper et de ActiveDecorator ainsi qu'un certain nombre d'articles très intéressants comme “The Simplest Rails Decorator Implementation That Just Might Work” ou “Using SimpleDelegator for your Decorators” nous ont permis de mieux appréhender les notions essentielles du design pattern presenter/decorator et nous donner envie d'essayer de coder une implémentation minimaliste de celui ci. Chez Level UP, on préfère en général la simplicité. On est plutôt Minitest que RSpec !

Le cadre désormais posé, les prochaines parties de cette série détailleront l'implémentation que nous avons créée, ce qu'elle nous permet de faire, ce qu'elle ne fait pas par choix, ce qu'elle nous a appris sur Ruby & Rails, les écueils que nous avons rencontrés, et comment nous les avons résolus ou contournés.