ruby on rails - notidentifiedbyimagemagickerror - ¿Por qué Ruby open-uri''s open devuelve un StringIO en mi prueba de unidad, pero un FileIO en mi controlador?
paperclip::errors::notidentifiedbyimagemagickerror (2)
Heredé una aplicación Rails 2.2.2 que almacena imágenes cargadas por el usuario en Amazon S3. El modelo de Photo
basado en attachment_fu ofrece un método de rotate
que usa open-uri
para recuperar la imagen de S3 y MiniMagick para realizar la rotación.
El método de rotate
contiene esta línea para recuperar la imagen para su uso con MiniMagick:
temp_image = MiniMagick::Image.from_file(open(self.public_filename).path)
self.public_filename
devuelve algo así como
http://s3.amazonaws.com/bucketname/photos/98/photo.jpg
Recuperar la imagen y rotarla funciona muy bien en la aplicación en ejecución en producción y desarrollo. Sin embargo, la prueba de unidad falla con
TypeError: can''t convert nil into String
/Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `initialize''
/Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `open''
/Users/santry/Development/totspot/vendor/gems/mini_magick-1.2.3/lib/mini_magick.rb:34:in `from_file''
El motivo es que cuando se llama al método del modelo en el contexto de la prueba unitaria, open(self.public_filename)
devuelve un objeto StringIO
que contiene los datos de la imagen. El método de path
en este objeto devuelve nil
y MiniMagick::Image.from_file
explota.
Cuando se llama este mismo método de modelo desde PhotosController
, open(self.public_filename)
devuelve una instancia de FileIO
vinculada a un archivo llamado, por ejemplo, /tmp/open-uri7378-0
y el archivo contiene los datos de la imagen.
Pensando que la causa debe ser alguna diferencia ambiental entre la prueba y el desarrollo, encendí la consola en el entorno de desarrollo. Pero al igual que en la prueba unitaria, open(''http://...'')
devolvió un StringIO
, no un FileIO
.
He recorrido mi camino a través de open-uri y todo el código relevante específico de la aplicación y no puedo encontrar ninguna razón para la diferencia.
El código responsable de esto está en la clase Buffer en open-uri. Comienza creando un objeto StringIO y solo crea un archivo temporal real en el sistema de archivos local cuando los datos exceden un determinado tamaño (10 KB).
Supongo que cualquier dato que cargue su prueba es lo suficientemente pequeño como para guardarlo en un StringIO y las imágenes que está utilizando en la aplicación real son lo suficientemente grandes como para garantizar un TempFile. La solución es usar métodos que sean comunes a ambas clases, en particular el método de lectura, con MiniMagick :: Image # from_blob:
temp_image = MiniMagick::Image.from_blob(open(self.public_filename, &:read))
La biblioteca open-uri usa una constante para establecer el límite de tamaño de 10 KB para objetos StringIO.
> OpenURI::Buffer::StringMax
=> 10240
Puede cambiar esta configuración a 0 para evitar que open-uri cree un objeto StringIO. En cambio, esto lo obligará a generar siempre un archivo temporal.
Solo tira esto en un inicializador:
# Don''t allow downloaded files to be created as StringIO. Force a tempfile to be created.
require ''open-uri''
OpenURI::Buffer.send :remove_const, ''StringMax'' if OpenURI::Buffer.const_defined?(''StringMax'')
OpenURI::Buffer.const_set ''StringMax'', 0
No puedes simplemente establecer la constante directamente. Necesitas eliminar la constante y luego volver a configurarla (como se indica arriba), de lo contrario recibirás una advertencia:
warning: already initialized constant StringMax
ACTUALIZADO el 12/18/2012 : Rails 3 no requiere OpenURI de forma predeterminada, por lo que debe agregar require ''open-uri''
en la parte superior del inicializador. Actualicé el código anterior para reflejar ese cambio.