From: Zachary Holt Date: 2007-06-08T03:11:09+09:00 Subject: Re: KVO in Ruby On Jun 7, 2007, at 7:53 AM, Joshua Ballanco wrote: > Zachary Holt wrote: >> On Jun 6, 2007, at 10:47 PM, Joshua Ballanco wrote: >> >>> Hey all! >> Hey >> >>> Is there a good method for implementing Key-Value Observing in Ruby? >> Check out the Observable module. >> >> http://ruby-doc.org/core/classes/Observable.html > > Hmm...how did I miss that? This should do very well for the project > I'm > currently working on. > > However, looking through the Observable module, I see one major > downside: it's not automatic. That is, the Observable module should > work > well enough for custom classes, but what about Built-in and Standard > Library classes? If, say, you wanted to be notified when the length > of a > string was changed, you would have to extend the String class, but > then > also implement a method to constantly observe the length...or am I > missing something here? I suppose you wouldn't have to modify the String class, if you just want to observe one string. require 'observer' s = "Something fishy" class << s include( Observable ) #etc.. end > The way I understand how this works in Cocoa is > that isa-swizzling is used to replace the class of the property being > observed with a thin pseudo-class that notifies any registered > observers > when a change is made, then passes the change to the original class. Could be. It's been a while since I played with NSAnything. > Is there a good reason not to do things this way? to do things this > way? > The only KVO implementation that I've ever used is Cocoa's, so I'm > just > curious about how/why it's done in Ruby. If I'm making a big fuss over > nothing please feel free to say so. You could do something like this. I'm not sure whether masking the class like this is kosher, however. It would take some tinkering to make it work with more complex classes, but you get the idea. require 'observer' class Observee include Observable def initialize( thing ) @thing = thing end def method_missing( meth, *args ) changed true notify_observers( meth, @thing.dup, @thing.send( meth, *args ) ) end def class ; @thing.class ; end end class Observer def update( meth, ol, ne ) puts "#{meth} #{ ol == ne ? 'did not change' : 'changed' } the #{ol.class.name}" end end s = Observee.new( "Something fishy" ) puts s.class.name so = Observer.new s.add_observer( so ) s.gsub!( /([aeiouy])/, 'Y' ) s << ' Mmm. Tasty' s.gsub!( /([aeiouy])/, '' ) h = Observee.new( :a => 'b' ) puts h.class.name ho = Observer.new h.add_observer( ho ) h[:c] = 'd' h.delete( :a )