モジュールとクラスと名前空間

名前空間の話。入門的なサイトだと全く解説されてなかったりするけど、手元のスクリプト以上になれば必須だよねー。

  • モジュール/クラスの内部で定義された内部モジュール/内部クラスを外部から参照したい場合、「::」で参照する。
  • 内部モジュール/内部クラスのメソッドを外部から書き換えたい場合、外側のクラス/モジュールを定義しなおして書き換える。直接は書き換えられない。
  • モジュールメソッド/クラスメソッドは、自身の内部で定義する場合、"def self.foo"で定義できる。
    • 内部から"def Foo.foo"と定義するのと基本的には同じ。だが、自身のモジュール/クラス名と同じ名前の内部モジュール/クラス名が既に定義されていた場合、"def Foo.foo"は"Foo::Foo.foo"として定義されるので要注意。
      • つまり、実行時点の状態によって名前空間が微妙にずれる場合があるってことか。
      • 普段は"def self.foo"で内部から定義すべきだろね。
      • でも、これ使ってキモイことも出来そう。かな。

以下、自分用メモ。

#一通り定義してみる
module Foo
  def self.foo  # 「def Foo.foo」と同義
    puts "Foo.foo is called"
  end
  class Bar
    def self.bar # 「def Bar.bar」と同義
      puts "Foo::Bar.bar is called"
    end
  end
end

class Bar
  def self.bar
    puts "Bar.bar is called"
  end
  
  class InnerBar
  end
end

#確かめる
Foo.foo # => Foo.foo is called
Foo::Bar.bar #=> Foo::Bar.bar is called
Bar.bar # => Bar.bar is called



#Barクラスのbarメソッドを書き換える
def Bar.bar
  puts "Bar.bar is rewrited"
end

#Barクラスの内部クラスInnerBarクラスにメソッドを追加する
#「def Bar::InnerBar.bar」とは書けない。
class Bar
  def InnerBar.bar
    puts "Bar::InnerBar.bar is called"
  end
end

Bar.bar # => Bar.bar is rewrited
Bar::InnerBar.bar # => "Bar::InnerBar.bar is called"



#Fooモジュールのfooメソッドを書き換える
def Foo.foo
  puts "Foo.foo is rewrited"
end

Foo.foo # => Foo.foo is rewrited



#Foo::Barクラスのbarメソッドを書き換えたい場合、
#「def Foo::Bar.bar」とは書けない。
#そのモジュールを再定義し、必要な部分だけを追記する。
module Foo
  def Bar.bar
    puts "Foo::Bar.bar is rewrited"
  end
end

Foo::Bar.bar #=> Foo::Bar.bar is rewrited



#Fooモジュールに内部モジュールを定義してみる
module Foo
  module InnerFoo
    def self.foo
      puts "Foo::InnerFoo.foo is called"
    end
  end
end

Foo::InnerFoo.foo # => Foo.foo is rewrited
Foo.foo # => Foo.foo is rewrited :当然、Fooモジュールのメソッドとは区別される



#Barクラスに内部モジュールを定義してみる
class Bar
  module InnerModule
    def self.bar
      puts "Bar::InnerModule.bar is called"
    end
  end
end

Bar::InnerModule.bar # => Bar::InnerModule.bar is called