Rubyのモジュールメソッドの作り方

モジュールクラスのオブジェクトの特異メソッド。
モジュールメソッドと呼んで良いのか迷いますが、ここでは、モジュールメソッドと呼んでおきます。
日本語での説明は苦手なので、最初に、何がしたいのかを例として書いておきます。
また長い記事なので、結果だけを知りたい方は、下の方のまとめをご覧ください。

# はじめに例

```ruby
module Hoge
  def self.hoge
    puts "hoge"
  end
end
Hoge.hoge #=> hoge

class Foo
  include Hoge
  extend Hoge
end
Foo.new.hoge #=> undefined method `hoge' for #<Foo:0x0000010105cf60> (NoMethodError)
Foo.hoge #=> undefined method `hoge' for Foo:Class (NoMethodError)
```
こんな感じで、mixinやinstance化などされたくない、直接呼ばれるためだけのメソッド。
たまに作りたいですよね？

でも、複数書くとなると、クラスメソッドよりめんどくさい

* クラスメソッドの場合

```ruby
class FooClass
  class << self
    def method1 ; end
    def method2 ; end
  end
  def self.method3 ; end
end
```

* モジュールメソッドの場合

```ruby
module HogeModule
  def self.method1 ; end
  def self.method2 ; end
  def self.method3 ; end
end
```

毎回selfが必要になってめんどくさい。
**『なんかスッキリ書く方法ないの？（´・ω・｀)』**というのが今回の発端

# 書き方

## それなりに見かける書き方

```ruby
module Bar
  extend self

  def method1 ; end
  def method2 ; end
end
```

この書き方で、『インスタンスメソッドは定義されない』とか、『module_functionの代わりになる』という記述を見かける場合もありますが、そんな事はないです。

### インスタンスメソッドも定義される

```ruby
module Bar
  extend self

  def method1 ; end
  def method2 ; end
end

puts "-- instance methods"
puts Bar.instance_methods.grep(/method\d/)

puts "-- private instance methods"
puts Bar.private_instance_methods.grep(/method\d/)

puts "-- singleton methods"
puts Bar.singleton_methods.grep(/method\d/)
```

* 実行結果

```
-- instance methods
method1
method2
-- private instance methods
-- singleton methods
method1
method2
```

モジュールメソッドとインスタンスメソッドが作成されます。
インスタンスメソッドを作成した後、自身にextendするからです。
コードをイメージしやすいように書き直すと以下のような感じです。

```ruby
module Bar
  def method1 ; end
  def method2 ; end
end

module Bar
  extend self
end
```

またパブリックなので、以下のようにincludeすれば他のクラスから呼べます

```ruby
module Bar
  extend self
  def method ; end
end

class Foo
  include Bar
end

Foo.new.method
```

### module_functionとの違い

[module_function](http://doc.ruby-lang.org/ja/1.9.3/class/Module.html#I_MODULE_FUNCTION)

> モジュール関数とは、プライベートメソッドであると同時に モジュールの特異メソッドでもあるようなメソッドです。
> 例えば Math モジュールのメソッドはすべてモジュール関数です。

* つまり、モジュールメソッドとプライベートなインスタンスメソッドを作ります

```ruby
module Bar
  def method1 ; end
  module_function :method1
end

puts "-- instance methods"
puts Bar.instance_methods.grep(/method\d/)

puts "-- private instance methods"
puts Bar.private_instance_methods.grep(/method\d/)

puts "-- singleton methods"
puts Bar.singleton_methods.grep(/method\d/)

class Foo
  include Bar
end

Foo.new.method1
```

* 実行結果

```
-- instance methods
-- private instance methods
method1
-- singleton methods
method1
sample.rb:19:in `<main>': private method `method1' called for #<Foo:0x00000102058338> (NoMethodError)
```

プライベートメソッドも作られる

## モジュールメソッドの書き方

以上までのような事を踏まえて、以下のように落ち着きました。

```ruby
module Bar
  module ModuleMethods
    def method1 ; end
    def method2 ; end
  end

  extend ModuleMethods
end

puts "-- instance methods"
puts Bar.instance_methods.grep(/method\d/)

puts "-- private instance methods"
puts Bar.private_instance_methods.grep(/method\d/)

puts "-- singleton methods"
puts Bar.singleton_methods.grep(/method\d/)
```

* 実行結果

```
$ ruby sample.rb
-- instance methods
-- private instance methods
-- singleton methods
method1
method2
```

はい。
モジュールメソッドだけしか居ませんね。目標達成です。

# まとめ

```ruby
module Hoge
  module ModuleMethods
    def method1 ; end
    def method2 ; end
  end

  extend ModuleMethods
end
```

* moduleの中でmoduleを作成して、extendする
* module_functionやextend selfはちゃんと使い分ける
* アドバイスを頂きましたが、```class << self```を使っても書けるようです
 * 下記「追記(2015/1/16）」の項に記載しました

いや〜。
今日も不毛に悩みましたね！！（＾q＾
ご指摘・アドバイスお待ちしております〜<(＿ ＿)>

## 追記(2015/1/16)：アドバイスをいただきました！
hilohiroさんにコメントで教えていただきました。
> モジュールもClassクラスのインスタンスは持つので、クラスメソッドを定義するときと同じように書けると思います。

言われてみればそうですね。
気が付きませんでした。
確かにModuleもClassクラスのインスタンスですので、特異クラスとしてオープン出来る気がします！！

という事で、実際にやってみましょう。

### やってみた

```rb
module Bar
  class << self
    def method1 ; end
    def method2 ; end
  end
end

puts "-- instance methods"
puts Bar.instance_methods.grep(/method\d/)

puts "-- private instance methods"
puts Bar.private_instance_methods.grep(/method\d/)

puts "-- singleton methods"
puts Bar.singleton_methods.grep(/method\d/)
```

* 実行結果

```shell-session
$ ruby test.rb 
-- instance methods
-- private instance methods
-- singleton methods
method1
method2
```
得たい結果となっていますね。
こちらの方がスッキリしていると思います。

# おまけ

## 「include 俺」でクラスメソッド

モジュールをインクルードしてクラスメソッドにしたい時

```ruby
module Bar
  def self.included(klass)
    klass.extend ClassMethods
  end

  module ClassMethods
    def bar
      puts "bar"
    end
  end
end

class Foo
  include Bar
end

Foo.bar #=> bar
```

よく書かれるイディオムとはいえ、漂うやりたい放題感w