Usamos Ruby on Rails para enseñar desarrollo de aplicaciones web en la Escuela de Extensión EPFL. Es un gran marco de trabajo sólido, probado y confiable y presenta una estructura fácil de seguir al dar sus primeros pasos para aprender sobre desarrollo web.
Pero también es una pieza de software en constante evolución, con mejoras y nuevas características que se lanzan cada año. Una de las adiciones recientes a Rails en la versión 5.2 es la función ActiveStorage, que agrega por primera vez una fácil administración de carga de archivos al marco central de Rails.
En la primera iteración de nuestro programa de desarrollo de aplicaciones web, incluimos un tema para aprender a agregar cargas de archivos en una aplicación Rails. Sin embargo, usamos Rails 5.1 en el programa, y el sujeto de carga de archivos utiliza una de las soluciones de terceros más utilizadas para agregar cargas de archivos, llamada CarrierWave. Esta es una joya sólida de terceros que nos permite implementar cargas de archivos en una aplicación Rails y está en uso en muchas aplicaciones de Ruby on Rails del mundo real, ¡incluida nuestra plataforma de aprendizaje!
Pero con la introducción de ActiveStorage, estas gemas de terceros se vuelven menos atractivas que esta nueva solución incluida dentro de Rieles. De hecho, Paperclip, una de las otras gemas populares de carga de archivos de terceros, ya ha anunciado que está en desuso a favor de ActiveStorage. Los desarrolladores de Paperclip han reconocido que ActiveStorage es una mejor opción para los desarrolladores y han dejado de trabajar en su propia joya.
Veamos específicamente lo que se necesita para pasar de CarrierWave en Rails 5.1 a ActiveStorage en Rails 5.2.
La aplicación
En el programa de desarrollo de aplicaciones web, trabajamos creando una aplicación web llamada Mi lista de deseos. Esta es una aplicación que permite a los usuarios crear y rastrear experiencias que siempre han querido lograr. Los usuarios pueden ver lo que otras personas han creado y rastrearlos también. Una de las funciones de la aplicación permite a un usuario cargar su propio avatar, y creamos esa función para aprender sobre la carga de archivos y archivos adjuntos utilizando CarrierWave.
Así que primero, vamos a tener una revisión rápida de lo que implica agregar un archivo adjunto simple usando CarrierWave.
Configuración de onda portadora
Para incluir onda portadora en un proyecto, necesitamos:
- Agregue la gema CarrierWave a
Gemfile
. - Incluya el adaptador de onda portadora para ActiveRecord dentro de un inicializador de configuración,
config/initializers/carrierwave.rb
.
y luego para agregar un archivo adjunto de imagen a un modelo, necesitamos:
- Genera un cargador usando
rails generate uploader UploaderName
. - Agregue un atributo en el modelo para almacenar el nombre de archivo adjunto para cada registro, con la migración de la base de datos asociada.
- Edite valores en el cargador, como la ubicación de almacenamiento de carga y la imagen predeterminada.
- Use la macro
mount_uploader
en el modelo para incluir el cargador en particular. - Agregue un campo de formulario de archivo donde se puede cargar una imagen.
- Agregue el atributo model a la lista de parámetros fuertes en el controlador que maneja el formulario que contiene el campo file.
Consideremos uno de estos puntos más adelante: en CarrierWave, es necesario agregar atributos específicos a cualquier modelo que necesite tener un archivo adjunto. Por ejemplo, un modelo User
que necesita una imagen avatar
adjunta a él necesitará un atributo llamado :avatar
que es String
. Esto requiere una migración de base de datos para agregar la columna de base de datos subyacente, además de crear y montar una clase Uploader
a ese atributo en el modelo.
Esto no es particularmente laborioso, pero, como veremos, ActiveStorage requiere mucho menos código caso por caso.
Un punto más a destacar es que en mi aplicación, la aplicación Mi lista de deseos, el cargador de avatar CarrierWave está configurado con el almacenamiento de archivos simple predeterminado. En palabras ordenadas, las imágenes cargadas se almacenan en algún lugar dentro de la carpeta public/
de la aplicación. Esa ruta precisa se define dentro de la clase Uploader
para el avatar, y se establece en "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
. Esta es una ruta relativa a la carpeta public/
, de modo que las imágenes cargadas son accesibles directamente a través del servidor web. Esto permite el acceso directo a los archivos adjuntos, que podemos contrastar con ActiveStorage más adelante.
Actualización de Rails
Antes de ver ActiveStorage, es necesario actualizar a Rails 5.2. Esta es una tarea que puede ser bastante complicada, dependiendo de la complejidad de su aplicación. La aplicación que estoy usando aquí, que construimos en nuestro programa WAD, no es particularmente difícil de actualizar, pero aún necesita un poco de cuidado y atención.
Esto es lo que hice para actualizar la aplicación Mi lista de deseos de Rails 5.1 a Rails 5.2.
Primero, creé una nueva rama en mi repositorio git para aislar la actualización, ¡es importante poder volver a la versión de trabajo anterior de una aplicación si algo sale mal en el proceso de actualización!
En la nueva rama, cambié la versión de Rails en Gemfile
de '~> 5.1.2'
a '~> 5.2.0'
, y luego corrí bin/bundle update rails
— este comando descarga la nueva versión de rails
y todas las gemas relacionadas en la aplicación. Para mi aplicación, no hubo conflictos de gemas, pero si está utilizando gemas de terceros, es posible que deba resolver conflictos entre diferentes versiones de gemas en este punto.
Después de instalar la nueva gema Rails y sus dependencias, es necesario actualizar la propia aplicación. Hice esto usando bin/bundle exec rails app:update
. Este comando actualiza mediante programación todos los archivos necesarios en la aplicación para trabajar con Rails 5.2.
Pero this este comando sobrescribirá los cambios que haya realizado en su aplicación, como config/routes.rb
. Afortunadamente, el comando pregunta si sobrescribir cada archivo, y es posible diff
cualquiera de esos archivos durante este proceso para verificar qué se sobrescribiría. La solicitud para cada sobrescritura es , lo que significa:
-
Y
para «Sí, sobrescribir mi archivo», y es la opción predeterminada si solo presionaEnter
. -
n
para «no, no sobrescribir mi archivo» -
a
para » Sí, sobrescribir este archivo y sobrescribir todos los demás archivos también!». (Mi regla general es nunca usara
). -
q
para «Salir de este proceso» -
d
para «Muéstrame las diferencias entre mi archivo y la sobrescritura» -
h
para «Muéstrame una lista de lo queYnaqdh
significa»
En mi solicitud, acepté cada sobreescritura con Y
excepto config/routes.rb
y config/locales/en.yml
. Esos dos archivos eran archivos que había cambiado y quería conservar mi versión, así que elegí n
.
Este proceso de actualización podría ser más complicado si tiene una aplicación más compleja, particularmente si tiene mucha configuración personalizada en cualquiera de los archivos de configuración del entorno, p. ej. config/environments/production.rb
, config/environments/test.rb
y config/environments/development.rb
. La actualización agrega configuraciones importantes a estos archivos, pero también querrá mantener su propia configuración personalizada, aquí es donde ayuda tener la versión original de la aplicación en una rama git separada. A continuación, es posible que el código de esta rama antigua compruebe que todo el código y la configuración de su aplicación aún estén en su lugar después de la actualización.
Después de ejecutar bin/bundle exec rails app:update
, tuve que agregar la gema bootsnap
a Gemfile
. Esta es una nueva gema usada en Rails 5.2, pero no se ha añadido como parte del comando app:upgrade
. Así que simplemente agregué gem 'bootsnap'
a Gemfile
y luego corrí bin/bundle install
.
Después de esto, mi aplicación de Lista de deseos se inició como antes de usar rails server
.
Reemplazar CarrierWave con ActiveStorage
Por lo tanto, con la aplicación que ahora se ejecuta en Rails 5.2, podría comenzar a reemplazar CarrierWave con ActiveStorage.
Con ActiveStorage, no es necesario agregar atributos específicos del modelo para almacenar los nombres de archivo adjuntos que son necesarios con CarrierWave. ActiveStorage funciona con las mismas dos tablas asociadas para todos los archivos adjuntos.
Para configurar estas dos tablas, corrí bin/bundle exec rails active_storage:install
seguido de rails db:migrate
. Las dos tablas son active_storage_blobs
y active_storage_attachments
; la primera es para los detalles del archivo adjunto y la segunda es para la tabla de unión polimórfica que vincula un archivo adjunto a un registro de modelo.
Y aquí está el punto importante: una vez que hayamos creado esas dos tablas, no necesitamos crear ninguna otra migración para incluir archivos adjuntos en nuestros modelos. Esto hace que sea fácil trabajar con ActiveStorage desde la perspectiva de una base de datos.
A continuación, busqué reemplazar la implementación del adjunto :avatar
con ActiveStorage en lugar de CarrierWave. En mi aplicación, cada User
tiene un :avatar
. Debido a que estaba usando CarrierWave anteriormente, tenía un atributo :avatar
montado en un CarrierWave Uploader
en el modelo User
, declarado así:
class User < ApplicationRecord
# ... various codez mount_uploader :avatar, AvatarUploader # ... other codez
end
El accesorio CarrierWave :avatar
se puede reemplazar por un accesorio ActiveSupport usando has_one_attached :avatar
, como este:
class User < ApplicationRecord
# ... various codez # commented old uploader for reference
# mount_uploader :avatar, AvatarUploader
has_one_attached :avatar # ... other codez
end
Eso es un cambio a una línea de código. En este punto, ¿qué más hay que hacer? Por ejemplo, ¿cómo se establece la ubicación de almacenamiento de carga? Recuerde que en mi aplicación, el cargador de avatar CarrierWave está configurado con el almacenamiento de archivos simple predeterminado, y la ubicación para esto se establece explícitamente en el archivo avatar_uploader.rb
. El almacenamiento de archivos simple también es el predeterminado en ActiveStorage. La ubicación se establece dentro del archivo config/storage.yml
, con un valor predeterminado para las cargas locales.
La configuración predeterminada de ActiveStorage significa que no necesito escribir más código para que mis cargas funcionen para el atributo :avatar
modificado en mi aplicación. He cambiado completamente el mecanismo de carga de CarrierWave a ActiveStorage y no necesito hacer nada más: No necesito hacer ningún cambio en el Controlador que administra la carga, porque :avatar
ya está declarado como un parámetro fuerte. Y no necesito cambiar el campo de formulario de archivo en la vista que permite a los usuarios seleccionar una imagen para cargar, porque esto está respaldado por el «atributo» :avatar
.
Pero hay un cambio más que necesito hacer para migrar de CarrierWave a ActiveStorage, y es cómo usar la imagen adjunta en las vistas. Con CarierWave, utilicé el método avatar_url
para renderizar la ruta de imagen completa. Con ActiveStorage, la URL del archivo no se renderiza desde el atributo adjunto en sí, sino que se renderiza pasando el atributo a los métodos auxiliares. Por lo tanto, la URL de archivo ADJUNTO completa se puede representar usando url_for(user.avatar)
, o directamente con image_tag
.
También es necesario verificar explícitamente si el archivo adjunto está presente, por lo que un uso completo en una vista se vería algo como esto:
<% if current_user.avatar.attached? %>
<%= image_tag current_user.avatar, class: 'avatar' %>
<% else %>
<%= image_tag 'default-avatar', class: 'avatar' %>
<% end %>
Una vez que actualizé las referencias a user.avatar_url
en mis vistas con fragmentos como el anterior, me moví completamente de CarrierWave a ActiveStorage para todas las nuevas subidas.
El código antiguo de CarrierWave necesita ser ordenado, y hay una pregunta sobre qué hacer con las subidas existentes que se hicieron con CarrierWave. Pero vea a continuación sobre eso
Escenarios más complejos
Casi todos los escenarios de producción serán más complejos que el que describí anteriormente. Será necesario considerar cosas como las cargas que se almacenan en CDN o servidores remotos como AWS S3, y el cambio de tamaño de imágenes para miniaturas, etc. ActiveStorage nos permite hacer muchas de esas cosas fuera de la caja también y parece muy bien diseñado para las necesidades de los desarrolladores. Como es de esperar, hay buena documentación en la guía de Rails.
Con respecto a la migración de cargas existentes de CarrierWave a ActiveStorage, esto sin duda sería posible con un script de rake relativamente pequeño. Voy a tratar de cubrir eso en un futuro post.
Este fue un escenario simple, pero en general me encantó lo fácil que fue hacer uso de ActiveStorage y reemplazar CarrierWave con él. También me gusta el pensamiento que ha entrado en las diversas partes de ActiveStorage, ya que todo parece estar en archivos y carpetas que coinciden perfectamente con la estructura del resto de Rails. ¡Definitivamente creo que vale la pena entrar en Rails 5.2 para usar ActiveStorage para cargar archivos en sus aplicaciones!
¡Más información!
¿Está interesado en obtener más información sobre Ruby y Ruby on Rails? Enseño Desarrollo de Aplicaciones Web en la Escuela de Extensión EPFL, una plataforma de aprendizaje en línea certificada que enseña habilidades digitales en ciencia de datos, desarrollo de aplicaciones web y más. EPFL es una de las universidades líderes del mundo y está clasificada como la «Universidad Joven Número 1″por el Ranking de Educación Superior de Times.