aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activemodel/lib/active_model/validations.rb21
-rw-r--r--activemodel/lib/active_model/validations/with.rb9
-rw-r--r--activemodel/lib/active_model/validator.rb18
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations_test.rb27
5 files changed, 73 insertions, 3 deletions
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 03733a9c89..7f6748a660 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/keys'
require 'active_model/errors'
@@ -45,6 +46,9 @@ module ActiveModel
included do
extend ActiveModel::Translation
define_callbacks :validate, :scope => :name
+
+ class_attribute :_validators
+ self._validators = Hash.new { |h,k| h[k] = [] }
end
module ClassMethods
@@ -117,9 +121,20 @@ module ActiveModel
end
set_callback(:validate, *args, &block)
end
-
- private
-
+
+ # List all validators that being used to validate the model using +validates_with+
+ # method.
+ def validators
+ _validators.values.flatten.uniq
+ end
+
+ # List all validators that being used to validate a specific attribute.
+ def validators_on(attribute)
+ _validators[attribute.to_sym]
+ end
+
+ private
+
def _merge_attributes(attr_names)
options = attr_names.extract_options!
options.merge(:attributes => attr_names)
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index db563876af..83d3ea80d6 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -62,6 +62,15 @@ module ActiveModel
args.each do |klass|
validator = klass.new(options, &block)
validator.setup(self) if validator.respond_to?(:setup)
+
+ if validator.respond_to?(:attributes) && !validator.attributes.empty?
+ validator.attributes.each do |attribute|
+ _validators[attribute.to_sym] << validator
+ end
+ else
+ _validators[nil] << validator
+ end
+
validate(validator, options)
end
end
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index ad9729de00..b61f0cb266 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/module/anonymous"
+
module ActiveModel #:nodoc:
# A simple base class that can be used along with
# +ActiveModel::Validations::ClassMethods.validates_with+
@@ -88,11 +90,27 @@ module ActiveModel #:nodoc:
class Validator
attr_reader :options
+ # Returns the kind of the validator.
+ #
+ # == Examples
+ #
+ # PresenceValidator.kind #=> :presence
+ # UniquenessValidator.kind #=> :uniqueness
+ #
+ def self.kind
+ @kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
+ end
+
# Accepts options that will be made availible through the +options+ reader.
def initialize(options)
@options = options
end
+ # Return the kind for this validator.
+ def kind
+ self.class.kind
+ end
+
# Override this method in subclasses with validation logic, adding errors
# to the records +errors+ array where necessary.
def validate(record)
diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb
index 66b072ea38..92df4dd6cd 100644
--- a/activemodel/test/cases/validations/with_validation_test.rb
+++ b/activemodel/test/cases/validations/with_validation_test.rb
@@ -9,6 +9,7 @@ class ValidatesWithTest < ActiveRecord::TestCase
def teardown
Topic.reset_callbacks(:validate)
+ Topic._validators.clear
end
ERROR_MESSAGE = "Validation error from validator"
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index eb100d1c35..9fedd84c73 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -10,6 +10,10 @@ require 'models/custom_reader'
class ValidationsTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
+ def setup
+ Topic._validators.clear
+ end
+
# Most of the tests mess with the validations of Topic, so lets repair it all the time.
# Other classes we mess with will be dealt with in the specific tests
def teardown
@@ -220,4 +224,27 @@ class ValidationsTest < ActiveModel::TestCase
assert !t.valid?
assert ["NO BLANKS HERE"], t.errors[:title]
end
+
+ def test_list_of_validators_for_model
+ Topic.validates_presence_of :title
+ Topic.validates_length_of :title, :minimum => 2
+
+ assert_equal 2, Topic.validators.count
+ assert_equal [:presence, :length], Topic.validators.map(&:kind)
+ end
+
+ def test_list_of_validators_on_an_attribute
+ Topic.validates_presence_of :title, :content
+ Topic.validates_length_of :title, :minimum => 2
+
+ assert_equal 2, Topic.validators_on(:title).count
+ assert_equal [:presence, :length], Topic.validators_on(:title).map(&:kind)
+ assert_equal 1, Topic.validators_on(:content).count
+ assert_equal [:presence], Topic.validators_on(:content).map(&:kind)
+ end
+
+ def test_accessing_instance_of_validator_on_an_attribute
+ Topic.validates_length_of :title, :minimum => 10
+ assert_equal 10, Topic.validators_on(:title).first.options[:minimum]
+ end
end