Raíles no carga las clases de deserializar objetos YAML / Mariscal
-
11-10-2019 - |
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.
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.