
# See  http://electricsheep.blogspot.com/2007_04_01_archive.html

require 'pp'

module Properties
  
  def self.extended(base) 
    base.class_eval do
      def fire_event_for(sym, arg)
        @listener ||= { }
        @listener[sym] ||= []
        @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
