# Clean way to detect change using callbacks

**URL:** <https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920>\
**Category:** Rails\
**Created:** [March 6, 2007, 10:34pm UTC](https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920 "2007-03-06T22:34:35Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![dmcclean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/91b2a8/32.png) [@dmcclean](https://www.ruby-forum.com/u/dmcclean)\
**Post date:** [March 6, 2007, 10:34pm UTC](https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920/1 "2007-03-06T22:34:35Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![dmcclean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/91b2a8/32.png) [@dmcclean](https://www.ruby-forum.com/u/dmcclean)\
**Post date:** [March 7, 2007, 2:59pm UTC](https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920/2 "2007-03-07T14:59:34Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![dmcclean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/91b2a8/32.png) [@dmcclean](https://www.ruby-forum.com/u/dmcclean)\
**Post date:** [March 7, 2007, 3:59pm UTC](https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920/3 "2007-03-07T15:59:46Z")

</div>

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. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> old[:body] = @thing.body
> 
> ```
> 
> Don’t just make a dup of @thing, though. 'Cause when  
> On 3/6/07, Don.Mc \<removed\_email\_address@domain.invalid \> wrote:
> 
> > end
> > 
> > Thanks in advance,  
> > Don Mc

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

---

<div class="post-metadata">

**Author:** ![dmcclean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/91b2a8/32.png) [@dmcclean](https://www.ruby-forum.com/u/dmcclean)\
**Post date:** [March 7, 2007, 5:31pm UTC](https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920/4 "2007-03-07T17:31:08Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![dmcclean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/91b2a8/32.png) [@dmcclean](https://www.ruby-forum.com/u/dmcclean)\
**Post date:** [March 7, 2007, 10:28pm UTC](https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920/5 "2007-03-07T22:28:10Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![dmcclean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/91b2a8/32.png) [@dmcclean](https://www.ruby-forum.com/u/dmcclean)\
**Post date:** [March 11, 2007, 6:27pm UTC](https://www.ruby-forum.com/t/clean-way-to-detect-change-using-callbacks/87920/6 "2007-03-11T18:27:24Z")

</div>

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
