As a newcomer to Ruby and metaprogramming, I'm still looking for good resources on this and the art of creating DSLs. 
I'm picking up a little here and there by reading blog posts and 
articles like this one on InfoQ, as well as pouring over code from Rails and other Ruby projects, but it seems like there would be a market for a 
book written by someone who has done a lot in this area.
p.s. Here's a modification of an implementation from the aforementioned article that supports passing in a lambda expression, a block, or both. I suspect there may be better ways to do some of this and would love to hear it. Regardless, I'm continually astounded by the power of mixins and the reflection capabilities built into Ruby. 
module Properties
  
  def self.extended(base) 
    base.class_eval do
      def fire_event_for(sym, arg)
        @listener[sym].each {|l| l.call(arg) }
      end
    end
  end
  
  def property(sym, predicate=nil, &block)
    define_method(sym) do
      instance_variable_get("@#{sym}")
    end
  
    define_method("#{sym}=") do |arg|
      if block_given? and predicate
        return if !predicate.call(arg) and !block.call(arg) 
      elsif predicate
        return if !predicate.call(arg)
      elsif block_given?
        return if !block.call(arg)
      end
      instance_variable_set("@#{sym}", arg)
      fire_event_for(sym, arg)
    end
  
    define_method("add_#{sym}_listener") do |x| 
      @listener ||= {}
      @listener[sym] ||= []
      @listener[sym] << x
    end
    
    define_method("remove_#{sym}_listener") do |x| 
      @listener[sym].delete_at(x)
    end
  end
  
  def is?(test)
    lambda {|val| test === val }
  end
  
  def includes?(*test)
    lambda {|val| test.include? val }
  end
end
class CruiseShip
  extend Properties
  
  # property :direction
  # property :direction, includes?('north', 'south', 'east', 'west')
  property :speed, is?(0..300)
  property(:direction, includes?('north', 'south', 'east', 'west')) {|v| v == 'thataway' }
  # property(:speed) {|v| v >= 0 && v <= 300 }
end
h = CruiseShip.new
h.add_direction_listener(lambda {|x| puts "Oy... someone changed the direction to #{x}"})
h.direction = "north"
h.add_speed_listener(lambda {|x| puts "Oy... someone changed the speed to #{x}"})
h.add_speed_listener(lambda {|x| puts "Yo, dude... someone changed the speed to #{x}"})
h.speed = 200
h.speed = 300
h.speed = 301
h.speed = -1
h.direction = "south"
puts h.direction
puts h.speed
h.remove_speed_listener(1)
h.speed = 200
h.speed = 350
h.direction = "thataway"
h.direction = "whatever"
puts h.direction
puts h.speed
Output:
Oy... someone changed the direction to north
Oy... someone changed the speed to 200
Yo, dude... someone changed the speed to 200
Oy... someone changed the speed to 300
Yo, dude... someone changed the speed to 300
Oy... someone changed the direction to south
south
300
Oy... someone changed the speed to 200
Oy... someone changed the direction to thataway
thataway
200
Labels: ruby dsl metaprogramming