Wednesday, August 19, 2009

ActiveRecord Callbacks Trigger on Clean Objects

I came across this issue the other day at work, and I'm not really sure how I feel about it. It doesn't necessarily seem right or wrong, but was definitely unintuitive for me...

Since Rails 2.1, we have had dirty object checking, which prevents ActiveRecord from saving an object if if hasn't changed:
>> person = Person.first
  Person Load (0.9ms)   SELECT * FROM "people" LIMIT 1
=> #<Person id: 1, first_name: "Patrick", last_name: "Schless", nickname: nil, created_at: "2009-08-20 02:21:13", updated_at: "2009-08-20 02:22:32">

>> person.save
=> true

>> person.nickname = "nick"
=> "nick"

>> person.save
  Person Update (20.6ms)   UPDATE "people" SET "updated_at" = '2009-08-20 02:31:17', "nickname" = 'nick' WHERE "id" = 1
=> true


That part is good, but what happens when you have a callback defined?
>> Person.after_save { puts 'After save...' }
=> [#<ActiveSupport::Callbacks::Callback:0xb7147898 @identifier=nil, @method=#<Proc:0xb71479d8@(irb):6>, @kind=:after_save, @options={}>]

>> person.nickname = nil
=> nil

>> person.save
  Person Update (2.4ms)   UPDATE "people" SET "updated_at" = '2009-08-20 02:33:21', "nickname" = NULL WHERE "id" = 1
After save...
=> true

>> person.save
After save...
=> true


As we see there, the callback gets triggered even if the save is short-circuited.

In my trivial example, the behavior isn't terribly important either way. However, if your callback is doing something relatively expensive it may be pointless (and wasteful) to do that on a clean object. There are certainly situations where you would want it to always trigger, so I guess it's just one of those things that a developer ought to keep in the back of his mind when coding.

0 comments:

Post a Comment