mober
mober

Reputation: 361

Why is this callback executed?

I want a validation to run before a record gets updated. I know of before_update but I pretty much copy and pasted the first codesnippet out of the api docs.

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

My stripped down model looked then like

class User < ActiveRecord::Base
  attr_accessible :email
  validates :email, :presence => true

  before_save(:on => :update) do
    puts "******** before_save on => :update ********"
    # do something
  end
end

if I go into the console and do create a new entry this callback is being executed on a SQL insert call.

irb(main):001:0> User.new(:email => "[email protected]").save
  (0.1ms)  begin transaction
******** before_save on => :update ********
SQL (29.1ms)  INSERT INTO "users" ("created_at", "email", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["created_at", Fri, 30 Mar 2012 00:26:33 UTC +00:00], ["email", "[email protected]"], ["first_name", nil], ["last_name", nil], ["updated_at", Fri, 30 Mar 2012 00:26:33 UTC +00:00]]
   (433.1ms)  commit transaction
=> true
irb(main):002:0> 

I would have expected to see this only on an update call. Can anybody sheed some light on this?

[EDIT]

I just changed the callback into a function call with no change in the outcome. The callback is still executed on create.

class User < ActiveRecord::Base

attr_accessible :email
validates :email, :presence => true

before_save :my_before_update, :on => :update

private

def my_before_update
    puts "******** before_save on => :update ********"
    # do something
end

end

The output is the same.

Loading development environment (Rails 3.2.2)
irb(main):001:0> User.new(:email => "[email protected]").save
   (0.1ms)  begin transaction
******** before_save on => :update ********
  SQL (28.2ms)  INSERT INTO "users" ("created_at", "email", "first_name", "last_name",         "updated_at") VALUES (?, ?, ?, ?, ?)  [["created_at", Fri, 30 Mar 2012 02:28:45 UTC +00:00],     ["email", "[email protected]"], ["first_name", nil], ["last_name", nil], ["updated_at", Fri, 30     Mar 2012 02:28:45 UTC +00:00]]
   (131.2ms)  commit transaction
=> true

Upvotes: 2

Views: 203

Answers (3)

kafuchau
kafuchau

Reputation: 5593

The ActiveRecord::Callbacks don't support an :on option...

From the Rails codebase, the only place that mentions handling an :on option is in the validations module code in ActiveModel::Validations.

If you look through the ActiveRecord::Callbacks code, you'll see that there's no mention of :on, nor does the ActiveRecord::Callbacks module include any of the ActiveModel::Validations module that will handle that option. There is an include for ActiveModel::Validations::Callbacks, but that will just provide the definitions for the before_ and after_ validations methods. However, the before_validation and after_validation callbacks will handle the :on option as seen here in their definitions.

Upvotes: 3

Alex Marchant
Alex Marchant

Reputation: 2510

After a little more research, looks like you're right, it looks like you can pass :on => :update to before_save

Maybe the issue comes from the block notation, try calling a function like this:

before_save :run_this_before_update, :on => :update

def run_this_before_update
  puts "******** before_save on => :update ********"
  # do something
end

Looks like a major reason to use this is the order in which Rails runs the callbacks, check out this most excellent article from pivotallabs http://pivotallabs.com/users/danny/blog/articles/1767-activerecord-callbacks-autosave-before-this-and-that-etc-

Upvotes: 0

coreyward
coreyward

Reputation: 80140

I'm pretty sure this is one of those areas that the Rails API has changed across versions. I do recall there being a way to pass :on as an option to before_save, just as I recall when you had to define an after_initialize method (it wasn't available as a callback).

The current way is cleaner and more explicit.

If you do find that the current docs reference before_save(:on => :update), check out the new docrails Github repository where you can fork, change, and commit changes to the docs to be included (no pull requests necessary, or accepted).

Upvotes: 0

Related Questions