我有一个声明了许多实例方法的模块
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
。
我可以看到几种可能的解决方案:
Usefulthings
并仅引入其中的一些方法
UsefulThings
,然后将format_text
委托给该代理实例
为什么单个模块中存在大量不相关的功能?这是来自rails应用程序的ApplicationHelper
,我们的团队事实上决定将其作为任何其他地方不具备的特定内容的倾销场。大多数独立的实用程序方法随处可见。我可以将它分解成单独的助手,但是其中有30个,每个都有1个方法......这似乎没有效果
答案 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
方法include
和add1
。这样做,我们希望得到与上面相同的结果。
假设,如上所述,我们执行:
add3
结果如何?不需要的方法class Fixnum
def add2
puts "cat"
end
def add3
puts "dog"
end
include UsefulThings
end
已添加到: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
然后:
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