rails notidentifiedbyimagemagickerror missingrequiredvalidatorerror game errors ruby-on-rails ruby amazon-s3 imagemagick open-uri

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.