我想在特定对象的元类上使用实例方法中的值v
:
v = ParserMap[kind][:validation] # We want to use this value later.
s = ParserMap[kind][:specs]
const_set(name, lambda {
p = Parser.new(&s)
# This line starts a new scope...
class << p
define_method :validate do |opts|
v.call(self, opts) # => NameError! The `class` keyword above
# has started a new scope and we lost
# old `v`.
end
end
p
})
不幸的是,class
关键字启动了一个新的范围,所以我丢失了旧的范围,我得到了一个NameError。我该如何解决这个问题?
答案 0 :(得分:1)
将class << p
替换为class << p; self end.class_eval do
,它将有效。
class << p; self end
将返回p的元类,因此您可以在其上调用class_eval
。然后,给class_eval
的块将在元类的上下文中执行(与之前相同),但不启动新的作用域。
答案 1 :(得分:1)
您的第一个倾向可能是在class_eval
使用p
,如下所示:
p.class_eval {
...
}
唉,这不起作用,因为class_eval
是Module
上定义的方法,而不是Object
上定义的方法。由于p
是对象的实例,而不是Module
或Class
的实例,因此它没有class_eval
方法。
诀窍是首先获得p
的单例类,然后在其上运行class_eval
。由于 是Class
(扩展名为Module
),因此它具有class_eval
方法。如果您使用的是1.9.2或更高版本,则可以使用singleton_class
方法:
p.singleton_class.class_eval {
...
}
否则,你可以直接获得单例类:
(class << p; self; end).class_eval {
...
}
正如Jorg所指出的,你也可以使用define_singleton_method
:
p.define_singleton_method :validate { |opts|
v.call(self, opts)
}
但请注意,如果您这样做,生成的validate
方法将为private
,这可能不是您想要的。
答案 2 :(得分:0)
只是为了踢,这是Ruby 1.9.2中的样子:
v = ParserMap[kind][:validation]
s = ParserMap[kind][:specs]
const_set(name, ->{
Parser.new(&s).tap {|p|
p.define_singleton_method :validate do |opts| v.(self, opts) end
}
})
p
)替换最后Object#tap
的显式返回lambda
方法调用替换为Ruby 1.9.0中引入的proc文字obj.call(args)
替换为Ruby 1.9.0中引入的obj.(args)
Object#define_singleton_method