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