Rails不会在反序列化YAML / Marshal对象时加载类

时间:2011-01-16 14:26:35

标签: ruby-on-rails classloader deserialization

  • Rails:3.0.3
  • Ruby:1.9.2

尝试使用YAML.loadMarshal.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

使用此代码,我们可以运行此shell控制台会话而不会出现任何错误:

$ 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。

5 个答案:

答案 0 :(得分:21)

好了,在阅读了 @tadman 以及我在西班牙语邮件列表[1]中收到的一堆答案之后,我已经收集了一些热门提示,当你必须处理Ruby反序列化和类时在Rails中加载:

超快解决方案

config.cache_classes = true中使用development.rb但您将失去课程自动刷新功能。

更好的解决方案

要求所有要反序列化但不能使用require但使用require_dependency [2]的类,所以在 development 环境中,类自动刷新将继续有效。

优雅的解决方案

YAML Marshal gem进行修补,告诉他们在找到要反序列化的未定义类时调用require_dependency

并且 @Xavi 向我发送了一个猴子补丁Marshal的提议(他说他在空中写下它并没有经过测试,因此请自行承担风险)[ 3]

答案 1 :(得分:2)

我在GitHub上描述了这个“问题”:https://github.com/rails/rails/issues/1585

答案 2 :(得分:2)

要以 @fguillen 建议优雅的方式自动要求YAML加载课程,我写了这个简短的猴子补丁。

它只是尝试require_dependency Psych ToRuby类解析为类的任何类。

在序列化的Active Record中为我工作,它存储自定义类实例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

答案 3 :(得分:1)

我不得不调整@ ben-patterson的答案以使其工作(使用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

答案 4 :(得分:0)

据我所知,YAML和Marshal都没有使用Rails自动加载器。您必须继续并预加载任何可能需要反序列化的类。

有点大惊小怪,特别是在开发环境中,在需要之前几乎什么都没有加载。