別件でGemを自作している過程で、モジュール関数のハンドリングがややこしいことに気づいてしまったので、それをすっきりさせようとGemを作ってしまいました。
モジュール関数とは
Rubyのメソッドは「public/private/protected」や「インスタンスメソッド/クラスメソッド/特異メソッド」というように区分できますが、「モジュール関数」は、これらの区分とは大きく異なってくるものです。
というのも、「モジュール関数」は単体のメソッドを指す語ではなく、
- モジュールの
privateなインスタンスメソッド - モジュールの
publicな特異メソッド
という、同じ名前・動作の2つのメソッドが存在するときに、そのセットを指す語、ということです。
このように2つ定義することで、Math.sinのようにモジュールから呼ぶ、あるいはinclude Mathとすればsinだけで使える、というような利便性を持つことになります。
ハンドリングの複雑さ
「モジュール関数」が2つのメソッドのセットから成っているので、操作を加えようとすれば何かと厄介なことになります。たとえば、aliasでメソッドのコピーをしようとすると、privateなインスタンスメソッドだけコピーされますし、あるモジュールで定義されたモジュール関数を別なモジュールに持って行って、そちらのモジュール関数としようとしても一筋縄では行きません。
Gem化してみた
ということで、このあたりのハンドリングを担うGemとして、jkr2255/function_moduleを作ってみました。これには3つの機能があります。
Module#module_functions
引数なしのpublicメソッドで、privateインスタンスメソッドとpublic特異メソッドの両方で定義されているメソッド(≒モジュール関数)の一覧を返します。なお、Module自体やその先祖で定義されたメソッドは除外しています。
Module#include_module_functions(mod)
別なモジュール内でinclude SomeModuleの代わりにinclude_module_functions SomeModuleとすると、通常のincludeに加えて、SomeModule内のモジュール関数を、include先の特異メソッドとしても使えるように処理してくれます。
Module#alias_module_function(new_name, old_name)
alias_methodの代わりにこれを使うと、privateインスタンスメソッドのコピーだけでなく、特異メソッドの方もコピーします。