Rubyの最も強力であり、同時に最も特徴的な側面の一つが「メタプログラミング」です。これは「コードがコードを書く(あるいは変更する)」能力を指します。他の言語でコンパイル時やリフレクションAPIを通じて行っていた操作の多くを、Rubyでは実行時に直接、かつ柔軟に行うことができます。
Rubyは非常に動的な言語です。クラスは実行中に変更可能であり、メソッドの追加や削除、上書きがいつでも行えます。この章では、その動的な性質を利用したメタプログラミングの基本的な手法を学びます。
これらの性質が、DRY (Don't Repeat Yourself) の原則を追求し、柔軟なDSL(ドメイン固有言語)を構築するための基盤となります。
通常、メソッドは object.method_name のようにドット(.)を使って呼び出します。しかし、呼び出したいメソッド名が実行時までわからない場合、send メソッド(または public_send)が役立ちます。
send は、第1引数にメソッド名をシンボル(:)または文字列で受け取り、残りの引数をそのメソッドに渡して実行します。
注意:
sendはprivateメソッドも呼び出すことができます。意図せずprivateメソッドを呼び出さないように、通常はpublic_sendを使う方が安全です。
メソッドを動的に(実行時に)定義したい場合、define_method を使用します。これは主にクラスやモジュールの定義内で使われます。
define_method は、第1引数に定義したいメソッド名(シンボル)を、第2引数にブロック(ProcやLambda)を取ります。このブロックが、新しく定義されるメソッドの本体となります。
例えば、似たようなメソッドを多数定義する必要がある場合に非常に便利です。
ruby dynamic_greeter.rbオブジェクトに対して定義されていないメソッドが呼び出されると、Rubyは例外(NoMethodError)を発生させる前に、method_missing という特別なメソッドを呼び出そうと試みます。
この method_missing を自分でオーバーライドすることで、定義されていないメソッド呼び出しを「キャッチ」し、動的に処理できます。
method_missing は以下の引数を受け取ります。
ruby ghost_methods.rbRubyのメタプログラミングは、Ruby on Railsのようなフレームワークで広く活用されています。これにより、開発者は定型的なコード(ボイラープレート)を大量に書く必要がなくなり、宣言的な記述が可能になります。
method_missing の典型的な例です。User.find_by_email("test@example.com") のようなメソッドは、User クラスに明示的に定義されていません。Active Recordは method_missing を使って find_by_ プレフィックスを検出し、email カラムで検索するSQLを動的に生成します。has_many :posts や belongs_to :user といった記述。これらは単なる宣言に見えますが、内部では define_method を使い、user.posts や post.user といった便利なメソッドを実行時に定義しています。このように、メタプログラミングはRubyエコシステムの「魔法」の多くを支える技術であり、フレームワークの内部を理解する上で不可欠です。
send(または public_send)は、メソッド名を文字列やシンボルで指定し、動的にメソッドを呼び出します。define_method は、実行時にメソッドを動的に定義します。DRYを保つのに役立ちます。method_missing は、定義されていないメソッド呼び出しを捕捉し、柔軟なインターフェース(DSL)を構築するために使われます。define_method を使って、指定された属性名の配列からゲッター(attr_reader)とセッター(attr_writer)を動的に定義するメソッド my_attr_accessor を持つモジュールを作成してください。(ヒント: インスタンス変数 @name を読み書きするメソッドを定義します)
ruby practice12_1.rbmethod_missing を使って、ハッシュのように動作する SimpleConfig クラスを作成してください。config.api_key = "12345" のように値を設定でき、config.api_key で値を取得できるようにしてください。設定されていないキーを呼び出した場合は nil を返すようにします。
ruby practice12_2.rb