Última actualización
Última actualización
es una gema para manejar de manera simple la tarea de subir y adjuntar archivos.
Anteriormente usábamos Paperclip, hasta que fue deprecada en favor de la solución incluida en Rails, ActiveStorage. Dado esto, nos cambiamos a ActiveStorage (AS) para no quedarnos con una herramienta sin soporte.
Sin embargo, hay algunos detalles que se manejan mejor en Shrine, por ejemplo:
Shrine tiene un diseño modular, utilizando plugins
para las distintas funcionalidades que ofrece. Esto permite cargar solo las que en verdad se usen
Shrine promueve separación de responsabilidades, introduciendo el concepto de Uploader
. Estos se encargan de la lógica de subida de un tipo de archivo en particular
AS no tiene validaciones, por ejemplo de tamaño o tipo de archivo. En Shrine se pueden agregar fácilmente usando el plugin
Recién en Rails 6.1 se está dando soporte a acceso público de archivos, con Shrine se puede definir a nivel de configuración o por Uploader
Con Shrine se pueden setear default_url
por Uploader, para cuando el archivo es nil
La gema viene instalada si el proyecto se generó usando con la opción Shrine
elegida como storage. También se incluyen un par de uploaders que sirven como base. Si el proyecto se generó sin una opción de storage, se puede agregar corriendo potassium install file_storage
y seleccionando Shrine
.
Para estos ejemplos se utilizó la versión 3.2.1 de Shrine
Supongamos que tenemos un Uploader
de imágenes genérico, que podría verse así:
Con esto ya podemos empezar a incluir imágenes en los modelos. Si queremos un attachment
llamado photo
habría que agregar la columna photo_data
a la tabla (tipo text
o jsonb
) y agregar lo siguiente en el modelo:
El nombre de la columna siempre debe incluir el sufijo _data
Ahora, imaginemos que se quiere agregar una imagen de perfil para los usuarios. Podríamos usar el ImageUploader
definido antes, pero se quieren un par de cosas extra para esta profile_picture
:
Límite de peso, 5 MB. Pueden haber muchas imágenes de perfil distintas en una misma vista y no queremos que quede muy pesada
El usuario puede elegir no tener una foto de perfil
Comúnmente se usará la imagen en dos tamaños
Como esta sigue siendo una imagen y queremos mantener la validación definida en ImageUploader
, vamos a definir un nuevo uploader que herede de este:
Límite de peso
Sin foto de perfil
ActionController::Base.helpers.image_url
nos ayuda a obtener la url final de uno de los assets del proyecto.
Con esto, todo usuario cuya profile_picture
sea nil
retornará la url del asset al hacer user.profile_picture_url
.
Distintos tamaños
Después en el controlador se debe gatillar la creación de estas derivadas:
Y para acceder a la url de una de las derivadas:
Resultado
Con todo esto, nuestro Uploader
que hereda de ImageUploader
quedaría así:
Y para agregar el attachment al modelo de usuario:
Aquí tenemos el plugin , que nos permite usar validate_mime_type
para verificar que el tipo del archivo corresponda efectivamente a una imagen.
Para esto recurrimos nuevamente al validations_helper
, esta vez usando validate_max_size
. Para mantener las validaciones de la clase padre, , quedando así:
En estos casos queremos poner una imagen por defecto, que indique claramente la ausencia de la foto de perfil. Digamos que tenemos una imagen para este propósito en /app/assets/images/no-profile-picture.png
. Podemos usar el para usarla siempre que no haya una imagen de perfil:
Vamos a procesar la imagen para tener dos tamaños aparte del original: small
y medium
. Shrine tiene dos opciones para realizar el procesamiento: dinámicamente la transformación (on-the-fly) o , guardando el resultado. Para este ejemplo usaremos la segunda opción.
Como prerequisito necesitamos agregar la gema . Luego podemos definir las derivatives
para los tamaños small
y medium
:
: muy buena, con guías y secciones explicando los distintos plugins
/ : ambos sirven para subir un archivo antes de que se le haga submit a un form. La diferencia radica en que el de S3 lo sube directamente a AWS, mientras que el otro lo sube a un upload_endpoint
de la aplicación. Para ambientes que no tienen un bucket S3 (como development
, en que se guardan los archivos en el filesystem) habría que usar Direct App Upload
: presentación al equipo de Platanus para introducir Shrine. Se arma un componente Vue para manejar el Direct Upload que puede ser usado dentro de un form de Rails
: en la sección de dan un ejemplo de un helper que puede ser usado en las factories. En la sección de dan un ejemplo de como agregar un archivo como parámetro en tests de controladores usando Rack::Test::UploadedFile
: PR implementando Direct Upload para ser usado en ActiveAdmin