メタプログラミング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章読んでのポエム

メタプログラミングというものは存在しない

全てはただのプログラミングじゃ。

まだ自分は悟りを開けてはいない。しかしこの本を読んでいなかったら読めないコードは確かに存在したので、忘れられるほどは賢くないのだろう。