【一人 Ruby Advent Calendar 2017】module_function について【17日目】

一人 Ruby Advent Calendar 2017 17日目の記事になります。

#module_function とは

#module_function は任意のメソッドを『モジュール関数』として定義するための Module のインスタンスメソッドです。
#module_function は以下のように module 内で使用します。

module M
    def mado
        "mado"
    end
    # 任意のインスタンスメソッドをモジュール関数として定義する
    module_function :mado

    # 以降に定義されるインスタンスメソッドを全てモジュール関数として定義する
    module_function
    def homu
        "homu"
    end

    def mami
        "mami"
    end
end

モジュール関数とは

モジュール関数とは『モジュール内で関数を定義している』ように振る舞うメソッドになります。
例えば、以下のように『モジュールのクラスメソッド』として呼び出すことができます。

module M
    def mado
        "mado"
    end
    module_function :mado
end

p M.mado
# => "mado"

また、include を行うことでモジュール名を省略して直接メソッド名で呼び出すこともできます。

module M
    def mado
        "mado"
    end
    module_function :mado
end

# M を省略する
include M

p mado
# => "mado"

class X
    include M

    def homu
        mado + mado
    end
end

p X.new.homu
# => "madomado"

これは module_function がモジュールの『クラスメソッド』と『インスタンスメソッド』の両方のメソッドを定義しているためです。
ですので M.mado とすれば『モジュールのクラスメソッド』が呼ばれ、include M を行えば『モジュールのインスタンスメソッド』が呼び出されます。
また、この時に定義されるインスタンスメソッドは『private メソッド』として定義されるのでレシーバを付けてメソッドを呼び出すことはできません。

module M
    def mado
        "mado"
    end
    module_function :mado
end

class X
    include M

    def homu
        # Error
        self.mado
    end
end

x = X.new

# private なので外部からは呼び出せない
p x.mado

このように基本的には include したクラスのメソッドでのみ呼び出すことができます。

注意

module_function は『クラスメソッド』と『インスタンスメソッド』の両方のメソッドを『定義する』メソッドになります。
そのため、module_function 化する前のインスタンスメソッドを変更しても module_function 化後のメソッドは影響されません。

module M
    def mado
        "mado"
    end
    module_function :mado

    # module_function 化後に処理を書き換える
    def mado
        "homu"
    end

    # module_function :mado を呼び出した時の M#mado が呼び出される
    p M.mado
    # => "mado"
end

ですので、必ず最後に module_function を呼び出してください。

module M
    def mado
        "mado"
    end
    module_function :mado

    # module_function 化後に処理を書き換える
    def mado
        "homu"
    end
    # module_function 化し直す
    module_function :mado

    p M.mado
    # => "homu"
end

まとめ

  • 関数のようなメソッドを定義したければ module_function が利用できる
  • include を行うことでモジュール名を省略してメソッドと直接呼び出すことが出来る
  • インスタンスメソッドは private なので呼び出しが制限される(include したクラス外からは呼ばれない
  • module_function で定義したメソッドはかなり特殊なのでどのような挙動になるのか理解して使う


オブジェクトに依存せず、メソッド単体で完結するような場合は module_function を利用して定義すると外部から扱いやすくなると思います。