1
0
Fork 0
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:
Prem Sichanugrist 2010-02-18 22:28:48 +07:00 committed by José Valim
parent 250c809246
commit 8f97e9d19a
5 changed files with 73 additions and 3 deletions

View file

@ -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!

View file

@ -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

View file

@ -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)

View file

@ -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"

View file

@ -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