block を後で評価 & method_missing の練習

メタプログラミングRuby

メタプログラミングRuby

上の本を読みながら、研修向け Rubyイディオムをつくっているつもりが、自分のメタプログラミングの素振りになってたw。

describe "bockを 後で評価 & method_missing の例" do
  class Calc
    def initialize
      yield self if block_given?
    end

    def defun(key, &block)
      nodes[key.to_sym] = block
    end

    def exe(key, *args)
      nodes[key.to_sym].call(args)
    end

    def nodes
      @nodes ||= {}
    end

    def method_missing(m, *args, &block)
      exe(m.to_sym, *args)
    end
  end

  subject {
    Calc.new do |c|
      c.defun(:plus)  {|args| args.reduce(:+) }
      c.defun(:minus) {|args| args.reduce(:-) }
      c.defun(:multi) {|args| args.reduce(:*) }
      c.defun(:greeting)  {|name, world| "hello #{name}. I love #{world}" }
    end
  }

  it "処理の振り分けができること" do
    subject.exe(:plus, 4, 3, 2).should == 9
    subject.exe(:minus, 10, 1, 2).should == 7
    subject.exe(:multi, 2, 3).should == 6
    subject.exe(:greeting, "haru01", "Ruby").should == "hello haru01. I love Ruby"
  end

  it "メソッド呼び出しのようにできること" do
    subject.plus(4, 3, 2).should == 9
    subject.minus(10, 1, 2).should == 7
    subject.multi(2, 3).should == 6
    subject.greeting("haru01", "Ruby").should == "hello haru01. I love Ruby"
  end
end


defun で ブロックは即評価せず、ハッシュに格納し、exeのところで評価している。
method_missing で exe を呼ぶことで 「subject.greeting("haru01", "Ruby")」 のように呼べるようにしてる。

その他、自己 Self

    def initialize
      yield self if block_given?
    end
....
    Calc.new do |c|
      c.defun(:plus)  {|args| args.reduce(:+) }
      c.defun(:minus) {|args| args.reduce(:-) }
      c.defun(:multi) {|args| args.reduce(:*) }
      c.defun(:greeting)  {|name, world| "hello #{name}. I love #{world}" }
    end

nil ガード

      @nodes ||= {}

メタプログラミング Rubyで紹介されている イディオム なので覚えていて損はない。