explicit DSLとimplicit DSL

先輩のブックマーク経由で.
http://www.infoq.com/news/2008/02/presentation-ruby-art-of-dsl

explicitだと、通常のブロックを使う。storeの記述が重複して単調.

when_shopping do |store|
 store.buy :popcorn
 store.buy :soda, @soda_amount
end 

implicitだと、単調だったstoreの箇所を削り、よりコンパクトになる.
これならドメインエキスパートにもフレンドリーな表記♪

when_shopping {
 buy :popcorn
 buy :soda 
}

どうやったら、Rubyで実行可能にできるのかなと思っていたら
http://www.infoq.com/presentations/kilmer-ruby-dsls
で解説してあった.instance_evalを使うんだね.

class ShoppingTrip
  module Behavior
    def when_shopping(&block)
      ShoppingTrip.new(&block)
    end
  end

  def initialize(&block)
    @items = []
    instance_eval(&block)
  end
  
  def buy(item, count=1)
    @items << [item, count]
  end
  
  def show
    @items.each { |item| puts "item_name=>#{item[0]}, quantity=>#{item[1]}" }
  end
end

extend ShoppingTrip::Behavior

quantity_a = 2
@quantity_b = 3 

when_shopping { 
  buy :popcorn, quantity_a #  2は入る
  buy :soda, @quantity_b   #  3は入らない
}.show

ただし、上記の記述では、sodaの数は3にならない.
これは、instance_eval経由で実行されるbuyメソッドは、ShoppingTripオブジェクト側のスコープで評価するが、このスコープでは、インスタンス変数の@quantity_bに値が入っていないため.(たぶん
explicitアプローチなら、期待通りsodaの数は3になる.

popcornの数が2になる理由は、私には説明できないw.

ここら辺が、トレードオフか.