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

时间:2017-02-04 05:36:53

标签: ruby metaprogramming

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

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

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

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

请告诉我怎么做。

4 个答案:

答案 0 :(得分:5)

五个陈述

让我们一次一个地考虑这五个陈述(但是与呈现的顺序不同)。请注意,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]

Ruby会在考虑hello_world之前在SimpleClass中查找SimpleModule并找到它。

但是,可以使用Module#prependSimpleModule#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绑定到call的实例,然后使用sendsc = SimpleClass.new #=> #<SimpleClass:0x007fcbc2046010> um = SimpleModule.instance_method(:hello_world) #=> #<UnboundMethod: SimpleModule#hello_world> bm = um.bind(sc) #=> #<Method: SimpleModule#hello_world> bm.call #=> i am a SimpleModule method sc.send(:hello_world) #=> i am a SimpleModule method 执行它。

select
    c.id,
    group_concat(concat(u.name, ' ', u.ma_lastname) order by u.id separator ', ') pass
from config_project c
left join users u on find_in_set(u.id, c.pass) > 0
group by c.id;

答案 1 :(得分:2)

模块不具备实例方法,因为它们不是类。如果在模块中定义实例方法,则称为mixin。这些&#34; mixins&#34;可以包含在另一个类中,然后可以使用。

完整的说明在docs

编辑:

例如,您可以执行以下操作:

module SimpleModule
  def hello_world
    p 'hello world'
  end
end

class Example
  include SimpleModule
end

Example.new.send(:hello_world)

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

答案 2 :(得分:2)

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

查看Module.append_features文档

  

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

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

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

答案 3 :(得分:0)

您也可以使用constantize

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