TL;DR
クラスに対して、class_eval
を文字列で呼び出す。
横紙破りも時には必要
オブジェクト指向の言語ではカプセル化された内部状態には外からアクセスできない、純粋関数型言語では変数は状態を持たないなど、言語ごとに書く上での「ルール」がありますが、時にはそのルールを越えた形で書かないといけない場面もあります。C++であればfriend
宣言、Objective-Cでのオブジェクトシステムへのアクセスのように、そういう「横紙破り」を行うシステムを言語内部に持っている場合もあります。
Rubyでのeval系メソッド
他のスクリプト言語と同様、Rubyにもeval
がありますが、それだけではなくて、評価コンテキストを変えた上で指定されたものを実行するevalもあります。
-
instance_eval
…レシーバのインスタンスに評価コンテキストを変えてevalする -
class_eval
、module_eval
…クラスやモジュールの定義部に評価コンテキストを変えてevalする
そして、これらのメソッドは、普通のevalでのような文字列だけでなく、ブロックを引数とすることもできます。
クラス変数にアクセスしてみる
今回、外部からクラス変数にアクセスするという用事が発生したので、いろいろと試行錯誤したのですが、なかなかうまく行きません。いろいろ試してようやく結論にたどり着きました。
-
instance_eval
では、クラス変数は呼び出した環境と紐付いたままになっている -
class_eval
を使った場合も、クラス変数を当該クラスと紐付けるには、引数を文字列にする必要がある(るりま)。
そういうことで、SomeClass.class_eval '@@some_class_variable'
のように書くことで、アクセスすることができました。
余談
試行錯誤中に気づいたのですが、Rubyでのトップレベルコンテキストはすなわち、Object
のコンテキストになっています。つまり、(警告はされますが)トップレベルで@@foo=3
のような代入を実行すると、それはObjectのクラス変数、さらにはそれを継承するほぼすべてのクラスで共通するクラス変数になってしまいます。まずやらないことだとは思いますが、気をつけましょう。