使用している Ruby のバージョンは2.5.3
。
モジュールの定義
以下の構文で定義する。
module モジュール名
end
モジュールはクラスと違い、インスタンスを作ることは出来ない。
module MyModule
end
MyModule.new
ミックスイン
クラスが、あるモジュールを取り入れてそのモジュールのメソッドを使えるようにすることを、ミックスインと呼ぶ。
include
でミックスインすると、モジュールで定義したメソッドをインスタンスメソッドとして使えるようになる。
ミックスインしたクラスのサブクラスでも、そのまま使える。
module SelfIntroduction
def who_am_i(oneself)
p "I'm #{oneself}."
end
end
class Parent
include SelfIntroduction
def hello
print 'Hello! '
who_am_i self
end
end
class Child < Parent
def hey
print 'Hey! '
who_am_i self
end
end
parent = Parent.new
parent.hello
child = Child.new
child.hey
extend
を使ってミックスインすると、モジュールのメソッドを、そのクラスの特異メソッド(クラスメソッド)として使うことが出来る。
module SelfIntroduction
def who_am_i(oneself)
p "I'm #{oneself}."
end
end
class User
extend SelfIntroduction
end
User.who_am_i(User)
include されているモジュールを調べる
include?
メソッドの引数にモジュールを渡すと、そのモジュールをinclude
しているかどうかが返ってくる。
included_modules
メソッドは、include
しているモジュールを配列で返す。
module SelfIntroduction
end
class Parent
include SelfIntroduction
end
class Child < Parent
end
p Parent.include?(SelfIntroduction)
p Child.include?(SelfIntroduction)
p Parent.included_modules
p Child.included_modules
Kernel とトップレベル
p
やrequire
といったメソッドは、Kernel
というモジュールで定義されている。
そして、Object
クラスがKernel
をinclude
しているため、Object
を継承している全てのクラスで、p
などのメソッドを使える。
p Object.included_modules
Ruby では、クラス構文やモジュール構文などで囲まれていない一番外側の部分を、「トップレベル」と呼ぶ。
そしてトップレベルのself
は、main
という名前のオブジェクトを指すが、これはObject
クラスのインスタンスである。トップレベルでp
などを使えるのは、このため。
p self
p self.class
p self.class.include?(Kernel)
モジュールは Module クラスのインスタンス
モジュールは、Module
クラスのインスタンスである。
module SelfIntroduction
end
p SelfIntroduction.class
そして、Module
クラスは、Object
クラスを継承している。
p Module.superclass
Class
クラスはModule
クラスを継承している。
そのため、継承関係はClass -> Module -> Object -> BasicObject
になっている。
p Class.superclass
p Module.superclass
p Object.superclass
全てのクラスは、Class
クラスのインスタンスでもある。
p Class.class
p Module.class
p Object.class
p BasicObject.class
ここらへんは自分でも理解できていない。循環参照のようになっていないか? Class
はBasicObject
やObject
を継承しているが、それらスーパークラスは、Class
のインスタンスなのだから、Class
から生まれてくる。だからまず先にClass
が存在しているはず。しかしそのClass
はBasicObject
などを継承しておかないといけない。Class
定義時にはまだBasicObject
は存在しないにも拘わらず!
以下の状況が成立してしまうのがよく分からない。
p Module.instance_of?(Class)
p Class.superclass
名前空間としてモジュールを使う
クラス定義をモジュール構文で囲うことで、名前空間を分けることが出来る。
参照する際はモジュール名::クラス名
と記述する。
module Seller
class Alice
def role
p 'I am seller.'
end
end
end
module Buyer
class Alice
def role
p 'I am buyer.'
end
end
end
seller = Seller::Alice.new
seller.role
buyer = Buyer::Alice.new
buyer.role
モジュールを入れ子にすることも出来る。
module User
module PremiumUser
class Alice
def initialize
p 'This is premium user.'
end
end
end
end
User::PremiumUser::Alice.new
モジュールが既に定義済みの場合、class モジュール名::クラス名
という構文でクラスを定義することも可能。
module Seller
end
class Seller::Alice
def role
p 'I am seller.'
end
end
seller = Seller::Alice.new
seller.role
トップレベルで定義しているクラスを明示的に呼び出す場合は、::クラス名
と記述する。
class Alice
end
module Seller
class Alice
def initialize
print Alice
print ', '
p ::Alice
end
end
end
Seller::Alice.new
関数や定数を提供するためにモジュールを使う
モジュールに特異メソッドを定義すれば、ミックスインすることなくそのメソッドを使える。
module SelfIntroduction
def self.who_am_i(oneself)
p "I'm #{oneself}."
end
end
SelfIntroduction.who_am_i 1
ミックスインとしても特異メソッドとしても使えるメソッドを、モジュール関数という。
モジュール関数はmodule_function
を使って定義する。
モジュール関数は自動的にprivate
になるので、レシーバを指定して呼び出すことは出来ない。
module SelfIntroduction
def who_am_i(oneself)
p "I'm #{oneself}."
end
module_function :who_am_i
end
class User
include SelfIntroduction
def hello
print 'Hello! '
who_am_i self
end
end
SelfIntroduction.who_am_i 1
user = User.new
user.hello
user.who_am_i
モジュールは定数を定義することもでき、モジュール名::定数名
で取得できる。
module SelfIntroduction
SOME_VALUE = 'foo'
end
p SelfIntroduction::SOME_VALUE
メソッド探索
クラスに対してancestors
メソッドを使うと、クラスやモジュールの配列が返ってくる。
クラスのインスタンスがメソッドを呼び出すとき、ancestors
の返り値の順番でメソッドを探索し、最初に見つかったメソッドを実行する。
最後まで見つからなかった場合はNoMethodError
になる。
module A
end
class Parent
include A
def foo
p "Parent's foo"
end
def bar
p "Parent's bar"
end
end
class Child < Parent
def foo
p "Child's foo"
end
end
p Child.ancestors
child = Child.new
child.foo
child.bar
モジュールをinclude
した場合、モジュールのメソッドより先にクラスのインスタンスメソッドを探索するが、prepend
メソッドでミックスインした場合は、ミックスインしたモジュールのメソッドを先に探索する。
module A
end
class Foo
include A
end
class Bar
prepend A
end
p Foo.ancestors
p Bar.ancestors
参考資料
gihyo.jp