2011-01-16 7 views
10
  • Schienen: 3.0.3
  • Rubin: 1.9.2

ein sehr einfaches Objekt mit YAML.load oder Marshal.load erzeugt eine beschädigte deserialisieren Der Versuch Objekt, weil die Klasse, zu der gehört, beim Deserialisierungsprozess nicht benötigt wird.Rails nicht Klassen laden auf Deserialisieren YAML/Marshal Objekte

Beispiel:

# 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 

Mit diesem Code wir diese Shell-Konsole-Sitzung ohne Fehler ausführen können:

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

Aber wenn ich laufen die Deserialisierung aus einer Rails das Objekt Konsole nennt, ist wird nicht richtig deserialisiert, weil die Klasse niemals benötigt wird:

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

Ich weiß, das einzige Problem ist, dass die Klasse nicht, weil erforderlich ist, wenn ich es erfordern von Hand alles funktioniert:

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

ich nur die YAML Beispiele vorgestellt haben, aber mit Marschall ist ziemlich gleich.

Auch sagen, dass, obwohl ich das Problem in einer Rails-Konsole reproduzieren ursprünglich dieses Problem wurde mich verrückt in einer normalen Anfrage an meine Anwendung.

So ist die Frage: Wie kann ich Objekte in Rails deserialize ohne alle meine Klassen von Hand erfordern?

Dank

f.

+0

Ich habe gerade festgestellt, dass dies nur geschieht in einer ** Entwicklungsumgebung **. – fguillen

+1

Ich sehe, dass, wenn ich 'config.cache_classes' aktiviere die Deserialisierung gut funktioniert, aber natürlich: ** Ich habe die Klasse automatisches Auffrischen verloren **:/ – fguillen

Antwort

20

Nun, lesen Sie nach @tadman und einer Reihe von Antworten, die ich in der spanischen ror Mailing-Liste erhalten habe [1] Ich habe ein paar heißen Tipps gesammelt, wenn Sie mit Ruby-Deserialisieren und Laden von Klassen in Rails zu tun haben:

super schnelle Lösung

Verwenden config.cache_classes = true in Ihrem development.rb aber Sie werden die Klasse auto-Refresh verloren.

Bessere Lösung

alle Klassen verlangen, dass Gonna deserialisiert werden, aber nicht mit require aber mit require_dependency [2] so in Entwicklung Umgebung der Klasse Auto-erfrischend bleiben arbeiten.

elegante Lösung

Monkey-Patch des YAML und die Marschall gem ihnen zu sagen, require_dependency zu nennen, wenn sie eine nicht definierte Klasse deserialisieren finden.

Und @Xavi hat mir einen Satz von Affen-Patch schickte Marshal (er sagt, dass er es auf Sendung geschrieben und es ist nicht so verwenden Sie es in Ihrem eigenen Risiko getestet) [3]

+0

Diese Antwort ist ein Lebensretter. Dachte, ich müsste meine Architektur überdenken ... – fivetwentysix

+0

Danke! Genau das, was ich brauchte! Ich bin jedoch etwas verwirrt darüber, was das "Erforderliche" in Vorschlag 2 macht. –

+0

FYI, @fguillen kam zu mir zurück und hier sind ein paar Lösungen: '* in einem benutzerdefinierten Initialisierer wie: '/config/initializers/explicit_requires.rb' * am unteren Rand der '/config/application.rb' ' –

0

Soweit ich weiß, nutzen sowohl YAML als auch Marshal den Rails Autoloader nicht. Sie müssen fortfahren und alle Klassen vorbelasten, die möglicherweise deserialisiert werden müssen.

Es ist ein bisschen, wenn eine Aufregung, vor allem in der Entwicklungsumgebung, wo fast nichts geladen ist, bevor es benötigt wird.

2

automatisch verlangen Klassen auf YAML Laden in der Art und Weise @fguillen legt nahe, ist elegant, schrieb ich diesen kurzen Affen Patch.

Es versucht einfach Required_dependency jede Klasse die Psych ToRuby-Klasse in Klassen auflöst.

Funktioniert für mich in einem serialisierten Active Record, der eine benutzerdefinierte Klasseninstanz YMMV speichert.

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

Ich hatte @ ben-Pattersons Antwort anzupassen ein bisschen, um es (mit Rails 5.0.2) funktioniert:

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 
Verwandte Themen