メタプログラミングRuby読みメモ 1

メタプログラミングRuby読んだメモです。

1章 頭文字M

  • 多くのプログラミング言語では、コンパイラ後は変数やメソッドは実体を失う
    • メモリ上に配置されるだけの存在となる
  • 一方Rubyのような言語は、言語要素が残る
  • 言語要素について尋ねることもできる
  • これはイントロスペクションと呼ばれる
  • Object#classObject#instance_variables など

  • ActiveRecordではイントロスペクションを用いてクラス名を調べ、同名のテーブルへのマッピングなどの機能を実現している

  • メタプログラミングとは、言語用添えお実行時に操作するコードを記述することである

2章 オブジェクトモデル

  • 全ての言語要素はオブジェクトモデルの中に存在する
    • このメソッドはどのクラスに所属するものなのか
    • このモジュールをインクルードしたらなにが起こるのか

オープンクラス

  • Ruby ではクラスの定義の中に入ったときに初めてクラスが定義される
  • 定義されているクラスをもう一度記述すると再オープンとなり、既存のクラスに記述されたメソッドなどを追加する
  • いつでも既存のクラスを再オープンし、修正できる。この技法をオープンクラスと呼ぶ

オープンクラスの問題点

  • メソッドを追加しようとして同名の既存のメソッドを書き換えてしまう可能性がある
  • このような既存メソッドの書き換えをモンキーパッチと呼ぶ

オブジェクトモデルの内部

  • インスタンス変数
    • Object#instance_variablesで確認することができる
    • Ruby のオブジェクトのクラスとインスタンス変数には何のつながりもない
  • メソッド
    • Object#methods で一覧を取得できる
    • オブジェクトにはメソッドはなく、インスタンス変数とクラスへの参照しかない
    • 全てのオブジェクトはクラスのインスタンスであり、クラスに属している
    • メソッドはオブジェクトではなくクラスに存在する
  • クラスの真相
    • クラスはオブジェクト
    • Ruby の Class クラスのインスタンスはクラスそのもの
      • 他のオブジェクトのように操作することもできる
    • プログラム実行時に新しいクラスを生成したり、書き込むこともできる
    • クラスのクラスメソッドは Class クラスのインスタンスメソッド
  • モジュール
    • Class クラスのスーパークラスは Moduleである
      • つまり全てのクラスは"new, allocate, superclass"を継承したモジュールである
    • どこかでインクルードするときはモジュールを選択し、生成や継承をするときはクラスを選択する
    • クラスはオブジェクトであり、クラス名は定数である
  • 定数
    • 大文字で始まる参照は全て定数
      • クラス名やモジュール名も含めて
    • Rubyは定数の値も変更できる
    • 定数と変数の明確な違いはスコープ
    • プログラムにある全ての定数はファイルシステムのようにツリー上に配置されている
    • Moduleクラスは定数を呼び出すインスタンスメソッドとクラスメソッドを持っている
      • インスタンスメソッドのModule#constants は現在のスコープの定数全てを返す
      • クラスメソッドのModule#constantsは現在のプログラムのトップレベル定数を返す
    • 定数をまとめるモジュールのことをネームスペースと呼ぶ

メソッドの呼び出し

  • メソッド探索
    • オブジェクトのクラスを探索してメソッドを発見している
    • レシーバのクラスから継承チェーンを上りながら探索する
      • レシーバ: 呼び出すメソッドが属するオブジェクト
      • 継承チェーン: クラスのスーパークラススーパークラスのスーパクラス...からなる継承関係
        • インクルードするとモジュールがインクルードしたクラスの上に挿入される
        • prepend を用いるとクラスの下にモジュールが挿入される
    • Kernel
      • Rubyにはどこからでも呼び出せるメソッドがある(print など
      • これはKernelモジュールのprivateメソッドである
      • ObjectクラスがKernelモジュールをインクルードしているので全てのオブジェクトの継承チェーンに挿入されている
  • メソッドの実行
    • メソッドを実行するには発見したメソッドが誰から呼び出されたか知っている必要がある
    • それがレシーバでありこれを参照するキーワードがself
    • レシーバを明示しないメソッド呼び出しは全てselfに対する呼び出しとなる

トップレベ

  • Rubyのプログラムを開始したとき、メソッドをなにも呼び出していない時のselfはmainと呼ばれるオブジェクトである
  • これをトップレベルコンテキストと呼ぶ

クラス定義とself

  • クラスやモジュール定義の内側では、selfはクラスやモジュールそのものになる

private

  • private は二つのルールに従っている
    • 自分以外のオブジェクトのメソッドを呼び出すにはレシーバを明示的に指定する必要がある
    • private はレシーバを指定できない
      • self."privateなメソッド"な呼び方も明示的にselfを指定しているので呼び出すことができない
    • この二つのルールを合わせるとprivateのついたメソッドを呼び出すのは自分しかできなくなる

Refinements

  • クラスの修正をする時の問題点は、変更がグローバルに及ぶこと
  • refine をつけるとusingメソッドを使って明示的に有効化しないと有効にならないようにできる
  • using "モジュール"を呼び出した時点から有効化される
  • Refinements はモジュール定義の終わりまで有効