Advertisement
  1. Code
  2. Ruby on Rails

Subiendo Con Rails y Carrierwave

by
Length:MediumLanguages:

Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)

Este es otro artìuclo en la serie "Subiendo con Rails. Hoy vamos a conocer Carrierwave---una de las soluciones de subida màs populares para Rails. Me gusta Carrierwave porque es fácil comenzar, tiene muchas caracterìsticas fuera de la caja, y proporciona docenas de artículos "cómo hacer" escritos por los miembros de la comunidad, así que no te perderás.

En este artìculo, aprenderás cómo:

  • Integrar Carrierwave a tu aplicaciòn Rails
  • Agregar validaciones
  • Persistir archivos a lo largo de peticiones
  • Remover archivos
  • Generar viñetas
  • Subir archivos desde ubicaciones remotas
  • Introducir múltiples subidas de archivos
  • Agregar soporte para almacenamiento en la nube

El código fuente para este artículo está disponible en GitHub. ¡Disfruta leyendo!

Sentando las Bases

Como siempre, comienza creando una nueva aplicación Rails:

Para este demo estaré usando Rails 5.0.2. Por favor nota que Carrierwave 1 soporta solo Rails 4+ y Ruby 2. Si aún estás usando Rails 3, entonces usa Carrierwave versión 0.11.

Para ver a Carrierwave en acción, vamos a crear una aplicación muy simple de blogging con un solo modelo Post. Tendrá los siguientes atributos principales:

  • title (string)
  • body (text)
  • image (string)---este campo va a contener una imagen (un nombre de archivo, para ser preciso) adjunto a la publicación-

Genera y aplica una nueva migración:

Establece algunas reglas:

config/routes.rb

También, crea un controlador muy básico:

posts_controller.rb

Ahora construyamos la vista index:

views/posts/index.html.erb

Y el parcial correspondiente:

views/posts/_post.html.erb

Aquí estoy usando el método de Rails truncate para mostrar solo los primeros 150 símbolos de la publicación. Antes de crear otras vistas y formar un parcial, primero integremos Carrierwave a la aplicación.

Integrando Carrierwave

Pon una nueva gem en el Gemfile:

Gemfile

Ejecuta:

Carrierwave almacena su configuración dentro de uploaders que están incluidos en tus modelos. Para generar un uploader, usa el siguiente comando:

Ahora, dentro de app/uploaders, encontrarás un nuevo archivo llamado image_uploader.rb. Nota que tiene algunos comentarios útiles y ejemplos, así que podrías usarlos para comenzar. En este demo vamos a usar ActiveRecord, pero Carrierwave también tiene soporte para Mongoid, Sequel y DataMapper.

Después, necesitamos incluir o montar este uploader al modelo:

models/post.rb

El uploader ya tiene ajustes por defecto, pero al menos necesitamos elegir en dónde serán almacenados los archivos subidos. Por ahora, empleemos almacenamiento de archivos:

uploaders/image_uploader.rb

Por defecto, los archivos serán colocados dentro del directorio public/uploads, así que es mejor exluirlo del sistema de control de versión:

.gitignore

Podrás también modificar el método store_dir dentro de tu uploader para elegir alguna otra ubicación.

En este punto, vamos a crear una nueva vista y una forma parcial para comenzar a subir archivos:

views/posts/new.html.erb

views/posts/_form.html.erb

Nota que el PostController no necesita ser modificado ya que ya permitimos el atributo image.

Por último, crea la vista editar:

views/posts/edit.html.erb

¡Eso es todo! Debes iniciar el servidor e intentar crear una publicación con una imagen. El problema es que esta imagen no es visible en ningún lugar, así que procedamos a la siguiente sección y agreguemos una página para mostrar! 

Mostrando Imágenes

Así qué, la única vista que no hemos creado aún es show. Agrégala ahora:

views/posts/show.html.erb

Como puedes  ver, mostrar un adjunto es realmente sencillo, todo lo que necesitas hacer es decir @post.image.url para tomar la URL de una imagen. Para obtener una ruta al archivo, usa el método current_path. Nota que Carrierwave también proporciona un método image? para que revisemos si un adjunto está presente en absoluto (el método image por si mismo nunca regresará nil, incluso si el archivo no está presente).

Ahora, después de navegar a una publicación, deberías ver una imagen, pero podría aparecer muy grande: después de todo, no estamos restringiendo dimensiones en ningún lugar. Por supuesto, podríamos haber escalado la imagen con algunas reglas CSS, pero es mucho mejor generar una viñeta después de que el archivo ha sido cargado. Esto, sin embargo, requiere algunos pasos adicionales.

Generando Viñetas

Para poder cortar y escalar imágenes, necesitamos una herramienta por separado.  Fuera de la caja Carrierwave tiene soporte para gems RMagick y MiniMagick que, en su momento, son usadas para manipular imágenes con la ayuda de ImageMagick. ImageMagick es una solución de código abierto que te permite editar imágenes existentes y generar nuevas, así que antes de proceder, necesitas descargarlo e instalalrlo. Después, eres libre de elegir cualquiera de las dos gems. Me quedaré con MiniMagick, porque es mucho más sencillo de instalar y tiene mejor soporte:

Gemfile

Ejecuta:

Después incluye MiniMagick en tu uploader:

uploaders/image_uploader.rb

Ahora simplemente necesitamos introducir una nueva versión a nuestro uploader. El concepto de versiones (o estilos) es usado en muchas librerías de carga de archivos; simplemente significa que archivos adicionales basados en el adjunto original serán creados con, por ejemplo, diferentes dimensiones o formatos. Introduce una nueva versión llamada thumb:

uploaders/image_uploader.rb

Podrías tener tantas versiones como quieras y, lo que es más, las versiones pueden incluso ser construidas encima de otras:

uploaders/image_uploader.rb

Si ya has subido algunas imágenes, no tendrán miniaturas disponibles. Esto no es un problema, sin embargo, ya que puedes re-crearlas desde la consola Rails:

Por último, muestra tu miniatura con un enlace a la imagen original:

views/posts/show.html.erb

¡Inicia el servidor y observa el resultado!

Agregando Validaciones

Actualmente nuestra subida funciona, pero no hay entrada de usuario validada en absoluto, lo que es, por supuesto, malo. Mientras queremos trabajar solo con imágenes, aceptemos extensiones .png, .jpg, .gif.

uploaders/image_uploader.rb

También podrías agregar revisiones de tipo de contenido definiendo un método content_type_whitelist:

uploaders/image_uploader.rb

De manera alternativa, es posible bloquear algunos tipos de archivo, por ejemplo, ejecutables, definiendo el método content_type_blacklist:

Aparte de revisar el tipo y extensión de un archivo, forcémoslo a ser menor a 1 megabyte. Para hacerlo, requeriremos gem de soporte de validación de archivo para ActiveModel:

Gemfile

Instálalo:

Ahora introducimos las validaciones deseadas (nota que también estoy agregando revisiones para los atributos title y body):

models/post.rb

La siguiente cosa para hacer es agregar traducciones l18n para mensajes de error de Carrierwave:

config/locales/en.yml

Actualmente, no mostramos errores de validación en ningún lado, así que creemos un parcial compartido:

views/shared/_errors.html.erb

Emplea este parcial dentro del formulario:

views/posts/_form.html.erb

Ahora intenta cargar algunos archivos válidos y observa el resultado. Debería funcionar, pero si eliges un archivo válido y no llenas el title o body, entonces las revisiones aún fallarán y un error se mostrará. Sin embargo, el campo de archivo será limpiado y el usuario necesitará elegir la imagen de nuevo, lo que no es muy conveniente. Para corregirlo, necesitamos agregar otro campo al formulario.

Archivos Persistentes A lo Largo de Peticiones

Archivos persistentes a lo largo de redisplays de formulario son realmente fáciles. Lo único que necesitas hacer es agregar un nuevo campo oculto y permitirlo dentro del controlador:

views/shared/_form.html.erb

posts_controller.rb

Ahora el image_cache será poblado automáticamente y la imagen no se perderá. Podría ser de utilidad para mostrar una miniatura para que el usuario entienda que la imagen fue procesada exitósamente:

views/shared/_form.html.erb

Quitando Imágenes

Otra característica muy común es la habilidad de remover archivos adjuntos cuando se edita un registro. Con Carrierwave, implementar esta característica no es un problema. Agrega una nueva casilla al formulario:

views/shared/_form.html.erb

Y permite el atributo remove_image:

posts_controller.rb

¡Eso es! Para remover una imagen manualmente, usa el método remove_image!:

Subiendo Desde una Ubicación Remota

Carrierwave también proporciona una característica muy interesante fuera de la caja: la habilidad de subir archivos desde ubicaciones remotas por su URL. Introduzcamos esta habilidad ahora para agregar un nuevo campo y permitiendo el atributo correspondiente:

views/shared/_form.html.erb

posts_controller.rb

¿Qué tan genial es eso? No necesitas hacer ningún cambio en absoluto, ¡y puedes probar esta característica inmediatamente!

Trabajando Con Múltiples Subidas

Supón que queremos que nuestra publicación tenga varios adjuntos disponibles. Con la configuración actual no es posible, pero por suerte, Carrierwave soporta dicho escenario también. Para implementar esta característica, necesitas agregar ya sea un campo serializado (para SQLite) o un campo JSON (para Postgres o MySQL). Prefiero la última opción, así que cambiemos a un nuevo adaptador de base de datos ahora. Remueve el gem sqlite3 del Gemfile y agrega pg en su lugar:

Gemfile

Instálalo:

Modifica la configuración de base de datos como esto:

config/database.yml

Crea la base de datos Postgres correspondiente, y después genera y aplica la migración:

Si prefieres apegarte con SQLite, sigue las instrucciones listadas en la documentación de Carrierwave.

Ahora monta los cargadores (¡nota la forma en plural!)

model/post.rb

Estoy usando el mismo cargador para adjuntos, pero por supuesto puedes generar un nuevo con configuración diferente.

Agrega el campo de archivos múltiples a tu formulario:

views/shared/_form.html.erb

Mientras que el campo attachments va a contener una arreglo, debería ser permitido de la siguiente manera:

posts_controller.rb

Por último, debes iterar sobre los adjuntos de la publicación y mostrarlos de manera usual:

views/shared/show.html.erb

Nota que cada adjunto va a tener una miniatura como se configuró en nuestro ImageUploader. ¡Bien!

Usando Almacenamiento en la Nube

Apegarse al almacenamiento de archivos no siempre es conveniente y/o posible ya que, por ejemplo, en Heroku no es posible almacenar archivos personalizados. Así pues podrías preguntarte ¿cómo casar Carrierwave con el almacenamiento en la nube de Amazon S3? Bueno, esa es una tarea bastante fácil también. Carrierwave depende de la gem fog-aws para implementar esta característica:

Gemfile

Instálalo:

Creemos un inicializador para Carrierwave y configuremos el almacenamiento en la nube de manera global:

config/initializers/carrierwave.rb

Hay algunas otras opciones disponibles, las cuáles pueden ser encontradas en la documentación.

Estoy usando el gem dotenv-rails para establecer las variables de sesión de manera segura, pero podrías querer elegir cualquier otra opción. Sin embargo, asegúrate de que tu par de llave S3 no esté disponible de manera pública, ¡porque de otro modo cualquier puede subir cualquier cosa a tu cubeta!

Después, reemplaza la línea storage :file con:

uploaders/image_uploader.rb

Además de S3, Carrierwave soporta subidas para Google Storage y Rackspace. Estos servicios son fáciles de configurar también.

Conclusión

¡Esto es todo por hoy! Hemos cubierto todas las características mayores de Carrierwave, y ahora puedes comenzar a usarlo en tus proyectos. Tiene algunas opciones adicionales disponibles, así que navega la documentación.

Si te atoras, no dudes en publicar tus preguntas. También, podría ser útil echar un vistazo al wiki de Carrierwave, que hospeda artículos "como hacer" útiles contestando muchas preguntas canónicas.

Así que gracias por quedarte conmigo, ¡y feliz codificación!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.