背景:
我有一个模块,它声明了许多实例方法
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个,每个都有一个方法……这似乎没有什么效果
如果模块上的一个方法被转换为模块函数,你可以简单地将它从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,则包含仍然有效
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