我可以在不包含Ruby模块的情况下调用实例方法吗?

时间:2008-11-26 23:03:38

标签: ruby module methods

背景

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

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 UsefulThingsUsefulThings引入所有方法。在这种情况下,我只需要format_text并明确地不希望get_filedelete_file

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

  1. 以某种方式直接在模块上调用该方法,而不将其包含在任何地方
    • 我不知道如何做到这一点。 (因此这个问题)
  2. 以某种方式包含Usefulthings并仅引入其中的一些方法
    • 我也不知道如何做到这一点
  3. 创建一个代理类,在其中包含UsefulThings,然后将format_text委托给该代理实例
    • 这可行,但匿名代理类是黑客。呸。
  4. 将模块拆分为2个或更多小模块
    • 这也可行,并且可能是我能想到的最好的解决方案,但我宁愿避免它,因为我最终会增加数十个模块和数十个模块 - 管理这个会很麻烦
  5. 为什么单个模块中存在大量不相关的功能?这是来自rails应用程序的ApplicationHelper,我们的团队事实上决定将其作为任何其他地方不具备的特定内容的倾销场。大多数独立的实用程序方法随处可见。我可以将它分解成单独的助手,但是其中有30个,每个都有1个方法......这似乎没有效果

10 个答案:

答案 0 :(得分:130)

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

Class.new.extend(UsefulThings).get_file

答案 1 :(得分:128)

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

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

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

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  

但是,我很好奇为什么一组不相关的函数首先包含在同一个模块中?

已编辑以显示在public :foo

之后调用module_function :foo时仍然有效

答案 2 :(得分:76)

如果您“拥有”该模块,另一种方法是使用module_function

module UsefulThings
  def a
    puts "aaay"
  end
  module_function :a

  def b
    puts "beee"
  end
end

def test
  UsefulThings.a
  UsefulThings.b # Fails!  Not a module method
end

test

答案 3 :(得分:17)

要在不包含模块的情况下调用模块实例方法(并且不创建中间对象):

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

答案 4 :(得分:16)

如果要在不将模块包含在另一个类中的情况下调用这些方法,则需要将它们定义为模块方法:

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

  def self.format_text(x); ...
end

然后你可以用

打电话给他们
UsefulThings.format_text("xxx")

UsefulThings::format_text("xxx")

但无论如何,我建议您将相关方法放在一个模块或一个类中。如果你有问题,你只想从模块中包含一个方法,那么它听起来像是一个糟糕的代码味道,将不相关的方法放在一起并不是很好的Ruby风格。

答案 5 :(得分:5)

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

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

答案 6 :(得分:4)

一个。如果你总是想以“合格的”独立方式(UsefulThings.get_file)调用它们,那么就像其他人指出的那样让它们变为静态,

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

  def self.format_text(x); ...

  # Or.. make all of the "static"
  class << self
     def write_file; ...
     def commit_file; ...
  end

end

B中。如果你仍然希望在相同的情况下保持mixin方法,以及一次性独立调用,你可以使用mixin创建扩展的单行模块:

module UsefulThingsMixin
  def get_file; ...
  def delete_file; ...

  def format_text(x); ...
end

module UsefulThings
  extend UsefulThingsMixin
end

所以两者都有效:

  UsefulThings.get_file()       # one off

   class MyUser
      include UsefulThingsMixin  
      def f
         format_text             # all useful things available directly
      end
   end 

对于每一种方法,恕我直言都比module_function更清洁 - 如果想要所有方法的话。

答案 7 :(得分:4)

我理解这个问题,你想把一些模块的实例方法混合到一个类中。

让我们首先考虑Module#include的工作原理。假设我们有一个模块UsefulThings,它包含两个实例方法:

module UsefulThings
  def add1
    self + 1
  end
  def add3
    self + 3
  end
end

UsefulThings.instance_methods
  #=> [:add1, :add3]

Fixnum include该模块:

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  include UsefulThings
end

我们看到了:

Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
  #=> [:add2, :add3, :add1] 
1.add1
2 
1.add2
cat
1.add3
dog

您希望UsefulThings#add3覆盖Fixnum#add3,以便1.add3返回4吗?考虑一下:

Fixnum.ancestors
  #=> [Fixnum, UsefulThings, Integer, Numeric, Comparable,
  #    Object, Kernel, BasicObject] 

当类include成为模块时,模块成为类&#39;超类。因此,由于继承的工作原理,将add3发送到Fixnum的实例会导致Fixnum#add3被调用,返回dog

现在让我们向[{1}}添加方法:add2

UsefulThings

我们现在只希望module UsefulThings def add1 self + 1 end def add2 self + 2 end def add3 self + 3 end end Fixnum方法includeadd1。这样做,我们希望得到与上面相同的结果。

假设,如上所述,我们执行:

add3

结果如何?不需要的方法class Fixnum def add2 puts "cat" end def add3 puts "dog" end include UsefulThings end 已添加到:add2Fixnum已添加,并且由于上述原因,未添加:add1。所以我们所要做的就是:add3 undef。我们可以用一个简单的辅助方法来做到这一点:

:add2

我们这样调用:

module Helpers
  def self.include_some(mod, klass, *args)
    klass.send(:include, mod)
    (mod.instance_methods - args - klass.instance_methods).each do |m|
      klass.send(:undef_method, m)
    end
  end
end

然后:

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  Helpers.include_some(UsefulThings, self, :add1, :add3)
end

这是我们想要的结果。

答案 8 :(得分:1)

不确定10年后是否仍然需要它,但是我使用eigenclass解决了它。

body{
    background-image: url("");
}

.navbar{
    text-align: center;
    background-color: rgba(0,0,0,0.5);
    position: sticky;
    position: -webkit-sticky;
    top: 0;         
}

.menu-list{
    text-decoration: none;
    list-style-type: none;
}

.menu-list li {
    display: inline-block;
}

h1{
    color: white;
    padding-top: 50px;
}

答案 9 :(得分:0)

经过近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.send(:include, CreateModuleFunctions)后包含它。

或者您可以通过reflection_utils gem。

使用它
spec.add_dependency "reflection_utils", ">= 0.3.0"

require 'reflection_utils'
include ReflectionUtils::CreateModuleFunctions