[Ruby] メタプログラミングの備忘録

メタプログラミング

  • コードを記述するコードを記述すること

メタプログラミングの種類

    1. 静的メタプログラミング: アノテーションを付与したコードをコードジェネレーターを用いて、コードを自動生成すること
    1. 動的メタプログラミング: 実行時に内部で動的にコードを生成すること

インストロペクション

  • インストロペクション: オブジェクトのクラス名、インスタンスメソッド、インスタンス変数を確認すること
  • Rubyは実行時にインストロペクションが可能な言語
  • 例: Active Recordによるメタプログラミング
    • ActiveRecord::Baseを継承することで次のようなメリットを享受できる(ActiveRecord側の実装で、インストロペクションの活用されている)
      • クラス名を調べて、該当のテーブルにマッピングされる
      • アクセサメソッド(アトリビュートにアクセスするためのメソッド)などはActive Record側でテーブルからスキーマを読み取り、自動で生成される

オープンクラス

  • 既存のクラスを再オープンして、そのクラスに追加したいメソッドを書くだけで既存のクラスにメソッドを追加することができる

モンキーパッチ

  • 基本クラスなどに対して変更を加えること
  • 悪い意味としては、闇雲にクラスにメソッドを定義することで、既存のメソッドを上書きしてしまい、バグを誘発するコードのことを指す

インスタンスメソッド

  • 呼び出す場合、クラスのオブジェクト(インスタンス)が必要なメソッド

ネームスペース

  • 定数をまとめるためのモジュール

オブジェクト

  • インスタンス変数の集合にクラスのリンクがついたもの
  • オブジェクトは自分だけのインスタンス変数を持っており、他のオブジェクトが持っているインスタンス変数は無関係

クラス

  • Classクラスのインスタンスにインスタンスメソッドの一覧とスーパクラスのリンクがついたもの

メソッド探索

  • レシーバー
    • 呼び出すメソッドが所属するオブジェクト
  • 継承チェーン
    • スーパークラスに向かって探索する挙動
    • 継承チェーンの経路では、クラスだけではなくモジュールも探索対象になる

selfキーワード

  • インスタンス変数は全てselfのインスタンス変数になる
  • レシーバーを記述しないメソッドは全てselfで呼び出したことになる
  • トップレベルコンテキスト
    • irb起動直後にselfが参照しているオブジェクト(mainオブジェクトと呼ばれる)

リファインメント

  • モンキーパッチをスコープを定めて定義することができる
  • refineでモンキーパッチを定義して、usingを使ってモンキーパッチを利用する

動的ディスパッチ

  • コードの実行時に呼び出すメソッドを決めることができる
  • sendメソッドを使うと動的にメソッドを呼び出すことができる
    • send(<メソッド名>, <メソッドの引数> ...)
  • 第1引数はオブジェクトに送信するメッセージ
    • 一般的にはシンボルで指定する(sendの第1引数にシンボルを使う理由はほぼ慣習的なもの)

動的メソッド

  • Module#define_methodを使用することで動的にメソッドを定義することができる

ゴーストメソッド

  • method_missingの呼び出しを利用して、処理を加えた時に、呼び出し側のレシーバーには存在しないメソッドがあたかも存在するようにみえる。このあたかも存在するかのように見えるメソッドをゴーストメソッドと呼ぶ
  • ゴーストメソッドを上手く活用すると、外部変更などが発生したときにプログラマがわざわざメソッドを実装する対応が必要がなくなったりするメリットが享受できる
    • 基本的な考えとしては動的メソッドを使用して、やむ得ない場合にゴーストメソッドを使用するといった判断をすると良い
  • respond_to?メソッドを利用すると、メソッドがゴーストメソッドがどうかを調べることができる
  • ゴーストメソッドを呼び出したときに、継承先で実装されているメソッドが競合すると後者が呼び出される

ブランクスレート

  • 最低限のメソッドしか持たないクラス
    • ex: BasicObject ... etc

特異クラス

  • 別名、シングルトンクラスやメタクラスと呼ばれる
  • 特性
    • インスタンスを1つしか持てない
    • 継承ができない
  • classキーワードを使うと特異クラスのスコープに連れて行ってくれる
    • ex: class << self など
  • オブジェクトが特異メソッドを持っている場合、通常クラスではなく特異メソッドからメソッド検索開始する

特異メソッドとクラスメソッド

  • 特異メソッド
    • 1つのオブジェクトに特化したメソッドのこと(あるオブジェクトに存在する固有のメソッドとも言える)
    • クラスのインスタンス(オブジェクト)作成後に、そのオブジェクトに対してメソッドを定義することができる。追加したメソッドはこのオブジェクトからしか呼び出すことができない
    • Rubyの言語仕様として、特定のオブジェクトにメソッドを追加することができる
    • クラスマクロ
      • selfがModuleであっても、Classであっても使えるメソッド(ぱっとみキーワードのように見える)
        • ex: attr_accessor
  • クラスメソッド
    • 特異クラス(シングルトンクラス)にある特異メソッドのこと(特異クラスにメソッドを定義した場合はクラスメソッドになる。クラスの特異メソッドである)
    • クラスメソッドを定義する方法の1つとして class << self を使った方法がある
      • class << self の中でModuleをインクルードすると、Moduleのインスタンスメソッドはインクルードしたクラスのクラスメソッドになる

ブロック

  • スコープを制御することができる
  • yieldキーワードを使用することでブロックをコールバックする
    • ブロックの処理をyieldキーワードが埋め込まれている箇所に差し込むことができる
    • Kernel#block_given?メソッドをメソッドなどに埋め込むと、そのメソッドを呼び出す時にブロックを使用したかどうかを確認することができる
  • ブロック ⇄ Proc, lambda(呼出可能オブジェクト)の変換が可能
  • ブロック ≒ クロージャ
  • 束縛
    • ブロックのコードを実行するには以下の3つの要素が必要であり、これらは束縛と呼ばれる
      • インスタンス変数
      • ローカル変数
      • self
  • ローカル変数をブロック内で使用したときに、yieldキーワードが埋め込まれたメソッド内で定義されたローカル変数と名前が重複した場合、前者のブロック内で使用したローカル変数で上書きされる(ローカル束縛を包み込むとも言ったりする)

参考文献