On 09/12/2010 02:10 PM, Knut Franke wrote:
> Some time ago I stumbled over Cells[1], a Common Lisp extension allowing
> one
> to declare relations between instance variables (called slots in CL);
> i.e. a
> change to one variable will automatically recompute other variables
> depending
> on it. I think it's pretty neat, but using it in any interesting context
> is
> somewhat hampered by the fact that few people go through the
> considerable
> trouble involved in learning Common Lisp.
>
> Just today it occurred to me that it should be pretty easy to do
> something
> similar in Ruby. Indeed, a couple of codelines later, I had a Ruby
> version of
> the standard Cells example (well, a simplified version) running:
>
> class Motor
>     cell :temperature, :status
>     def initialize
>        self.temperature = 0
>        calculate :status do
>           if self.temperature<  100
>              :on
>           else
>              :off
>           end
>        end
>     end
> end
> m = Motor.new
> m.observe(:temperature) {|old, new|  puts "temperature: #{old} ->
> #{new}"  }
> m.observe(:status) {|old, new|  puts "status: #{old} ->  #{new}"  }
> m.temperature = 80
> m.temperature = 110
>

I wrote a library called observable for use in GUI MVC programming (with 
FXRuby), something along those lines. One nice feature was pattern 
matching on the changed value, so you could have separate handlers for 
different ranges etc. Ruby's #=== methods are very cool and somewhat 
underused. That might be a nice feature to add to your #observe method.

Here's the code:

http://redshift.sourceforge.net/observable/

Here's my version of your example:

require 'observable'

# Let's just include Observable globally. Conservatively, one would do:
#
#   class C
#     extend Observable
#     include Observable::Match   # if desired
#     ...
#   end

include Observable
include Observable::Match

class Motor
   observable :temperature, :status

   def initialize
     self.temperature = 0
     self.status = :on

     when_temperature 0..100 do
       self.status = :on
     end

     when_temperature 100..100000 do
       self.status = :off
     end
   end
end

m = Motor.new

m.when_temperature CHANGES do |new, old|
   puts "temperature: #{old} -> #{new}"
end

m.when_status CHANGES do |new, old|
   puts "status: #{old} -> #{new}"
end

m.temperature = 80
m.temperature = 110

__END__

temperature:  -> 0
status:  -> on
temperature: 0 -> 80
temperature: 80 -> 110
status: on -> off