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.
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.
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.
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.
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.