Hi --

On Mon, 28 Aug 2006, Paul Murton wrote:

> Hi,
>
> I have a whole load of objects which I would like to periodically dump
> as YAML. However, in order to save on resources, I would prefer to only
> dump those whose instance variables have changed since the last dump.
>
> Is it possible to somehow 'mark' an object when the value any of its
> instance variables is changed?

I don't know whether this will be of practical use to you, but your
question got me wanting to brush up on Observable, so I did :-)  See
below.  It probably threads a bit too narrow a needle, but maybe it
will give you some ideas.  And if someone knows an existing shortcut
for this, let me know :-)

I also recommend looking at observable.rb, in the standard libray,
just to see a great example of how powerful very simple Ruby can be.


require 'observer'

# A module which redefines attr_writer for classes that include it.
# The direct use of @observer_peers (the list of observers) is a bit
# inelegant, but I didn't want to keep adding the object watcher to
# the array over and over.

module AttrWriterInterceptor
   def self.included(m)
     m.class_eval do
       include Observable
       def self.attr_writer(*attrs)
         attrs.each do |att|
           define_method("#{att}=") do |value|
             @observer_peers ||= []
             unless @observer_peers.include?(ObjectWatcher.instance)
               add_observer(ObjectWatcher.instance)
             end
             instance_variable_set("@#{att}", value)
             changed
             notify_observers(self)
           end
         end
       end
     end
   end
end

# A class that uses the observable attr_writers
class Item
   include AttrWriterInterceptor
   attr_writer :description
end

# A class to observe changes.  It's a singleton to make it easier
# to add it as an observer (see above) without having to be passed
# an instance of it.

class ObjectWatcher
   require 'singleton'
   include Singleton

   attr_accessor :changed

   def initialize
     self.changed = true
   end

   def update(obj)
     self.changed = true
   end

   def dump_objects_if_changed
     if self.changed
       self.changed = false
       "Dumping objects!"
     else
       "Not dumping objects!"
     end
   end
end

if __FILE__ == $0

require 'test/unit'

class ObserverTest < Test::Unit::TestCase
   def setup
     @ow = ObjectWatcher.instance
     @i = Item.new
   end

# Test dumping at the beginning, then make sure the second dump
# doesn't happen, then change an attr and make sure it does happen
# the third time.
   def test_three_dumps
     assert(@ow.changed)
     assert_equal("Dumping objects!", @ow.dump_objects_if_changed)
     assert(!@ow.changed)
     assert_equal("Not dumping objects!", @ow.dump_objects_if_changed)
     @i.description = "Some item"
     assert(@ow.changed)
     assert_equal("Dumping objects!", @ow.dump_objects_if_changed)
   end
end

end