aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
authorJames Hill <jamie@jamie-laptop.local>2009-08-05 11:44:44 -0500
committerJoshua Peek <josh@joshpeek.com>2009-08-05 11:45:01 -0500
commitcfd421daa2b04216e27d666361eb4053020e027d (patch)
tree878e4980a47433990e96c2edb8b28ee5b1ae22f1 /activemodel
parent64268a0b06d32567c6e88b7293f332b79e10414b (diff)
downloadrails-cfd421daa2b04216e27d666361eb4053020e027d.tar.gz
rails-cfd421daa2b04216e27d666361eb4053020e027d.tar.bz2
rails-cfd421daa2b04216e27d666361eb4053020e027d.zip
Allow validations to use values from custom readers [#2936 state:resolved]
Signed-off-by: Joshua Peek <josh@joshpeek.com>
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/lib/active_model/errors.rb6
-rw-r--r--activemodel/lib/active_model/validations.rb24
-rw-r--r--activemodel/test/cases/validations/presence_validation_test.rb14
-rw-r--r--activemodel/test/cases/validations_test.rb14
-rw-r--r--activemodel/test/models/custom_reader.rb17
5 files changed, 71 insertions, 4 deletions
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index a4cf700231..a47d89ac0e 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -68,7 +68,7 @@ module ActiveModel
# Will add an error message to each of the attributes in +attributes+ that is empty.
def add_on_empty(attributes, custom_message = nil)
[attributes].flatten.each do |attribute|
- value = @base.send(attribute)
+ value = @base.instance_eval { read_attribute_for_validation(attribute) }
is_empty = value.respond_to?(:empty?) ? value.empty? : false
add(attribute, :empty, :default => custom_message) unless !value.nil? && !is_empty
end
@@ -77,7 +77,7 @@ module ActiveModel
# Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
def add_on_blank(attributes, custom_message = nil)
[attributes].flatten.each do |attribute|
- value = @base.send(attribute)
+ value = @base.instance_eval { read_attribute_for_validation(attribute) }
add(attribute, :blank, :default => custom_message) if value.blank?
end
end
@@ -146,7 +146,7 @@ module ActiveModel
defaults = defaults.compact.flatten << :"messages.#{message}"
key = defaults.shift
- value = @base.send(attribute)
+ value = @base.instance_eval { read_attribute_for_validation(attribute) }
options = { :default => defaults,
:model => @base.class.name.humanize,
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 54a869396d..0fca96e5cc 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -66,7 +66,7 @@ module ActiveModel
# Declare the validation.
send(validation_method(options[:on]), options) do |record|
attrs.each do |attr|
- value = record.send(attr)
+ value = record.instance_eval { read_attribute_for_validation(attr) }
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
yield record, attr, value
end
@@ -95,6 +95,28 @@ module ActiveModel
def invalid?
!valid?
end
+
+ protected
+ # Hook method defining how an attribute value should be retieved. By default this is assumed
+ # to be an instance named after the attribute. Override this method in subclasses should you
+ # need to retrieve the value for a given attribute differently e.g.
+ # class MyClass
+ # include ActiveModel::Validations
+ #
+ # def initialize(data = {})
+ # @data = data
+ # end
+ #
+ # private
+ #
+ # def read_attribute_for_validation(key)
+ # @data[key]
+ # end
+ # end
+ #
+ def read_attribute_for_validation(key)
+ send(key)
+ end
end
end
diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb
index aa5bdf1e62..bb6fb91774 100644
--- a/activemodel/test/cases/validations/presence_validation_test.rb
+++ b/activemodel/test/cases/validations/presence_validation_test.rb
@@ -54,4 +54,18 @@ class PresenceValidationTest < ActiveModel::TestCase
assert p.valid?
end
end
+
+ def test_validates_presence_of_for_ruby_class_with_custom_reader
+ repair_validations(Person) do
+ CustomReader.validates_presence_of :karma
+
+ p = CustomReader.new
+ assert p.invalid?
+
+ assert_equal ["can't be blank"], p.errors[:karma]
+
+ p[:karma] = "Cold"
+ assert p.valid?
+ end
+ end
end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 8c89494247..0b340e68bf 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -5,6 +5,7 @@ require 'cases/tests_database'
require 'models/topic'
require 'models/reply'
require 'models/developer'
+require 'models/custom_reader'
class ValidationsTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
@@ -97,6 +98,19 @@ class ValidationsTest < ActiveModel::TestCase
assert_equal %w(gotcha gotcha), t.errors[:title]
assert_equal %w(gotcha gotcha), t.errors[:content]
end
+
+ def test_validates_each_custom_reader
+ hits = 0
+ CustomReader.validates_each(:title, :content, [:title, :content]) do |record, attr|
+ record.errors.add attr, 'gotcha'
+ hits += 1
+ end
+ t = CustomReader.new("title" => "valid", "content" => "whatever")
+ assert !t.valid?
+ assert_equal 4, hits
+ assert_equal %w(gotcha gotcha), t.errors[:title]
+ assert_equal %w(gotcha gotcha), t.errors[:content]
+ end
def test_validate_block
Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
diff --git a/activemodel/test/models/custom_reader.rb b/activemodel/test/models/custom_reader.rb
new file mode 100644
index 0000000000..7ac70e6167
--- /dev/null
+++ b/activemodel/test/models/custom_reader.rb
@@ -0,0 +1,17 @@
+class CustomReader
+ include ActiveModel::Validations
+
+ def initialize(data = {})
+ @data = data
+ end
+
+ def []=(key, value)
+ @data[key] = value
+ end
+
+ private
+
+ def read_attribute_for_validation(key)
+ @data[key]
+ end
+end \ No newline at end of file