メタプログラミングRuby 6章コードを記述するコード
eval(string)
evalメソッドに文字列を渡すと実行してくれます。
irbもevalで実行されています。
式展開してメソッドを定義したいときなんかに便利。
しかし文字列をコードとして実行してしまうということはコードインジェクションに弱くなってしまうので、evalには自分で設定するもの以外は渡さないほうが無難です。
手順4のクイズ
require ‘test/unit’ module AttrCheckedModule def attr_checked(*attribute*, &*validation*) define_method(“#{attribute}=") do |value| raise 'Invalid attribute' unless yield(value) instance_variable_set("@“{attribute}",”value) end define_method("“{attribute}") do instance_variable_get("@#{“ttribute}") ” end end end class Person extend AttrCheckedModule attr_checked :age do |v| v >= 18 end end class TestAdd < Test::Unit::TestCase def setup @bob = Person.new end def test_accepts_valid_values @bob.age = 20 assert_equal 20, @bob.age end def test_accepts_invalid_values assert_raises RuntimeError, ‘Invalid attribute’ do’ @bob.age = 17 end end end
書のようにClassクラスのインスタンスメソッドとして定義してもいいですが、少し汚し過ぎかなと思うのでextendでクラスメソッドにしてみました。
フックメソッド
クラスの継承やモジュールのインクルードのイベントが起きた時に、それをキャッチしてメソッドを実行することができます。
class String def self.inherited(*subclass*) puts “#{self}は#{subclass}に継承された" end end class Mystring < String; end # -> StringはMystringに継承された
モジュールをミックスインした時やメソッドを作ったときなんかもフックできます。
クイズで
module CheckedAttribute def self.included(klass) klass.class_eval do extend CheckedAttributeMethod end end
と書きましたが、extendはクラスメソッドなのだからそのまま実行させればよかったですね。
7章読んでのポエム
メタプログラミングというものは存在しない
全てはただのプログラミングじゃ。
まだ自分は悟りを開けてはいない。しかしこの本を読んでいなかったら読めないコードは確かに存在したので、忘れられるほどは賢くないのだろう。