self を拡張する Ruby ユーティリティ モジュールのインスタンス変数?
概要
いくつかの単純な関数を備えたユーティリティモジュールを作成しています。インスタンス化する必要がないようにモジュールを自己拡張したいのです。同僚と私は、これらのタイプのモジュールにデータを保存するさまざまな方法について話し合っていたのですが、彼が提供した一例は、インスタンス変数を使用した次のようなものでした。
module ExampleModule
extend self
@my_instance_variable = "Hello from module"
def do_thing
puts "**** #{@my_instance_variable}"
@my_instance_variable
end
end
これは私たちが使用することに決めたアプローチではありませんが、それが機能する理由や最良の代替案をより深く理解したいと思いました。そこで、いくつか質問します。
なぜこれが機能するのでしょうか?インスタンスなしで呼び出されたモジュール メソッドはどのようにしてインスタンス変数にアクセスできるのでしょうか?この動作について説明している Ruby のドキュメント/記事を教えていただけますか?
これが適切なパターンであるかどうか、または適切な場合について考えますか? (このようなモジュールにインスタンス変数があるのは直観に反しているように思えるので、私はファンではありません)
ここでは代わりにクラス変数を使用する必要がありますか?私たちのrubocopは@@fooクラス変数にコード臭としてフラグを立てます
解決策
Rubyではモジュールにインスタンス変数を持たせることができます。これは、モジュールの一種であるクラスにも当てはまります。キーワードの代わりに Module.new メソッドを使用すると、これがさらに明確になります。
module Foo
@bar = "Hello World"
def self.bar
@bar
end
end
# is pretty much equivilent to
Foo = Module.new do
@bar = "Hello World"
def self.bar
@bar
end
end
上記の例と実際のコードの唯一の違いは、extende self を使用していることです。これにより、モジュールを含むクラスだけでなく、モジュール上でも do_thing を呼び出すことができます。
私はこれの大ファンではありません。個人的な意見としては、def self.method_name を使用してメソッドを明示的に定義するほうが良いでしょう。これにより、意図がより明確になり、静的分析で検出できるようになります。
モジュール インスタンス変数の非常に一般的な使用法の 1 つは、構成パターンです。
module MyGem
class << self
attr_accessor :configuration
end
def self.configure
self.configuration ||= Configuration.new
yield(configuration)
end
class Configuration
attr_accessor :mailer_sender
def initialize
@mailer_sender = '[email protected]'
end
end
end
ここでは、Gem 固有の設定のセットが @configuration に保存されています。次に、アプリの起動時に読み込まれる初期化ファイルにこれを設定します。
MyGem.configure do |config|
config.mailer_sender = '[email protected]'
end
これにより、グローバルに依存せずにアプリ全体の設定が可能になります。
Ruby を初めて使用する場合、モジュール/クラスのインスタンス変数は混乱するかもしれませんが、Ruby ではすべてがオブジェクトであり、インスタンス変数はクラスの定義されたプロパティではないことを覚えておいてください。これらは、モジュール、クラス、またはクラスのインスタンスである可能性のあるもののインスタンスに語彙的にスコープされる単なる変数です。
Ruby のクラス変数には予期しない動作がたくさんあるため、通常は避けるのが最善です。たとえば、クラスとそのすべてのサブクラス間で共有されます。