Skip to content
This repository
Browse code

Allow validations to use values from custom readers [#2936 state:reso…

…lved]

Signed-off-by: Joshua Peek <josh@joshpeek.com>
  • Loading branch information...
commit cfd421daa2b04216e27d666361eb4053020e027d 1 parent 64268a0
authored August 05, 2009 josh committed August 05, 2009
6  activemodel/lib/active_model/errors.rb
@@ -68,7 +68,7 @@ def add(attribute, message = nil, options = {})
68 68
     # Will add an error message to each of the attributes in +attributes+ that is empty.
69 69
     def add_on_empty(attributes, custom_message = nil)
70 70
       [attributes].flatten.each do |attribute|
71  
-        value = @base.send(attribute)
  71
+        value = @base.instance_eval { read_attribute_for_validation(attribute) }
72 72
         is_empty = value.respond_to?(:empty?) ? value.empty? : false
73 73
         add(attribute, :empty, :default => custom_message) unless !value.nil? && !is_empty
74 74
       end
@@ -77,7 +77,7 @@ def add_on_empty(attributes, custom_message = nil)
77 77
     # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?).
78 78
     def add_on_blank(attributes, custom_message = nil)
79 79
       [attributes].flatten.each do |attribute|
80  
-        value = @base.send(attribute)
  80
+        value = @base.instance_eval { read_attribute_for_validation(attribute) }
81 81
         add(attribute, :blank, :default => custom_message) if value.blank?
82 82
       end
83 83
     end
@@ -146,7 +146,7 @@ def generate_message(attribute, message = :invalid, options = {})
146 146
       defaults = defaults.compact.flatten << :"messages.#{message}"
147 147
 
148 148
       key = defaults.shift
149  
-      value = @base.send(attribute)
  149
+      value = @base.instance_eval { read_attribute_for_validation(attribute) }
150 150
 
151 151
       options = { :default => defaults,
152 152
         :model => @base.class.name.humanize,
24  activemodel/lib/active_model/validations.rb
@@ -66,7 +66,7 @@ def validates_each(*attrs)
66 66
         # Declare the validation.
67 67
         send(validation_method(options[:on]), options) do |record|
68 68
           attrs.each do |attr|
69  
-            value = record.send(attr)
  69
+            value = record.instance_eval { read_attribute_for_validation(attr) }
70 70
             next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
71 71
             yield record, attr, value
72 72
           end
@@ -95,6 +95,28 @@ def valid?
95 95
     def invalid?
96 96
       !valid?
97 97
     end
  98
+    
  99
+    protected
  100
+      # Hook method defining how an attribute value should be retieved. By default this is assumed
  101
+      # to be an instance named after the attribute. Override this method in subclasses should you
  102
+      # need to retrieve the value for a given attribute differently e.g.
  103
+      #   class MyClass
  104
+      #     include ActiveModel::Validations
  105
+      #
  106
+      #     def initialize(data = {})
  107
+      #       @data = data
  108
+      #     end
  109
+      #     
  110
+      #     private
  111
+      #     
  112
+      #     def read_attribute_for_validation(key)
  113
+      #       @data[key]
  114
+      #     end
  115
+      #   end
  116
+      #
  117
+      def read_attribute_for_validation(key)
  118
+        send(key)
  119
+      end
98 120
   end
99 121
 end
100 122
 
14  activemodel/test/cases/validations/presence_validation_test.rb
@@ -54,4 +54,18 @@ def test_validates_presence_of_for_ruby_class
54 54
       assert p.valid?
55 55
     end
56 56
   end
  57
+  
  58
+  def test_validates_presence_of_for_ruby_class_with_custom_reader
  59
+    repair_validations(Person) do
  60
+      CustomReader.validates_presence_of :karma
  61
+
  62
+      p = CustomReader.new
  63
+      assert p.invalid?
  64
+
  65
+      assert_equal ["can't be blank"], p.errors[:karma]
  66
+
  67
+      p[:karma] = "Cold"
  68
+      assert p.valid?
  69
+    end
  70
+  end
57 71
 end
14  activemodel/test/cases/validations_test.rb
@@ -5,6 +5,7 @@
5 5
 require 'models/topic'
6 6
 require 'models/reply'
7 7
 require 'models/developer'
  8
+require 'models/custom_reader'
8 9
 
9 10
 class ValidationsTest < ActiveModel::TestCase
10 11
   include ActiveModel::TestsDatabase
@@ -97,6 +98,19 @@ def test_validates_each
97 98
     assert_equal %w(gotcha gotcha), t.errors[:title]
98 99
     assert_equal %w(gotcha gotcha), t.errors[:content]
99 100
   end
  101
+  
  102
+  def test_validates_each_custom_reader
  103
+    hits = 0
  104
+    CustomReader.validates_each(:title, :content, [:title, :content]) do |record, attr|
  105
+      record.errors.add attr, 'gotcha'
  106
+      hits += 1
  107
+    end
  108
+    t = CustomReader.new("title" => "valid", "content" => "whatever")
  109
+    assert !t.valid?
  110
+    assert_equal 4, hits
  111
+    assert_equal %w(gotcha gotcha), t.errors[:title]
  112
+    assert_equal %w(gotcha gotcha), t.errors[:content]
  113
+  end
100 114
 
101 115
   def test_validate_block
102 116
     Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
17  activemodel/test/models/custom_reader.rb
... ...
@@ -0,0 +1,17 @@
  1
+class CustomReader
  2
+  include ActiveModel::Validations
  3
+
  4
+  def initialize(data = {})
  5
+    @data = data
  6
+  end
  7
+  
  8
+  def []=(key, value)
  9
+    @data[key] = value
  10
+  end
  11
+
  12
+  private
  13
+  
  14
+  def read_attribute_for_validation(key)
  15
+    @data[key]
  16
+  end
  17
+end

0 notes on commit cfd421d

Please sign in to comment.
Something went wrong with that request. Please try again.