最近在研究Watir的自动化测试,有了成果写文章上来。先转一个帖子:
转自:http://www.coolcode.cn/show-303-1.html
Ruby 中如何动态生成类
废话不多说,先看例子:
Ruby代码
- Object.module_eval("class Test1\nend")
- x = Object.const_get("Test1".to_sym)
- x.method(:attr_accessor).call("test1".to_sym)
- x.method(:public).call("test1".to_sym)
- x.method(:public).call("test1=".to_sym)
- x.method(:attr_accessor).call("test2".to_sym)
- x.method(:public).call("test2".to_sym)
- x.method(:public).call("test2=".to_sym)
- x.method(:attr_accessor).call("test3".to_sym)
- x.method(:public).call("test3".to_sym)
- x.method(:public).call("test3=".to_sym)
- y = Test1.new # also can use x.new
- y.test1 = 'Hello'
- y.test2 = 'Ruby'
- y.test3 = 1.86
- puts y.class
- puts y.test1, y.test2, y.test3
原理就是利用 module_eval 定义一个空类,然后利用 const_get 获取到该类的引用,最后利用反射机制调用 attr_accessor、public 等方法完成类的定义。
这样做有什么好处呢?好处是,只要我们知道类名及其属性名,就可以动态的创建一个类,这在反序列化未知类的对象时是非常有用的。
但是上面的方法看上去还是比较麻烦,下面定义一个通用的:
Ruby代码
- class Object
- def self.attr_add(*symbols)
- symbols.each { |sym|
- self.send(:attr_accessor, sym)
- self.send(:public, sym, sym.to_s.concat('=').to_sym)
- }
- self
- end
- def self.define_class(name, *attrs)
- (name.split('::').inject(self) { |x, y| x.const_set(y, Class.new) }).attr_add(*attrs)
- end
- def self.class_get(name)
- name.split("::").inject(self) {|x,y| x.const_get(y) }
- end
- end
现在用 define_class 定义类,用 class_get 获取类就方便多了。