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
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
=> [#<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