Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

moving before_validation and after_validation functionality from Acti…

…veRecord to ActiveModel

[#4653 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
  • Loading branch information...
commit 51739d3228d12907d60fb1b0a2b1ef96c55f66a3 1 parent 312f433
@neerajdotname neerajdotname authored josevalim committed
View
14 activemodel/lib/active_model/validations.rb
@@ -3,6 +3,7 @@
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_model/errors'
+require 'active_model/validations/callbacks'
module ActiveModel
@@ -45,6 +46,7 @@ module ActiveModel
module Validations
extend ActiveSupport::Concern
include ActiveSupport::Callbacks
+ include ActiveModel::Validations::Callbacks
included do
extend ActiveModel::Translation
@@ -158,18 +160,6 @@ def errors
@errors ||= Errors.new(self)
end
- # Runs all the specified validations and returns true if no errors were added
- # otherwise false. Context can optionally be supplied to define which callbacks
- # to test against (the context is defined on the validations using :on).
- def valid?(context = nil)
- current_context, self.validation_context = validation_context, context
- errors.clear
- _run_validate_callbacks
- errors.empty?
- ensure
- self.validation_context = current_context
- end
-
# Performs the opposite of <tt>valid?</tt>. Returns true if errors were added,
# false otherwise.
def invalid?(context = nil)
View
64 activemodel/lib/active_model/validations/callbacks.rb
@@ -0,0 +1,64 @@
+require 'active_support/callbacks'
+
+module ActiveModel
+ module Validations
+ module Callbacks
+ # == Active Model Validation callbacks
+ #
+ # Provides an interface for any class to have <tt>before_validation</tt> and
+ # <tt>after_validation</tt> callbacks.
+ #
+ # First, extend ActiveModel::Callbacks from the class you are creating:
+ #
+ # class MyModel
+ # include ActiveModel::Validations::Callbacks
+ #
+ # before_validation :do_stuff_before_validation
+ # after_validation :do_tuff_after_validation
+ # end
+ #
+ # Like other before_* callbacks if <tt>before_validation</tt> returns false
+ # then <tt>valid?</tt> will not be called.
+ extend ActiveSupport::Concern
+
+ included do
+ include ActiveSupport::Callbacks
+ define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
+ end
+
+ module ClassMethods
+ def before_validation(*args, &block)
+ options = args.last
+ if options.is_a?(Hash) && options[:on]
+ options[:if] = Array.wrap(options[:if])
+ options[:if] << "self.validation_context == :#{options[:on]}"
+ end
+ set_callback(:validation, :before, *args, &block)
+ end
+
+ def after_validation(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array.wrap(options[:if])
+ options[:if] << "!halted && value != false"
+ options[:if] << "self.validation_context == :#{options[:on]}" if options[:on]
+ set_callback(:validation, :after, *(args << options), &block)
+ end
+ end
+
+ # Runs all the specified validations and returns true if no errors were added
+ # otherwise false. Context can optionally be supplied to define which callbacks
+ # to test against (the context is defined on the validations using :on).
+ def valid?(context = nil)
+ current_context, self.validation_context = validation_context, context
+ errors.clear
+ @validate_callback_result = nil
+ validation_callback_result = _run_validation_callbacks { @validate_callback_result = _run_validate_callbacks }
+ (validation_callback_result && @validate_callback_result) ? errors.empty? : false
+ ensure
+ self.validation_context = current_context
+ end
+
+ end
+ end
+end
View
76 activemodel/test/cases/validations/callbacks_test.rb
@@ -0,0 +1,76 @@
+# encoding: utf-8
+require 'cases/helper'
+
+class Dog
+ include ActiveModel::Validations
+
+ attr_accessor :name, :history
+
+ def history
+ @history ||= []
+ end
+end
+
+class DogWithMethodCallbacks < Dog
+ before_validation :set_before_validation_marker
+ after_validation :set_after_validation_marker
+
+ def set_before_validation_marker; self.history << 'before_validation_marker'; end
+ def set_after_validation_marker; self.history << 'after_validation_marker' ; end
+end
+
+class DogValidtorsAreProc < Dog
+ before_validation { self.history << 'before_validation_marker' }
+ after_validation { self.history << 'after_validation_marker' }
+end
+
+class DogWithTwoValidators < Dog
+ before_validation { self.history << 'before_validation_marker1' }
+ before_validation { self.history << 'before_validation_marker2' }
+end
+
+class DogValidatorReturningFalse < Dog
+ before_validation { false }
+ before_validation { self.history << 'before_validation_marker2' }
+end
+
+class DogWithMissingName < Dog
+ before_validation { self.history << 'before_validation_marker' }
+ validates_presence_of :name
+end
+
+class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
+
+ def test_before_validation_and_after_validation_callbacks_should_be_called
+ d = DogWithMethodCallbacks.new
+ d.valid?
+ assert_equal ['before_validation_marker', 'after_validation_marker'], d.history
+ end
+
+ def test_before_validation_and_after_validation_callbacks_should_be_called_with_proc
+ d = DogValidtorsAreProc.new
+ d.valid?
+ assert_equal ['before_validation_marker', 'after_validation_marker'], d.history
+ end
+
+ def test_before_validation_and_after_validation_callbacks_should_be_called_in_declared_order
+ d = DogWithTwoValidators.new
+ d.valid?
+ assert_equal ['before_validation_marker1', 'before_validation_marker2'], d.history
+ end
+
+ def test_further_callbacks_should_not_be_called_if_before_validation_returns_false
+ d = DogValidatorReturningFalse.new
+ output = d.valid?
+ assert_equal [], d.history
+ assert_equal false, output
+ end
+
+ def test_validation_test_should_be_done
+ d = DogWithMissingName.new
+ output = d.valid?
+ assert_equal ['before_validation_marker'], d.history
+ assert_equal false, output
+ end
+
+end
View
1  activerecord/lib/active_record/base.rb
@@ -1874,6 +1874,7 @@ def object_from_yaml(string)
extend ActiveSupport::DescendantsTracker
include ActiveModel::Conversion
+ include ActiveModel::Validations::Callbacks
include Validations
extend CounterCache
include Locking::Optimistic, Locking::Pessimistic
View
23 activerecord/lib/active_record/callbacks.rb
@@ -235,7 +235,7 @@ module Callbacks
included do
extend ActiveModel::Callbacks
- define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name]
+ attr_accessor :validation_context
define_model_callbacks :initialize, :find, :only => :after
define_model_callbacks :save, :create, :update, :destroy
@@ -250,28 +250,11 @@ def method_added(meth)
end
end
- def before_validation(*args, &block)
- options = args.last
- if options.is_a?(Hash) && options[:on]
- options[:if] = Array.wrap(options[:if])
- options[:if] << "@_on_validate == :#{options[:on]}"
- end
- set_callback(:validation, :before, *args, &block)
- end
-
- def after_validation(*args, &block)
- options = args.extract_options!
- options[:prepend] = true
- options[:if] = Array.wrap(options[:if])
- options[:if] << "!halted && value != false"
- options[:if] << "@_on_validate == :#{options[:on]}" if options[:on]
- set_callback(:validation, :after, *(args << options), &block)
- end
end
def valid?(*) #:nodoc:
- @_on_validate = new_record? ? :create : :update
- _run_validation_callbacks { super }
+ self.validation_context = new_record? ? :create : :update
+ super
end
def destroy #:nodoc:
View
4 activerecord/lib/active_record/validations.rb
@@ -49,12 +49,12 @@ def save!(options={})
# Runs all the specified validations and returns true if no errors were added otherwise false.
def valid?(context = nil)
context ||= (new_record? ? :create : :update)
- super(context)
+ output = super(context)
deprecated_callback_method(:validate)
deprecated_callback_method(:"validate_on_#{context}")
- errors.empty?
+ errors.empty? && output
end
protected
Please sign in to comment.
Something went wrong with that request. Please try again.