有以下代码:
class MyOpenStruct
def initialize(initial_values = {})
@values = initial_values
end
def _singleton_class
class << self
self
end
end
def method_missing(name, *args, &block)
if name[-1] == "="
base_name = name[0..-2].intern
puts "add_method_to_set"
self.class.add_method_to_set(base_name)
@values[base_name] = args[0]
else
puts "add_method_to_get"
self.class.add_method_to_get(base_name)
@values[name]
end
end
def self.add_method_to_get(name)
define_method(name) do |value|
@values[name]
end
end
def self.add_method_to_set(name)
define_method(name) do |value|
@values[name] = value
end
end
end
obj1 = MyOpenStruct.new(name: "Dave")
obj1.address = "1"
obj2 = MyOpenStruct.new(name: "Dave")
obj2.address = "2"
我想做以下事情:当我执行某个方法(obj1.address)并且它丢失时我想将此方法添加到MyOpenStruct类中。但是当我执行我的代码时,我会“错过”两次而不是一次。为什么?我不明白。请向我解释一下。谢谢。
答案 0 :(得分:0)
@koffeinfrei发现你的代码存在一个问题,但我发现了其他一些问题。下面我有我认为是更正版本。我还提出了一种构造代码的替代方法。我的主要建议是提取实例方法的动态创建,因为它非常通用。您甚至可以将其放在一个模块中,其中包含您可以根据需要包含的其他方法。
您的维修代码
class MyOpenStruct
def initialize(initial_values = {})
@values = initial_values
end
def method_missing(name, *args, &block)
puts "in mm, name = #{name}"
if name[-1] == "="
base_name = name[/\w+/]
puts "add_method_to_set: '#{name}'"
self.class.add_method_to_set(base_name)
@values[base_name.to_sym] = args[0]
else
puts "add_method_to_get: '#{name}'"
self.class.add_method_to_get(name)
@values[name.to_sym]
end
end
def self.add_method_to_get(name)
define_method(name.to_sym) do
@values[name.to_sym]
end
end
def self.add_method_to_set(name)
define_method((name+'=').to_sym) do |value|
@values[name.to_sym] = value
end
end
end
替代建筑
def create_instance_eval(klass, method, &block)
klass.class_eval { define_method(method, &block) }
end
class MyOpenStruct
def initialize(initial_values = {})
@values = initial_values
end
def method_missing(name, *args, &block)
if name[-1] == "="
base_name = name[/\w+/]
method_name = (base_name+'=').to_sym
puts "create method '#{method_name}'"
method = create_instance_eval(self.class, method_name) do |value|
@values[base_name.to_sym] = value
end
send(method, args[0])
else
method_name = name.to_sym
puts "create method '#{method_name}'"
method = create_instance_eval(self.class, method_name) do
@values[method_name]
end
send(method)
end
end
end
示例强>
MyOpenStruct.instance_methods(false)
#=> [:method_missing]
obj1 = MyOpenStruct.new(name: "Dave")
#=> #<MyOpenStruct:0x00000102805b58 @values={:name=>"Dave"}>
obj1.address = "1"
# create method 'address='
#=> "1"
MyOpenStruct.instance_methods(false)
#=> [:method_missing, :address=]
obj2 = MyOpenStruct.new(name: "Mitzy")
#=> #<MyOpenStruct:0x00000101848878 @values={:name=>"Mitzy"}>
obj2.address = 2
#=> 2
obj2.address
# create method 'address'
# => 2
MyOpenStruct.instance_methods(false)
$#=> [:method_missing, :address=, :address]
obj1.instance_variable_get(:@values)
#=> {:name=>"Dave", :address=>"1"}
obj2.instance_variable_get(:@values)
#=> {:name=>"Mitzy", :address=>2}
答案 1 :(得分:0)
setter方法的方法名称需要包含尾随 = ,因此您需要使用name
而不是base_name
来定义方法。
self.class.add_method_to_set(name)