在 Ruby 中创建 Expando 对象
有没有更好的方法来编写这个 Expando 类?按照这样的写法是行不通的。 我正在使用 Ruby 1.8.7
起始代码,引用自 https://gist.github.com/300462/3fdf51800768f2c7089a537 26384350c890bc7c3
class Expando
def method_missing(method_id, *arguments)
if match = method_id.id2name.match(/(\w*)(\s*)(=)(\s*)(\.*)/)
puts match[1].to_sym # think this was supposed to be commented
self.class.class_eval{ attr_accessor match[1].to_sym }
instance_variable_set("#{match[1]}", match[5])
else
super.method_missing(method_id, *arguments)
end
end
end
person = Expando.new
person.name = "Michael"
person.surname = "Erasmus"
person.age = 29
Is there a better way to write this Expando class? The way it is written does not work.
I'm using Ruby 1.8.7
starting code quoted from https://gist.github.com/300462/3fdf51800768f2c7089a53726384350c890bc7c3
class Expando
def method_missing(method_id, *arguments)
if match = method_id.id2name.match(/(\w*)(\s*)(=)(\s*)(\.*)/)
puts match[1].to_sym # think this was supposed to be commented
self.class.class_eval{ attr_accessor match[1].to_sym }
instance_variable_set("#{match[1]}", match[5])
else
super.method_missing(method_id, *arguments)
end
end
end
person = Expando.new
person.name = "Michael"
person.surname = "Erasmus"
person.age = 29
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
最简单的写法就是根本不写! :) 请参阅标准库中包含的 OpenStruct 类:
如果我是不过,要写它,我会这样做:
The easiest way to write it is to not write it at all! :) See the OpenStruct class, included in the standard library:
If I was going to write it, though, I'd do it like this:
如果您只是想获得一个可用的
Expando
,请改用OpenStruct
。但如果您这样做是为了教育价值,那么让我们修复这些错误。method_missing
的参数当您调用
person.name = "Michael"
时,这会被转换为对person.method_missing(:name=, "Michael") 的调用
,所以你不需要用正则表达式把参数拉出来。您分配的值是一个单独的参数。因此,instance_variable_set
实例变量名称均以
@
字符开头。它不仅仅是语法糖,它实际上是名称的一部分。因此,您需要使用以下行来设置实例变量:(另请注意我们如何从
arguments
数组中提取我们分配的值)class_eval
self.class
引用整个Expando
类。如果您在其上定义一个attr_accessor
,那么每个 Expando 都会有一个该属性的访问器。我不认为这就是你想要的。相反,您需要在
class << 中执行此操作self
块(这是self
的单例类或特征类)。这在self
的特征类内部进行操作。所以我们会执行
然而,变量
name
实际上在里面是不可访问的,所以我们需要首先挑选出单例类,然后运行class_eval
。一种常见的方法是使用自己的方法 eigenclass 来解决这个问题,因此我们定义调用 self.eigenclass.class_eval { attr_accessor name.to_sym })
并
结合所有这些,最终的解决方案是
If you're just trying to get a working
Expando
for use, useOpenStruct
instead. But if you're doing this for educational value, let's fix the bugs.The arguments to
method_missing
When you call
person.name = "Michael"
this is translated into a call toperson.method_missing(:name=, "Michael")
, so you don't need to pull the parameter out with a regular expression. The value you're assigning is a separate parameter. Hence,instance_variable_set
Instance variable names all start with the
@
character. It's not just syntactic sugar, it's actually part of the name. So you need to use the following line to set the instance variable:(Notice also how we pulled the value we're assigning out of the
arguments
array)class_eval
self.class
refers to theExpando
class as a whole. If you define anattr_accessor
on it, then every expando will have an accessor for that attribute. I don't think that's what you want.Rather, you need to do it inside a
class << self
block (this is the singleton class or eigenclass ofself
). This operates inside the eigenclass forself
.So we would execute
However, the variable
name
isn't actually accessible inside there, so we're going to need to single out the singleton class first, then runclass_eval
. A common way to do this is to out this with its own methodeigenclass
So we defineand then call
self.eigenclass.class_eval { attr_accessor name.to_sym }
instead)The solution
Combine all this, and the final solution works out to