From: Anders Borch Date: 2003-07-03T16:27:44+09:00 Subject: Re: Auto-serialization (as in Vapor, sql-serialize, etc.): detecting changes to instance variables Jim Cain wrote: > All, > > I'm working on the same problem as has been encountered in the projects > named above: It would be very handy to have a way to trap changes to an > instance variable... perhaps a callback in the same spirit as > Kernel#trace_var. To be really useful it would have to be attached to > the instance variable itself, not just to the accessor methods. > > There is a problem even with this low level of control, however. Suppose > you have a reference R outside of a particular object S that you're > interested in serializing, and that the reference R points to an object > that one of S's instance variables points to. In other words, this is true: > > R.equal?(S.iv) > > How can you detect, from S, that a change to S.iv has been made through > R, without doing something like comparing the before and after > serializations of S? Would it be possible to create a callback that was > attached to the referred-to object (*R or *S.iv in C-speak), rather than > to the instance variable? I think you should not allow anyone to modify your instance variables without going through your accessor methods. It ruins everything that encapsulation gives you. With that out of the way I have heard of method:before and method:after callbacks comming (are they in ruby 1.8?) - they would help you to transparently test if serialization is needed. Again, this depends on using accessor methods. Another way to do it would be to wrap the object like I have done in the supplied example. The idea, in this example, is to load an object as late as possible which *might* save us from fetching some of your objects from a database. Imagine fetching a large array of objects where you in reality only need a few of those objects. This wrapper object would postpone the loading of the object until it was needed. -- 8< -- # code to test the idea of a wrapper object class SomeObject attr_accessor :myvar end class Wrapper def method_missing(meth, *args) if (@obj.nil?) @obj = SomeObject.new # load the object from the database here end @obj.__send__(meth, *args) end end w=Wrapper.new # SomeObject isn't loaded yet w.myvar = 'hej' # now we need the object so we load it... p w.myvar # the loaded object is called once again -- 8< -- Using the method_missing to test if the object has changed after each method call *could* solve your problem. You should be aware, tho, that there could be a serious performance hit with this approach. Imagine a Person object that needs a lot of changes: p.first_name = "Anders" # update in db once first_name= has been called p.last_name = "Borch" # update in db again p.zip_code = 90210 # update in db again This *may* not be what you really want. Rather I suggest that you have some #checkpoint method that serializes the object, and you may want to call that in an object finalizer. /Anders -- dc -e 4dd*od3*dddn1-89danrn10-dan3+ann6*dan*2*an13dn1+dn2-dn3+5*ddan2/9+an13nap