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

Ori*_*rds 171 ruby methods module

背景:

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

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

  def format_text(x); ...
end
Run Code Online (Sandbox Code Playgroud)

我想从一个类中调用其中的一些方法.你通常如何在ruby中这样做是这样的:

class UsefulWorker
  include UsefulThings

  def do_work
    format_text("abc")
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

问题

include UsefulThings从中引入所有方法UsefulThings.在这种情况下,我只想要format_text并明确地不想get_filedelete_file.

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

  1. 以某种方式直接在模块上调用该方法,而不将其包含在任何地方
    • 我不知道如何做到这一点.(因此这个问题)
  2. 以某种方式包括Usefulthings并且只引入一些方法
    • 我也不知道如何做到这一点
  3. 创建一个代理类,包含UsefulThings在其中,然后委托format_text给该代理实例
    • 这可行,但匿名代理类是一个黑客.呸.
  4. 将模块拆分为2个或更多较小的模块
    • 这也可行,可能是我能想到的最好的解决方案,但我宁愿避免它,因为我最终会扩散数十个和几十个模块 - 管理这将是繁重的

为什么单个模块中有很多不相关的功能?它ApplicationHelper来自一个rails应用程序,我们的团队事实上决定将其作为任何其他地方不具备的特定任何东西的倾倒场.大多数独立的实用程序方法随处可见.我可以将它分解成单独的助手,但是它们中有30个,每个都有1个方法......这似乎没有效果

dol*_*nko 139

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

Class.new.extend(UsefulThings).get_file
Run Code Online (Sandbox Code Playgroud)

  • @AlbertCatalà根据我的测试和http://stackoverflow.com/a/23645285/54247匿名类是垃圾收集就像其他一切一样,所以它不应该浪费内存. (3认同)
  • 如果您不喜欢使用匿名类作为代理,您也可以使用对象作为该方法的代理。`Object.new.extend(UsefulThings).get_file` (3认同)
  • 对文件 erb 非常有用。html.erb 或 js.erb。谢谢 !不知道这个系统会不会浪费内存 (2认同)

dgt*_*zed 130

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

module Mods
  def self.foo
     puts "Mods.foo(self)"
  end
end
Run Code Online (Sandbox Code Playgroud)

下面的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  
Run Code Online (Sandbox Code Playgroud)

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

编辑显示,如果public :foo被调用后仍然有效module_function :foo


Dus*_*tin 79

如果您"拥有"该模块,另一种方法是使用它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
Run Code Online (Sandbox Code Playgroud)

  • 对于您不拥有它的情况:UsefulThings.send:module_function,:b (25认同)
  • module_function将方法转换为私有方法(无论如何它在我的IRB中),这会打破其他调用者:-( (3认同)
  • 猎户座 - 我不相信这是真的.根据http://www.ruby-doc.org/docs/ProgrammingRuby/html/ref_c_module.html#Module.module_function,module_function"为命名方法创建模块函数.可以使用模块作为接收器调用这些函数,并且也可以作为混合在模块中的类的实例方法.模块函数是原始的副本,因此可以独立更改.实例方法版本是私有的.如果没有参数使用,随后定义的方法成为模块功能." (2认同)
  • 你也可以把它定义为`def self.a; 把'aaay'; end` (2认同)

Rai*_*kis 18

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

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

  def self.format_text(x); ...
end
Run Code Online (Sandbox Code Playgroud)

然后你可以打电话给他们

UsefulThings.format_text("xxx")
Run Code Online (Sandbox Code Playgroud)

要么

UsefulThings::format_text("xxx")
Run Code Online (Sandbox Code Playgroud)

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


ren*_*ier 17

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

class UsefulWorker
  def do_work
    UsefulThings.instance_method(:format_text).bind(self).call("abc")
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 很难看,但是可以用。你一定喜欢Ruby :) (2认同)

Dus*_*tin 6

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

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
Run Code Online (Sandbox Code Playgroud)


ing*_*ger 6

A. 如果您总是想以“合格的”独立方式(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
Run Code Online (Sandbox Code Playgroud)

B. 如果你仍然想在相同的情况下保留 mixin 方法,以及一次性的独立调用,你可以有一个单行模块,用 mixin扩展自己:

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

  def format_text(x); ...
end

module UsefulThings
  extend UsefulThingsMixin
end
Run Code Online (Sandbox Code Playgroud)

所以两者都有效:

  UsefulThings.get_file()       # one off

   class MyUser
      include UsefulThingsMixin  
      def f
         format_text             # all useful things available directly
      end
   end 
Run Code Online (Sandbox Code Playgroud)

恕我直言,它比module_function每种方法都更干净-以防万一。


Car*_*and 5

据我了解,您想将模块的一些实例方法混合到一个类中。

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

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

UsefulThings.instance_methods
  #=> [:add1, :add3]
Run Code Online (Sandbox Code Playgroud)

Fixnum include那个模块:

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  include UsefulThings
end
Run Code Online (Sandbox Code Playgroud)

我们看到:

Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
  #=> [:add2, :add3, :add1] 
1.add1
2 
1.add2
cat
1.add3
dog
Run Code Online (Sandbox Code Playgroud)

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

Fixnum.ancestors
  #=> [Fixnum, UsefulThings, Integer, Numeric, Comparable,
  #    Object, Kernel, BasicObject] 
Run Code Online (Sandbox Code Playgroud)

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

现在,让我们添加一个方法:add2UsefulThings

module UsefulThings
  def add1
    self + 1
  end
  def add2
    self + 2
  end
  def add3
    self + 3
  end
end
Run Code Online (Sandbox Code Playgroud)

现在,我们希望Fixnuminclude只有方法add1add3。这样做,我们期望得到和上面一样的结果。

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

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  include UsefulThings
end
Run Code Online (Sandbox Code Playgroud)

结果是什么?不需要的方法:add2被添加到Fixnum: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
Run Code Online (Sandbox Code Playgroud)

我们这样调用:

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

然后:

Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
  #=> [:add2, :add3, :add1] 
1.add1
2 
1.add2
cat
1.add3
dog
Run Code Online (Sandbox Code Playgroud)

这就是我们想要的结果。


小智 5

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

module UsefulThings
  def useful_thing_1
    "thing_1"
  end

  class << self
    include UsefulThings
  end
end

class A
  include UsefulThings
end

class B
  extend UsefulThings
end

UsefulThings.useful_thing_1 # => "thing_1"
A.new.useful_thing_1 # => "thing_1"
B.useful_thing_1 # => "thing_1"
Run Code Online (Sandbox Code Playgroud)