Permalink
Browse files

validation macros can now be used within an instance

  • Loading branch information...
1 parent 2203c78 commit 9131a88bb8e82f139ec49b4057fb6065ba0a2c6a @joshk joshk committed with Carl Lerche May 11, 2010
@@ -46,6 +46,9 @@ module Validations
included do
extend ActiveModel::Translation
+
+ extend HelperMethods; include HelperMethods
+
define_callbacks :validate, :scope => :name
attr_accessor :validation_context
@@ -138,12 +141,6 @@ def validators_on(attribute)
def attribute_method?(attribute)
method_defined?(attribute)
end
- private
-
- def _merge_attributes(attr_names)
- options = attr_names.extract_options!
- options.merge(:attributes => attr_names.flatten)
- end
end
# Returns the Errors object that holds all information about attribute error messages.
@@ -21,7 +21,7 @@ def setup(klass)
end
end
- module ClassMethods
+ module HelperMethods
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
#
# class Person < ActiveRecord::Base
@@ -12,7 +12,7 @@ def setup(klass)
end
end
- module ClassMethods
+ module HelperMethods
# Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
#
# Model:
@@ -12,7 +12,7 @@ def validate_each(record, attribute, value)
end
end
- module ClassMethods
+ module HelperMethods
# Validates that the value of the specified attribute is not in a particular enumerable object.
#
# class Person < ActiveRecord::Base
@@ -24,7 +24,7 @@ def check_validity!
end
end
- module ClassMethods
+ module HelperMethods
# Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided.
# You can require that the attribute matches the regular expression:
#
@@ -0,0 +1,12 @@
+module ActiveModel
+ module Validations
+ module HelperMethods
+ private
+
+ def _merge_attributes(attr_names)
+ options = attr_names.extract_options!
+ options.merge(:attributes => attr_names.flatten)
+ end
+ end
+ end
+end
@@ -12,7 +12,7 @@ def validate_each(record, attribute, value)
end
end
- module ClassMethods
+ module HelperMethods
# Validates whether the value of the specified attribute is available in a particular enumerable object.
#
# class Person < ActiveRecord::Base
@@ -51,7 +51,7 @@ def validate_each(record, attribute, value)
end
end
- module ClassMethods
+ module HelperMethods
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
#
@@ -70,7 +70,7 @@ def parse_raw_value_as_an_integer(raw_value)
end
- module ClassMethods
+ module HelperMethods
# Validates whether the value of the specified attribute is numeric by trying to convert it to
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
@@ -8,7 +8,7 @@ def validate(record)
end
end
- module ClassMethods
+ module HelperMethods
# Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example:
#
# class Person < ActiveRecord::Base
@@ -75,5 +75,71 @@ def validates_with(*args, &block)
end
end
end
+
+ # Passes the record off to the class or classes specified and allows them
+ # to add errors based on more complex conditions.
+ #
+ # class Person
+ # include ActiveModel::Validations
+ #
+ # validates :instance_validations
+ #
+ # def instance_validations
+ # validates_with MyValidator
+ # end
+ # end
+ #
+ # class MyValidator < ActiveModel::Validator
+ # def validate(record)
+ # if some_complex_logic
+ # record.errors[:base] << "This record is invalid"
+ # end
+ # end
+ #
+ # private
+ # def some_complex_logic
+ # # ...
+ # end
+ # end
+ #
+ # You may also pass it multiple classes, like so:
+ #
+ # class Person
+ # include ActiveModel::Validations
+ #
+ # validates :instance_validations, :on => :create
+ #
+ # def instance_validations
+ # validates_with MyValidator, MyOtherValidator
+ # end
+ # end
+ #
+ # Standard configuration options (:on, :if and :unless), which are
+ # available on the class version of validates_with, should instead be
+ # placed on the <tt>validates</tt> method as these are applied and tested
+ # in the callback
+ #
+ # If you pass any additional configuration options, they will be passed
+ # to the class and available as <tt>options</tt>:
+ #
+ # class Person
+ # include ActiveModel::Validations
+ # validates_with MyValidator, :my_custom_key => "my custom value"
+ # end
+ #
+ # class MyValidator < ActiveModel::Validator
+ # def validate(record)
+ # options[:my_custom_key] # => "my custom value"
+ # end
+ # end
+ #
+ def validates_with(*args, &block)
+ options = args.extract_options!
+ args.each do |klass|
+ validator = klass.new(options, &block)
+ validator.setup(self) if validator.respond_to?(:setup)
+ validator.validate(self)
+ end
+ end
end
end
@@ -4,6 +4,7 @@
require 'models/topic'
require 'models/reply'
require 'models/custom_reader'
+require 'models/automobile'
class ValidationsTest < ActiveModel::TestCase
@@ -252,4 +253,16 @@ 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
+
+ def test_validations_on_the_instance_level
+ auto = Automobile.new
+
+ assert auto.invalid?
+ assert_equal 2, auto.errors.size
+
+ auto.make = 'Toyota'
+ auto.model = 'Corolla'
+
+ assert auto.valid?
+ end
end
@@ -0,0 +1,12 @@
+class Automobile
+ include ActiveModel::Validations
+
+ validate :validations
+
+ attr_accessor :make, :model
+
+ def validations
+ validates_presence_of :make
+ validates_length_of :model, :within => 2..10
+ end
+end

0 comments on commit 9131a88

Please sign in to comment.