オブジェクトモデル

オープンクラス

既存のクラスをオープンしてメソッドを追加すること

class String
  def to_alphanumeric
    gsub /[^\w\s]/, ''
  end
end

既に同名のメソッドが存在するのに、オープンクラスで追加してしまうとエラーになる。何も考えずにクラスにコードを追加してしまうことを「モンキーパッチ」と呼ぶそうだ。

オブジェクト、クラス

Java等の静的言語と違いオブジェクトのインスタンス変数はクラスと何のつながりもなく値を代入した時に始めて出現する。

class MyClass
  def my_method
    @v = 1
  end
end
obj = MyClass.new #この時点ではインスタンス変数は存在しない
obj.my_method #ここで始めてインスタンス変数が出現する

メソッドはオブジェクトには存在しない。オブジェクトにはクラスへの参照だけがある。メソッドはクラスに存在している。クラスに定義されているメソッドをインスタンスメソッドと呼ぶ。

String.instance_methods == "abc".methods # true
String.methods == "abc".methods          # false

Rubyではクラスもオブジェクトである。クラスにもクラスがある。クラスはClassクラスのインスタンスということ。

"hello".class # String
String.class  # Class

クラスにもオブジェクトと同じようにメソッドがある。オブジェクトのメソッドはクラスのインスタンスメソッドなのでクラスのメソッドはClassクラスのインスタンスメソッドでもある

inherited = false
Class.instance_methods(inherited) #[:superclass, :allocate, :new]

すべてのクラスはObjectクラスを継承している。ObjectクラスはBasicObjectクラスを継承している。ClassクラスはModuleを継承している。ModuleはObjectクラスを継承している。
クラスはModuleにnew(),allocate(),superclass()を追加しただけのもの。クラスとモジュールはほとんど同じもの。

大文字で始まる参照はクラス、モジュール含めてすべて定数になる。そのためnamespaceの役割でModuleを使用することができる。

module MyModule
  OuterConstant = 'hoge'
  class MyClass
    InnerConstant = 'fuga'
  end
end

MyModule::MyClass::InnerConstantでアクセスできる。

メソッド探索

メソッドを呼び出す時、Rubyはオブジェクトのクラスを探索してメソッドを見つける。レシーバとは呼び出すメソッドが属するオブジェクトのこと。メソッドを探すため、Rubyがレシーバのクラスに背理、メソッドを見つけるまで継承チェーンを上る。

モジュールをincludeする時Rubyは無名クラスを作ってモジュールをラップし継承チェーンに挿入する(インクルードするクラスの真上に入る)。

module M
  def my_method
    'M#hoge'
  end
end

class C
  include M
end

class D < C; end

実効結果

D.new.my_method # M#hoge

print等のどこからでも呼び出せるメソッドはKernelモジュールのプライベートインスタンスメソッド。ObjectクラスがKernelモジュールをインクルードしているのでKernelモジュールのメソッドはどこからでも呼び出せる。

Rubyのコードはカレントオブジェクトの内部で実行される。カレントオブジェクトはselfキーワードでアクセスでき、selfとも呼ぶ。selfの役割を担うオブジェクトは同時に複数存在しない。メソッドを呼び出すときはレシーバがselfになる。レシーバを指定しない場合はselfに対するメソッド呼び出しになる。
クラス、モジュールの定義の中(メソッドの外側)では、selfはクラスやモジュールになる。

class MyClass
  self    # MyClass
end

モジュールを二つincludeした場合、後のモジュールをincludeすると先のモジュールを継承チェーンの上に押し上げる。そのため先にincludeしたモジュールが後のモジュールのスーパークラスになる。

class MyClass
  include Hoge
  include Fuga
  
  ancestors   #[MyClass, Fuga,Hoge, Object, Kernel, BasicObject]
end