Pregunta

  • Rieles: 3.0.3
  • Ruby: 1.9.2

Tratando de deserializar un objeto muy simple usando YAML.load o Marshal.load produce un objeto dañado porque la clase que pertenece a no es necesario en el proceso de deserializar.

Ejemplo:

# app/models/my_model.rb
class MyModel
  attr_accessor :id
end

# test/unit/serializing_test.rb
require 'test_helper'

class SerializingTest < Test::Unit::TestCase
  def test_yaml_serialize_structure
    my_model = MyModel.new
    my_model.id = 'my model'

    File.open( "#{Rails.root}/tmp/object.yml" , 'w' ) do |f|
      YAML::dump(my_model, f)
    end
  end

  def test_yaml_deserialize_structure
    object = YAML.load_file "#{Rails.root}/tmp/object.yml"
    assert( object.instance_of? MyModel )
    assert_equal( 'my model', object.id )
  end
end

Con este código podemos ejecutar esta sesión de consola de shell sin ningún error:

$ ruby -Itest test/unit/serializing_test.rb -n test_yaml_serialize_structure
$ ruby -Itest test/unit/serializing_test.rb -n test_yaml_deserialize_structure

Pero si me quedo los llamadas deserialización desde unos rieles de consola, el objeto no se deserializado correctamente porque la clase no se requiere:

$ rails c
ruby-1.9.2-p0 > object = YAML.load_file "#{Rails.root}/tmp/object.yml"
 => #<Syck::Object:0x0000010322ea30 @class="MyModel", @ivars={"id"=>"my model"}> 

Sé que el único problema es que no se requiere la clase porque si yo requiero que por todo lo que los trabajos manuales:

ruby-1.9.2-p0 > require "#{Rails.root}/app/models/my_model"
 => ["MyModel"] 
ruby-1.9.2-p0 > object = YAML.load_file "#{Rails.root}/tmp/object.yml"
 => #<MyModel:0x0000010320c8e0 @id="my model"> 

He presentado solamente los ejemplos YAML pero con Mariscal es bastante la misma.

También decir que a pesar de que estoy reproducir el problema en un consola de Rails originalmente este problema se volvía loco en una solicitud normal a mi solicitud.

Así que la pregunta es: ¿Cómo puedo deserializar objetos en los carriles sin tener que requerir todas mis clases a mano

?

Gracias

f.

¿Fue útil?

Solución

Bueno, después de leer @tadman y un montón de respuestas que he recibido en la lista de correo español ROR [1] He recogido algunos consejos calientes cuando se tiene que lidiar con Ruby y deserializar la clase de carga en los carriles:

solución de Super rápido

Uso config.cache_classes = true en su development.rb pero se perdió la clase de auto-refrescante.

Mejor solución

Requerir todas las clases que van a estar deserializado pero no con require pero con require_dependency [2] por lo que en Desarrollo entorno de la clase de auto-restauración permanecerá trabajando.

solución elegante

mono-Patch YAML y Mariscal joya de decir que llamen a require_dependency cuando encuentran una clase no definida deserializar.

y @Xavi me ha enviado una propuesta de Marshal mono-parche (él dice que escribió en el aire y no se prueba a fin de utilizarlo en su propio riesgo) [3]

Otros consejos

I described this "issue" on GitHub: https://github.com/rails/rails/issues/1585

To automatically require classes on YAML loading in the manner @fguillen suggests is elegant, I wrote this short monkey-patch.

It simply attempts to require_dependency any class the Psych ToRuby class resolves to classes.

Works for me in a serialised Active Record that stores a custom class instance, YMMV.

module Psych::Visitors
  ToRuby.class_eval do
    alias :resolve_class_without_autoload :resolve_class
    def resolve_class klassname
      begin
        require_dependency klassname.underscore 
      rescue NameError, LoadError
      end
      resolve_class_without_autoload klassname
    end
  end
end

I had to adapt @ben-patterson's answer a bit to make it work (using Rails 5.0.2):

module Psych::Visitors
    ToRuby.class_eval do
        def resolve_class(klassname)
            begin
                class_loader.load klassname
            rescue ArgumentError
                require_dependency klassname.underscore
                klassname.constantize
            end
        end
    end
end

As far as I know, both YAML and Marshal do not make use of the Rails autoloader. You must go ahead and pre-load any classes that might need to be deserialized.

It's a bit if a fuss, especially in the development environment where almost nothing is loaded before it is needed.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top