Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

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

はじめに例

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との違い

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした