当您遇到类似这样的难题时,请尝试使用 puts self 语句对您的代码进行加盐:
module HashInitialized
puts "self when parsed=#{self}"
def hash_initialized(*fields)
puts "self within hash_initialized=#{self}"
define_method(:initialize) do |h|
missing = fields - h.keys
raise ArgumentError, "Not all fields set: #{missing}" if missing.any?
fields.each { |k| instance_variable_set("@#{k}", h[k]) }
end
private :initialize
end
end
#-> self when parsed=HashInitialized
class Cheese
extend HashInitialized
attr_accessor :color, :odor, :taste
hash_initialized :color, :odor, :taste
end
#-> self within hash_initialized=Cheese
如您所见,self 是类 Cheese,而不是 Cheese 的 singleton_class。因此,Module#define_method 的接收者是Cheese,因此该方法在Cheese 上创建实例方法initialize。
Cheese.instance_methods(false)
#=> [:color, :color=, :odor, :odor=, :taste, :taste=]
initialize 不在Cheese 创建的实例方法中,因为我稍微修改了代码使其成为私有方法:
Cheese.private_instance_methods(false)
#=> [:initialize]
我还稍微修改了为实例变量赋值的代码,并使异常类型更加具体。
如果合适,您可以将参数测试更改为:
raise ArgumentError, "Fields #{fields} and keys #{h.keys} don't match" if
(fields-h.keys).any? || (h.keys-fields).any?
您可能希望initialize 创建评估者:
module HashInitialized
def hash_initialized(*fields)
define_method(:initialize) do |h|
missing = fields - h.keys
raise ArgumentError, "Not all fields set: #{missing}" if missing.any?
fields.each do |k|
instance_variable_set("@#{k}", h[k])
self.class.singleton_class.send(:attr_accessor, k)
end
end
private :initialize
end
end
class Cheese
extend HashInitialized
hash_initialized :color, :odor, :taste
end
Cheese.new :color=>'blue', odor: 'whew!', taste: "wow!"
=> #<Cheese:0x007f97fa07d2a0 @color="blue", @odor="whew!", @taste="wow!">