2012-12-06 11 views
9

Sto implementando funzionalità di tipo paper trail nella mia app rails. Per fare questo, sto serializzando l'oggetto in YAML. Ho il metodo item_at_version, che fondamentalmente fa YAML::load(cached_object) - funziona abbastanza bene, tuttavia, non ho idea del perché, a volte restituisce undefined class/module _class name_. Funziona con modelli come Evento, Conversazione, Nota, e molti altri, ma senza alcun motivo, sembra gettare quell'errore per modelli come Dataset, Comment, Student (ho cercato di trovare qualche pattern per quello, senza fortuna).YAML :: load solleva un errore di classe/modulo non definito

Sto usando rails 3.2.8, ruby ​​1.9.3p327, psych come YAML Engine (Psych :: Version restituisce 1.3.4).

Ps. Quando aggiungo richiedono 'model_name' in cima a quel file, funziona come un incantesimo.

Qualche idea che cosa dovrei modificare/aggiungere per farlo funzionare?

Edit: Non c'è molto codice che posso condividere:

def item_at_version 
    YAML::load(cached_object) 
end 

Ma forse backtrace sarebbe interessante:

~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:312:in `path2class' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:312:in `resolve_class' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:219:in `visit_Psych_Nodes_Mapping' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/visitor.rb:15:in `visit' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/visitor.rb:5:in `accept' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:20:in `accept' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:238:in `visit_Psych_Nodes_Document' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/visitor.rb:15:in `visit' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/visitor.rb:5:in `accept' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:20:in `accept' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/nodes/node.rb:35:in `to_ruby' 
~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych.rb:128:in `load' 
app/models/history_version.rb:7:in `item_at_version' 
app/controllers/history_controller.rb:8:in `show' 
actionpack (3.2.8) lib/action_controller/metal/implicit_render.rb:4:in `send_action' 
actionpack (3.2.8) lib/abstract_controller/base.rb:167:in `process_action' 
actionpack (3.2.8) lib/action_controller/metal/rendering.rb:10:in `process_action' 
actionpack (3.2.8) lib/abstract_controller/callbacks.rb:18:in `block in process_action' 
activesupport (3.2.8) lib/active_support/callbacks.rb:502:in `_run__1697733322876708236__process_action__1122943786273335015__callbacks' 
activesupport (3.2.8) lib/active_support/callbacks.rb:405:in `__run_callback' 
activesupport (3.2.8) lib/active_support/callbacks.rb:385:in `_run_process_action_callbacks' 
activesupport (3.2.8) lib/active_support/callbacks.rb:81:in `run_callbacks' 
actionpack (3.2.8) lib/abstract_controller/callbacks.rb:17:in `process_action' 
actionpack (3.2.8) lib/action_controller/metal/rescue.rb:29:in `process_action' 
actionpack (3.2.8) lib/action_controller/metal/instrumentation.rb:30:in `block in process_action' 
activesupport (3.2.8) lib/active_support/notifications.rb:123:in `block in instrument' 
activesupport (3.2.8) lib/active_support/notifications/instrumenter.rb:20:in `instrument' 
activesupport (3.2.8) lib/active_support/notifications.rb:123:in `instrument' 
actionpack (3.2.8) lib/action_controller/metal/instrumentation.rb:29:in `process_action' 
actionpack (3.2.8) lib/action_controller/metal/params_wrapper.rb:207:in `process_action' 
activerecord (3.2.8) lib/active_record/railties/controller_runtime.rb:18:in `process_action' 
actionpack (3.2.8) lib/abstract_controller/base.rb:121:in `process' 
actionpack (3.2.8) lib/abstract_controller/rendering.rb:45:in `process' 
actionpack (3.2.8) lib/action_controller/metal.rb:203:in `dispatch' 
actionpack (3.2.8) lib/action_controller/metal/rack_delegation.rb:14:in `dispatch' 
actionpack (3.2.8) lib/action_controller/metal.rb:246:in `block in action' 
actionpack (3.2.8) lib/action_dispatch/routing/route_set.rb:73:in `call' 
actionpack (3.2.8) lib/action_dispatch/routing/route_set.rb:73:in `dispatch' 
actionpack (3.2.8) lib/action_dispatch/routing/route_set.rb:36:in `call' 
journey (1.0.4) lib/journey/router.rb:68:in `block in call' 
journey (1.0.4) lib/journey/router.rb:56:in `each' 
journey (1.0.4) lib/journey/router.rb:56:in `call' 
actionpack (3.2.8) lib/action_dispatch/routing/route_set.rb:600:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call' 
rack (1.4.1) lib/rack/etag.rb:23:in `call' 
rack (1.4.1) lib/rack/conditionalget.rb:25:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/head.rb:14:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/params_parser.rb:21:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/flash.rb:242:in `call' 
rack (1.4.1) lib/rack/session/abstract/id.rb:205:in `context' 
rack (1.4.1) lib/rack/session/abstract/id.rb:200:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/cookies.rb:339:in `call' 
activerecord (3.2.8) lib/active_record/query_cache.rb:64:in `call' 
activerecord (3.2.8) lib/active_record/connection_adapters/abstract/connection_pool.rb:473:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call' 
activesupport (3.2.8) lib/active_support/callbacks.rb:405:in `_run__2589517259026276185__call__1369641113040304056__callbacks' 
activesupport (3.2.8) lib/active_support/callbacks.rb:405:in `__run_callback' 
activesupport (3.2.8) lib/active_support/callbacks.rb:385:in `_run_call_callbacks' 
activesupport (3.2.8) lib/active_support/callbacks.rb:81:in `run_callbacks' 
actionpack (3.2.8) lib/action_dispatch/middleware/callbacks.rb:27:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/reloader.rb:65:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/remote_ip.rb:31:in `call' 
bugsnag (1.2.5) lib/bugsnag/rack.rb:35:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call' 
railties (3.2.8) lib/rails/rack/logger.rb:26:in `call_app' 
railties (3.2.8) lib/rails/rack/logger.rb:16:in `call' 
quiet_assets (1.0.1) lib/quiet_assets.rb:20:in `call_with_quiet_assets' 
actionpack (3.2.8) lib/action_dispatch/middleware/request_id.rb:22:in `call' 
rack (1.4.1) lib/rack/methodoverride.rb:21:in `call' 
rack (1.4.1) lib/rack/runtime.rb:17:in `call' 
activesupport (3.2.8) lib/active_support/cache/strategy/local_cache.rb:72:in `call' 
rack (1.4.1) lib/rack/lock.rb:15:in `call' 
actionpack (3.2.8) lib/action_dispatch/middleware/static.rb:62:in `call' 
railties (3.2.8) lib/rails/engine.rb:479:in `call' 
railties (3.2.8) lib/rails/application.rb:223:in `call' 
railties (3.2.8) lib/rails/railtie/configurable.rb:30:in `method_missing' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:147:in `handle' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:99:in `rescue in block (2 levels) in start' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:96:in `block (2 levels) in start' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:86:in `each' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:86:in `block in start' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:66:in `loop' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:66:in `start' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/lib/nack/server.rb:13:in `run' 
~/Library/Application Support/Pow/Versions/0.4.0/node_modules/nack/bin/nack_worker:4:in `<main>' 
+0

È probabile che tu riceva un aiuto migliore se mostri del codice rilevante o un backtrace. – deefour

+0

Non c'è nulla di magico nel mio codice, co non ho molto da aggiungere. Ho aggiunto il backtrace completo, forse questo può aiutare. – user1105595

risposta

12

Quando si utilizza YAML.dump di serializzare un oggetto in Ruby, la classe il nome viene utilizzato come parte del tag Yaml in modo che sia possibile utilizzare la classe corretta durante il caricamento dell'oggetto. Per esempio:

require 'yaml' 

class Foo; end 

puts YAML.dump Foo.new 

produce

--- !ruby/object:Foo {} 

Quando si utilizza YAML.load su quella stringa, Psych sa quale classe istanziare per l'oggetto deserializzato.

Se si tenta di chiamare YAML.load su una stringa Yaml che specifica una classe che non è stato definito, allora si ottiene l'errore:

require 'yaml' 

# No Bar class has been defined 
YAML.load '--- !ruby/object:Bar {}' 

produce:

/Users/matt/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:312:in `path2class': undefined class/module Bar (ArgumentError) 
    from /Users/matt/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:312:in `resolve_class' 
... 

Questo è perché Psych ha bisogno di creare un'istanza di classe Bar, ma non ha la definizione della classe disponibile. Questo spiega perché aggiungere require 'whatever' prima di caricare lo Yaml funziona - ora Ruby ha la definizione della classe caricata e quindi può crearne un'istanza (si noti che non esiste un collegamento definitivo tra il nome della classe e il nome del file in Ruby, è solo una convenzione).

La soluzione è quindi assicurarsi che quando si carica qualsiasi oggetto Ruby da Yaml sia già richiesto qualsiasi file che possa contenere definizioni di qualsiasi classe potenzialmente in quella Yaml.

+2

- grazie per la tua risposta, tuttavia non sono ancora sicuro del motivo per cui questo non funziona nei binari. Quando aggiungo '' 'Bar.new (some:: params)' '' funziona, quindi anche l'oggetto istantaneo _should_ funziona, giusto? – user1105595

+1

@ user1105595 È interessante. Ha a che fare con il caricamento automatico delle classi di Rails. Quando si esegue 'Bar.new' si attiva il normale processo di autoloading, ma la deserializzazione di Yaml non lo fa. Se usi la tua app in produzione (o solo con 'cache_classes = true'), dovrebbe funzionare poiché tutte le classi saranno già caricate (probabilmente non lo farai sempre). Non sono sicuro di quale sia la soluzione migliore, potresti dover semplicemente richiedere esplicitamente i file in un inizializzatore o qualcosa del genere. – matt

+1

Ho trovato questo bug report che si riferisce allo stesso problema (si tratta di Marshal, ma il metodo 'rb_path_to_class' è lo stesso che causa l'errore in Psych) http://bugs.ruby-lang.org/issues/3511 . – matt

2

Matt's Answer fornisce i dettagli necessari.

Ma quando eseguo delle modifiche nel codice e quindi eseguo alcune attività, questo esegue la de-serializzazione di alcuni dati, senza caricare la pagina (in AJAX), quindi non riesce con lo stesso errore.

È preferibile utilizzare require_dependency rispetto a require per caricare automaticamente le modifiche.

In caso di modulo (non testato con class) e di serializzazione con YAML, è anche possibile creare un'istanza del modulo prima della serializzazione con require per risolvere il problema. See here.

Fonte: SO answer e Psych issue report in Github

P.S: Questo problema persiste solo in sviluppo come config.cache_classes è attiva nella produzione.

1

Modifica a Matt's answer. Al posto di una soluzione basata su puro hash (che il team di Ps sembra non avere interesse a fare), modifica la stringa di intestazione per rimuovere la classificazione dell'oggetto. L'ho fatto semplicemente con il seguente codice.

yamltext = File.read("somefile","r") 
yamltext.sub!(/^--- \!.*$/,'---') 
hash = YAML.load(yamltext) 

Questo potrebbe non riuscire (non so) se il flusso di input è costituito da diversi documenti con diversi classificatori oggetti (Non so se questo è ancora valido).

Problemi correlati