Passage de CarrierWave à ActiveStorage dans une application Rails

 NJ Pearman
NJ Pearman

Suivre

Juin 20, 2018 * 9 min de lecture

Nous utilisons Ruby on Rails pour enseigner le développement d’applications web à l’École d’extension de l’EPFL. C’est un excellent cadre solide, éprouvé et fiable et qui présente une structure facile à suivre lors de vos premiers pas en matière de développement Web.

Mais c’est aussi un logiciel en constante évolution, avec des améliorations et de nouvelles fonctionnalités qui sortent chaque année. L’un des ajouts récents à Rails dans la version 5.2 est la fonctionnalité ActiveStorage, qui ajoute une gestion facile du téléchargement de fichiers au framework core Rails pour la première fois.

Dans la première itération de notre programme de développement d’applications web, nous incluons un sujet pour apprendre à ajouter des téléchargements de fichiers dans une application Rails. Nous utilisons cependant Rails 5.1 dans le programme, et le sujet de téléchargement de fichiers utilise l’une des solutions tierces largement utilisées pour ajouter des téléchargements de fichiers, appelée CarrierWave. Il s’agit d’un joyau tiers solide qui nous permet d’implémenter des téléchargements de fichiers dans une application Rails et est utilisé dans de nombreuses applications Ruby on Rails du monde réel – y compris notre plate—forme d’apprentissage!

Mais avec l’introduction d’ActiveStorage, ces gemmes tierces deviennent moins attrayantes que cette nouvelle solution embarquée à l’intérieur de Rails. En fait, Trombone, l’un des autres joyaux populaires de téléchargement de fichiers tiers, a déjà annoncé qu’il était obsolète en faveur d’ActiveStorage. Les développeurs de Paperclip ont reconnu qu’ActiveStorage est un meilleur choix pour les développeurs et ont cessé de travailler sur leur propre joyau.

Regardons donc spécifiquement ce qu’il faut pour passer de CarrierWave dans Rails 5.1 à ActiveStorage dans Rails 5.2.

L’application

Dans le programme de développement d’applications Web, nous travaillons à la création d’une application Web appelée My Bucket List. Il s’agit d’une application qui permet aux utilisateurs de créer et de suivre les expériences qu’ils ont toujours voulu accomplir. Les utilisateurs peuvent voir ce que d’autres personnes ont créé et les suivre également. L’une des fonctionnalités de l’application permet à un utilisateur de télécharger son propre avatar, et nous construisons cette fonctionnalité afin d’en savoir plus sur les téléchargements de fichiers et les pièces jointes à l’aide de CarrierWave.

Un utilisateur peut télécharger un avatar avec ce formulaire, en utilisant CarrierWave

Alors d’abord, passons en revue rapidement ce qui implique l’ajout d’une pièce jointe simple à l’aide de CarrierWave.

Configuration CarrierWave

Pour inclure CarrierWave dans un projet, nous devons:

  • Ajoutez la gemme CarrierWave au Gemfile.
  • Incluez l’adaptateur CarrierWave pour ActiveRecord dans un initialiseur de configuration, config/initializers/carrierwave.rb.

puis pour ajouter une pièce jointe à un modèle, il faut:

  • Générez un Uploader en utilisant rails generate uploader UploaderName.
  • Ajoutez un attribut sur le modèle pour stocker le nom de fichier joint pour chaque enregistrement, avec la migration de base de données associée.
  • Modifiez les valeurs dans l’uploader, telles que l’emplacement de stockage de téléchargement et l’image par défaut.
  • Utilisez la macro mount_uploader dans le modèle pour inclure le téléchargeur particulier.
  • Ajoutez un champ de formulaire de fichier où une image peut être téléchargée.
  • Ajoutez l’attribut model à la liste des paramètres forts dans le contrôleur qui gère le formulaire contenant le champ fichier.

Considérons un de ces points plus loin: dans CarrierWave, il est nécessaire d’ajouter des attributs spécifiques à tout modèle qui doit avoir un fichier joint. Par exemple, un modèle User qui a besoin d’une image avatar qui lui est attachée aura besoin d’un attribut appelé :avatar qui est un String. Cela nécessite une migration de base de données pour ajouter la colonne de base de données sous-jacente, ainsi que la création et le montage d’une classe Uploader à cet attribut dans le modèle.

Ce n’est pas particulièrement laborieux mais, comme nous le verrons, ActiveStorage nécessite beaucoup moins de code au cas par cas.

Un autre point à souligner est que dans mon application – l’application Ma liste de seau — le téléchargeur d’avatar CarrierWave est configuré avec le stockage de fichiers simple par défaut. En termes d’ordre, les images téléchargées sont stockées quelque part dans le dossier public/ de l’application. Ce chemin précis est défini dans la classe Uploader pour l’avatar et est défini sur "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}". Il s’agit d’un chemin relatif au dossier public/, de sorte que les images téléchargées sont directement accessibles via le serveur Web. Cela permet un accès direct aux fichiers joints, que nous pouvons comparer avec ActiveStorage plus tard.

Mise à niveau des rails

Avant d’examiner ActiveStorage, il est nécessaire de passer à Rails 5.2. C’est une tâche qui peut être assez complexe, en fonction de la complexité de votre application. L’application que j’utilise ici, que nous construisons dans notre programme WAD, n’est pas particulièrement difficile à mettre à jour, mais elle nécessite tout de même un peu de soin et d’attention.

Voici ce que j’ai fait pour mettre à jour l’application Ma liste de compartiments de Rails 5.1 à Rails 5.2.

Tout d’abord, j’ai créé une nouvelle branche dans mon référentiel git pour isoler la mise à niveau — il est important de pouvoir revenir à la version de travail précédente d’une application si quelque chose tourne mal dans le processus de mise à niveau !

Sur la nouvelle branche, j’ai changé la version de Rails dans le Gemfile de '~> 5.1.2' à '~> 5.2.0', puis j’ai exécuté bin/bundle update rails — cette commande télécharge la nouvelle version de rails et toutes les gemmes associées dans l’application. Pour mon application, il n’y avait pas de conflits de gemmes, mais si vous utilisez des gemmes tierces, vous devrez peut-être résoudre les conflits entre différentes versions de gemmes à ce stade!

Après avoir installé la nouvelle gemme Rails et ses dépendances, il est nécessaire de mettre à jour l’application elle-même. J’ai fait cela en utilisant bin/bundle exec rails app:update. Cette commande met à jour par programme tous les fichiers nécessaires dans l’application pour fonctionner avec Rails 5.2.

MaisBut cette commande écrasera les modifications que vous avez apportées à votre application, telles que config/routes.rb. Heureusement, la commande demande s’il faut écraser chaque fichier, et il est possible de diff n’importe lequel de ces fichiers pendant ce processus pour vérifier ce qui serait écrasé. L’invite pour chaque écrasement est , ce qui signifie:

  • Y pour « Oui, écrasez mon fichier », et est l’option par défaut si vous appuyez simplement sur Enter.
  • n pour « non, n’écrasez pas mon fichier »
  • a pour « Oui, écrasez ce fichier et écrasez tous les autres fichiers aussi! ». (Ma règle générale est de ne jamais utiliser a).
  • q pour « Quitter ce processus »
  • d pour « Montrez-moi les différences entre mon fichier et l’écrasement »
  • h pour « Montrez-moi une liste de ce que Ynaqdh signifie »

Dans ma demande, j’ai accepté chaque écrasement avec Y sauf config/routes.rb et config/locales/en.yml. Ces deux fichiers étaient des fichiers que j’avais modifiés et je voulais donc garder ma version, alors j’ai choisi n.

Ce processus de mise à niveau peut être plus impliqué si vous avez une application plus complexe, en particulier si vous avez beaucoup de configuration personnalisée dans l’un des fichiers de configuration de l’environnement, c’est-à-dire config/environments/production.rb, config/environments/test.rb et config/environments/development.rb. La mise à niveau ajoute des paramètres importants à ces fichiers, mais vous voudrez également conserver votre propre configuration personnalisée — c’est là que la version originale de l’application dans une branche git séparée aide. Il est alors possible au code de cette ancienne branche de vérifier que tout le code et la configuration de votre application sont toujours en place après la mise à niveau.

Après avoir exécuté bin/bundle exec rails app:update, il m’a fallu ajouter la gemme bootsnap au Gemfile. Il s’agit d’une nouvelle gemme utilisée dans Rails 5.2 mais elle n’est pas ajoutée dans le cadre de la commande app:upgrade. Je viens donc d’ajouter gem 'bootsnap' au Gemfile puis d’exécuter bin/bundle install.

Après cela, mon application de liste de compartiments a démarré comme avant d’utiliser rails server.

Remplacement de CarrierWave par ActiveStorage

Ainsi, avec l’application fonctionnant maintenant sur Rails 5.2, je pourrais commencer à remplacer CarrierWave par ActiveStorage.

Avec ActiveStorage, il n’est pas nécessaire d’ajouter des attributs spécifiques au modèle pour stocker les noms de fichiers de pièces jointes nécessaires avec CarrierWave. ActiveStorage fonctionne avec les deux mêmes tables associées pour toutes les pièces jointes.

Afin de configurer ces deux tables, j’ai exécuté bin/bundle exec rails active_storage:install suivi de rails db:migrate. Les deux tables sont active_storage_blobs et active_storage_attachments ; la première concerne les détails du fichier joint et la seconde concerne la table de jointure polymorphe qui relie un fichier joint à un enregistrement de modèle.

Et voici le point important : une fois que nous avons créé ces deux tables, nous n’avons pas besoin de créer d’autres migrations pour inclure des pièces jointes dans nos modèles ! Cela rend ActiveStorage facile à utiliser du point de vue de la base de données.

Ensuite, j’ai envisagé de remplacer l’implémentation de la pièce jointe :avatar par ActiveStorage au lieu de CarrierWave. Dans mon application, chaque User a un :avatar. Parce que j’utilisais CarrierWave auparavant, j’avais un attribut :avatar monté sur un CarrierWave Uploader dans le modèle User, déclaré comme ceci:

class User < ApplicationRecord
# ... various codez mount_uploader :avatar, AvatarUploader # ... other codez
end

La pièce jointe CarrierWave :avatar peut être remplacée par une pièce jointe ActiveSupport en utilisant has_one_attached :avatar, comme ceci:

class User < ApplicationRecord
# ... various codez # commented old uploader for reference
# mount_uploader :avatar, AvatarUploader
has_one_attached :avatar # ... other codez
end

C’est un changement à une ligne de code. À ce stade, que faut-il faire d’autre? Par exemple, comment l’emplacement de stockage de téléchargement est-il défini ? N’oubliez pas que dans mon application, le téléchargeur d’avatar CarrierWave est configuré avec le stockage de fichiers simple par défaut, et l’emplacement pour cela est explicitement défini dans le fichier avatar_uploader.rb. Le stockage de fichiers simple est également la valeur par défaut dans ActiveStorage. L’emplacement est défini dans le fichier config/storage.yml, avec une valeur par défaut pour les téléchargements locaux.

La configuration par défaut pour ActiveStorage signifie que je n’ai plus besoin d’écrire de code pour que mes téléchargements fonctionnent pour l’attribut :avatar modifié dans mon application. J’ai entièrement changé le mécanisme de téléchargement de CarrierWave à ActiveStorage et je n’ai rien à faire de plus: Je n’ai pas besoin d’apporter de modifications au contrôleur qui gère le téléchargement, car :avatar est déjà déclaré comme paramètre fort. Et je n’ai pas besoin de changer le champ de formulaire de fichier dans la vue qui permet aux utilisateurs de sélectionner une image à télécharger, car cela est soutenu par l' »attribut » :avatar.

Mais il y a encore un changement que je dois apporter pour migrer de CarrierWave vers ActiveStorage, et c’est comment utiliser l’image jointe dans les vues. Avec CarierWave, j’ai utilisé la méthode avatar_url pour rendre le chemin d’image complet. Avec ActiveStorage, l’URL du fichier n’est pas rendue à partir de l’attribut de pièce jointe lui-même, mais en passant l’attribut aux méthodes d’assistance. Ainsi, l’URL complète de la pièce jointe peut être rendue en utilisant url_for(user.avatar), ou directement avec image_tag.

Il est également nécessaire de vérifier explicitement si la pièce jointe est présente, de sorte qu’une utilisation complète dans une vue ressemblerait à ceci:

<% if current_user.avatar.attached? %>
<%= image_tag current_user.avatar, class: 'avatar' %>
<% else %>
<%= image_tag 'default-avatar', class: 'avatar' %>
<% end %>

Une fois que j’ai mis à jour les références au user.avatar_url dans mes vues avec des extraits comme celui-ci ci-dessus, j’étais complètement passé de CarrierWave à ActiveStorage pour tous les nouveaux téléchargements.

L’ancien code CarrierWave doit être rangé, et il y a une question sur ce qu’il faut faire avec les téléchargements existants qui ont été effectués avec CarrierWave. Mais voir ci-dessous à ce sujet

Scénarios plus complexes

Presque tous les scénarios de production seront plus complexes que celui que j’ai décrit ci-dessus. Il sera nécessaire de prendre en compte des éléments tels que les téléchargements stockés sur des CDN ou des serveurs distants comme AWS S3, et le redimensionnement des images pour les vignettes, etc. ActiveStorage nous permet également de faire beaucoup de choses prêtes à l’emploi et semble très bien conçu pour les besoins des développeurs. Comme vous pouvez vous y attendre, il existe une bonne documentation dans le guide Rails.

En ce qui concerne la migration des téléchargements existants de CarrierWave vers ActiveStorage, cela serait certainement possible avec un script rake relativement petit. Je m’efforcerai de couvrir cela dans un prochain article.

C’était un scénario simple, mais dans l’ensemble, j’étais vraiment satisfait de la facilité avec laquelle il était possible d’utiliser ActiveStorage et de remplacer CarrierWave par celui-ci. J’aime aussi la pensée qui est allée dans les différentes parties d’ActiveStorage, car tout semble être dans des fichiers et des dossiers qui correspondent bien à la structure du reste de Rails. Je pense vraiment que cela vaut la peine d’entrer dans Rails 5.2 afin d’utiliser ActiveStorage pour les téléchargements de fichiers dans vos applications!

En savoir plus !

Vous souhaitez en savoir plus sur Ruby et Ruby on Rails ? J’enseigne le Développement d’Applications Web à l’EPFL Extension School, une plateforme d’apprentissage en ligne certifiée qui enseigne les compétences numériques en science des données, en développement d’applications web, etc. L’EPFL est l’une des plus grandes universités au monde et est classée « Jeune Université numéro 1 » par le classement Times Higher Education.



+