Rubyの定数が怖いなんて言わせない


Ruby Advent Calendar 2018の 15 日目です！

# 定数よ。お前はなぜそんなに難しいのか

使いやすいRubyのメソッドやクラスインスタンス変数に比べて、定数は難しいですね。

私自身、半年に一回は泣かされている弱小エンジニアのひとりです。

せっかくのアドベントの機会を借りて、このハマりがちなRuby定数の謎を徹底的に読み解いてみたいと思います[^1][^2]

対象としてはこんな方を想定しています。

* Rubyにおけるselfの扱い、継承チェーン、メソッド探索は理解している

* 定数の挙動でハマったことがある

* この機会にマニアックな理解をしたい

参考文献としては[CRubyのソースコード](https://github.com/ruby/ruby)に加えて、最後に列挙した[数々の貴重な資料](https://qiita.com/fursich/items/a1b742795cf10eebc73f#%E5%8F%82%E8%80%83%E6%96%87%E7%8C%AE)を頼らせていただきました（ぺこり）。


[^1]: なぜRubyがこんなことをしているのか（WHY)については、私の経験・力量の及ぶところではありませんが、どんな挙動になっているのか」（HOW）という表面的なふるまいだけでも、可能な限り掘り下げたいと思っています。

[^2]: なお、以下ではrefinementsやautoloadについては触れませんが、Rubyのしくみを読むと大体わかると思います。


## 定数クイズ

さっそくですが、定数に関する簡単な問題です。

Rubyistなら全部答えられます・・よね？

まず、こんな形であちこちにMyConstという定数を定義します。

```ruby
MyConst = 'TOPLEVEL'

class GrandParent
  # 定義しない
end

class Parent < GrandParent
  MyConst = 'Parent'
end

module NameSpace
  MyConst = 'NameSpace'
end

module MonkeyPatch
  MyConst = 'MonkeyPatch'
end
```

下のようなクラスChildを考えたとき・・

```ruby
module NameSpace
  class Child < Parent
    prepend MonkeyPatch

    def put_myconst_at_base
      puts ::MyConst
    end
  
    def put_myconst
      puts MyConst
    end
    
    def put_myconst_with_child
      puts ::NameSpace::Child::MyConst
    end

    def put_anything(arg)
      puts arg
    end
    
    def put_const_with_grandparent
	    puts GrandParent::MyConst
    end
  end
end
```

さて､次のメソッドはそれぞれ何を返すでしょう？

※ Module#prependが導入されたRuby 2.0、現時点最新のRuby 2.5.3の両方で確認しています
  
```ruby
# 問題1
NameSpace::Child.new.put_myconst_at_base

# 問題2
NameSpace::Child.new.put_myconst

# 問題3
NameSpace::Child.new.put_myconst_with_child

# 問題4
NameSpace::Child.new.put_anything(MyConst)

# 問題5
NameSpace::Child.class_eval { puts MyConst }
```

これはちょっとトリッキーなおまけです｡ (Ruby2.4以前、2.5以降で挙動が変わります)

```ruby
# 問題6
NameSpace::Child.new.put_const_with_grandparent
```

先に答えを知りたい方は[こちら](https://qiita.com/fursich/items/a1b742795cf10eebc73f#%E3%82%AF%E3%82%A4%E3%82%BA%E3%81%AE%E7%AD%94%E3%81%88)へ！


## さあ迷宮へ

というわけで、楽しい定数の旅です。

ここからの話は混乱要素がたくさんあるので､まずいくつかの前提を説明するところから入りたいと思います。(以下ではRuby2.5以降を対象とします)

何はともあれRTFMということで､[Ruby リファレンスマニュアル](https://docs.ruby-lang.org/ja/2.5.0/doc/spec=2fvariables.html#const)を読むところから始めましょう。

## ディレクトリ構造に似ている定数参照

るりまを読むと、

> あるクラスまたはモジュールで定義された定数を外部から参照する ためには::演算子を用います。またObjectクラスで 定義されている定数(トップレベルの定数と言う)を確実に参照する ためには左辺無しの::演算子が使えます。

ということが書かれています。

定数は::で区切られるということは､もし定数の参照をディレクトリ構造に例えるなら、 `::` は `/` として類推できそうです。

そして `::` からはじまるものはルートディレクトリ、つまりObjectクラスに属すると考えれば感覚的にわかりますね｡

・・となると、 `::Foo::Bar::Baz` という定数参照は、パスで言うところの「絶対参照」のようなイメージでしょうか。

以下のケースについて考えてみましょう。

```ruby
# クイズの設定を流用します｡
module NameSpace
  class LittleBrother < Parent
  end
end

puts ::NameSpace::LittleBrother::MyConst
```

このとき、 `::NameSpace::LittleBrother::MyConst` は何を指すでしょうか？

もちろんLittleBrotherにそんな定数はないので、もしこれが僕たちの想像する「絶対参照」ならエラーになりそうです｡

```ruby
puts ::NameSpace::LittleBrother::MyConst
=> Parent
```

なんでや・・なんでなんや・・ \:open_mouth:

## 定数参照の基本

定数参照の解決アルゴリズムは、パス構造のそれとは異なります。

/name_space/little_brother/my_const.rbのようなディレクトリ構造の場合、 `/` で区切られた前後のディレクトリが直接的に結びついているという前提がありました。

結果として複数の `/` を挟んで隣り合う要素同士も包含関係を持ちます。つまり最終的に探索されたファイルは、探索開始地点の/name_space/ディレクトリに包含されていると期待できます。

一方Rubyは::NameSpace::LittleBrother::MyConstという定数を、それぞれにパーツに分離して計3回の探索をします。
発見されたオブジェクトは､次の探索の開始点として渡されますが、探索はあらためて行われます。

探索により最終的に見つかった定数名は、::Parent::MyConstであり、最初の探索開始地点である::NameSpaceと直接の関係を持ちません。（混乱ポイント）

<img width="676" alt="スクリーンショット 2018-12-15 23.33.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/151343/4b3bc3a4-5526-5cea-7ac1-526ced1810f3.png">


実は「絶対参照」になるのは**最初の定数だけ**であり、全体が絶対参照になるわけではありません。[^30]

後半の定数は上位の定数の探索結果をベースに、あらためて（絶対参照ではない別の手段で）探索されます。
このとき、上位モジュール内だけではなく、その継承元についても探索対象に含まれるのです。

<img width="686" alt="スクリーンショット 2018-12-15 23.58.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/151343/fa7b8c59-8a0d-a78f-49d5-9d0934c95cb0.png">



なおこれは絶対参照だけの話ではなく、例えばNameSpace::LittleBrother::MyConstという::から始まらない形についても同じことが言えます。この場合、NameSpaceが見つかったからと言って、下位のLittleBrother, MyConstの位置が自動的に決まるわけではなく、あらためて探索が行われるのです。

[^30]: 絶対参照という言葉はいろいろ誤解をはらむので、「暗黙のObject修飾つき参照」とでも呼びたいところです。が、ちょっと長いのと他に適当な言葉が思いつかないので、この記事では引き続き「絶対参照」と呼ぶことにします。

さて、ここでちらっと説明した「::のつき方で参照方法が変わる」というのがどういうことなのか、少し掘り下げてみます。

## ダブルコロンが挙動を変える

RubyのVMコードであるIseqを観察してみると､定数の探索は `getconstant` という命令が担当しています｡

```ruby
code = <<~RUBY
  module M
    puts ::Foo
    puts Foo
  end
RUBY

puts RubyVM::InstructionSequence.compile(code).disasm # VMコードを確認する

# （以下、大幅に省略しています）

# ::Fooの取得
0005 putobject        Object  # Objectクラスをスタックに積む
0007 getconstant      :Foo    # これを手がかりに定数名:Fooの対象を取得する

# Fooの取得
0015 putself                  # selfをスタックに積む
0019 getconstant      :Foo    # これを手がかりに定数名:Fooの対象を取得する
```

定数表記のやり方を変えるとgetconstant命令に引数(スタック)を与える直前のコードが､微妙に変化することがわかります｡

CRubyにおける [compile.cの実装](https://github.com/ruby/ruby/blob/c4b66522238e9e1e164fca2b9497a80acbfc397e/compile.c#L4026) を見ると、定数名を区切る::の使い方によって、３通りの微妙に異なるコンパイルがされるようです[^20]｡

[^20]: ささだ大先生のYARV maniacsに[謎解きの解説記事](https://magazine.rubyist.net/articles/0010/0010-YarvManiacs.html#%E5%AE%9A%E6%95%B0%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E5%91%BD%E4%BB%A4)がありました。やはり３種類あるようです。

深入りする前に､この3種類の参照方法に対して、この記事で使うための仮の名前をつけておきます[^55]｡
[^55]: （Ruby公式の呼び名がわからないのですが、Rails GuideやC++の類推からつけました）

#### ::Foo 

* 何もない::の後に置かれた定数名を、**絶対参照** と呼ぶ。
* Objectクラスを出発点として探索が行われる
* ::の前にObjectクラスが自動で補完される以外は、下の修飾つき参照とだいたい同じ。

#### Foo

* 頭に何もつかないハダカの定数名を、 **相対参照** と呼ぶ。
* 現在のクラス（厳密に言うともう少し補足がある）を暗黙の出発点として、定数を探索する。
* この形においては唯一レキシカルコンテキストが関係する

#### Bar::Foo

*  前半に置かれた定数名Bar::によって修飾されたFooを、**修飾つき参照** と呼ぶ｡
*  前半のBarの部分は、Fooとは独立に相対参照として解決される

具体的にどういうふうに探索方法が変わるかについては次のチャプターで解説します。

ここで重要なことは、2点あります。

* 定数探索の方法に **３つの異なるパターン** があること
* どの参照方法をとるかは､ **個々の定数名と::の位置関係だけ** で決まる

定数参照の仕組みについてはもう少し先で掘り下げます。

少しだけ我慢して、まずは参照と比べてシンプルな定数定義を考えたいと思います。

## 定数が定義されるまで

### 定数はモジュールかクラスに属する

大前提として、クラスのインスタンスレベルでは定数を持つことはできません。

つまりどんな定数Fooも所属するクラス（モジュール）を用いて、 `[クラス（モジュール）参照]::Foo` という形に書き下せます[^38]。

逆にもし、 `Thing::Something` という定数参照があるとすれば、前半の `Thing`は、 **必ずクラスかモジュール** でなければいけない、ということにもなります（そうでなければSomethingが定義されるべきクラスがない）

[^38]: `p Class.new.tap{|c| c.const_set :Foo, 'unreachable'}` みたいにして「参照できない使い捨て定数」もつくれるけど、いいですよねw

もちろん定数の所属するクラス・モジュールは、特異クラスでも、BasicObjectのような基底クラスでもかまいません。

### 定数のsetter

定数を定義する方法は色々あります。
`Module#const_set`を使うか、`[大文字から始まる定数名]=`を使う、もしくは`class`/`module`キーワードを使う、という感じでしょうか。

（`class/module`キーワードには、定数定義に加えて「クラス（モジュール）オブジェクトをつくる」という特別な機能があります）

定数定義をする処理に関してだけ言うと、おおむねどれも似ています。

簡単のために、ここでは = を使って定数がどのように定義されるのかを見てみましょう。

```ruby
module M
  Foo = 123
end
```

### 定数定義はどう行われるのか

実はRubyの実行コンテキストには、self以外にもクラスを指し示すcrefというものがついています。

クラス参照というと、 `self.class` と同じもののように聞こえますが、必ずしも一致するとは限らない[^105]ので、RubyVMはこれらを独立に保持しています。

[^105]: eval系のメソッドでは違いが出る。たとえば[メタプログラミングRuby](http://amzn.asia/d/8Wm94gR)にある「5.1.2 カレントクラス」のところで詳しく説明されています。

コード内にclass/moduleキーワードを置いたり、class_evalやinstance_evalといったメソッドのブロックに入る際に、このcrefが更新されてVMのスタックに積まれていきます。

![スクリーンショット 2018-12-17 12.39.27.png](https://qiita-image-store.s3.amazonaws.com/0/151343/31621b6a-a8c8-b855-56ff-bc5eaac9643c.png)

「この場所でdefキーワードを書いたら、どのクラス（モジュール）のメソッドになるのか」ということは、このcrefが示しています。

同じように、「ここで定数を定義したとしたらどのクラス（モジュール）の定数になるのか」ということも、crefをたどることで決定することができます[^50]。

[^50]: メソッドを定義する基準としてのcrefから、[eval系でスタックに積まれたcrefを除外](https://github.com/ruby/ruby/blob/c4b66522238e9e1e164fca2b9497a80acbfc397e/vm_insnhelper.c#L821-L823)しています。つまり、メソッド定義先はclass_evalで変更できるが、定数の定義先は変わりません。定数探索のベースとなるcrefは、Module.nestingで確認することができます（完全に一致する）。

もちろん自分のクラスの外側にあるcrefも、スタックをたどることでたどれるようになっています。

### crefを使った定数定義

まとめると、定数定義はこうなっています。

プログラムの実行中に :MyConst = 'Here I am!' というコードがあったとき、その地点でのcrefが示すクラスにおいて、`MyConst`が探索されます。

そのクラスに同名の定数が存在していた場合は警告つきで内容を更新します。

そうでなければ新しいエントリーが定義されて文字列オブジェクト "Here I am!" が登録されます。

もしトップレベルと、現在のコードの間にclass/moduleキーワードがなければ、定数はトップレベル定義になります。

この辺りまで理解できれば、問題４、５が理解できますね。

### ネストされた定数定義

ここはRailsなどでよくハマるところなのですが、ネストされた定数定義についても軽く見ておきます。

```ruby
Parent::Child::Count = 5
```

こういうコードがあった時、実際は前半の`Parent::Child`までの部分はただのgetterです。

こう書くと、実際の実行環境のcrefが指すクラスに定数を定義するかわりに、`Parent::Child`で取得されたクラスかモジュールに対して、 `Count = 5` という定数定義が行われます。

Parent::Childという定数名がどういう風に探索されるかということは後でやりますが、基本的に定数定義をしているのは最後の項だけ。
最初の２つの定数名は、Countを修飾しているだけです。シンプルですね。

ネストされた定数を見るときは、1カタマリで見るのではなく、個々がバラバラに動くことを頭に入れておくとわかりやすいかもしれませんね。

### まとめ

* RubyVMはselfとcrefというクラス参照を持っている
* 頭に何もつかない定数をセットした場合、その地点でのcrefがしめすクラスに対して定数が登録される
* 修飾済み定数を定義した場合、まず修飾部分の定数が示すクラスが取得され、そのクラスに対して定数が定義される

## 定数参照の謎を解き明かす

定数参照は、Module#constantsやModule#const_get、class/moduleキーワードを使うことでも発生します。ここではシンプルにFoo::Bar::Bazのような定数名による直接的な参照を考えます。

### ３つの参照方法

先ほど３パターンの参照があるということを書きました。

絶対参照(::Foo)、相対参照（Foo）、修飾つき参照（Foo::BarにおけるBar）になります。

どの形で定数を参照しても、Ruby内部では最終的に[１つのC関数](https://github.com/ruby/ruby/blob/c4b66522238e9e1e164fca2b9497a80acbfc397e/vm_insnhelper.c#L805)が呼び出されます。

大きな挙動の違いは２つあります。
* 出発点：　　探索をスタートする場所が違います
* 探索方法：　レキシカルスコープを見るかどうかが異なります

厳密に言うと、トップレベルを見るかとか、privateな定数を対象にするか、など細かい違いもあります。

### 相対参照

まず、普段よく使う相対参照(Foo）を見ていきましょう。

スタート地点となるcrefを、 **cbase** と呼ぶことにします。このcbaseは、ほぼ「現地点におけるcref」と同じですが、eval系のメソッドによってスタックに積まれたcrefを飛ばす、という違いがあります。

つまり、cbaseは「一番直近にあったclass/moduleキーワード」によって示されるクラス（モジュール）を指し、これが参照において探索開始点になります。

![スクリーンショット 2018-12-17 12.39.27.png](https://qiita-image-store.s3.amazonaws.com/0/151343/63dfd9ed-66e3-ca1b-143c-9f3abbeabb92.png)

この構造においては、eval系のメソッドがないため、各地点でのcbaseはそのままcrefに一致します。

#### １） レキシカルな探索

まず最初の探索は、cbaseクラスから出発して、トップレベルの直前まで[^900]crefをたどり、それぞれのクラスに定数が定義されているかを見にいきます。
[^900]: トップレベル直前のcrefクラスで探索を終わり、トップレベルは対象になりません。もしトップレベルで相対参照の探索を始めた場合、探索１）は行わずに探索２）に入ります。


たどる順番は **class/moduleキーワードの位置関係のみ** により、レキシカルに決まるため、defの内側・外側だとか、class_evalの中だったとしても影響しません。

外側にあるcrefは、自分自身とは無関係なクラスやモジュールであったとしても、関係なく探索されます。

```ruby
class C
  Foo = 'Foo'
  module ::M  # トップレベルに定義する（Cとは無関係）
    puts Foo  # ネストの外側にあるFooを探索する
  end
end
=> Foo
```

この場合は、探索開始点cbaseが::Mにセットされ、次にCが探索されます。（ここでFooの定義が見つかる）

なお、対象の場所でModule.nestingを叩くと、レキシカルな検索の対象となるクラスの一覧が出てきます。

```ruby
class C
  Foo = 'Foo'
  module ::M
    Module.nesting
  end
end
=> [M, C]  # [C::M, C]ではなく、::Mと::Cがスタックに積まれている
```

この探索は継承関係に全く影響されません。

またこれはちょっとクセがありますが、cbaseクラスや外側のクラスに対して、prependしたりincludeして定数を加えたとしても、このアプローチでは探索対象になりません[^200]。

![スクリーンショット 2018-12-16 0.05.34.png](https://qiita-image-store.s3.amazonaws.com/0/151343/35f34c28-e4bc-c017-3fe6-08579ddb28d0.png)

この辺が理解できると、問題２が納得いくのではないでしょうか。（深い・・）


[^200]: prependの実装は、自分の化身クラスをつくり、prependされたモジュールをサンドイッチのように挟み込んでいる。つまり、元のcbaseクラスをCとすると、化身クラスI(C) < prependモジュール M < originクラス C という状態になっている。[I(C)のメソッドテーブルは空にされており、全てのメソッドエントリが上位（origin）クラスCにある](https://github.com/ruby/ruby/blob/c4b66522238e9e1e164fca2b9497a80acbfc397e/class.c#L987-L988)ため、メソッド探索ではprependモジュールのメソッドが優先される。一方、定数テーブルは手前にあるI(C)に残されている。そのためレキシカルな検索においては自分自身の定数だけが参照され、祖先方向にたどった場合にはじめてprependされたモジュールの定数が探索されるらしい。


#### ２） 継承チェーンの探索

探索１が失敗に終わると、Rubyは次に動的なアプローチで探索を始めます。

あらためて最初のcbaseから探索を再開し、今度は継承チェーンを祖先方向にたどります。

こちらはメソッド探索に似ていて理解しやすいですね。

cbaseクラスに対してprependしたりincludeしたモジュール内に、探している定数があった場合はここではじめて検知されます。

なおcbaseがモジュールだった場合、一般的にモジュールは祖先を持たないため、継承探索はすぐに終わります。

ただし、 **相対参照のケースに限り**、トップレベル（Object）とその祖先を探索対象に加えてくれます。

![スクリーンショット 2018-12-16 0.11.49.png](https://qiita-image-store.s3.amazonaws.com/0/151343/f816abfb-0a8e-4cb1-5ec9-746ab27e6d13.png)

#### まとめ

* 相対参照は、２つの探索方法（方向）を持っています。

* 継承してもレキシカルスコープ上の定数のせいで、探索されないケースがあるので混乱しやすい

* ３つの中でレキシカルスコープの影響を受けるのは相対参照だけです[^110]。

[^110]: ソースコードを見ると、前半に[レキシカルスコープをたどる](https://github.com/ruby/ruby/blob/c4b66522238e9e1e164fca2b9497a80acbfc397e/vm_insnhelper.c#L811)、後半に[自分の継承チェーンをたどる](https://github.com/ruby/ruby/blob/c4b66522238e9e1e164fca2b9497a80acbfc397e/vm_insnhelper.c#L856)という処理があるのがわかります。

### 修飾つき参照

ここまで来られれば簡単・・（簡単とは）

次に、 XXX::Fooという形式の修飾つき参照です。

だいたいこのような形で行われます。

* 直前にくっついている定数をcbaseにとる
* 「継承チェーンのみ」をたどる
* トップレベル（Objectクラス）とその祖先は探索しない[^300] 
* privateな定数は探索対象にしない（ヒットしたらエラーをはく）

[^300]: 厳密に言えば、Object自身で修飾した場合、つまりObject::Fooというように、cbaseをObjectにおいた時だけは祖先を探索する

修飾つき参照ということは、**直前にクラスかモジュールを指す定数があるはず**なので、そのクラス（モジュール）を探索開始点cbaseにとります。

またこれは、メソッドで言うところの「クラス外からの参照」を意味するためか、private定数は参照できません。
（どうしてもやりたければ、XXX.const_get(:PrivateConst)ということが可能）

なお、修飾つきの定数で継承が出てくると関係性が遠くなって思わぬ定数にヒットをすることがあります。

問題２と問題３の違いに、探索手法の違いによるややこしさが反映されています。伝わるでしょうか・・？ \:innocent:

### 絶対参照

最後に絶対参照、一番シンプルです。

再度強調しておくと、絶対参照という名前は仮につけたものなので[^30]少しイメージとは異なります。
（「絶対」だからと言っても、Objectに属する定数に限定されるわけではなく、継承チェーンも探索される）

::の後にくるという形式も含めて、だいたい修飾つき参照と同じような形ですが、少し違いがあります。

* 探索開始点cbaseがObjectクラスにセットされる
* 修飾つき参照と異なり、Object（トップレベル）とその祖先を探索する
* それ以外は、修飾つき参照とほぼ同じ（private定数にヒットしたらエラー）

ここまでくると、::Somethingと、Object::Somethingがどう違うのか？ということが気になりませんか？
実は下のコーナーケース程度の微差があります。

```ruby
MySecretNumber = 12345
Object = Module.new # Objectという定数名を上書きする
```

こういうイケナイ遊びをしてみたらどうなるでしょう。


```ruby
puts Object::MySecretNumber # 相対参照
NameError: uninitialized constant Object::MySecretNumber # 取り出せない！！
```

Object::MySecretNumberと言う形式だと、まず前半のObjectという相対参照を解決しに行きます。
Objectという名前で示される中身を空のモジュールに差し替えているため、後半はそのモジュールをcbaseに使った修飾つき参照になってしまい、元の値が取り出せなくなります。

一方絶対参照だと・・

```ruby
puts ::MySecretNumber # 絶対参照
12345 # 取り出せる！
```

絶対参照は、定数名のマッピングとは関係なく、常に**実体としてのObjectクラス**をcbaseとして定数探索をしてくれます。

絶対参照があることのメリットが少しだけわかりますね。
いや、こんなことやらないけどw

## Ruby2.5の進化

ここまで読み切ったあなたは定数を**完全に理解した**状態になっているのではと思います。お疲れ様でした！

ここで述べたことはおおむねRuby2.0以降で同じことが言えます。

ただ１点だけ、Ruby2.5から導入された仕様があります。

最後に、ハマりがちなこの挙動について理解して終わりたいと思います。

### 禁止された探索

修飾つき参照のところで、 **トップレベル（Objectクラス）とその祖先は探索しない** ということを書きました。

（厳密に言うと、Objectによる修飾つき参照の場合を除き、探索しない[^300] ）

以前は警告を出しつつも、探索してくれていました。

なぜこれが問題だったのかを[Ruby公式のチケット](https://bugs.ruby-lang.org/issues/11547)から読み解いてみましょう。

```ruby
# すでに２つのクラスがロードされているとする
class Auth
end

class Twitter
end
```

Ruby 2.4以前だとこうなります。

```ruby
Twitter::Auth # 名前空間つきの別のクラスのつもりで参照
(irb):8: warning: toplevel constant Auth referenced by Twitter::Auth
=> Auth # 最初に定義したAuthクラスが帰る
```

Twitter::Authの部分を、先ほどの話を踏まえて考えてみます。

まず先頭のTwitterの部分は相対参照です。
 
コードはトップレベルに書かれているため、探索開始点となるcbaseはトップレベルを指します。

つまりトップレベルに定義されたTwitterクラスを返します（ここまではどのバージョンも同じ）

さて、次にRubyは後半の::Authの部分を、cbaseをTwitterクラスにセットした上で探索します。

修飾付き参照なので先祖へと探索に行くはず、ですね。

Twitterクラスの先祖には、様々な基底クラスが含まれています。

```ruby
> Twitter.ancestors
=> [Twitter, Object, Kernel, BasicObject]
```

ここで問題となるのはObjectです。
Objectは**全てのクラスの祖先でもあり、トップレベルに定義した定数の登録先でもある**、という性質を持っています。

この修飾付き参照による先祖方向への探索の結果、トップレベルに定義したはずの定数`Auth`がヒットしてしまうのです。`Twitter::Auth`と書いたエンジニアは、当然`::AUth`とは別のクラスを指しているつもりですよね。

このせいでクラスが存在するにも関わらず（とくに開発環境で）コードが反映されない、という謎エラーがあるというのが割とツラミでした[^11]。

いやー・・こわい話ですねえ。（やられた人）


[^11]: Railsは定数探索失敗時に、Rubyが投げるNameErrorを検知することで、ファイルを動的にオートロードをするという魔改造仕様になっています。間違えたクラスをあてがわれてしまった場合、このオートロードを発動することができず、素知らぬ顔で別のクラスが応答することになります。


チケットを読んで見るとRails界隈からの強い要望もあったようで、この警告がエラーになったようです。

この挙動はRubyのバージョンを上げることで解決しますが、なるべくならトップレベルに定数をつくらないように（参照もなるべくしないように）しておくのが幸せの道かもしれません。

### 大事な教訓　トップレベルに気をつけろ

とはいえ、Railsとかやっているとトップレベルであれこれしないといけなかったり・・ね。

## まとめ

定数と仲良くするために大切そうな教訓をまとめてみました。

#### ネームスペースつき定数の参照は、「素の定数」とは挙動が異なる

   * 具体的には、前者はレキシカルスコープを持つ。後者は持たない
   * レキシカルスコープ上の定数は継承関係にある定数より強い

#### 定数はselfに紐づかない。クラス参照に紐づく

   * 定数探索はメソッド探索に似ているがぜんぜん違う
   * defキーワードやブロックにも影響を受けない

#### 定義や相対参照の際に開始点となるクラス参照がある

   * class_evalやinstance_evalでcrefを切り替えられるが、cbaseは影響を受けない
   * cbaseを切り替えるのは、class/moduleキーワードのみ（class << selfを含む）
   * cbaseはModule.nestingで確認できる（Objectの場合nilになる）

#### Object（トップレベル定数）とその祖先に注意

   * Objectや、それより上位（KernelやBasicObject）に定数を定義すると色々なことがある
   * Ruby2.4以前と2.5で違いがある
   * よいこはいじらない

#### 定数に他の定数を代入しない
   * 循環参照になる可能性があります
   * とくにクラスやモジュールの名前を代入するのはよろしくないので注意

#### Railsの定数探索アルゴリズムは異なる
   * Rubyによる定数探索が失敗に終わると、Railsの定数探索が発動します
   * Rubyアドベントなので、詳しくは別のエントリで触れます

・・・やっぱり定数怖ええ！！ \:scream:



## 最後に

最近、こういうコーナーケースでハマって数時間を溶かしました。

```ruby
# 粗雑な例です
class Member
  # ...メソッドなど
end

Member.class_eval do # オートロード失敗しないように、classを再オープンせずclass_evalを使っていた
  RegistrationFee = 10_000
end
```

この手の定義がいつの間にかトップレベルで衝突していて、二重定義の警告が出ていたという悲しい話です。

ここまで読んだみなさんは、このどこがいけないのかを完全に理解されたのではないでしょうか？

この手の謎挙動と戦っていたエンジニア仲間の探究心にも触発され、よおし、ボクもこの機会に定数の沼に徹底的につかってやるぞぉという気持ちで書きました。

きっかけを与えてくれた仲間の[こんな記事](https://qiita.com/h-shima/items/483283cc0bdf2d1648ed)や[こんな記事](https://qiita.com/wakasa5/items/5f145380ab7bbf8fee15)にはこの場を借りて感謝させてください m(_ _)m

マニアックすぎる雑記事になりましたが、どなたかのお役に立てれば幸いです。（最後まで読む人がいるのだろうか）

## 参考文献

たくさんの記事を読みましたが、とくに役に立った参考文献を挙げておきます。

* 言わずと知れた[Ruby on Rails Guides](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html)。Rubyにおける定数の性質にさかのぼって懇切丁寧に解説してくれている。RubyDocがあっさりしている中、細かいところまで定数の仕様を説明してくれている、（おそらく唯一の）公式資料。Ruby公式よりRails公式の方が充実しているのもなんだかな..w

* 上にあげたRails Guildesの[定数解決の章に関する資料読み解き](https://esa-pages.io/p/sharing/2927/posts/62/7b7a658a0e590d051c42.html)です。めちゃくちゃわかりやすい神資料なので、とくにRailsで困っている人は最初に読むと良いかと思います。たまたまツイッターで目にして、理解の手がかりにさせていただきました。

* 『初めてのRuby』の著者でもあるコミッタ[yugui先生による詳細解説](https://blog.yugui.jp/entry/846)。ソースコードリーディングで行き詰まった頃に知って、何度も膝を打ちました。なんでこれを最初に読まなかったんやワシ・・

* Rubyのソースコードリーディングといえば[「Rubyのしくみ」](http://amzn.asia/d/0p4lYvQ)。定数探索やスタックフレームの仕組み、関連するRubyコードがどこでどうやってiseqにコンパイルされるのか、などなど。定数の沼はこの武器なしには渡りきれないです。

* 資料ではないのですが、やはりコミッタであるささださん、遠藤さんが主催されている[Cookpad Ruby Hack Challenge](https://techlife.cookpad.com/entry/2017/09/29/224024)。ソースコードのいじり方を教えていただきました。神々から直接アドバイスがもらえる機会なのでマジオススメ。

* 最後に、[Ruby Hacking Guide](http://i.loveruby.net/ja/rhg/book/variable.html)です。当時とはコードが随分変わっていますが、やっていることの本質を把握するために何度も戻りました（本買ってない・・）


#### <クイズの答え>

```ruby
# 問題1
> NameSpace::Child.new.put_myconst_at_base
TOPLEVEL

# 問題2
> NameSpace::Child.new.put_myconst
NameSpace

# 問題3
> NameSpace::Child.new.put_myconst_with_child
MonkeyPatch

# 問題4
> NameSpace::Child.new.put_anything(MyConst)
TOPLEVEL

# 問題5
> NameSpace::Child.class_eval { puts MyConst }
TOPLEVEL

# 問題6
> NameSpace::Child.new.put_const_with_grandparent
# Ruby 2.4以前
warning: toplevel constant MyConst referenced by GrandParent::MyConst
TOPLEVEL

# Ruby 2.5以降
NameError: uninitialized constant GrandParent::MyConst
```

1問も悩まなかった方は、ぜひコメント欄に鋭いマサカリをお願いいたします m(_ _)m
※ 僕は最初、自信を持って回答できたのが１番だけでした・・

## 追記

2018/12/16
問題３の記述を間違えていたため修正。タイポを修正、補足追加

2018/12/17
画像にゴミが入っていたため差し替えました。表記揺れ、タイポの修正など

Twitter::Authの説明を修正 「相対参照」 → 「修飾つき参照」
@takaram さん的確な編集リクエストありがとうございました！

2018/12/18
記事内リンク２ヶ所修正。@ryoff さんご指摘ありがとうございました。