背景:

我有一个模块,它声明了许多实例方法

module UsefulThings
  def get_file; ...
  def delete_file; ...

  def format_text(x); ...
end

我想在类中调用其中一些方法。在ruby中你通常是这样做的:

class UsefulWorker
  include UsefulThings

  def do_work
    format_text("abc")
    ...
  end
end

问题

include UsefulThings引入了UsefulThings中的所有方法。在这种情况下,我只需要format_text和显式不需要get_file和delete_file。

我可以看到几个可能的解决方案:

Somehow invoke the method directly on the module without including it anywhere I don't know how/if this can be done. (Hence this question) Somehow include Usefulthings and only bring in some of it's methods I also don't know how/if this can be done Create a proxy class, include UsefulThings in that, then delegate format_text to that proxy instance This would work, but anonymous proxy classes are a hack. Yuck. Split up the module into 2 or more smaller modules This would also work, and is probably the best solution I can think of, but I'd prefer to avoid it as I'd end up with a proliferation of dozens and dozens of modules - managing this would be burdensome

为什么一个模块中有很多不相关的功能?它是来自rails应用程序的ApplicationHelper,我们的团队实际上已经决定将它作为任何不够特定而不属于其他任何地方的东西的垃圾场。大多数是独立的实用程序方法,在任何地方都可以使用。我可以把它分解成独立的助手,但有30个,每个都有一个方法……这似乎没有什么效果


当前回答

今天我在学习Ruby的时候,发现这个老问题很有趣,所以我想用我的新知识来回答。

假设您拥有该模块

module MyModule
  def say
    'I say'
  end

  def cheer
    'I cheer'
  end
end 

然后在类中调用Animal,我可以从MyModule中采用cheer方法,如下所示

class Animal
  define_method(:happy, MyModule.method(:cheer))
end

这就是所谓的unbound方法,所以你可以把一个可调用的对象绑定到另一个地方。

从这里开始,您可以像往常一样使用该方法,例如

my_dog = Animal.new

my_dog.happy # => "I cheer"

希望这对你有所帮助,因为我今天也学到了一些新东西。

为了进一步了解,您可以使用irb并查看Method对象。

其他回答

如果模块上的一个方法被转换为模块函数,你可以简单地将它从Mods中调用,就像它被声明为一样

module Mods
  def self.foo
     puts "Mods.foo(self)"
  end
end

下面的module_function方法将避免破坏任何包含所有Mods的类。

module Mods
  def foo
    puts "Mods.foo"
  end
end

class Includer
  include Mods
end

Includer.new.foo

Mods.module_eval do
  module_function(:foo)
  public :foo
end

Includer.new.foo # this would break without public :foo above

class Thing
  def bar
    Mods.foo
  end
end

Thing.new.bar  

然而,我很好奇为什么一组不相关的函数都包含在同一个模块中?

编辑后显示,如果在module_function:foo之后调用public:foo,则包含仍然有效

调用模块实例方法时不包含模块(也不创建中间对象):

class UsefulWorker
  def do_work
    UsefulThings.instance_method(:format_text).bind(self).call("abc")
    ...
  end
end

9年之后,这里有一个通用的解决方案:

module CreateModuleFunctions
  def self.included(base)
    base.instance_methods.each do |method|
      base.module_eval do
        module_function(method)
        public(method)
      end
    end
  end
end

RSpec.describe CreateModuleFunctions do
  context "when included into a Module" do
    it "makes the Module's methods invokable via the Module" do
      module ModuleIncluded
        def instance_method_1;end
        def instance_method_2;end

        include CreateModuleFunctions
      end

      expect { ModuleIncluded.instance_method_1 }.to_not raise_error
    end
  end
end

不幸的是,您需要应用的技巧是在定义方法之后才包含模块。或者,您还可以在上下文定义为ModuleIncluded之后将其包含进来。发送(:包括,CreateModuleFunctions)。

或者您可以通过reflection_utils gem使用它。

spec.add_dependency "reflection_utils", ">= 0.3.0"

require 'reflection_utils'
include ReflectionUtils::CreateModuleFunctions

首先,我建议将模块分解为您需要的有用内容。但是你总是可以创建一个类来扩展你的调用:

module UsefulThings
  def a
    puts "aaay"
  end
  def b
    puts "beee"
  end
end

def test
  ob = Class.new.send(:include, UsefulThings).new
  ob.a
end

test

我认为丢弃单个调用(不改变现有模块或创建新模块)的最短方法如下:

Class.new.extend(UsefulThings).get_file