2012-07-30 135 views
1

我正在使用ruby封送器在两个客户端之间发送数据。每个客户端都有一组类定义,它们将用来帮助加载编组数据。该定义存储在外部ruby文件,他们可以加载任何时候他们想要的(但通常,当他们启动)封送和未定义的属性/类

一个简单的例子是

  • 客户端A元帅转储数据并将其发送到客户端B
  • 客户端B编组加载的数据,然后将其写入到一个文件

然而,有时一个客户端发送包含未在其他客户端的定义所定义的对象的数据,在这种情况另一个客户应该是你相应地更新其定义。

它可能是一个新的实例变量,应该添加到类xyz的定义中,或者它可能完全是一个新类。

Marshal#Load当前刚刚抛出一个异常,当它运行到一个未定义的变量(例如:undefined类/方法abc)。

有没有办法让我采取这种例外并相应地更新定义,以便客户端可以愉快地读取数据并写出来?

所有类都将包含Marshal已经知道如何编码/解码的数据,例如字符串,数组,哈希,数字等。不会有任何数据需要自定义方法dump/load方法。

回答

1

我的解决方案是自动创建类(和恒定的层次结构,即Foo::Bar::Baz),并使类autorespond属性访问尝试。

class AutoObject 
    def method_missing(*args,&b) 
    if args.size == 1 
     name = args[0] 
     if instance_variable_defined? "@#{name}" 
     self.class.send :attr_accessor, name 
     send(*args) 
     else 
     super 
     end 
    elsif args.size == 2 && args[0].to_s[/=$/] 
     name = args[0].to_s[0...-1] 
     if instance_variable_defined? "@#{name}" 
     self.class.send :attr_accessor, name 
     send(*args) 
     else 
     super 
     end 
    end 
    end 
end 

def Marshal.auto_load(data) 
    Marshal.load(data) 
rescue ArgumentError => e 
    classname = e.message[%r(^undefined class/module (.+)$), 1] 
    raise e unless classname 

    classname.split("::").inject(Object) do |outer, inner| 
    if !outer.const_defined? inner 
     outer.const_set inner, Class.new(AutoObject) 
    else 
     outer.const_get inner 
    end 
    end 
    retry 
end 

这可以很容易地扩展到记录所有创建的类,甚至可以确定它们可能具有的实例变量。这可以帮助你更新文件,也许编程。