有没有更好的方法来编写此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
答案 0 :(得分:9)
编写它的最简单方法是根本不写它! :)请参阅标准库中包含的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
答案 1 :(得分:2)
如果您只想尝试使用Expando
,请改用OpenStruct
。但是,如果你这样做是为了教育价值,让我们修复错误。
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)
end
instance_variable_set
实例变量名称都以@
字符开头。这不仅仅是语法糖,它实际上是名称的一部分。因此,您需要使用以下行来设置实例变量:
instance_variable_set("@#{name}", arguments[0])
(另请注意我们如何提取我们从arguments
数组中分配的值)
class_eval
self.class
指整个Expando
类。如果您在其上定义attr_accessor
,那么每个 expando都将拥有该属性的访问者。我认为这不是你想要的。
相反,您需要在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
end