我想用一些方法编写小类,这些方法实际上属于其他类,所以如何在其他类中定义现有副本的方法。我相信这是我不理解的元编程魔术师。
class Foo
def initialize
# with blocks, I would just pass block, but this is methods
# so this won't work
Bar.class_eval(perform)
Bar.class_eval(process)
Bar.class_eval(save)
end
def perform
1+1
end
def process
# some code
end
def save
# some code
end
end
class Bar; end
foo = Foo.new
foo.perform
#=> 2
Bar.test
#=> 1
为什么我需要这个?我正在研究gem,它只需要三个方法。在初始化时(可能隐藏在父类中),它会将此方法传递给不同的类。我可以使用块来制作它,但使用方法它可以更清洁。
PS :就像将方法从一个类复制到另一个类
PSS :或者......如何将方法转换为proc,因此我可以将其传递给class_eval
答案 0 :(得分:13)
要将方法转换为可以像Proc一样调用的方法,请使用obj.method(:method_name)
。这将为您提供一个绑定方法对象,该对象在call
时将在obj
上调用。如果要在同一个类的其他对象上调用它,可以调用method.unbind.bind(different_obj)
。
仍然不允许你复制"从一个类到另一个类的方法。如果您希望允许用户传递一个定义3个方法的类,而不是传递3个块,那么如果在内部存储对该类(或其实例)的引用,并根据需要调用其上的方法,它可能会更好。这就是评论"代表团"的意思。
或者,您可以让用户传递一个模块,并创建自己的类include
或extend
模块(根据需要)。
答案 1 :(得分:0)
可以将方法从一个类复制到另一个类,但是有一个主要警告:目标类必须是kind_of?源类,或者源必须是模块。 UnboundMethod#bind的文档中部分记录了此限制,但是要查看模块异常,您必须查看方法的源代码。 This answer包含有关该主题的更多讨论。
这里是一个例子:
module A
def say_hello
puts "Hello"
end
end
class B
define_method(:say_hello, A.instance_method(:say_hello))
end
b = B.new
b.say_hello
=> Hello
如果A
是一个类,而B
是从A
继承的类,这也将起作用。但是在那种情况下,您已经通过继承获得了该方法,因此我个人认为它没有用处。
我发现这种模式有用的一种情况是在使用从BasicObject
继承的对象设计DSL时。由于BasicObject
不像include Kernel
那样Object
,因此您无法轻松访问许多有用的方法,例如#instance_variables
甚至是#class
。但是您可以将它们分别从内核复制到您的班级中:
class Foo < BasicObject
define_method(:class, ::Kernel.instance_method(:class))
end
f = Foo.new
puts f.class
=> Foo