オープンクラス
Rubyでは標準で用意されているclassでも、その場で再オープンして、メソッドを追加することができる。
->うまく使ったgemの例が2.1.2にある。
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 クラスである。
"hello".class # => String
String.class # => Class
つまり、StringやArrayといったクラスはClassクラスのインスタンスであるということができる。よってClassクラスに定義してあるメソッドはStringやArrayのインスタンスメソッドであるということができる。
Rubyにおける変数と定数の違い
rubyでは警告が出るものの、定数を置き換えるという事ができてしまう。定数が置き換えれてしまうなら、定数と変数の違いってないにも等しいのではないのだろうか。
変数と定数の大きな違いはスコープ。定数と変数のスコープのルールは異なっている。
定数のスコープのルールはファイルシステムと同じようにツリー状に配置されている。モジュールやクラスがディレクトリで、定数がfileといった感じ。異なるディレクトリだったら、同じ名前のfileを複数持つ事が可能である。
また、定数はfileやディレクトリのようにネストする事が可能。
module M
class C
X = 'ある定数'
end
C::X # => "ある定数"
end
M::C::X # => "ある定数"
また、ツリーの奥の方にいる場合は、
Y = "ルートレベルの定数"
module M
Y = 'Mにある定数'
Y # => "Mにある定数"
::Y # => "ルートレベルの定数"
end
ネームスペースを使う
自分で新しくclassを定義したときに使っているライブラリのclass名と被っていることにきずかずに拡張してしまったり、module名と被っていることにきずかずにエラーが吐かれてしまう場合がある。このような場合ネームスペースを使えば解決をすることができる。
例えば、自分の書いたclass TextがAction Mailerのmoduleと被ってしまった場合
module Myclass
class Text
とすることで、Textクラスへの参照をMyclass::Textと書き直すことで対象することができる。
メソッドを呼び出すときにおきていること
メソッド探索
メソッドを呼び出したとき、まずはそのレシーバのクラスにメソッドを探しにいく。その後、見つからなければそのsuper classへ見に行く。途中でmoduleをincludeしていた場合はincludeしているclassとsuper classの間にmoduleを差し込んで、メソッドを見に行く。 同じmoduleを多重includeしていた場合には先にincludeされた方が有効になり、後からincludeされた方は無効化される。
self
メソッドを呼び出すときはそのメソッドを呼び出すレシーバがselfになる。他のオブジェクトを明示してメソッドを呼び出したときはそのオブジェクトがレシーバになる。
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システムのようなツリー状で配置されている。