4

I've been looking through the ActiveRecord source to find out how :if => proc_or_method_name works on ActiveRecord validations, but the only instances of :if in the source are in the comments explaining how the feature should be called.

For example, you can have a line like the following in a model:

validates_presence_of :name, :if => :nameable?

and the validation only gets checked if the nameable? method returns a truthy value for the given model.

Where is this functionality actually defined, as I can't find this behaviour anywhere in the (Rails2) source?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Gareth
  • 133,157
  • 36
  • 148
  • 157

2 Answers2

3

As of Rails 3, ActiveRecord callbacks are defined in active_record/callbacks.rb, but because an ActiveRecord model inherits from ActiveModel, then you should also look at the active_model/callbacks.rb file.

The Callback feature itself is a separate component. In fact, ActionController before/after filters are callbacks, actually. For this reason, the callback system is a module defined in ActiveSupport::Callbacks.

Merge altogether these 3 pieces and you get the ActiveRecord callbacks feature.

Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
  • Sorry for taking so long to reply. I actually meant Rails2 but apart from the separation into ActiveModel I don't think that's an issue here. I've updated the question with more information because I've given the wrong impression. – Gareth Jan 05 '11 at 11:53
3

The :if option is checked for in the file activesupport\lib\active_support\callbacks.rb.

The method should_run_callback is called to check if the callback should be executed or not.

Look also at how the callback chain is processed, starting from the run_callbacks method in the same file.

Some code from v2.3.8 of that file is:

def should_run_callback?(*args)
  [options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
  ![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
end

And here is how I found out (in case anyone is interested):

  1. Downloaded Rails v2.3.8 from github and unzipped it.
  2. grepp'ed for :if in all .rb files
  3. In a activerecord/CHANGELOG, located a comment that mentioned:
    Added the :if option to all validations that can either use a block or a method pointer to determine whether the validation should be run or not. #1324 [Duane Johnson/jhosteny].
  4. Google'd for that comment. Found it in a google cache.
  5. Found that the comment/addition was made on 05/21/05 10:57:18 by david
  6. Located date 2005-05-21 on rails github history on page 546:
  7. Got an inkling of how the :if works
  8. Found that the code that commit referred to was no longer existing in v2.3.8. had to find latest location of that code
  9. grepp'ed :if again and went though each file that felt "good". came to activesupport/lib/active_support/callbacks.rb
  10. searched for :if in the file and it was found in only one location, in the method should_run_callback.
  11. Posted answer
  12. Crossed fingers and waited for bounty. :D

That was fun!

Zabba
  • 64,285
  • 47
  • 179
  • 207