给出以下节目,在其中我想:
1.使用一些密钥创建一个Struct
1.提供一些默认定制
1.允许传递块以进行进一步定制
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
类,而custom_instance_method
却没有?这种用法在the Struct documentation中有明确的说明,所以我恐怕这里遗漏了一些作用域或上下文问题。
我更喜欢保持良好的def method() ... end
语法,而不是求助于在定制块中使用define_method("method")
的严格要求,这确实有效。
2条答案
按热度按时间6ojccjat1#
在Ruby中有“当前类”的概念,它是
def
等关键字的目标。使用
instance_eval
时,当前类设置为self.singleton_class
。换句话说,def x
和def self.x
是等价的。在您的代码中,custom_instance_method
定义在新创建的Struct
的Singleton类上,使其成为类方法。使用
class_eval
时,当前类设置为self
。由于此方法仅在类上可用,因此它会将当前类设置为您对其调用该方法的类。换句话说,def x
将定义一个可用于该类的所有对象的方法。这就是你想要的。有关更多详细信息,请参见my other answer。
r55awzrz2#
This是我编写的一个gem,我相信它做得最多,如果不是您想要的全部的话(虽然我不太确定是否要添加类方法,但我不得不尝试一下)。虽然创建的
Struct
是不可变的,但您可以查看代码并更改它以满足您的需要;它非常简单。您感兴趣的应该是here,这基本上等于您在下面看到的内容。模块的扩展使用instance_eval
,我相信这也是您想要的:使用详情可在README.md in the github repository中找到。
我希望这能有所帮助,至少在一定程度上如此。