Rails doesn't load classes on deserializing YAML/Marshal objects

旧街凉风 提交于 2019-11-29 01:51:08

Well, after read @tadman and a bunch of answers I have received in the spanish ror mailing list [1] I have collected a few hot tips when you have to deal with Ruby deserializing and class loading in Rails:

Super fast solution

Use config.cache_classes = true in your development.rb but you will lost the class auto-refreshing.

Better solution

Require all the classes that are gonna be deserialized but not with require but with require_dependency[2] so in development environment the class auto-refreshing will remain working.

Elegant solution

Monkey-patch the YAML and the Marshal gem to tell them to call require_dependency when they find a non-defined class to deserialize.

And @Xavi has sent me a proposition of monkey-patch Marshal (he says he wrote it on the air and it is not tested so use it in your own risk) [3]

I described this "issue" on GitHub: https://github.com/rails/rails/issues/1585

To automatically require classes on YAML loading in the manner @fguillen suggests is elegant, I wrote this short monkey-patch.

It simply attempts to require_dependency any class the Psych ToRuby class resolves to classes.

Works for me in a serialised Active Record that stores a custom class instance, 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

I had to adapt @ben-patterson's answer a bit to make it work (using 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

As far as I know, both YAML and Marshal do not make use of the Rails autoloader. You must go ahead and pre-load any classes that might need to be deserialized.

It's a bit if a fuss, especially in the development environment where almost nothing is loaded before it is needed.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!