Effective Ruby勉強メモです。
Rubyで大きなアプリケーションなどを作る場合には、どうしても既存クラスと同じ名前のクラスを定義したいときがあります。
以下のように書くとクラスを新しく定義しているかのように見えますが、実際には既存のArrayクラスを再オープンしてメソッドを追加してるだけにすぎません。
Rubyではクラスはミュータブル(可変)なのです。
class Array def print_first p first end end
これでは思わぬ副作用をもたらしてしまいます。最悪、メソッドを完全に上書きしてしまいシステム全体に不具合が生じてしまいます。
そこで、新しくモジュールを定義し、その中でクラス定義をすることで既存のクラスと同じ名前の異なるクラスを定義することができます。これは名前空間と言われるテクニックです。
module Demo class Array def print_first p "first" end end end a = Demo::Array.new a.print_first #=> "first"
呼び出す際はDemo::Array
のようにモジュールを明示的に書くのが望ましいです。
呼び出すときにRubyがどのように定数を探すのかは後述します。
スポンサーリンク
モジュールを名前空間として使う
モジュールは任意の階層作ることができるので、いくらでも深くすることができます。
module A module B module C class D end end end end
このとき、クラスDはA::B::C::D
というように呼び出します。
また、一般的にはモジュールの階層とディレクトリの階層を同じにするのが普通です。
Demo::Array
の例では、demo/array.rb
とし、A::B::C::D
では、a/b/c/d.rb
とします。
すでにモジュールが定義されている場合であれば、
class Demo::String end
のように直接階層を書くこともできます。
これによって、無駄なインデントを省くことができます。
名前空間はクラスやモジュールだけのものではなく、定数に対しても名前空間に属することができます。(というか、クラスも定数なので、正確には名前空間は定数を一意に識別するために使います)
Railsを触ったことがある方なた、自分で作ったクラス内で定義した定数を呼び出すときは以下のように書くのをご存知かと思います。
class User KEY = "users_key" end User::KEY
グローバル名前空間
では、何も前につかないクラスなどはどこに存在しているのでしょうか。
実はトップレベル(どの階層の下にも含まれない)のクラスやモジュールは、Objectクラスに含まれています。
すなわち、Arrayクラスは何もつけなくても使えるクラスですが、Objectクラスに含まれているため、Object::Array
としても同様のクラスが呼び出されます。
このように修飾(Demo::
のようなもの)を付けなくても使えるクラスはグローバル名前空間に属すると言われます。
グローバル名前空間に属するクラスは普通そのまま使いますが、何かしらモジュールの内部で呼び出す場合やあいまいになる場合は、Object::Array
や::Array
と呼び出します。一般的には後者を用います。
定数の見つけ方
では、定数(ここではクラスやクラス内で定義された定数両方を含みます)を呼び出したときに、Rubyがどのように定数を探し出すかを見ていきます。
メソッド探索では継承階層を遡っていくだけですが、定数を探す場合にはその前に探す場所があります。
それはレキシカルスコープと言われているものです。
- レキシカルスコープ
文章だけで説明するのは難しいのですが、モジュールやクラスが定義されているスコープのことです。
例えば、以下は一つのレキシカルスコープです。
module User KEY = "users_key" end
次のモジュールは定義されてから、再オープンされて別の定数を定義していますが、それらは別々のレキシカルスコープに属しています
module User KEY = "users_key" end module User PASS = "users_password" end
KEYとPASSは同じモジュールに定義されていますが、別々のレキシカルスコープに属しています。
話を戻しますが、Rubyはまず現在の(呼び出した場所の)レキシカルスコープとその中に含まれるすべてのレキシカルスコープの中から探索を行います。
もしその中で見つからなければ継承階層の中から定数を探します。そのため、親クラスの定数が子クラスから参照可能となるのです。
module User KEY = "users_key" class Car def print_key p KEY end end end
この例では、KEY定数が呼び出された場所はCarクラスであり、KEY定数が定義された場所と同じレキシカルスコープに入っています。
なのでCarクラスからKEY定数は呼び出せます。
以下の例はどうでしょうか
module User class Car KEY = "car_key" end class House def car_key p KEY end end end
この例では、KEYが定義されたのはCarクラスが定義されているレキシカルスコープですが、呼び出されているのはスコープが閉じて、別のレキシカルスコープの内部からです。
したがってRubyは見つけ出せずNameErrorの例外を出します。end
が来るとその内部で定義した定数のことを忘れてしまうと覚えてしまってもいいかもですね。
もしKEY定数を参照したい場合は、
class House def car_key p User::Car::KEY end end
のように修飾を付けることで可能になります。
- 作者: Peter J. Jones,arton,長尾高弘
- 出版社/メーカー: 翔泳社
- 発売日: 2015/01/09
- メディア: 大型本
- この商品を含むブログ (13件) を見る
Ruby on Rails 5アプリケーションプログラミング
- 作者: 山田祥寛
- 出版社/メーカー: 技術評論社
- 発売日: 2017/04/14
- メディア: 大型本
- この商品を含むブログを見る
プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plusシリーズ)
- 作者: 伊藤淳一
- 出版社/メーカー: 技術評論社
- 発売日: 2017/11/25
- メディア: 大型本
- この商品を含むブログを見る