Skip to content

Commit

Permalink
Allow validations to use values from custom readers [#2936 state:reso…
Browse files Browse the repository at this point in the history
…lved]

Signed-off-by: Joshua Peek <josh@joshpeek.com>
  • Loading branch information
James Hill authored and josh committed Aug 5, 2009
1 parent 64268a0 commit cfd421d
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 4 deletions.
6 changes: 3 additions & 3 deletions activemodel/lib/active_model/errors.rb
Expand Up @@ -68,7 +68,7 @@ def add(attribute, message = nil, options = {})
# 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
Expand All @@ -77,7 +77,7 @@ def add_on_empty(attributes, custom_message = nil)
# 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
Expand Down Expand Up @@ -146,7 +146,7 @@ def generate_message(attribute, message = :invalid, options = {})
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,
Expand Down
24 changes: 23 additions & 1 deletion activemodel/lib/active_model/validations.rb
Expand Up @@ -66,7 +66,7 @@ def validates_each(*attrs)
# 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
Expand Down Expand Up @@ -95,6 +95,28 @@ def valid?
def invalid?
!valid?
end

protected
# Hook method defining how an attribute value should be retieved. By default this is assumed

This comment has been minimized.

Copy link
@henrik

henrik Aug 15, 2009

Contributor

retieved -> retrieved

# 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

Expand Down
14 changes: 14 additions & 0 deletions activemodel/test/cases/validations/presence_validation_test.rb
Expand Up @@ -54,4 +54,18 @@ def test_validates_presence_of_for_ruby_class
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
14 changes: 14 additions & 0 deletions activemodel/test/cases/validations_test.rb
Expand Up @@ -5,6 +5,7 @@
require 'models/topic'
require 'models/reply'
require 'models/developer'
require 'models/custom_reader'

class ValidationsTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
Expand Down Expand Up @@ -97,6 +98,19 @@ def test_validates_each
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") }
Expand Down
17 changes: 17 additions & 0 deletions 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

0 comments on commit cfd421d

Please sign in to comment.