2011-01-16 6 views
10
  • Rails 3.0.3
  • Ruby 1.9.2

Próbując deserializowania bardzo prosty obiekt za pomocą YAML.load lub Marshal.load produkuje uszkodzony obiekt, ponieważ klasa, do której należy, nie jest wymagana w procesie deserializacji.Szyny nie ładuje zajęcia na deserializacji YAML/Marszałek obiekty

Przykład:

# 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 

Z tego kodu możemy uruchomić tę sesję konsoli powłoki bez jakiegokolwiek błędu:

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

Ale jeśli uruchomić deserializacji wzywa z Rails pocieszyć obiekt jest nie jest poprawnie deserializowany, ponieważ klasa nigdy nie jest wymagana:

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

wiem, jedynym problemem jest to, że klasa nie jest wymagane, ponieważ jeśli wymaga to ręcznie wszystko działa:

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

Przedstawiłem tylko przykłady YAML ale z Marszałkiem jest całkiem to samo.

Mówię również, że chociaż odtwarzam problem w konsoli Railsowej pierwotnie ten problem doprowadzał mnie do szaleństwa w normalnym żądaniu do mojej aplikacji.

Pytanie brzmi: W jaki sposób można deserializować obiekty w Railsach bez konieczności ręcznego wykonywania wszystkich zajęć?

Dzięki

f.

+0

Właśnie sobie sprawę, że to się stało tylko w środowisku programistycznym ** **. – fguillen

+1

Widzę, że jeśli aktywuję 'config.cache_classes' deserialization działa dobrze, ale oczywiście: ** Przegrałem automatyczne odświeżanie klasy **:/ – fguillen

Odpowiedz

20

Cóż, po czytać @tadman i kilka odpowiedzi, które otrzymałem na liście mailingowej hiszpanski ROR [1] zebrałem kilka gorących wskazówek, gdy masz do czynienia z Ruby deserializacji i klasa obciążenia w Rails:

Super szybkie rozwiązanie

Zastosowanie config.cache_classes = true w development.rb ale stracił klasę auto odświeżanie.

lepszym rozwiązaniem

wymagać od wszystkich klas, które są gonna rozszeregować ale nie z require ale z require_dependency [2] tak w rozwój środowisku klasy auto-odświeżanie pozostanie pracy.

eleganckie rozwiązanie

Monkey-załatać YAML i Marszałek gem, aby powiedzieć im, aby zadzwonić require_dependency gdy znajdą się niezdefiniowane klasę deserializowania.

I @Xavi wysłał mi propozycję monkey-plaster Marshal (mówi, że napisał go na powietrzu i nie jest testowany więc używać go w własne ryzyko) [3]

+0

Ta odpowiedź jest ratunkiem. Pomyślałem, że będę musiał ponownie przemyśleć moją architekturę ... – fivetwentysix

+0

Dzięki! Dokładnie to, czego potrzebowałem! Jednak jestem nieco zdezorientowany tym, co robi "wymagający" w sugestii # 2. –

+0

FYI, @fguillen wrócił do mnie, a oto kilka rozwiązań: '* w niestandardowym inicjalizatorze typu: '/config/initializers/explicit_requires.rb' * u dołu pliku" /config/application.rb " ' –

0

O ile mi wiadomo, zarówno YAML, jak i Marszałek nie korzystają z autoloadera Rails. Musisz wcześniej załadować wszystkie klasy, które mogą wymagać deserializacji.

To trochę zamieszanie, zwłaszcza w środowisku programistycznym, w którym prawie nic nie jest ładowane, zanim będzie potrzebne.

2

Aby automatycznie wymagać zajęcia z YAML załadunku w sposób @fguillen wskazuje jest elegancki, napisałem ten krótki małpa poprawkę.

Po prostu próbuje wymagać_zależności od klasy, którą klasa Psych ToRuby rozwiązuje na zajęcia.

Działa dla mnie w postaci serializowanej rekordu aktywnego, który przechowuje niestandardową instancję klasy 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

musiałem przystosować @ Ben Patterson odpowiedzieć na to trochę, aby to działało (przy użyciu 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