Cho*_*ett 5 ruby metaprogramming
我一直在尝试建立一个系统,我可以生成一系列类似的Ruby类,用整数参数区分,然后将其保存到相关类的类变量中 - 类似于C++模板.
但是,引用(因此,创建)模板化类的新版本会覆盖先前版本中保存的参数,而我无法解决原因.
这是一个最小的例子
class Object
def self.const_missing(name)
if name =~ /^Templ(\d+)$/
return make_templ $1.to_i
else
raise NameError.new("uninitialised constant #{name}")
end
end
private
def make_templ(base)
# Make sure we don't define twice
if Object.const_defined? "Templ#{base}"
return Object.const_get "Templ#{base}"
else
# Define a stub class
Object.class_eval "class Templ#{base}; end"
# Open the class and define the actual things we need.
Object.const_get("Templ#{base}").class_exec(base) do |in_base|
@@base = in_base
def initialize
puts "Inited with base == #{@@base}"
end
end
Object.const_get("Templ#{base}")
end
end
end
irb(main):002:0> Templ1.new
Inited with base == 1
=> #<Templ1:0x26c11c8>
irb(main):003:0> Templ2.new
Inited with base == 2
=> #<Templ2:0x20a8370>
irb(main):004:0> Templ1.new
Inited with base == 2
=> #<Templ1:0x261d908>
Run Code Online (Sandbox Code Playgroud)
我在Ruby中发现了一个错误(ruby 1.9.2p290(2011-07-09)[i386-mingw32]),还是我编写了错误的错误?
因为您首先在类 Object 的上下文中进行语法引用@@base,所以它是 Object 的类变量,并且 object 的所有 TemplX 子类都引用超类的类变量。您可以更改代码以使用Module#class_variable_set并class_variable_get避免超类中的绑定。
您的代码还有一些其他问题:我注意到您没有创建 的make_templ类方法对等体self.const_missing,尽管它已成功分派,因为 Object 是 Class 的祖先。当存在其他方法时,最好避免所有形式的 eval(string)。如果你不处理 const_missing ,你不应该引发 NameError ,而是分派给 super ,因为其他人可能在链中并且想要做一些事情来解析常量。
class Object
def self.const_missing(name)
if name =~ /^Templ(\d+)$/
return make_templ $1.to_i
end
super
end
private
def self.make_templ(base)
klass_name = "Templ#{base}"
unless const_defined? klass_name
klass = Class.new(Object) do
class_variable_set :@@base, base
def initialize
puts "Inited with base == #{self.class.class_variable_get(:@@base)}"
end
end
const_set klass_name, klass
end
const_get klass_name
end
end
Run Code Online (Sandbox Code Playgroud)
类变量通过继承具有有趣且通常不受欢迎的信息混合属性。你遇到了其中一个陷阱。我不知道您还需要什么其他属性@@base,但看起来您可能会使用类实例变量获得更好的隔离和更少的令人惊讶的结果。更多解释:Fowler,RailsTips