LoginSignup
3
2

More than 5 years have passed since last update.

Ruby Objectモデル(メタプログラミングruby 2章まとめ)

Last updated at Posted at 2018-05-04

オープンクラス

Rubyでは標準で用意されているclassでも、その場で再オープンして、メソッドを追加することができる。
->うまく使ったgemの例が2.1.2にある。

sample.rb
class String
  def to_alphanumeric(s)
    s.gsub(/[^\w\s]/, '')
  end
end

今回はString型にto_alphanumericというメソッドがなかったため問題なくいったが、もし、to_alphanumericが既存のメソッド名と被ってしまっていたりしたらコンフリクトを起こして、他のコードにも影響を与えてしまうという問題もある。

また、Rubyでは言語要素として、classを定義するだけでは実際にclassが存在していることにはならない。Rubyでは定義したclassにRubyが入った時に初めて、classが定義される。

インスタンス変数

Rubyのオブジェクトのクラスとインスタンス変数にはなんの繋がりもない。インスタンス変数は値が代入された時に初めて出現する。そのため、同じクラスのインスタンスを生成しても、インスタンス変数を呼び出していなければ、そのインスタンス変数は存在しないし、呼び出していれば、インスタンス変数が存在する。また、同じクラスのインスタンス変数であっても、値が異なることもある。
class|{@v=2}{@v=1}{}{@v=3}|
{}一つ一つはインスタンス
図にするとこんなイメージ。

メソッド

classの中にメソッドを定義しているが、実際にそのメソッドを持っているのはそのclassから作られたインスタンスであり、class自体がメソッドを持っている訳ではない。classの中に定義されているメソッドはそのインスタンスが参照できるメソッドということで、インスタンスメソッドと呼ぶ。

classもオブジェクト

Rubyではクラスもオブジェクトである。なので、オブジェクトに当てはまるものはクラスにも当てはまる。クラスもオブジェクトなので、当然クラスがあって、それはClass クラスである。

sample.rb
"hello".class # => String
String.class # => Class 

つまり、StringやArrayといったクラスはClassクラスのインスタンスであるということができる。よってClassクラスに定義してあるメソッドはStringやArrayのインスタンスメソッドであるということができる。

Rubyにおける変数と定数の違い

rubyでは警告が出るものの、定数を置き換えるという事ができてしまう。定数が置き換えれてしまうなら、定数と変数の違いってないにも等しいのではないのだろうか。

変数と定数の大きな違いはスコープ。定数と変数のスコープのルールは異なっている。

定数のスコープのルールはファイルシステムと同じようにツリー状に配置されている。モジュールやクラスがディレクトリで、定数がfileといった感じ。異なるディレクトリだったら、同じ名前のfileを複数持つ事が可能である。

また、定数はfileやディレクトリのようにネストする事が可能。

sample.rb
module M
  class C
    X = 'ある定数'
  end

  C::X # => "ある定数"
end

M::C::X # => "ある定数"

また、ツリーの奥の方にいる場合は、

sample.rb
Y = "ルートレベルの定数"
module M
  Y = 'Mにある定数'
  Y # => "Mにある定数"
  ::Y # => "ルートレベルの定数" 
end

ネームスペースを使う

自分で新しくclassを定義したときに使っているライブラリのclass名と被っていることにきずかずに拡張してしまったり、module名と被っていることにきずかずにエラーが吐かれてしまう場合がある。このような場合ネームスペースを使えば解決をすることができる。
例えば、自分の書いたclass TextがAction Mailerのmoduleと被ってしまった場合

sample.rb
module Myclass
  class Text

とすることで、Textクラスへの参照をMyclass::Textと書き直すことで対象することができる。

メソッドを呼び出すときにおきていること

メソッド探索

メソッドを呼び出したとき、まずはそのレシーバのクラスにメソッドを探しにいく。その後、見つからなければそのsuper classへ見に行く。途中でmoduleをincludeしていた場合はincludeしているclassとsuper classの間にmoduleを差し込んで、メソッドを見に行く。 同じmoduleを多重includeしていた場合には先にincludeされた方が有効になり、後からincludeされた方は無効化される。

self

メソッドを呼び出すときはそのメソッドを呼び出すレシーバがselfになる。他のオブジェクトを明示してメソッドを呼び出したときはそのオブジェクトがレシーバになる。

sample.rb
class Myclass
  def testing_self
    @var = 10
    my_method #self.my_methodと同じ
    self 
  end

  def my_method
    @var = @var + 1
  end
end
obj = Myclass.new
obj.testing_self # => <Myclass:~~~ @var = 11>

private

privateメソッドはレシーバを明示的に書くことができないので、selfをレシーバにしないと呼び出すことができない。

classの意図しない書き換えを防ぐために

String classを拡張したいけど、変更が全てに及ぶと予期せぬエラーがおきそうで怖い場合、変更が及ぶ範囲を限定することができる。それがrefineとusing
ただ、refineはまだ、仕様が変わりうるメソッドなので、気をつけて使わなくてはならない。

まとめ

・オブジェクトは複数のインスタンス変数とclassへのリンクでできている
・オブジェクトのメソッドはオブジェクトのclassにある。クラス側から見れば、インスタンスメソッドと呼ばれる。
・定数はfileシステムのようなツリー状で配置されている。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2