如果您只是想获得使用的工作Expando
,请OpenStruct
改用。但是如果你这样做是为了教育价值,让我们修复错误。
论据method_missing
当您调用person.name = "Michael"
this 时,它会转换为对 的调用person.method_missing(:name=, "Michael")
,因此您无需使用正则表达式将参数拉出。您分配的值是一个单独的参数。因此,
if method_id.to_s[-1,1] == "=" #the last character, as a string
name=method_id.to_s[0...-1] #everything except the last character
#as a string
#We'll come back to that class_eval line in a minute
#We'll come back to the instance_variable_set line in a minute as well.
else
super.method_missing(method_id, *arguments)
end
instance_variable_set
实例变量名称都以@
字符开头。它不仅仅是语法糖,它实际上是名称的一部分。因此,您需要使用以下行来设置实例变量:
instance_variable_set("@#{name}", arguments[0])
(还请注意我们如何将分配的值从arguments
数组中提取出来)
class_eval
self.class
指Expando
整个班级。如果您attr_accessor
在其上定义 an,那么每个expando 都将具有该属性的访问器。我不认为那是你想要的。
相反,您需要在一个class << self
块内进行(这是单例类或 eigenclass self
)。这在 的 eigenclass 内部运行self
。
所以我们会执行
class << self; attr_accessor name.to_sym ; end
但是,该变量name
实际上无法在其中访问,因此我们需要先挑出单例类,然后运行class_eval
. 一种常见的方法是用它自己的方法来解决这个问题eigenclass
所以我们定义
def eigenclass
class << self; self; end
end
然后打电话self.eigenclass.class_eval { attr_accessor name.to_sym }
代替)
解决方案
结合所有这些,最终的解决方案是
class Expando
def eigenclass
class << self; self; end
end
def method_missing(method_id, *arguments)
if method_id.to_s[-1,1] == "="
name=method_id.to_s[0...-1]
eigenclass.class_eval{ attr_accessor name.to_sym }
instance_variable_set("@#{name}", arguments[0])
else
super.method_missing(method_id, *arguments)
end
end
end