11
  • レール:3.0.3
  • Ruby:1.9.2

YAML.loadに属するクラスが逆シリアル化プロセスに必要ないため、またはMarshal.load破損したオブジェクトを使用して非常に単純なオブジェクトを逆シリアル化しようとしています。

例:

# 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

このコードを使用すると、このシェルコンソールセッションをエラーなしで実行できます。

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

しかし、Railsコンソールから逆シリアル化呼び出しを実行すると、クラスが必要になることがないため、オブジェクトは適切に逆シリアル化されません。

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

唯一の問題は、クラスが必要ないことです。手動で必要な場合は、すべてが機能するためです。

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

YAMLの例のみを示しましたが、Marshalの場合もほとんど同じです。

また、私はRailsコンソールで問題を再現していますが、この問題は、アプリケーションへの通常の要求で私を夢中にさせていたとも言います。

したがって、問題は次のとおりです。すべてのクラスを手動で要求せずに、Railsでオブジェクトを逆シリアル化するにはどうすればよいですか?

ありがとう

f。

4

5 に答える 5

20

さて、@ tadmanとスペイン語のrorメーリングリストで受け取ったたくさんの回答を読んだ後[1]、RailsでRubyの逆シリアル化とクラスの読み込みに対処する必要があるときにいくつかのホットなヒントを集めました。

超高速ソリューション

で使用config.cache_classes = trueしますdevelopment.rbが、クラスの自動更新は失われます。

より良い解決策

デシリアライズされるが[2]ではrequireなく、require_dependency[2]を使用するすべてのクラスが必要であるため、開発環境では、クラスの自動更新は引き続き機能します。

エレガントなソリューション

YAMLMarshalgemにモンキーパッチを適用しrequire_dependencyて、逆シリアル化する未定義のクラスが見つかったときに呼び出すように指示します。

そして、@ Xaviは私にモンキーパッチの提案を送ってくれましたMarshal(彼はそれを放送中に書いたと言っています、そしてそれはテストされていないのであなた自身の責任でそれを使ってください)[3]

于 2011-01-23T15:32:42.967 に答える
2

この「問題」については、GitHubで説明しました:https ://github.com/rails/rails/issues/1585

于 2011-06-08T22:01:00.380 に答える
2

@fguillenがエレガントであると示唆する方法でYAMLロードのクラスを自動的に要求するために、私はこの短いモンキーパッチを作成しました。

PsychToRubyクラスがクラスに解決する任意のクラスをrequire_dependencyしようとするだけです。

カスタムクラスインスタンス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
于 2013-10-24T06:36:37.817 に答える
1

私はそれを機能させるために@ben-pattersonの答えを少し適応させる必要がありました(Rails5.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
于 2017-03-22T22:39:10.317 に答える
0

私の知る限り、YAMLとMarshalはどちらもRailsオートローダーを使用していません。先に進んで、逆シリアル化が必要になる可能性のあるクラスをプリロードする必要があります。

特に、必要になる前にほとんど何もロードされない開発環境では、大騒ぎするのは少しです。

于 2011-01-16T18:28:30.467 に答える