Math のように、同じメソッドを Module の特異メソッドとしても、インスタンスメソッドとしても使えるメソッドをモジュール関数といいます。
Math.log2 2 #=> 1.0
include Math
log2 2 #=> 1.0
ActiveSupport のソースを読んでいると、いくつかの方法でモジュール関数を作っていたので、気になった2つを紹介します。
extend self
いろいろなところで使われていました。
# こんな感じで
module ActiveSupport
module Inflector
extend self
...
end
end
インスタンスメソッドを実装して、自分自身を extend
することで、特異メソッドにもしているんですね。言われてみると当たり前だけど、こういう風に extend self
ってやると初めて知りました。
module A
extend self
def method_a
'モジュール関数です'
end
end
A.method_a #=> "モジュール関数です"
include A
method_a #=> "モジュール関数です"
確かに、モジュール関数になっています。便利ですね!
module_function
こちらも、いろいろな所で使われています。
# 例えばこんな感じ
class ERB
module Util
...
def html_escape(s)
...
end
...
module_function :html_escape
...
end
end
module_function
は引数にインスタンスメソッド名を、シンボルか文字列で渡すと特異メソッドを定義するメソッドです。
module A
def method_a
'モジュール関数です'
end
module_function :method_a
end
A.method_a #=> "モジュール関数です"
また、引数を渡さないで呼び出すと、module_function
の後に定義したインスタンス変数は全てモジュール関数になります。
module B
module_function
def method_a
'これも'
end
def method_b
'あれも'
end
end
B.method_a #=> "これも"
B.method_b #=> "あれも"
これも便利ですね。
extend self
と module_function
2つのモジュール関数の定義の仕方を紹介しましたが、この2つ若干挙動が異なります。
実際に以下のように定義して、インスタンスメソッドを実行してみると
module BySlef
extend self
def hello_by_self
'Hello extend self'
end
end
module ByModuleFunction
def hello_by_module_function
'Hello module_function'
end
module_function :hello_by_module_function
end
obj = Object.new
obj.extend(BySlef).extend(ByModuleFunction)
obj.hello_by_self #=> "Hello extend self"
obj.hello_by_module_function #=> NoMethodError: private method `hello_by_module_function'
module_function
で定義したモジュール関数の方は、インスタンスメソッドが private になっていることが分かります。このように、module_function
はインスタンスメソッドと同名の特異メソッドを定義すると同時に、そのインスタンスメソッドを private にします。
ActiveSupport では、どちらの方法も良く利用されていましたが、ここら辺で上手く使い分けをするんですかね。以上、モジュール関数の定義の仕方でした。