2016-06-28 6 views
4

私は最近、Ruby(2.2.1)に「面白い」動作がいくつかあることを発見しました。Rubyのオブジェクト#const_getは実際にどのように機能しますか?

module Foo 
    class Foo 
    end 
    class Bar 
    end 
end 

Foo.const_get('Foo') #=> Foo::Foo 
Foo.const_get('Bar') #=> Foo::Bar 
Foo.const_get('Foo::Foo') #=> Foo 
Foo.const_get('Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar 
Foo.const_get('Foo::Foo::Bar') #=> Foo::Bar 
Foo.const_get('Foo::Foo::Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar 
Foo.const_get('Foo::Foo::Foo::Foo::Bar') #=> Foo::Bar 
Foo.const_get('Foo::Foo::Foo') #=> Foo::Foo 
Foo.const_get('Foo::Foo::Foo::Foo') #=> Foo 
Foo.const_get('Foo::Foo::Foo::Foo::Foo') #=> Foo::Foo 
Foo.const_get('Foo::Foo::Foo::Foo::Foo::Foo') #=> Foo 

これは少し驚くべきことです。私の理解は、const_getが受信側の定数コレクションで定数を探してから、Objectの定数を調べるというものでした。いいよ。なぜ、第4のFoo#const_getは失敗し、第3のものは失敗しますか?

また、Foo#const_getを呼び出す理由が、あなたが追加した::Fooの数に応じて、モジュールとクラスの間で交互に表示されるのは興味があります。

+0

3番目の例を確認できますか( 'Foo.const_get( 'Foo :: Foo')')? 'Foo :: Foo'ではなく' Foo'だけを取得します。 – matt

+0

@matt正しいですが、 'Foo'を返すだけです。元の質問を編集しました。 – Huliax

答えて

5

The docs say

名前空間、クラス名が提供されている場合、このメソッドは再帰的に定数名を検索します。

従ってFoo.const_get('Foo::Bar')は、Foo.const_get('Foo').const_get('Bar')と基本的に同じです。この解釈を使用すると、結果は意味をなさないことになります。

あなたの第三の例:

Foo.const_get('Foo::Foo') 

Foo.const_get('Foo').const_get('Foo') 

最初const_getがトップレベルFoo(モジュール)内で定義された定数を見ると同じであり、ネストされたクラスを発見します。だから、全体のことが効果的に次のようになります。

Foo::Foo.const_get('Foo') 

2回目の呼び出しは、クラスを見て、最初のいずれかを探して(何を見つけていない)、その後、その先祖に見て定数を含んでいました。 Objectは祖先であり、最上位レベルはFooです。したがって、これが見つかって返されます。

これはまた、追加の::Fooを追加するときの代替方法についても説明します。交互は、ネストされたクラスを見つける最上位モジュールのconst_getと、継承チェーンをルックアップし、最上位モジュールを見つけるネストされたクラスの間です。

例外を発生させる第4の例であるFoo.const_get('Foo::Bar')も説明できます。それは

Foo.const_get('Foo').const_get('Bar') 

最初の部分に相当し、Foo.const_get('Foo')Foo::Fooに評価し、上記の場合と同じであるので、全部が今効果的に次のようになります。

Foo::Foo.const_get('Bar') 

今、ネストされたFooクラスは」doesnのBar定数が含まれており、継承チェーンを参照すると、どちらもObjectでないため、結果はNameErrorになります。

関連する問題