私はいくつかのインスタンスメソッドを宣言するモジュールを持っています
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
のメソッドのallを取り込みます。この場合、format_text
のみが必要であり、get_file
とdelete_file
は明示的には必要ありません。
これに対するいくつかの可能な解決策を見ることができます:
Usefulthings
をインクルードし、そのメソッドの一部のみを取り込みますUsefulThings
を含め、format_text
をそのプロキシインスタンスに委任します単一のモジュールに多くの無関係な関数があるのはなぜですか? RailsアプリのApplicationHelper
であり、私たちのチームが事実上、他の場所に属するほど具体的でないもののゴミ捨て場として決定しました。ほとんどすべての場所で使用されるスタンドアロンのユーティリティメソッド。私はそれを別々のヘルパーに分割することができましたが、30個あり、それぞれ1つのメソッドがあります...これは非生産的です
モジュールのメソッドがモジュール関数に変換される場合、次のように宣言されているかのように、単にModから呼び出すことができます。
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
が呼び出された場合にインクルードがまだ機能することを示す
(既存のモジュールを変更したり、新しいモジュールを作成したりせずに)使い捨ての呼び出しを行う最短の方法は次のようになると思います。
Class.new.extend(UsefulThings).get_file
モジュールを「所有」している場合の別の方法は、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
モジュールを含めずに(および中間オブジェクトを作成せずに)モジュールインスタンスメソッドを呼び出すには:
class UsefulWorker
def do_work
UsefulThings.instance_method(:format_text).bind(self).call("abc")
...
end
end
モジュールを別のクラスに含めずにこれらのメソッドを呼び出す場合は、モジュールメソッドとして定義する必要があります。
module UsefulThings
def self.get_file; ...
def self.delete_file; ...
def self.format_text(x); ...
end
そして、あなたはそれらを呼び出すことができます
UsefulThings.format_text("xxx")
または
UsefulThings::format_text("xxx")
とにかく、関連するメソッドを1つのモジュールまたは1つのクラスに配置することをお勧めします。モジュールのメソッドを1つだけ含めたいという問題がある場合は、コードのにおいが悪いように聞こえ、無関係なメソッドをまとめるのは良くないRubyスタイルではありません。
まず、モジュールを必要な便利なものに分割することをお勧めします。ただし、呼び出し用にそれを拡張するクラスをいつでも作成できます。
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
私は質問を理解しているので、モジュールのインスタンスメソッドのいくつかをクラスにミックスしたいと思います。
Module#include の仕組みを検討することから始めましょう。 2つのインスタンスメソッドを含むモジュールUsefulThings
があるとします。
module UsefulThings
def add1
self + 1
end
def add3
self + 3
end
end
UsefulThings.instance_methods
#=> [:add1, :add3]
およびFixnum
include
sそのモジュール:
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
sモジュールである場合、モジュールはクラスのスーパークラスになります。したがって、継承の仕組みにより、add3
をFixnum
のインスタンスに送信すると、Fixnum#add3
が呼び出され、dog
が返されます。
次に、: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
次に:
Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
#=> [:add2, :add3, :add1]
1.add1
2
1.add2
cat
1.add3
dog
これが私たちが望む結果です。
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
B.同じケースでミックスインアプローチを維持する場合、および1回限りのスタンドアロン呼び出しを維持する場合は、mixinでextendsのワンライナーモジュールを使用できます。
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
よりもきれいです-すべてのメソッドが必要な場合に備えて。
ほぼ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