LoginSignup
3
4

More than 5 years have passed since last update.

Rubyのクラス変数に外部からアクセスしようとしてハマった

Posted at

TL;DR

クラスに対して、class_eval文字列で呼び出す。

横紙破りも時には必要

オブジェクト指向の言語ではカプセル化された内部状態には外からアクセスできない、純粋関数型言語では変数は状態を持たないなど、言語ごとに書く上での「ルール」がありますが、時にはそのルールを越えた形で書かないといけない場面もあります。C++であればfriend宣言、Objective-Cでのオブジェクトシステムへのアクセスのように、そういう「横紙破り」を行うシステムを言語内部に持っている場合もあります。

Rubyでのeval系メソッド

他のスクリプト言語と同様、Rubyにもevalがありますが、それだけではなくて、評価コンテキストを変えた上で指定されたものを実行するevalもあります。

  • instance_eval…レシーバのインスタンスに評価コンテキストを変えてevalする
  • class_evalmodule_eval…クラスやモジュールの定義部に評価コンテキストを変えてevalする

そして、これらのメソッドは、普通のevalでのような文字列だけでなく、ブロックを引数とすることもできます。

クラス変数にアクセスしてみる

今回、外部からクラス変数にアクセスするという用事が発生したので、いろいろと試行錯誤したのですが、なかなかうまく行きません。いろいろ試してようやく結論にたどり着きました。

  • instance_evalでは、クラス変数は呼び出した環境と紐付いたままになっている
  • class_evalを使った場合も、クラス変数を当該クラスと紐付けるには、引数を文字列にする必要がある(るりま)。

そういうことで、SomeClass.class_eval '@@some_class_variable'のように書くことで、アクセスすることができました。

余談

試行錯誤中に気づいたのですが、Rubyでのトップレベルコンテキストはすなわち、Objectのコンテキストになっています。つまり、(警告はされますが)トップレベルで@@foo=3のような代入を実行すると、それはObjectのクラス変数、さらにはそれを継承するほぼすべてのクラスで共通するクラス変数になってしまいます。まずやらないことだとは思いますが、気をつけましょう。

3
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4