【问题标题】:How to programmatically remove "singleton information" on an instance to make it marshal?如何以编程方式删除实例上的“单例信息”以使其编组?
【发布时间】:2014-05-19 09:47:19
【问题描述】:

我创建了一个由于“在运行时执行的单元元类定义”而无法编组的对象(对代码所做内容的描述是否正确?)。

这是通过以下代码执行的:

# define class X that my use singleton class metaprogramming features
# through call of method :break_marshalling!
class X
   def break_marshalling!
     meta_class = class << self
       self 
     end
     meta_class.send(:define_method, :method_y) do 
      return 
    end
  end
end

# prepare my instance of X now
instance_of_x = X.new

# marshalling fine here
Marshal.dump instance_of_x

# break marshalling with metprogramming features
instance_of_x.break_marshalling!

Marshal.dump instance_of_x
# fails with TypeError: singleton can't be dumped 

我该怎么做才能使对象编组正确?是否可以从对象instance_of_xclass X 中“删除”单例组件?

我真的需要一个建议,因为我们的一些对象需要通过 Marshal.dump 序列化机制进行缓存。此代码在 ruby​​-1.9.3 中执行,但我希望它在 ruby​​-2.0 或 ruby​​-2.1 中表现相似

【问题讨论】:

  • 问得好,但你引发了我最大的烦恼之一!它被称为singleton_class。而且我们也不需要使用define_singleton_method 发送黑客攻击。
  • 那么@Max 您是否遇到过任何以编程方式“重置”任何 ruby​​ 类实例的 singleton_class 的方式?可能是remove_singleton_class_information! 之类的东西? ;)

标签: ruby singleton metaprogramming marshalling


【解决方案1】:

(通常)从instance_of_x 中删除单例信息的单线方法:

instance_of_x = instance_of_x.dup

【讨论】:

    【解决方案2】:

    你可以define custom marshal_dump and marshal_load methods:

    class X
      def break_marshalling!
        meta_class = class << self
          self 
        end
        meta_class.send(:define_method, :method_y) do 
          return 
        end
      end
    
      # This should return an array of instance variables
      # needed to correctly restore any X instance. Assuming none is needed
      def marshal_dump
        []
      end
    
      # This should define instance variables
      # needed to correctly restore any X instance. Assuming none is needed
      def marshal_load(_)
        []
      end
    end
    
    # Works fine
    restored_instance_of_x = 
      Marshal.load Marshal.dump(X.new.tap { |x| x.break_marshalling! })
    
    # Does not work
    restored_instance_of_x.method_y
    

    如果您愿意,您可以通过method_missing 管理动态方法定义:

    class X
      def method_missing(name, *args)
        if name == :method_y
          break_marshalling!
          public_send name, *args
        else
          super
        end
      end
    end
    
    # Works fine
    Marshal.load(Marshal.dump(X.new)).method_y
    

    【讨论】:

    • 你的意思是meta_class = class &lt;&lt; self ...?我猜(并希望)提问者有充分的理由使用它=)
    • 在我在问题中看到它之前,不知何故我在你的回答中注意到了它。哎呀。
    • 首先感谢您的回答。提问者实际上并没有使用这种在他的代码中添加单例方法的方法,而是尝试编组由他的项目中使用的库创建的对象^^。顺便提一句。他也非常有兴趣了解 singleton_class 在 ruby​​ 中的行为。
    • Criss 你为什么用第三人称指代提问者???你不是提问者???我很困惑!!! :D
    • 其实我是。所以你真的可以停止困惑。
    【解决方案3】:

    我认为remove_singleton_class_information! 不存在这样的东西,但是您可以尝试以编程方式取消定义单例类中的东西。

    根据the documentation Marshal 不喜欢带有单例方法的对象。通过一些实验,它似乎也不喜欢具有实例变量的单例类的对象。

    class Foo
      def break_marshal
        singleton_class.class_eval do
          @@x = true
          @x = false
          define_method :bar do
            puts @@x
            puts singleton_class.instance_variable_get(:@x)
            puts singleton_class.send(:baz)
            puts singleton_class.const_get(:X)
          end
          define_singleton_method :baz do
            "Singleton method in a singleton"
          end
        end
      end
    
      def fix_marshal
        singleton_methods.each { |m| singleton_class.send(:remove_method, m) }
        singleton_class.class_eval do
          instance_variables.each { |v| remove_instance_variable v }
        end
      end
    end
    
    f = Foo.new
    f.break_marshal
    f.singleton_class.const_set(:X, 1)
    f.bar
    f.fix_marshal
    
    Marshal.dump f
    

    请注意我是多么努力地试图打破 Marshal。我给了单例一个类变量、一个实例变量、一个方法、它自己的单例方法和一个常量。但是,如果您只删除单例方法和实例变量,Marshal 就可以正常工作。

    但是,我认为这不是编组此类对象的正确方法。 @mdesantis 定义自己的marshal_dumpmarshal_load 的答案更合适。

    【讨论】:

      【解决方案4】:

      我想出了这个方法,它递归地从一个对象中删除singleton_methods,并从分配给实例变量的对象中删除:

      def self.strip_singleton(obj)
        obj = obj.dup unless (obj.nil? || obj.singleton_methods.empty?)
        obj.instance_variables.each do |var|
          obj.instance_variable_set(var, strip_singleton(obj.instance_variable_get(var)))
        end
        obj
      end
      

      【讨论】:

        猜你喜欢
        • 2014-05-07
        • 1970-01-01
        • 1970-01-01
        • 2021-08-13
        • 1970-01-01
        • 2013-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多