Clean way to detect change using callbacks

I would like in my ‘after_update’ callback to detect if an attribute
has been
changed, and if so change some related objects. Is the best way
something like this

class Thing < ActiveRecord::Base

def before_update
@old = Thing.find(self.id)
end

end after_update
if @old.val != self.val
do_stuff
end
end

Or is there a better way? I wish I could avoid the ‘find’ call if
possible.

Thanks in advance,
Don Mc

What I’ve done is handle this from the controller, something like this:

class TheController < ActionController
def update
@thing = Thing.find(params[:id]) rescue nil
redirect_to X unless @thing # Where X = named route or routing hash
[controller, action, etc]
old = {}
old[:name] = @thing.name
old[:body] = @thing.body
# Process @thing
if @thing.name != old[:name]
# Do whatcha gotta do…
end
if @thing.body != old[:body]
# You get the picture…
end
end
end

Don’t just make a dup of @thing, though. 'Cause when @
thing.update_attributes(params[:thing]) gets called it will change old
as
well due to Ruby’s shallow copying. You could marshal the whole object
but
I’ve found it easier to just copy specific attributes. I like the
old[:attribute] method but it’d work the same with separate variables
for
each, like old_name = @thing.name, etc.

Hope that helps.

RSL

I prefer the OP’s instinct to handle the dependencies within the
model. It’s centralized for all users of the model (i.e.,
controllers), easier to test and better encapsulated. That said, I
think the Rails way to deal with this is to override the default
setter method for the attribute.

class Thing < ActiveRecord::Base
def val=(v)
write_attribute(:val, v)
do_stuff # Operations that depend on new val
end
end

This is also independent of whether the Thing is ever written to the
DB, which is generally what you want.

HTH,

Colin

On 3/7/07, Russell N. [email protected] wrote:

old[:body] = @thing.body

Don’t just make a dup of @thing, though. 'Cause when
On 3/6/07, Don.Mc <[email protected] > wrote:

end

Thanks in advance,
Don Mc


Colin Strasser
Union Square Internet Development
917.723.6930 (m)
646.219.0332 (f)

Will this work for things like calling a sweeper if the url of a page
changes? It’s been a while but I remember having one heck of a time
getting
ActiveRecord models to communicate with their controllers that way. But
A)
it’s been a while and B) I only half know what I’m doing know and
probably
less back then.

Oh yeah, and what’s the OP?

RSL

Thanks for responding!

In this particular case, I really don’t want to update the related
objects unless the object has been
saved, since the related objects are in the database also.

Thats a good point about the shallow copying issue.I would like to
leave the
operation in the model, to handle updates from different sources.

I just wanted to thank you for drawing my attention to this methodology.
I’ve done some further looking into it and it will indeed do exactly the
kind of things I need. What I ended up doing was something like

class Thing < ActiveRecord::Base
attr_reader old_name

def name=(val)
@old_name = (val == name) ? nil : name
super val
end

def vitals_changed?
@old_name ? true : false
end
end

then checking @ thing.vitals_changed? which works marvelously. I
seriously
cannot thank you enough. This really helps clean up some rather sticky,
messy, and misplaced code fragments in my app. Thanks, thanks, thanks.

RSL