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](http://thing.name)
old[:body] = @thing.body
# Process @thing
if @[thing.name](http://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

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