有没有更好的方法来编写这个Expando类?它的编写方式不起作用。我使用的是Ruby 1.8.7
引用自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 发布于 2011-01-16 11:40:17
编写它的最简单方法是根本不编写它!:)请参阅标准库中包含的OpenStruct类:
require 'ostruct'
record = OpenStruct.new
record.name = "John Smith"
record.age = 70
record.pension = 300但是,如果我要写它,我会这样做:
# Access properties via methods or Hash notation
class Expando
def initialize
@properties = {}
end
def method_missing( name, *args )
name = name.to_s
if name[-1] == ?=
@properties[name[0..-2]] = args.first
else
@properties[name]
end
end
def []( key )
@properties[key]
end
def []=( key,val )
@properties[key] = val
end
end
person = Expando.new
person.name = "Michael"
person['surname'] = "Erasmus"
puts "#{person['name']} #{person.surname}"
#=> Michael Erasmus发布于 2011-01-16 12:02:33
如果您只是想获得一个可以正常工作的Expando,请改用OpenStruct。但是,如果您这样做是为了教育价值,那么让我们来修复这些bug。
method_missing的参数
当您调用person.name = "Michael"时,这将转换为对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)
endinstance_variable_set
实例变量名称都以@字符开头。这不仅仅是语法上的糖,它实际上是名称的一部分。因此,您需要使用以下行来设置实例变量:
instance_variable_set("@#{name}", arguments[0])(还要注意我们是如何从arguments数组中提取赋值的)
class_eval
self.class将Expando类作为一个整体引用。如果你在它上面定义了一个attr_accessor,那么每个扩展都会有一个该属性的访问器。我不认为这是你想要的。
相反,您需要在class << self块(这是self的单例类或特征类)中执行此操作。这是在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
endhttps://stackoverflow.com/questions/4703642
复制相似问题