define_method 在 class 方法中
define_method in a class method
在下面的代码中,扩展了模块,这意味着方法 hash_initialized 被视为 class 方法,或者本征 class 的实例方法。这是我们需要的,因为 hash_initialized 是在本征 class 的上下文中调用的。
我不明白的是,如果模块的上下文是本征 class,那么 define_method 应该创建本征 [=22] 的名为 "initialize" 的实例方法=],或者换句话说,class 奶酪的 class 方法。我们这里不需要实例方法"initialize"吗?
module HashInitialized
def hash_initialized(*fields)
define_method(:initialize) do |h|
missing = fields - h.keys
raise Exception, "Not all fields set: #{missing}" if missing.any?
h.each do |k,v|
instance_variable_set("@#{k}", v) if fields.include?(k)
end
end
end
end
class Cheese
extend HashInitialized
attr_accessor :color, :odor, :taste
hash_initialized :color, :odor, :taste
end
从技术上讲,调用 extend
会将模块放入调用它的本征对象的查找链中,在本例中与 class 对象相同。所以你是正确的 hash_initialized
的上下文是 class。此外,define_method
的上下文是 class 是正确的。但是,您的最后一步不正确。当在该上下文中调用 define_method
时,它定义了一个实例方法,而不是单例方法。
IOW,当您在上下文中调用 define_method
时,它在 def
将在该上下文中定义它的同一位置定义方法。
我已经通过简化上面的示例并添加了一些打印输出来明确这一点。
class Test
def self.define_something
define_method(:inside_class_method){puts "method defined inside a class method"}
puts "self inside class method "+self.to_s
proc = Proc.new{puts "method defined using send inside class method"}
self.send(:define_method, :define_using_send_inside_class_method, proc)
end
class << self
puts "self inside eigen class "+self.to_s
end
def test
puts "self inside of instance method "+self.to_s
end
puts "self outside of class method "+self.to_s
define_method(:outside_class_method){puts "method defined outside a class method"}
define_something
end
Test.new().inside_class_method
Test.new().outside_class_method
Test.new().test
Test.define_using_send_inside_class_method
此代码产生以下输出:
self inside eigen class #
自身在 class 方法测试之外
self inside class 方法测试
在class方法中定义的方法
在 class 方法外定义的方法
实例方法中的self #
test.rb:26:in <main>': undefined method
define_using_send_inside_class_method' for Test:Class (NoMethodError)
此代码:
self.send(:define_method, :define_using_send_inside_class_method, proc)
它还定义了一个实例方法,因为它是在 self 上调用的,而 self 指的是 class 测试。
如果我们需要定义一个 class 方法,需要像这样在本征 class 上调用发送:
class << self
self.send(:define_method, :define_using_send_inside_class_method, proc)
end
当你遇到像这个这样的看似难题时,尝试用 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
是 class 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!">
在下面的代码中,扩展了模块,这意味着方法 hash_initialized 被视为 class 方法,或者本征 class 的实例方法。这是我们需要的,因为 hash_initialized 是在本征 class 的上下文中调用的。
我不明白的是,如果模块的上下文是本征 class,那么 define_method 应该创建本征 [=22] 的名为 "initialize" 的实例方法=],或者换句话说,class 奶酪的 class 方法。我们这里不需要实例方法"initialize"吗?
module HashInitialized
def hash_initialized(*fields)
define_method(:initialize) do |h|
missing = fields - h.keys
raise Exception, "Not all fields set: #{missing}" if missing.any?
h.each do |k,v|
instance_variable_set("@#{k}", v) if fields.include?(k)
end
end
end
end
class Cheese
extend HashInitialized
attr_accessor :color, :odor, :taste
hash_initialized :color, :odor, :taste
end
从技术上讲,调用 extend
会将模块放入调用它的本征对象的查找链中,在本例中与 class 对象相同。所以你是正确的 hash_initialized
的上下文是 class。此外,define_method
的上下文是 class 是正确的。但是,您的最后一步不正确。当在该上下文中调用 define_method
时,它定义了一个实例方法,而不是单例方法。
IOW,当您在上下文中调用 define_method
时,它在 def
将在该上下文中定义它的同一位置定义方法。
我已经通过简化上面的示例并添加了一些打印输出来明确这一点。
class Test
def self.define_something
define_method(:inside_class_method){puts "method defined inside a class method"}
puts "self inside class method "+self.to_s
proc = Proc.new{puts "method defined using send inside class method"}
self.send(:define_method, :define_using_send_inside_class_method, proc)
end
class << self
puts "self inside eigen class "+self.to_s
end
def test
puts "self inside of instance method "+self.to_s
end
puts "self outside of class method "+self.to_s
define_method(:outside_class_method){puts "method defined outside a class method"}
define_something
end
Test.new().inside_class_method
Test.new().outside_class_method
Test.new().test
Test.define_using_send_inside_class_method
此代码产生以下输出:
self inside eigen class #
自身在 class 方法测试之外
self inside class 方法测试
在class方法中定义的方法
在 class 方法外定义的方法
实例方法中的self #
test.rb:26:in <main>': undefined method
define_using_send_inside_class_method' for Test:Class (NoMethodError)
此代码:
self.send(:define_method, :define_using_send_inside_class_method, proc)
它还定义了一个实例方法,因为它是在 self 上调用的,而 self 指的是 class 测试。
如果我们需要定义一个 class 方法,需要像这样在本征 class 上调用发送:
class << self
self.send(:define_method, :define_using_send_inside_class_method, proc)
end
当你遇到像这个这样的看似难题时,尝试用 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
是 class 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!">