mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add validators reflection so you can do 'Person.validators' and 'Person.validators_on(:name)'.
Signed-off-by: José Valim <jose.valim@gmail.com>
This commit is contained in:
parent
250c809246
commit
8f97e9d19a
5 changed files with 73 additions and 3 deletions
|
@ -1,4 +1,5 @@
|
||||||
require 'active_support/core_ext/array/extract_options'
|
require 'active_support/core_ext/array/extract_options'
|
||||||
|
require 'active_support/core_ext/class/attribute'
|
||||||
require 'active_support/core_ext/hash/keys'
|
require 'active_support/core_ext/hash/keys'
|
||||||
require 'active_model/errors'
|
require 'active_model/errors'
|
||||||
|
|
||||||
|
@ -45,6 +46,9 @@ module ActiveModel
|
||||||
included do
|
included do
|
||||||
extend ActiveModel::Translation
|
extend ActiveModel::Translation
|
||||||
define_callbacks :validate, :scope => :name
|
define_callbacks :validate, :scope => :name
|
||||||
|
|
||||||
|
class_attribute :_validators
|
||||||
|
self._validators = Hash.new { |h,k| h[k] = [] }
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
@ -118,7 +122,18 @@ module ActiveModel
|
||||||
set_callback(:validate, *args, &block)
|
set_callback(:validate, *args, &block)
|
||||||
end
|
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)
|
def _merge_attributes(attr_names)
|
||||||
options = attr_names.extract_options!
|
options = attr_names.extract_options!
|
||||||
|
|
|
@ -62,6 +62,15 @@ module ActiveModel
|
||||||
args.each do |klass|
|
args.each do |klass|
|
||||||
validator = klass.new(options, &block)
|
validator = klass.new(options, &block)
|
||||||
validator.setup(self) if validator.respond_to?(:setup)
|
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)
|
validate(validator, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
require "active_support/core_ext/module/anonymous"
|
||||||
|
|
||||||
module ActiveModel #:nodoc:
|
module ActiveModel #:nodoc:
|
||||||
# A simple base class that can be used along with
|
# A simple base class that can be used along with
|
||||||
# +ActiveModel::Validations::ClassMethods.validates_with+
|
# +ActiveModel::Validations::ClassMethods.validates_with+
|
||||||
|
@ -88,11 +90,27 @@ module ActiveModel #:nodoc:
|
||||||
class Validator
|
class Validator
|
||||||
attr_reader :options
|
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.
|
# Accepts options that will be made availible through the +options+ reader.
|
||||||
def initialize(options)
|
def initialize(options)
|
||||||
@options = options
|
@options = options
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return the kind for this validator.
|
||||||
|
def kind
|
||||||
|
self.class.kind
|
||||||
|
end
|
||||||
|
|
||||||
# Override this method in subclasses with validation logic, adding errors
|
# Override this method in subclasses with validation logic, adding errors
|
||||||
# to the records +errors+ array where necessary.
|
# to the records +errors+ array where necessary.
|
||||||
def validate(record)
|
def validate(record)
|
||||||
|
|
|
@ -9,6 +9,7 @@ class ValidatesWithTest < ActiveRecord::TestCase
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
Topic.reset_callbacks(:validate)
|
Topic.reset_callbacks(:validate)
|
||||||
|
Topic._validators.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
ERROR_MESSAGE = "Validation error from validator"
|
ERROR_MESSAGE = "Validation error from validator"
|
||||||
|
|
|
@ -10,6 +10,10 @@ require 'models/custom_reader'
|
||||||
class ValidationsTest < ActiveModel::TestCase
|
class ValidationsTest < ActiveModel::TestCase
|
||||||
include ActiveModel::TestsDatabase
|
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.
|
# 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
|
# Other classes we mess with will be dealt with in the specific tests
|
||||||
def teardown
|
def teardown
|
||||||
|
@ -220,4 +224,27 @@ class ValidationsTest < ActiveModel::TestCase
|
||||||
assert !t.valid?
|
assert !t.valid?
|
||||||
assert ["NO BLANKS HERE"], t.errors[:title]
|
assert ["NO BLANKS HERE"], t.errors[:title]
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in a new issue