Ruby元编程:无法向模块发送方法

例如,我有以下自定义类和模块:

module SimpleModule def hello_world puts 'i am a SimpleModule method' end def self.class_hello_world puts 'i am a SimpleModule class method' end end class SimpleClass def hello_world puts 'i am SimpleClass method' end def self.class_hello_world puts 'i am a SimpleClass class method' end end 

我尝试使用方法send调用类和模块中的那些方法

 SimpleClass.send(class_hello_world) # work SimpleClass.new.send(hello_world) # work SimpleModule.send(class_hello_world) # work SimpleModule.new.send(hello_world) # not work SimpleModule.send(hello_world) # not work 

换句话说,我不知道如何从SimpleModule调用hello_world 。 如果该方法之前使用self定义,则可能。

我需要这样做,因为我想实现一个“自定义包含”:包括从模块到另一个类的所有方法。

请告诉我怎么做。

五个陈述

让我们一次一个地考虑这五个陈述(但是与呈现的顺序不同)。 请注意, send的参数必须是表示为字符串或符号的方法的名称。

 SimpleModule.send("class_hello_world") # i am a SimpleModule class method 

这是正常的,尽管这种方法通常称为模块方法 。 一些常见的内置模块(如Math)仅包含模块方法。

 SimpleClass.send(:class_hello_world) # i am a SimpleClass class method 

由于类是模块,因此行为与上述相同。 class_hello_world通常被称为类方法

 SimpleClass.new.send(:hello_world) # i am SimpleClass method 

这是实例方法的正常调用。

 SimpleModule.send("hello_world") #=> NoMethodError: undefined method `hello_world' for SimpleModule:Module 

没有模块方法hello_world

 SimpleModule.new.send(hello_world) #=> NoMethodError: undefined method `new' for SimpleModule:Module 

无法创建模块的实例。

include vs prepend

假设有人写道

 SimpleClass.include SimpleModule #=> SimpleClass SimpleClass.new.hello_world # i am SimpleClass method 

所以SimpleClass的原始方法hello_world不会被模块的方法用同名的方法覆盖。 考虑一下SimpleClass的祖先。

 SimpleClass.ancestors #=> [SimpleClass, SimpleModule, Object, Kernel, BasicObject] 

在考虑SimpleModule之前,Ruby会在SimpleClass寻找hello_world并找到它。

但是,可以使用Module SimpleModule#hello_worldSimpleClass#hello_world之前放置SimpleModule#hello_world SimpleClass#hello_world

 SimpleClass.prepend SimpleModule #=> SimpleClass SimpleClass.new.hello_world # i am a SimpleModule method SimpleClass.ancestors #=> [SimpleModule, SimpleClass, Object, Kernel, BasicObject] 

绑定未绑定的方法

还有一件事你做。 SimpleModule的实例方法(这里只有一个)是未绑定的。 您可以使用UnboundMethod#bind将每个绑定到SimpleClass的实例,然后使用callsend执行它。

 sc = SimpleClass.new #=> # um = SimpleModule.instance_method(:hello_world) #=> # bm = um.bind(sc) #=> # bm.call #=> i am a SimpleModule method sc.send(:hello_world) #=> i am a SimpleModule method 

模块不能有实例方法,因为它们不是类。 如果在模块中定义实例方法,则称为mixin。 这些“mixins”可以包含在另一个类中,然后可以使用。

完整的解释在文档中

编辑:

例如,你可以这样做:

 module SimpleModule def hello_world p 'hello world' end end class Example include SimpleModule end 

Example.new.send(:hello_world)

这是调用模块混合的一种方法。

因为您似乎想要创建自己的包含版本。

看看Module.append_features文档

当这个模块包含在另一个模块中时,Ruby在这个模块中调用append_features,并在mod中传递接收模块。 Ruby的默认实现是将此模块的常量,方法和模块变量添加到mod,如果此模块尚未添加到mod或其祖先之一。 另请参见模块#include。

因此,如果您想重写自己,那么这就是您必须要做的事情。

既然你喜欢冒险,也许你会喜欢阅读Ruby的源代码,看看它是如何在内部实现的。 请参阅https://github.com/ruby/ruby…class.c#L853:L934

你也可以使用constantize

 def call_method(method) "SimpleModule::#{method}".constantize end