モジュールクラスのオブジェクトの特異メソッド。
モジュールメソッドと呼んで良いのか迷いますが、ここでは、モジュールメソッドと呼んでおきます。
日本語での説明は苦手なので、最初に、何がしたいのかを例として書いておきます。
また長い記事なので、結果だけを知りたい方は、下の方のまとめをご覧ください。
はじめに例
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化などされたくない、直接呼ばれるためだけのメソッド。
たまに作りたいですよね?
でも、複数書くとなると、クラスメソッドよりめんどくさい
- クラスメソッドの場合
class FooClass
class << self
def method1 ; end
def method2 ; end
end
def self.method3 ; end
end
- モジュールメソッドの場合
module HogeModule
def self.method1 ; end
def self.method2 ; end
def self.method3 ; end
end
毎回selfが必要になってめんどくさい。
**『なんかスッキリ書く方法ないの?(´・ω・`)』**というのが今回の発端
書き方
それなりに見かける書き方
module Bar
extend self
def method1 ; end
def method2 ; end
end
この書き方で、『インスタンスメソッドは定義されない』とか、『module_functionの代わりになる』という記述を見かける場合もありますが、そんな事はないです。
インスタンスメソッドも定義される
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するからです。
コードをイメージしやすいように書き直すと以下のような感じです。
module Bar
def method1 ; end
def method2 ; end
end
module Bar
extend self
end
またパブリックなので、以下のようにincludeすれば他のクラスから呼べます
module Bar
extend self
def method ; end
end
class Foo
include Bar
end
Foo.new.method
module_functionとの違い
モジュール関数とは、プライベートメソッドであると同時に モジュールの特異メソッドでもあるようなメソッドです。
例えば Math モジュールのメソッドはすべてモジュール関数です。
- つまり、モジュールメソッドとプライベートなインスタンスメソッドを作ります
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)
プライベートメソッドも作られる
モジュールメソッドの書き方
以上までのような事を踏まえて、以下のように落ち着きました。
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
はい。
モジュールメソッドだけしか居ませんね。目標達成です。
まとめ
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クラスのインスタンスですので、特異クラスとしてオープン出来る気がします!!
という事で、実際にやってみましょう。
やってみた
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/)
- 実行結果
$ ruby test.rb
-- instance methods
-- private instance methods
-- singleton methods
method1
method2
得たい結果となっていますね。
こちらの方がスッキリしていると思います。
おまけ
「include 俺」でクラスメソッド
モジュールをインクルードしてクラスメソッドにしたい時
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