メタプログラミングとは、「プログラミングするためのプログラムを書くこと」らしい。
実務で遭遇した例としては、「任意の数の変数を作成し、その変数を利用する」場面。
[*1..3].each do |count|
# user_1のような変数を作成し、値を「ユーザー1」と設定する
instance_variable_set("@user_#{count}", "ユーザー#{count}")
# evalを使って文字列をRubyプログラムとして評価させてputsする
puts eval("@user_#{count}")
end
# 実行結果:
# ユーザー1
# ユーザー2
# ユーザー3
eval使うと、Rubocopに警告もらうので推奨されたやり方ではないのですが、こういうのがメタプログラミングというものだそうです。
もう少し実践的なメタプログラミングの概念は「オープンクラス」。
あるクラスを拡張しようと思ったら継承が必要ですが、継承せずに実行できること。
例えば、「このプロジェクトでは、足し算をするときに、足した結果にさらに1をプラスする必要がある」という要件を満たそうとする場合。
class Integer
def plus_one(v)
self + v + 1
end
end
puts 1.plus_one 1
# 実行結果:
# 3
Integerクラスを継承することなく、plus_oneというメソッドを追加できました。
追加だけではなく、既にあるメソッドと同名を定義すれば上書きできます(これがモンキーパッチ)。
先程の要件を、メソッドの拡張ではなくモンキーパッチでやってみます。
def +(v)
を上書きするわけです。
けれど以下の書き方だと+の定義が無限ループに陥るのでダメです。
class Integer
def +(v)
self + v + 1
end
end
# 実行結果:
# `+': stack level too deep (SystemStackError)
こういう場合は、alias_method
を使用します。
alias_methodとは「メソッドに別名を定義する」というもの。
class Integer
alias_method :plus, :+
def +(v)
self.plus(v).plus(1)
end
end
puts 1 + 2 #=> 4
※この問題は、お仕事の面接の時に問題として出題されたものです。
当時はRubyを触って半年でメタプログラミング自体分かってなかったので答えられなかったのですが、最近勉強を始め、「これについて聞いていたのか!」と分かりました。
すごくすっきりした……!