使用预定义定义和自定义块自定义 Ruby 结构
Customizing a Ruby Struct with pre-defined definitions and a custom block
给出以下程序,我想在其中:
- 用一些键创建一个
Struct
- 提供一些默认的自定义设置
- 允许传递块以进行进一步自定义
module Magic
def self.MagicStruct(keys_array, &block)
Struct.new(*keys_array) do
@keys = keys_array
def self.magic_class_method
puts "constructed with #{@keys}"
end
def magic_instance_method
puts "instance method"
end
# --- DOESN'T WORK, CONTEXT IS OUTSIDE OF MODULE --- #
# yield if block_given?
instance_eval(&block) if block_given?
end
end
end
Foo = Magic.MagicStruct([:a, :b, :c]) do
puts "class customizations executing..."
def self.custom_class_method
puts "custom class method"
end
def custom_instance_method
puts "custom instance method"
end
end
Foo.magic_class_method # works
Foo.custom_class_method # works
x = Foo.new({a: 10, b: 20, c: 30})
x.magic_instance_method # works
x.custom_instance_method # fails
输出:
class customizations executing...
constructed with [:a, :b, :c]
custom class method
instance method
Traceback (most recent call last):
`<main>': undefined method `custom_instance_method' for #<struct Foo a={:a=>10, :b=>20, :c=>30}, b=nil, c=nil> (NoMethodError)
为什么 self.custom_class_method
正确地添加到 Foo
class,但 custom_instance_method
不是?这种用法在 the Struct documentation 中有明确说明,所以恐怕我在这里遗漏了某种范围界定或上下文问题。
我更愿意保留漂亮的 def method() ... end
语法,而不是诉诸于在自定义块中使用 define_method("method")
的严格要求,这确实有效。
在 Ruby 中有一个 current class 的概念,它是关键字的目标,例如 def
.
当您使用instance_eval
时,当前class设置为self.singleton_class
。换句话说,def x
和 def self.x
是等价的。在您的代码中,custom_instance_method
是在新创建的 Struct
的单例 class 上定义的,使其成为 class 方法。
当您使用class_eval
时,当前class设置为self
。由于此方法仅在 classes 上可用,它会将当前 class 设置为您调用该方法的那个。换句话说,def x
将定义一个可用于 class 的所有对象的方法。这就是你想要的。
有关详细信息,请参阅 my other answer。
给出以下程序,我想在其中:
- 用一些键创建一个
Struct
- 提供一些默认的自定义设置
- 允许传递块以进行进一步自定义
module Magic
def self.MagicStruct(keys_array, &block)
Struct.new(*keys_array) do
@keys = keys_array
def self.magic_class_method
puts "constructed with #{@keys}"
end
def magic_instance_method
puts "instance method"
end
# --- DOESN'T WORK, CONTEXT IS OUTSIDE OF MODULE --- #
# yield if block_given?
instance_eval(&block) if block_given?
end
end
end
Foo = Magic.MagicStruct([:a, :b, :c]) do
puts "class customizations executing..."
def self.custom_class_method
puts "custom class method"
end
def custom_instance_method
puts "custom instance method"
end
end
Foo.magic_class_method # works
Foo.custom_class_method # works
x = Foo.new({a: 10, b: 20, c: 30})
x.magic_instance_method # works
x.custom_instance_method # fails
输出:
class customizations executing...
constructed with [:a, :b, :c]
custom class method
instance method
Traceback (most recent call last):
`<main>': undefined method `custom_instance_method' for #<struct Foo a={:a=>10, :b=>20, :c=>30}, b=nil, c=nil> (NoMethodError)
为什么 self.custom_class_method
正确地添加到 Foo
class,但 custom_instance_method
不是?这种用法在 the Struct documentation 中有明确说明,所以恐怕我在这里遗漏了某种范围界定或上下文问题。
我更愿意保留漂亮的 def method() ... end
语法,而不是诉诸于在自定义块中使用 define_method("method")
的严格要求,这确实有效。
在 Ruby 中有一个 current class 的概念,它是关键字的目标,例如 def
.
当您使用instance_eval
时,当前class设置为self.singleton_class
。换句话说,def x
和 def self.x
是等价的。在您的代码中,custom_instance_method
是在新创建的 Struct
的单例 class 上定义的,使其成为 class 方法。
当您使用class_eval
时,当前class设置为self
。由于此方法仅在 classes 上可用,它会将当前 class 设置为您调用该方法的那个。换句话说,def x
将定义一个可用于 class 的所有对象的方法。这就是你想要的。
有关详细信息,请参阅 my other answer。