This project is archived and is in readonly mode.
Potentially resolved… Callback API change breaks #run_callbacks
-
James Conroy-Finn
I've written a quick test in
activerecord/test/cases/callbacks_test.rbthat demonstrates the exact failure I'm experiencing and have included a patch file with the test in case that's any use. I'll see if I can fix it myself and upload another patch if and when I get it done.BTW I'm not a test-unit kinda guy so it may be there's a better way to implement the test. Apologies for any cardinal test-unit sin I may have committed.
-
James Conroy-Finn
OK… We've worked this one out. The API has changed but the documentation doesn't reflect the changes too clearly. Hopefully Yehuda can clarify this but essentially callbacks work as follows…
This will run only the before save callback…
p = Post.new p.run_callbacks(:save) { false }…and this will run both the before and after callbacks…
p = Post.new p.run_callbacks(:save) { true }Essentially you can't specify the before or after anymore (using #run_callbacks(:before_save) etc.) because callbacks work like this.
You call a group of callbacks, for example save, create, validation. There will potentially be a before and/or after callback to any of these groups. All will be executed but only if the block supplied evaluates to true. Examples will explain this better so here we go…
# Before and after callbacks # This will run both the before_save and after_save callbacks… Post.new.run_callbacks(:save) # …as will this Post.new.run_callbacks(:save) { true } # This will only run the before_save and ignore any after_save callback Post.new.run_callbacks(:save) { false }This is darned handy if you have a condition that determines whether or not to execute the next callback as you do in ActiveRecord when saving (e.g. errors.empty?).
I might be mistaken but after some debugging, source tasting and with some mad class/instance method magic I'm pretty sure this is how things work in Rails 3.
Again, if Mr. Katz or one of his learned friends could confirm this it would be appreciated.
Thanks.new.run_callbacks(:finished) { true }
puts "James ;)"
-
James Conroy-Finn
- Title changed from Unable to manually run ActiveRecord object callbacks to Potentially resolved… Callback API change breaks #run_callbacks
-
Jeremy Kemper
- Milestone cleared.
- State changed from new to open
- Assigned user set to Yehuda Katz (wycats)
- Importance changed from to Low
-
Neeraj Singh
I recently fixed #5419 http://github.com/rails/rails/commit/2ffa50f5a9fac08e08869687006031... where after_validation was not getting called if valid? returns false.
I will have to check with rails 2.3.x to see if what is the existing behavior. Any change in behavior should be documented.
-
Neeraj Singh
@James you are spot on.
Here is definition of method _define_after_model_callback
def _define_after_model_callback(klass, callback) #:nodoc: klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1 def self.after_#{callback}(*args, &block) options = args.extract_options! options[:prepend] = true options[:if] = Array.wrap(options[:if]) << "!halted && value != false" set_callback(:#{callback}, :after, *(args << options), &block) end CALLBACK end endNotice the part where it says value != false . That code is saying that proceed with callback only if the returned value is NOT false. Otherwise halt the chain.
if you change line from
options[:if] = Array.wrap(options[:if]) << "!halted && value != false"to
options[:if] = Array.wrap(options[:if]) << "!halted"then after_save callback will be called irrespective of the returned value from the save operation.
Hope that helps you understand why the code is behaving the way it is behaving.
