2011-01-16 11 views
10
  • Rotaie: 3.0.3
  • rubino: 1.9.2

Cercando di deserializzare un oggetto molto semplice usando YAML.load o Marshal.load producono un corrotto oggetto perché la classe a cui appartiene non è richiesta nel processo di deserializzazione.rotaie non caricare classi sulla deserializzazione YAML/Marshal oggetti

Esempio:

# 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 questo codice si può eseguire questa sessione di console di shell senza alcun errore:

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

Ma se faccio funzionare il deserializzazione chiama da un Rails console l'oggetto è non deserializzato correttamente perché la classe non è mai richiesta:

$ 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"}> 

so l'unico problema è che la classe non è necessario perché se ho bisogno a mano tutto funziona:

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"> 

ho presentato solo gli esempi YAML, ma con il maresciallo è abbastanza lo stesso.

anche dire che anche se sto riprodurre il problema in una console Rails originariamente questo problema stava girando impazzire in una richiesta normale alla mia richiesta.

Quindi la domanda è: Come posso deserializzare oggetti in Rails senza dover richiedere manualmente tutte le mie classi?

Grazie

f.

+0

ho solo capito che questo solo happend in un ambiente di sviluppo ** **. – fguillen

+1

Sto vedendo che se attivo 'config.cache_classes' la deserializzazione funziona bene, ma ovviamente: ** Ho perso la classe di aggiornamento automatico **:/ – fguillen

risposta

20

Bene, dopo aver letto @tadman e un sacco di risposte che ho ricevuto nella mailing list spagnola ror [1] Mi hanno raccolto alcuni suggerimenti caldo quando si ha a che fare con Ruby deserializzazione e la classe di carico in Rails:

soluzione veloce eccellente

Uso config.cache_classes = true nel vostro development.rb ma sarà perso la classe di auto-rinfrescante.

migliore soluzione

necessitano di tutte le classi che vengono andando essere deserializzato ma non con require ma con require_dependency [2] Così in sviluppo ambiente l'auto-rinfrescante classe rimarrà di lavoro.

soluzione elegante

scimmia-patch il YAML e la gemma maresciallo per dire loro di chiamare require_dependency quando trovano una classe non definita per deserializzare.

E @Xavi mi ha inviato una proposta di scimmia-cerotto Marshal (egli dice di aver scritto in onda e non è testato in modo da utilizzare nel vostro rischio e pericolo) [3]

+0

Questa risposta è un vero toccasana. Pensavo di dover ripensare la mia architettura ... – fivetwentysix

+0

Grazie! Esattamente quello di cui avevo bisogno! Tuttavia, sono un po 'confuso da ciò che sta facendo il' richiedere 'nel suggerimento # 2. –

+0

FYI, @fguillen è tornato da me e qui ci sono un paio di soluzioni: '* in un inizializzatore personalizzato come: '/config/initializers/explicit_requires.rb' * nella parte inferiore di '/config/application.rb' ' –

0

Per quanto ne so, sia YAML che Marshal non utilizzano il caricatore automatico Rails. Devi andare avanti e precaricare tutte le classi che potrebbero dover essere deserializzate.

È un po 'complicato, soprattutto nell'ambiente di sviluppo in cui quasi nulla viene caricato prima che sia necessario.

2

Per richiedere automaticamente le classi su YAML carico nel modo @fguillen suggerisce è elegante, ho scritto questo breve scimmia-patch.

Semplicemente tenta di richiedere_dipendenza qualsiasi classe che la classe Psych ToRuby risolva in classi.

Per me funziona in un record attivo serializzato che memorizza un'istanza di classe personalizzata, 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 
0

ho dovuto adattare @ Ben-Patterson risposta di un po 'per farlo funzionare (usando 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 
Problemi correlati