aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2010-01-04 03:24:39 +0530
committerPratik Naik <pratiknaik@gmail.com>2010-01-04 03:24:39 +0530
commitcda36a0731f14b33a920bf7e32255661e06f890a (patch)
tree79ccba37953f9fe3055503be42b1610faa6d64ad /activemodel
parentbd4a3cce4ecd8e648179a91e26506e3622ac2162 (diff)
parenta115b5d79a850bb56cd3c9db9a05d6da35e3d7be (diff)
downloadrails-cda36a0731f14b33a920bf7e32255661e06f890a.tar.gz
rails-cda36a0731f14b33a920bf7e32255661e06f890a.tar.bz2
rails-cda36a0731f14b33a920bf7e32255661e06f890a.zip
Merge remote branch 'mainstream/master'
Diffstat (limited to 'activemodel')
-rwxr-xr-xactivemodel/Rakefile1
-rw-r--r--activemodel/lib/active_model.rb47
-rw-r--r--activemodel/lib/active_model/callbacks.rb91
-rw-r--r--activemodel/lib/active_model/errors.rb11
-rw-r--r--activemodel/lib/active_model/lint.rb14
-rw-r--r--activemodel/lib/active_model/locale/en.yml3
-rw-r--r--activemodel/lib/active_model/naming.rb26
-rw-r--r--activemodel/lib/active_model/railtie.rb2
-rw-r--r--activemodel/lib/active_model/translation.rb22
-rw-r--r--activemodel/lib/active_model/validations.rb57
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb21
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb20
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb26
-rw-r--r--activemodel/lib/active_model/validations/format.rb28
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb26
-rw-r--r--activemodel/lib/active_model/validations/length.rb129
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb122
-rw-r--r--activemodel/lib/active_model/validations/presence.rb15
-rw-r--r--activemodel/lib/active_model/validations/with.rb11
-rw-r--r--activemodel/lib/active_model/validations_repair_helper.rb35
-rw-r--r--activemodel/lib/active_model/validator.rb56
-rw-r--r--activemodel/test/cases/callbacks_test.rb70
-rw-r--r--activemodel/test/cases/helper.rb7
-rw-r--r--activemodel/test/cases/lint_test.rb2
-rw-r--r--activemodel/test/cases/naming_test.rb3
-rw-r--r--activemodel/test/cases/tests_database.rb1
-rw-r--r--activemodel/test/cases/translation_test.rb36
-rw-r--r--activemodel/test/cases/validations/acceptance_validation_test.rb33
-rw-r--r--activemodel/test/cases/validations/conditional_validation_test.rb5
-rw-r--r--activemodel/test/cases/validations/confirmation_validation_test.rb34
-rw-r--r--activemodel/test/cases/validations/exclusion_validation_test.rb23
-rw-r--r--activemodel/test/cases/validations/format_validation_test.rb33
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb23
-rw-r--r--activemodel/test/cases/validations/inclusion_validation_test.rb33
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb53
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb41
-rw-r--r--activemodel/test/cases/validations/presence_validation_test.rb54
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb66
-rw-r--r--activemodel/test/cases/validations_test.rb11
-rw-r--r--activemodel/test/models/person.rb6
-rw-r--r--activemodel/test/models/track_back.rb4
41 files changed, 744 insertions, 557 deletions
diff --git a/activemodel/Rakefile b/activemodel/Rakefile
index 1f4a8466c9..f098ce0671 100755
--- a/activemodel/Rakefile
+++ b/activemodel/Rakefile
@@ -15,7 +15,6 @@ task :default => :test
Rake::TestTask.new do |t|
t.libs << "#{dir}/test"
t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort
- t.verbose = true
t.warning = true
end
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 505e16c195..f14016027c 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -21,34 +21,41 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
-$:.unshift(activesupport_path) if File.directory?(activesupport_path)
+activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
+$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'active_support'
+
module ActiveModel
- autoload :AttributeMethods, 'active_model/attribute_methods'
- autoload :Conversion, 'active_model/conversion'
- autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods'
- autoload :Dirty, 'active_model/dirty'
- autoload :Errors, 'active_model/errors'
- autoload :Lint, 'active_model/lint'
+ extend ActiveSupport::Autoload
+
+ autoload :AttributeMethods
+ autoload :Callbacks
+ autoload :Conversion
+ autoload :DeprecatedErrorMethods
+ autoload :Dirty
+ autoload :Errors
+ autoload :Lint
autoload :Name, 'active_model/naming'
- autoload :Naming, 'active_model/naming'
+ autoload :Naming
autoload :Observer, 'active_model/observing'
- autoload :Observing, 'active_model/observing'
- autoload :Serialization, 'active_model/serialization'
- autoload :StateMachine, 'active_model/state_machine'
- autoload :TestCase, 'active_model/test_case'
- autoload :Translation, 'active_model/translation'
- autoload :Validations, 'active_model/validations'
- autoload :ValidationsRepairHelper, 'active_model/validations_repair_helper'
- autoload :Validator, 'active_model/validator'
- autoload :VERSION, 'active_model/version'
+ autoload :Observing
+ autoload :Serialization
+ autoload :StateMachine
+ autoload :Translation
+ autoload :Validations
+ autoload :Validator
+ autoload :EachValidator, 'active_model/validator'
+ autoload :BlockValidator, 'active_model/validator'
+ autoload :VERSION
module Serializers
- autoload :JSON, 'active_model/serializers/json'
- autoload :Xml, 'active_model/serializers/xml'
+ extend ActiveSupport::Autoload
+
+ autoload :JSON
+ autoload :Xml
end
end
+require 'active_support/i18n'
I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
new file mode 100644
index 0000000000..f66a1ddcaa
--- /dev/null
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -0,0 +1,91 @@
+require 'active_support/callbacks'
+
+module ActiveModel
+ module Callbacks
+ def self.extended(base)
+ base.class_eval do
+ include ActiveSupport::Callbacks
+ end
+ end
+
+ # Define callbacks similar to ActiveRecord ones. It means:
+ #
+ # * The callback chain is aborted whenever the block given to
+ # _run_callbacks returns false.
+ #
+ # * If a class is given to the fallback, it will search for
+ # before_create, around_create and after_create methods.
+ #
+ # == Usage
+ #
+ # First you need to define which callbacks your model will have:
+ #
+ # class MyModel
+ # define_model_callbacks :create
+ # end
+ #
+ # This will define three class methods: before_create, around_create,
+ # and after_create. They accept a symbol, a string, an object or a block.
+ #
+ # After you create a callback, you need to tell when they are executed.
+ # For example, you could do:
+ #
+ # def create
+ # _run_create_callbacks do
+ # super
+ # end
+ # end
+ #
+ # == Options
+ #
+ # define_model_callbacks accepts all options define_callbacks does, in
+ # case you want to overwrite a default. Besides that, it also accepts
+ # an :only option, where you can choose if you want all types (before,
+ # around or after) or just some:
+ #
+ # define_model_callbacks :initializer, :only => :after
+ #
+ def define_model_callbacks(*callbacks)
+ options = callbacks.extract_options!
+ options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)
+
+ types = Array(options.delete(:only))
+ types = [:before, :around, :after] if types.empty?
+
+ callbacks.each do |callback|
+ define_callbacks(callback, options)
+
+ types.each do |type|
+ send(:"_define_#{type}_model_callback", self, callback)
+ end
+ end
+ end
+
+ def _define_before_model_callback(klass, callback) #:nodoc:
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__
+ def self.before_#{callback}(*args, &block)
+ set_callback(:#{callback}, :before, *args, &block)
+ end
+ CALLBACK
+ end
+
+ def _define_around_model_callback(klass, callback) #:nodoc:
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__
+ def self.around_#{callback}(*args, &block)
+ set_callback(:#{callback}, :around, *args, &block)
+ end
+ CALLBACK
+ end
+
+ def _define_after_model_callback(klass, callback) #:nodoc:
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__
+ def self.after_#{callback}(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array(options[:if]) << "!halted && value != false"
+ set_callback(:#{callback}, :after, *(args << options), &block)
+ end
+ CALLBACK
+ end
+ end
+end \ No newline at end of file
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index e8bb62953d..abc084a74b 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -97,18 +97,19 @@ module ActiveModel
full_messages = []
each do |attribute, messages|
- messages = Array.wrap(messages)
+ messages = Array(messages)
next if messages.empty?
if attribute == :base
messages.each {|m| full_messages << m }
else
- attr_name = @base.class.human_attribute_name(attribute)
- options = { :default => ' ', :scope => @base.class.i18n_scope }
- prefix = attr_name + I18n.t(:"errors.format.separator", options)
+ attr_name = attribute.to_s.gsub('.', '_').humanize
+ attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
+ options = { :default => "{{attribute}} {{message}}", :attribute => attr_name,
+ :scope => @base.class.i18n_scope }
messages.each do |m|
- full_messages << "#{prefix}#{m}"
+ full_messages << I18n.t(:"errors.format", options.merge(:message => m))
end
end
end
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index 1c2347adbf..0be82aa180 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -41,6 +41,20 @@ module ActiveModel
assert_boolean model.destroyed?, "destroyed?"
end
+ # naming
+ # ------
+ #
+ # Model.model_name must returns a string with some convenience methods as
+ # :human and :partial_path. Check ActiveModel::Naming for more information.
+ #
+ def test_model_naming
+ assert model.class.respond_to?(:model_name), "The model should respond to model_name"
+ model_name = model.class.model_name
+ assert_kind_of String, model_name
+ assert_kind_of String, model_name.human
+ assert_kind_of String, model_name.partial_path
+ end
+
# errors
# ------
#
diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml
index 0c2cf9ea33..1cdb897f13 100644
--- a/activemodel/lib/active_model/locale/en.yml
+++ b/activemodel/lib/active_model/locale/en.yml
@@ -1,6 +1,9 @@
en:
activemodel:
errors:
+ # model.errors.full_messages format.
+ format: "{{attribute}} {{message}}"
+
# The values :model, :attribute and :value are always available for interpolation
# The value :count is available when applicable. Can be used for pluralization.
messages:
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 675d62b9a6..4cd68a0c89 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -2,11 +2,11 @@ require 'active_support/inflector'
module ActiveModel
class Name < String
- attr_reader :singular, :plural, :element, :collection, :partial_path, :human
+ attr_reader :singular, :plural, :element, :collection, :partial_path
alias_method :cache_key, :collection
- def initialize(klass, name)
- super(name)
+ def initialize(klass)
+ super(klass.name)
@klass = klass
@singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze
@plural = ActiveSupport::Inflector.pluralize(@singular).freeze
@@ -15,13 +15,31 @@ module ActiveModel
@collection = ActiveSupport::Inflector.tableize(self).freeze
@partial_path = "#{@collection}/#{@element}".freeze
end
+
+ # Transform the model name into a more humane format, using I18n. By default,
+ # it will underscore then humanize the class name (BlogPost.model_name.human #=> "Blog post").
+ # Specify +options+ with additional translating options.
+ def human(options={})
+ return @human unless @klass.respond_to?(:lookup_ancestors) &&
+ @klass.respond_to?(:i18n_scope)
+
+ defaults = @klass.lookup_ancestors.map do |klass|
+ klass.model_name.underscore.to_sym
+ end
+
+ defaults << options.delete(:default) if options[:default]
+ defaults << @human
+
+ options.reverse_merge! :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults
+ I18n.translate(defaults.shift, options)
+ end
end
module Naming
# Returns an ActiveModel::Name object for module. It can be
# used to retrieve all kinds of naming-related information.
def model_name
- @_model_name ||= ActiveModel::Name.new(self, name)
+ @_model_name ||= ActiveModel::Name.new(self)
end
end
end
diff --git a/activemodel/lib/active_model/railtie.rb b/activemodel/lib/active_model/railtie.rb
new file mode 100644
index 0000000000..63ffe5db63
--- /dev/null
+++ b/activemodel/lib/active_model/railtie.rb
@@ -0,0 +1,2 @@
+require "active_model"
+require "rails" \ No newline at end of file
diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb
index 42ca463f82..e5ef1e6114 100644
--- a/activemodel/lib/active_model/translation.rb
+++ b/activemodel/lib/active_model/translation.rb
@@ -37,28 +37,8 @@ module ActiveModel
# Model.human_name is deprecated. Use Model.model_name.human instead.
def human_name(*args)
- ActiveSupport::Deprecation.warn("human_name has been deprecated, please use model_name.human instead", caller[0,1])
+ ActiveSupport::Deprecation.warn("human_name has been deprecated, please use model_name.human instead", caller[0,5])
model_name.human(*args)
end
end
-
- class Name < String
- # Transform the model name into a more humane format, using I18n. By default,
- # it will underscore then humanize the class name (BlogPost.human_name #=> "Blog post").
- # Specify +options+ with additional translating options.
- def human(options={})
- return @human unless @klass.respond_to?(:lookup_ancestors) &&
- @klass.respond_to?(:i18n_scope)
-
- defaults = @klass.lookup_ancestors.map do |klass|
- klass.model_name.underscore.to_sym
- end
-
- defaults << options.delete(:default) if options[:default]
- defaults << @human
-
- options.reverse_merge! :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults
- I18n.translate(defaults.shift, options)
- end
- end
end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 064ec98f3a..d5460a58bd 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/keys'
+require 'active_model/errors'
module ActiveModel
module Validations
@@ -12,6 +13,29 @@ module ActiveModel
end
module ClassMethods
+ # Validates each attribute against a block.
+ #
+ # class Person < ActiveRecord::Base
+ # validates_each :first_name, :last_name do |record, attr, value|
+ # record.errors.add attr, 'starts with z.' if value[0] == ?z
+ # end
+ # end
+ #
+ # Options:
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
+ # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
+ # method, proc or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
+ # method, proc or string should return or evaluate to a true or false value.
+ def validates_each(*attr_names, &block)
+ options = attr_names.extract_options!.symbolize_keys
+ validates_with BlockValidator, options.merge(:attributes => attr_names.flatten), &block
+ end
+
# Adds a validation method or block to the class. This is useful when
# overriding the +validate+ instance method becomes too unwieldly and
# you're looking for more descriptive declaration of your validations.
@@ -39,39 +63,6 @@ module ActiveModel
# end
#
# This usage applies to +validate_on_create+ and +validate_on_update as well+.
-
- # Validates each attribute against a block.
- #
- # class Person < ActiveRecord::Base
- # validates_each :first_name, :last_name do |record, attr, value|
- # record.errors.add attr, 'starts with z.' if value[0] == ?z
- # end
- # end
- #
- # Options:
- # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
- # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
- # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- def validates_each(*attrs)
- options = attrs.extract_options!.symbolize_keys
- attrs = attrs.flatten
-
- # Declare the validation.
- validate options do |record|
- attrs.each do |attr|
- value = record.send(:read_attribute_for_validation, attr)
- next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
- yield record, attr, value
- end
- end
- end
-
def validate(*args, &block)
options = args.last
if options.is_a?(Hash) && options.key?(:on)
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index b65c9b933d..bd9463ed27 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -1,5 +1,17 @@
module ActiveModel
module Validations
+ class AcceptanceValidator < EachValidator
+ def initialize(options)
+ super(options.reverse_merge(:allow_nil => true, :accept => "1"))
+ end
+
+ def validate_each(record, attribute, value)
+ unless value == options[:accept]
+ record.errors.add(attribute, :accepted, :default => options[:message])
+ end
+ end
+ end
+
module ClassMethods
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
#
@@ -25,8 +37,7 @@ module ActiveModel
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_acceptance_of(*attr_names)
- configuration = { :allow_nil => true, :accept => "1" }
- configuration.update(attr_names.extract_options!)
+ options = attr_names.extract_options!
db_cols = begin
column_names
@@ -37,11 +48,7 @@ module ActiveModel
names = attr_names.reject { |name| db_cols.include?(name.to_s) }
attr_accessor(*names)
- validates_each(attr_names,configuration) do |record, attr_name, value|
- unless value == configuration[:accept]
- record.errors.add(attr_name, :accepted, :default => configuration[:message])
- end
- end
+ validates_with AcceptanceValidator, options.merge(:attributes => attr_names)
end
end
end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index d414224dd2..b06effdceb 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -1,5 +1,13 @@
module ActiveModel
module Validations
+ class ConfirmationValidator < EachValidator
+ def validate_each(record, attribute, value)
+ confirmed = record.send(:"#{attribute}_confirmation")
+ return if confirmed.nil? || value == confirmed
+ record.errors.add(attribute, :confirmation, :default => options[:message])
+ end
+ end
+
module ClassMethods
# Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
#
@@ -30,15 +38,9 @@ module ActiveModel
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_confirmation_of(*attr_names)
- configuration = attr_names.extract_options!
-
- attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" }))
-
- validates_each(attr_names, configuration) do |record, attr_name, value|
- unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
- record.errors.add(attr_name, :confirmation, :default => configuration[:message])
- end
- end
+ options = attr_names.extract_options!
+ attr_accessor(*(attr_names.map { |n| :"#{n}_confirmation" }))
+ validates_with ConfirmationValidator, options.merge(:attributes => attr_names)
end
end
end
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 2cfdec97a5..f8759f253b 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -1,5 +1,17 @@
module ActiveModel
module Validations
+ class ExclusionValidator < EachValidator
+ def check_validity!
+ raise ArgumentError, "An object with the method include? is required must be supplied as the " <<
+ ":in option of the configuration hash" unless options[:in].respond_to?(:include?)
+ end
+
+ def validate_each(record, attribute, value)
+ return unless options[:in].include?(value)
+ record.errors.add(attribute, :exclusion, :default => options[:message], :value => value)
+ end
+ end
+
module ClassMethods
# Validates that the value of the specified attribute is not in a particular enumerable object.
#
@@ -21,17 +33,9 @@ module ActiveModel
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_exclusion_of(*attr_names)
- configuration = attr_names.extract_options!
-
- enum = configuration[:in] || configuration[:within]
-
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
-
- validates_each(attr_names, configuration) do |record, attr_name, value|
- if enum.include?(value)
- record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
- end
- end
+ options = attr_names.extract_options!
+ options[:in] ||= options.delete(:within)
+ validates_with ExclusionValidator, options.merge(:attributes => attr_names)
end
end
end
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index c670dafc7c..d5427c2b03 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -1,5 +1,15 @@
module ActiveModel
module Validations
+ class FormatValidator < EachValidator
+ def validate_each(record, attribute, value)
+ if options[:with] && value.to_s !~ options[:with]
+ record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
+ elsif options[:without] && value.to_s =~ options[:without]
+ record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
+ end
+ end
+ end
+
module ClassMethods
# 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:
@@ -33,29 +43,21 @@ module ActiveModel
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_format_of(*attr_names)
- configuration = attr_names.extract_options!
+ options = attr_names.extract_options!
- unless configuration.include?(:with) ^ configuration.include?(:without) # ^ == xor, or "exclusive or"
+ unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
end
- if configuration[:with] && !configuration[:with].is_a?(Regexp)
+ if options[:with] && !options[:with].is_a?(Regexp)
raise ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash"
end
- if configuration[:without] && !configuration[:without].is_a?(Regexp)
+ if options[:without] && !options[:without].is_a?(Regexp)
raise ArgumentError, "A regular expression must be supplied as the :without option of the configuration hash"
end
- if configuration[:with]
- validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) if value.to_s !~ configuration[:with]
- end
- elsif configuration[:without]
- validates_each(attr_names, configuration) do |record, attr_name, value|
- record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) if value.to_s =~ configuration[:without]
- end
- end
+ validates_with FormatValidator, options.merge(:attributes => attr_names)
end
end
end
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 0d7dc5cd64..a122e9e737 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -1,5 +1,17 @@
module ActiveModel
module Validations
+ class InclusionValidator < EachValidator
+ def check_validity!
+ raise ArgumentError, "An object with the method include? is required must be supplied as the " <<
+ ":in option of the configuration hash" unless options[:in].respond_to?(:include?)
+ end
+
+ def validate_each(record, attribute, value)
+ return if options[:in].include?(value)
+ record.errors.add(attribute, :inclusion, :default => options[:message], :value => value)
+ end
+ end
+
module ClassMethods
# Validates whether the value of the specified attribute is available in a particular enumerable object.
#
@@ -21,17 +33,9 @@ module ActiveModel
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_inclusion_of(*attr_names)
- configuration = attr_names.extract_options!
-
- enum = configuration[:in] || configuration[:within]
-
- raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
-
- validates_each(attr_names, configuration) do |record, attr_name, value|
- unless enum.include?(value)
- record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
- end
- end
+ options = attr_names.extract_options!
+ options[:in] ||= options.delete(:within)
+ validates_with InclusionValidator, options.merge(:attributes => attr_names)
end
end
end
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 6227239c9b..5db2060fe5 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -1,7 +1,75 @@
module ActiveModel
module Validations
+ class LengthValidator < EachValidator
+ OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze
+ MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
+ CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
+
+ DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
+ attr_reader :type
+
+ def initialize(options)
+ @type = (OPTIONS & options.keys).first
+ super(options.reverse_merge(:tokenizer => DEFAULT_TOKENIZER))
+ end
+
+ def check_validity!
+ ensure_one_range_option!
+ ensure_argument_types!
+ end
+
+ def validate_each(record, attribute, value)
+ checks = options.slice(:minimum, :maximum, :is)
+ value = options[:tokenizer].call(value) if value.kind_of?(String)
+
+ if [:within, :in].include?(type)
+ range = options[type]
+ checks[:minimum], checks[:maximum] = range.begin, range.end
+ checks[:maximum] -= 1 if range.exclude_end?
+ end
+
+ checks.each do |key, check_value|
+ custom_message = options[:message] || options[MESSAGES[key]]
+ validity_check = CHECKS[key]
+
+ valid_value = if key == :maximum
+ value.nil? || value.size.send(validity_check, check_value)
+ else
+ value && value.size.send(validity_check, check_value)
+ end
+
+ record.errors.add(attribute, MESSAGES[key], :default => custom_message, :count => check_value) unless valid_value
+ end
+ end
+
+ protected
+
+ def ensure_one_range_option! #:nodoc:
+ range_options = OPTIONS & options.keys
+
+ case range_options.size
+ when 0
+ raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
+ when 1
+ # Valid number of options; do nothing.
+ else
+ raise ArgumentError, 'Too many range options specified. Choose only one.'
+ end
+ end
+
+ def ensure_argument_types! #:nodoc:
+ value = options[type]
+
+ case type
+ when :within, :in
+ raise ArgumentError, ":#{type} must be a Range" unless value.is_a?(Range)
+ when :is, :minimum, :maximum
+ raise ArgumentError, ":#{type} must be a nonnegative Integer" unless value.is_a?(Integer) && value >= 0
+ end
+ end
+ end
+
module ClassMethods
- ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
#
@@ -38,62 +106,9 @@ module ActiveModel
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
# count words as in above example.)
# Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
- def validates_length_of(*attrs)
- # Merge given options with defaults.
- options = { :tokenizer => lambda {|value| value.split(//)} }
- options.update(attrs.extract_options!.symbolize_keys)
-
- # Ensure that one and only one range option is specified.
- range_options = ALL_RANGE_OPTIONS & options.keys
- case range_options.size
- when 0
- raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
- when 1
- # Valid number of options; do nothing.
- else
- raise ArgumentError, 'Too many range options specified. Choose only one.'
- end
-
- # Get range option and value.
- option = range_options.first
- option_value = options[range_options.first]
- key = {:is => :wrong_length, :minimum => :too_short, :maximum => :too_long}[option]
- custom_message = options[:message] || options[key]
-
- case option
- when :within, :in
- raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
-
- validates_each(attrs, options) do |record, attr, value|
- value = options[:tokenizer].call(value) if value.kind_of?(String)
-
- min, max = option_value.begin, option_value.end
- max = max - 1 if option_value.exclude_end?
-
- if value.nil? || value.size < min
- record.errors.add(attr, :too_short, :default => custom_message || options[:too_short], :count => min)
- elsif value.size > max
- record.errors.add(attr, :too_long, :default => custom_message || options[:too_long], :count => max)
- end
- end
- when :is, :minimum, :maximum
- raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
-
- # Declare different validations per option.
- validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
-
- validates_each(attrs, options) do |record, attr, value|
- value = options[:tokenizer].call(value) if value.kind_of?(String)
-
- valid_value = if option == :maximum
- value.nil? || value.size.send(validity_checks[option], option_value)
- else
- value && value.size.send(validity_checks[option], option_value)
- end
-
- record.errors.add(attr, key, :default => custom_message, :count => option_value) unless valid_value
- end
- end
+ def validates_length_of(*attr_names)
+ options = attr_names.extract_options!
+ validates_with LengthValidator, options.merge(:attributes => attr_names)
end
alias_method :validates_size_of, :validates_length_of
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 32dbcd82d0..f2aab8c5b8 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -1,10 +1,68 @@
module ActiveModel
module Validations
- module ClassMethods
- ALL_NUMERICALITY_CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=',
- :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=',
- :odd => 'odd?', :even => 'even?' }.freeze
+ class NumericalityValidator < EachValidator
+ CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
+ :equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
+ :odd => :odd?, :even => :even? }.freeze
+
+ def initialize(options)
+ super(options.reverse_merge(:only_integer => false, :allow_nil => false))
+ end
+
+ def check_validity!
+ options.slice(*CHECKS.keys) do |option, value|
+ next if [:odd, :even].include?(option)
+ raise ArgumentError, ":#{option} must be a number, a symbol or a proc" unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
+ end
+ end
+
+ def validate_each(record, attr_name, value)
+ before_type_cast = "#{attr_name}_before_type_cast"
+
+ raw_value = record.send("#{attr_name}_before_type_cast") if record.respond_to?(before_type_cast.to_sym)
+ raw_value ||= value
+
+ return if options[:allow_nil] && raw_value.nil?
+
+ unless value = parse_raw_value(raw_value, options)
+ record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message])
+ return
+ end
+
+ options.slice(*CHECKS.keys).each do |option, option_value|
+ case option
+ when :odd, :even
+ unless value.to_i.send(CHECKS[option])
+ record.errors.add(attr_name, option, :value => value, :default => options[:message])
+ end
+ else
+ option_value = option_value.call(record) if option_value.is_a?(Proc)
+ option_value = record.send(option_value) if option_value.is_a?(Symbol)
+
+ unless value.send(CHECKS[option], option_value)
+ record.errors.add(attr_name, option, :default => options[:message], :value => value, :count => option_value)
+ end
+ end
+ end
+ end
+
+ protected
+
+ def parse_raw_value(raw_value, options)
+ if options[:only_integer]
+ raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
+ else
+ begin
+ Kernel.Float(raw_value)
+ rescue ArgumentError, TypeError
+ nil
+ end
+ end
+ end
+ end
+
+ module ClassMethods
# 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).
@@ -44,61 +102,9 @@ module ActiveModel
# validates_numericality_of :width, :greater_than => :minimum_weight
# end
#
- #
-
def validates_numericality_of(*attr_names)
- configuration = { :only_integer => false, :allow_nil => false }
- configuration.update(attr_names.extract_options!)
-
- numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys
-
- (numericality_options - [ :odd, :even ]).each do |option|
- value = configuration[option]
- raise ArgumentError, ":#{option} must be a number, a symbol or a proc" unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
- end
-
- validates_each(attr_names,configuration) do |record, attr_name, value|
- before_type_cast = "#{attr_name}_before_type_cast"
-
- if record.respond_to?(before_type_cast.to_sym)
- raw_value = record.send("#{attr_name}_before_type_cast") || value
- else
- raw_value = value
- end
-
- next if configuration[:allow_nil] and raw_value.nil?
-
- if configuration[:only_integer]
- unless raw_value.to_s =~ /\A[+-]?\d+\Z/
- record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
- next
- end
- raw_value = raw_value.to_i
- else
- begin
- raw_value = Kernel.Float(raw_value)
- rescue ArgumentError, TypeError
- record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
- next
- end
- end
-
- numericality_options.each do |option|
- case option
- when :odd, :even
- unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
- record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
- end
- else
- configuration[option] = configuration[option].call(record) if configuration[option].is_a? Proc
- configuration[option] = record.method(configuration[option]).call if configuration[option].is_a? Symbol
-
- unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
- record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option])
- end
- end
- end
- end
+ options = attr_names.extract_options!
+ validates_with NumericalityValidator, options.merge(:attributes => attr_names)
end
end
end
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index 3ff677c137..a4c6f866a7 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -2,6 +2,12 @@ require 'active_support/core_ext/object/blank'
module ActiveModel
module Validations
+ class PresenceValidator < EachValidator
+ def validate(record)
+ record.errors.add_on_blank(attributes, options[:message])
+ end
+ end
+
module ClassMethods
# Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example:
#
@@ -28,13 +34,8 @@ module ActiveModel
# The method, proc or string should return or evaluate to a true or false value.
#
def validates_presence_of(*attr_names)
- configuration = attr_names.extract_options!
-
- # can't use validates_each here, because it cannot cope with nonexistent attributes,
- # while errors.add_on_empty can
- validate configuration do |record|
- record.errors.add_on_blank(attr_names, configuration[:message])
- end
+ options = attr_names.extract_options!
+ validates_with PresenceValidator, options.merge(:attributes => attr_names)
end
end
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index edc2133ddc..8d521173c6 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -48,14 +48,9 @@ module ActiveModel
# end
# end
#
- def validates_with(*args)
- configuration = args.extract_options!
-
- validate configuration do |record|
- args.each do |klass|
- klass.new(record, configuration.except(:on, :if, :unless)).validate
- end
- end
+ def validates_with(*args, &block)
+ options = args.extract_options!
+ args.each { |klass| validate(klass.new(options, &block), options) }
end
end
end
diff --git a/activemodel/lib/active_model/validations_repair_helper.rb b/activemodel/lib/active_model/validations_repair_helper.rb
deleted file mode 100644
index 40741e6dbe..0000000000
--- a/activemodel/lib/active_model/validations_repair_helper.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-module ActiveModel
- module ValidationsRepairHelper
- extend ActiveSupport::Concern
-
- module ClassMethods
- def repair_validations(*model_classes)
- setup do
- @_stored_callbacks = {}
- model_classes.each do |k|
- @_stored_callbacks[k] = k._validate_callbacks.dup
- end
- end
- teardown do
- model_classes.each do |k|
- k._validate_callbacks = @_stored_callbacks[k]
- k.__update_callbacks(:validate)
- end
- end
- end
- end
-
- def repair_validations(*model_classes, &block)
- @__stored_callbacks = {}
- model_classes.each do |k|
- @__stored_callbacks[k] = k._validate_callbacks.dup
- end
- return block.call
- ensure
- model_classes.each do |k|
- k._validate_callbacks = @__stored_callbacks[k]
- k.__update_callbacks(:validate)
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 09de72b757..01695cb73a 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -1,5 +1,4 @@
module ActiveModel #:nodoc:
-
# A simple base class that can be used along with ActiveModel::Base.validates_with
#
# class Person < ActiveModel::Base
@@ -52,17 +51,60 @@ module ActiveModel #:nodoc:
# @my_custom_field = options[:field_name] || :first_name
# end
# end
- #
class Validator
- attr_reader :record, :options
+ attr_reader :options
- def initialize(record, options)
- @record = record
+ def initialize(options)
@options = options
end
- def validate
- raise "You must override this method"
+ def validate(record)
+ raise NotImplementedError
+ end
+ end
+
+ # EachValidator is a validator which iterates through the attributes given
+ # in the options hash invoking the validate_each method passing in the
+ # record, attribute and value.
+ #
+ # All ActiveModel validations are built on top of this Validator.
+ class EachValidator < Validator
+ attr_reader :attributes
+
+ def initialize(options)
+ @attributes = Array(options.delete(:attributes))
+ raise ":attributes cannot be blank" if @attributes.empty?
+ super
+ check_validity!
+ end
+
+ def validate(record)
+ attributes.each do |attribute|
+ value = record.send(:read_attribute_for_validation, attribute)
+ next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
+ validate_each(record, attribute, value)
+ end
+ end
+
+ def validate_each(record, attribute, value)
+ raise NotImplementedError
+ end
+
+ def check_validity!
+ end
+ end
+
+ # BlockValidator is a special EachValidator which receives a block on initialization
+ # and call this block for each attribute being validated. +validates_each+ uses this
+ # Validator.
+ class BlockValidator < EachValidator
+ def initialize(options, &block)
+ @block = block
+ super
+ end
+
+ def validate_each(record, attribute, value)
+ @block.call(record, attribute, value)
end
end
end
diff --git a/activemodel/test/cases/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb
new file mode 100644
index 0000000000..b996f51d6b
--- /dev/null
+++ b/activemodel/test/cases/callbacks_test.rb
@@ -0,0 +1,70 @@
+require "cases/helper"
+
+class CallbacksTest < ActiveModel::TestCase
+
+ class CallbackValidator
+ def around_create(model)
+ model.callbacks << :before_around_create
+ yield
+ model.callbacks << :after_around_create
+ end
+ end
+
+ class ModelCallbacks
+ attr_reader :callbacks
+ extend ActiveModel::Callbacks
+
+ define_model_callbacks :create
+ define_model_callbacks :initialize, :only => :after
+
+ before_create :before_create
+ around_create CallbackValidator.new
+
+ after_create do |model|
+ model.callbacks << :after_create
+ end
+
+ after_create "@callbacks << :final_callback"
+
+ def initialize(valid=true)
+ @callbacks, @valid = [], valid
+ end
+
+ def before_create
+ @callbacks << :before_create
+ end
+
+ def create
+ _run_create_callbacks do
+ @callbacks << :create
+ @valid
+ end
+ end
+ end
+
+ test "complete callback chain" do
+ model = ModelCallbacks.new
+ model.create
+ assert_equal model.callbacks, [ :before_create, :before_around_create, :create,
+ :after_around_create, :after_create, :final_callback]
+ end
+
+ test "after callbacks are always appended" do
+ model = ModelCallbacks.new
+ model.create
+ assert_equal model.callbacks.last, :final_callback
+ end
+
+ test "after callbacks are not executed if the block returns false" do
+ model = ModelCallbacks.new(false)
+ model.create
+ assert_equal model.callbacks, [ :before_create, :before_around_create,
+ :create, :after_around_create]
+ end
+
+ test "only selects which types of callbacks should be created" do
+ assert !ModelCallbacks.respond_to?(:before_initialize)
+ assert !ModelCallbacks.respond_to?(:around_initialize)
+ assert ModelCallbacks.respond_to?(:after_initialize)
+ end
+end
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index c1a3f6a4a7..30193956ea 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -1,15 +1,14 @@
-root = File.expand_path('../../../..', __FILE__)
begin
- require "#{root}/vendor/gems/environment"
+ require File.expand_path('../../../../vendor/gems/environment', __FILE__)
rescue LoadError
- $:.unshift("#{root}/activesupport/lib")
end
-lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib")
+lib = File.expand_path('../../../lib', __FILE__)
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
require 'config'
require 'active_model'
+require 'active_model/test_case'
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
diff --git a/activemodel/test/cases/lint_test.rb b/activemodel/test/cases/lint_test.rb
index da7d2112dc..63804004ee 100644
--- a/activemodel/test/cases/lint_test.rb
+++ b/activemodel/test/cases/lint_test.rb
@@ -4,6 +4,8 @@ class LintTest < ActiveModel::TestCase
include ActiveModel::Lint::Tests
class CompliantModel
+ extend ActiveModel::Naming
+
def to_model
self
end
diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb
index fe1ea36450..dc39b84ed8 100644
--- a/activemodel/test/cases/naming_test.rb
+++ b/activemodel/test/cases/naming_test.rb
@@ -1,8 +1,9 @@
require 'cases/helper'
+require 'models/track_back'
class NamingTest < ActiveModel::TestCase
def setup
- @model_name = ActiveModel::Name.new(self, 'Post::TrackBack')
+ @model_name = ActiveModel::Name.new(Post::TrackBack)
end
def test_singular
diff --git a/activemodel/test/cases/tests_database.rb b/activemodel/test/cases/tests_database.rb
index 8dd00ea147..79668dd941 100644
--- a/activemodel/test/cases/tests_database.rb
+++ b/activemodel/test/cases/tests_database.rb
@@ -2,6 +2,7 @@ require 'logger'
$:.unshift(File.dirname(__FILE__) + '/../../../activerecord/lib')
require 'active_record'
+require 'active_record/test_case'
require 'active_record/fixtures'
module ActiveModel
diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb
index d171784963..bfc1ca12e6 100644
--- a/activemodel/test/cases/translation_test.rb
+++ b/activemodel/test/cases/translation_test.rb
@@ -1,11 +1,5 @@
require 'cases/helper'
-
-class SuperUser
- extend ActiveModel::Translation
-end
-
-class User < SuperUser
-end
+require 'models/person'
class ActiveModelI18nTests < ActiveModel::TestCase
@@ -14,38 +8,38 @@ class ActiveModelI18nTests < ActiveModel::TestCase
end
def test_translated_model_attributes
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:super_user => {:name => 'super_user name attribute'} } }
- assert_equal 'super_user name attribute', SuperUser.human_attribute_name('name')
+ I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } }
+ assert_equal 'person name attribute', Person.human_attribute_name('name')
end
def test_translated_model_attributes_with_symbols
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:super_user => {:name => 'super_user name attribute'} } }
- assert_equal 'super_user name attribute', SuperUser.human_attribute_name(:name)
+ I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } }
+ assert_equal 'person name attribute', Person.human_attribute_name(:name)
end
def test_translated_model_attributes_with_ancestor
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:user => {:name => 'user name attribute'} } }
- assert_equal 'user name attribute', User.human_attribute_name('name')
+ I18n.backend.store_translations 'en', :activemodel => {:attributes => {:child => {:name => 'child name attribute'} } }
+ assert_equal 'child name attribute', Child.human_attribute_name('name')
end
def test_translated_model_attributes_with_ancestors_fallback
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:super_user => {:name => 'super_user name attribute'} } }
- assert_equal 'super_user name attribute', User.human_attribute_name('name')
+ I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } }
+ assert_equal 'person name attribute', Child.human_attribute_name('name')
end
def test_translated_model_names
- I18n.backend.store_translations 'en', :activemodel => {:models => {:super_user => 'super_user model'} }
- assert_equal 'super_user model', SuperUser.model_name.human
+ I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'person model'} }
+ assert_equal 'person model', Person.model_name.human
end
def test_translated_model_names_with_sti
- I18n.backend.store_translations 'en', :activemodel => {:models => {:user => 'user model'} }
- assert_equal 'user model', User.model_name.human
+ I18n.backend.store_translations 'en', :activemodel => {:models => {:child => 'child model'} }
+ assert_equal 'child model', Child.model_name.human
end
def test_translated_model_names_with_ancestors_fallback
- I18n.backend.store_translations 'en', :activemodel => {:models => {:super_user => 'super_user model'} }
- assert_equal 'super_user model', User.model_name.human
+ I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'person model'} }
+ assert_equal 'person model', Child.model_name.human
end
end
diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb
index 88e5fdb358..11c9c1edfd 100644
--- a/activemodel/test/cases/validations/acceptance_validation_test.rb
+++ b/activemodel/test/cases/validations/acceptance_validation_test.rb
@@ -9,9 +9,10 @@ require 'models/person'
class AcceptanceValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_terms_of_service_agreement_no_acceptance
Topic.validates_acceptance_of(:terms_of_service, :on => :create)
@@ -53,28 +54,18 @@ class AcceptanceValidationTest < ActiveModel::TestCase
assert t.save
end
- def test_validates_acceptance_of_with_custom_error_using_quotes
- repair_validations(Developer) do
- Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.salary = "0"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors[:salary].last
- end
- end
-
def test_validates_acceptance_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_acceptance_of :karma
+ Person.validates_acceptance_of :karma
- p = Person.new
- p.karma = ""
+ p = Person.new
+ p.karma = ""
- assert p.invalid?
- assert_equal ["must be accepted"], p.errors[:karma]
+ assert p.invalid?
+ assert_equal ["must be accepted"], p.errors[:karma]
- p.karma = "1"
- assert p.valid?
- end
+ p.karma = "1"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb
index 4c716d5d48..5260162a58 100644
--- a/activemodel/test/cases/validations/conditional_validation_test.rb
+++ b/activemodel/test/cases/validations/conditional_validation_test.rb
@@ -6,9 +6,10 @@ require 'models/topic'
class ConditionalValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_if_validation_using_method_true
# When the method returns true
diff --git a/activemodel/test/cases/validations/confirmation_validation_test.rb b/activemodel/test/cases/validations/confirmation_validation_test.rb
index 1d6f2a6ec5..55554d5054 100644
--- a/activemodel/test/cases/validations/confirmation_validation_test.rb
+++ b/activemodel/test/cases/validations/confirmation_validation_test.rb
@@ -8,9 +8,10 @@ require 'models/person'
class ConfirmationValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_no_title_confirmation
Topic.validates_confirmation_of(:title)
@@ -39,30 +40,19 @@ class ConfirmationValidationTest < ActiveModel::TestCase
assert t.save
end
- def test_validates_confirmation_of_with_custom_error_using_quotes
- repair_validations(Developer) do
- Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "John"
- d.name_confirmation = "Johnny"
- assert !d.valid?
- assert_equal ["confirm 'single' and \"double\" quotes"], d.errors[:name]
- end
- end
-
def test_validates_confirmation_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_confirmation_of :karma
+ Person.validates_confirmation_of :karma
- p = Person.new
- p.karma_confirmation = "None"
- assert p.invalid?
+ p = Person.new
+ p.karma_confirmation = "None"
+ assert p.invalid?
- assert_equal ["doesn't match confirmation"], p.errors[:karma]
+ assert_equal ["doesn't match confirmation"], p.errors[:karma]
- p.karma = "None"
- assert p.valid?
- end
+ p.karma = "None"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb
index 584f009e84..7d851f546c 100644
--- a/activemodel/test/cases/validations/exclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/exclusion_validation_test.rb
@@ -7,9 +7,10 @@ require 'models/person'
class ExclusionValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_validates_exclusion_of
Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
@@ -30,17 +31,17 @@ class ExclusionValidationTest < ActiveModel::TestCase
end
def test_validates_exclusion_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_exclusion_of :karma, :in => %w( abe monkey )
+ Person.validates_exclusion_of :karma, :in => %w( abe monkey )
- p = Person.new
- p.karma = "abe"
- assert p.invalid?
+ p = Person.new
+ p.karma = "abe"
+ assert p.invalid?
- assert_equal ["is reserved"], p.errors[:karma]
+ assert_equal ["is reserved"], p.errors[:karma]
- p.karma = "Lifo"
- assert p.valid?
- end
+ p.karma = "Lifo"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb
index e19e4bf7b3..e10089208a 100644
--- a/activemodel/test/cases/validations/format_validation_test.rb
+++ b/activemodel/test/cases/validations/format_validation_test.rb
@@ -8,9 +8,10 @@ require 'models/person'
class PresenceValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_validate_format
Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
@@ -100,28 +101,18 @@ class PresenceValidationTest < ActiveModel::TestCase
assert_raise(ArgumentError) { Topic.validates_format_of(:title, :without => "clearly not a regexp") }
end
- def test_validates_format_of_with_custom_error_using_quotes
- repair_validations(Developer) do
- Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
- d = Developer.new
- d.name = d.name_confirmation = "John 32"
- assert !d.valid?
- assert_equal ["format 'single' and \"double\" quotes"], d.errors[:name]
- end
- end
-
def test_validates_format_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_format_of :karma, :with => /\A\d+\Z/
+ Person.validates_format_of :karma, :with => /\A\d+\Z/
- p = Person.new
- p.karma = "Pixies"
- assert p.invalid?
+ p = Person.new
+ p.karma = "Pixies"
+ assert p.invalid?
- assert_equal ["is invalid"], p.errors[:karma]
+ assert_equal ["is invalid"], p.errors[:karma]
- p.karma = "1234"
- assert p.valid?
- end
+ p.karma = "1234"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index 68b1b27f75..a7656fe219 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -1,6 +1,5 @@
require "cases/helper"
require 'cases/tests_database'
-
require 'models/person'
class I18nValidationTest < ActiveModel::TestCase
@@ -22,20 +21,6 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.backend = @old_backend
end
- def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated
- assert_not_deprecated do
- default = "%s interpolation syntax was deprecated"
- assert_equal default, I18n.t(:does_not_exist, :default => default, :value => 'this')
- end
- end
-
- def test_percent_d_interpolation_syntax_in_error_messages_was_deprecated
- assert_not_deprecated do
- default = "%d interpolation syntaxes are deprecated"
- assert_equal default, I18n.t(:does_not_exist, :default => default, :count => 2)
- end
- end
-
def test_errors_add_on_empty_generates_message
@person.errors.expects(:generate_message).with(:title, :empty, {:default => nil})
@person.errors.add_on_empty :title
@@ -58,10 +43,16 @@ class I18nValidationTest < ActiveModel::TestCase
def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
@person.errors.add('name', 'empty')
- I18n.expects(:translate).with(:"person.name", :default => ['Name'], :scope => [:activemodel, :attributes], :count => 1).returns('Name')
+ I18n.expects(:translate).with(:"person.name", :default => ['Name', 'Name'], :scope => [:activemodel, :attributes], :count => 1).returns('Name')
@person.errors.full_messages
end
+ def test_errors_full_messages_uses_format
+ I18n.backend.store_translations('en', :activemodel => {:errors => {:format => "Field {{attribute}} {{message}}"}})
+ @person.errors.add('name', 'empty')
+ assert_equal ["Field Name empty"], @person.errors.full_messages
+ end
+
# ActiveRecord::Validations
# validates_confirmation_of w/ mocha
def test_validates_confirmation_of_generates_message
diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb
index bc1b0365d2..6b2bcd9c60 100644
--- a/activemodel/test/cases/validations/inclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/inclusion_validation_test.rb
@@ -8,9 +8,10 @@ require 'models/person'
class InclusionValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_validates_inclusion_of
Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
@@ -53,28 +54,18 @@ class InclusionValidationTest < ActiveModel::TestCase
assert_equal ["option uhoh is not in the list"], t.errors[:title]
end
- def test_validates_inclusion_of_with_custom_error_using_quotes
- repair_validations(Developer) do
- Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.salary = "90,000"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors[:salary].last
- end
- end
-
def test_validates_inclusion_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_inclusion_of :karma, :in => %w( abe monkey )
+ Person.validates_inclusion_of :karma, :in => %w( abe monkey )
- p = Person.new
- p.karma = "Lifo"
- assert p.invalid?
+ p = Person.new
+ p.karma = "Lifo"
+ assert p.invalid?
- assert_equal ["is not included in the list"], p.errors[:karma]
+ assert_equal ["is not included in the list"], p.errors[:karma]
- p.karma = "monkey"
- assert p.valid?
- end
+ p.karma = "monkey"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb
index 2c97b762f1..f3ef5e648a 100644
--- a/activemodel/test/cases/validations/length_validation_test.rb
+++ b/activemodel/test/cases/validations/length_validation_test.rb
@@ -8,9 +8,10 @@ require 'models/person'
class LengthValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_validates_length_of_with_allow_nil
Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
@@ -419,48 +420,18 @@ class LengthValidationTest < ActiveModel::TestCase
assert_equal ["Your essay must be at least 5 words."], t.errors[:content]
end
- def test_validates_length_of_with_custom_too_long_using_quotes
- repair_validations(Developer) do
- Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "Jeffrey"
- assert !d.valid?
- assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name]
- end
- end
-
- def test_validates_length_of_with_custom_too_short_using_quotes
- repair_validations(Developer) do
- Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "Joe"
- assert !d.valid?
- assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name]
- end
- end
-
- def test_validates_length_of_with_custom_message_using_quotes
- repair_validations(Developer) do
- Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "Joe"
- assert !d.valid?
- assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name]
- end
- end
-
def test_validates_length_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_length_of :karma, :minimum => 5
+ Person.validates_length_of :karma, :minimum => 5
- p = Person.new
- p.karma = "Pix"
- assert p.invalid?
+ p = Person.new
+ p.karma = "Pix"
+ assert p.invalid?
- assert_equal ["is too short (minimum is 5 characters)"], p.errors[:karma]
+ assert_equal ["is too short (minimum is 5 characters)"], p.errors[:karma]
- p.karma = "The Smiths"
- assert p.valid?
- end
+ p.karma = "The Smiths"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb
index d3201966dc..75cd654f98 100644
--- a/activemodel/test/cases/validations/numericality_validation_test.rb
+++ b/activemodel/test/cases/validations/numericality_validation_test.rb
@@ -8,9 +8,10 @@ require 'models/person'
class NumericalityValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
NIL = [nil]
BLANK = ["", " ", " \t \r \n"]
@@ -138,37 +139,19 @@ class NumericalityValidationTest < ActiveModel::TestCase
assert_equal ["greater than 4"], topic.errors[:approved]
end
- def test_numericality_with_getter_method
- repair_validations(Developer) do
- Developer.validates_numericality_of( :salary )
- developer = Developer.new("name" => "michael", "salary" => nil)
- developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
- assert developer.valid?
- end
- end
-
- def test_numericality_with_allow_nil_and_getter_method
- repair_validations(Developer) do
- Developer.validates_numericality_of( :salary, :allow_nil => true)
- developer = Developer.new("name" => "michael", "salary" => nil)
- developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
- assert developer.valid?
- end
- end
-
def test_validates_numericality_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_numericality_of :karma, :allow_nil => false
+ Person.validates_numericality_of :karma, :allow_nil => false
- p = Person.new
- p.karma = "Pix"
- assert p.invalid?
+ p = Person.new
+ p.karma = "Pix"
+ assert p.invalid?
- assert_equal ["is not a number"], p.errors[:karma]
+ assert_equal ["is not a number"], p.errors[:karma]
- p.karma = "1234"
- assert p.valid?
- end
+ p.karma = "1234"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
private
diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb
index 90b0951a77..8b9795a90c 100644
--- a/activemodel/test/cases/validations/presence_validation_test.rb
+++ b/activemodel/test/cases/validations/presence_validation_test.rb
@@ -9,9 +9,6 @@ require 'models/custom_reader'
class PresenceValidationTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
-
- repair_validations(Topic)
def test_validate_presences
Topic.validates_presence_of(:title, :content)
@@ -30,43 +27,44 @@ class PresenceValidationTest < ActiveModel::TestCase
t.content = "like stuff"
assert t.save
+ ensure
+ Topic.reset_callbacks(:validate)
end
- # def test_validates_presence_of_with_custom_message_using_quotes
- # repair_validations(Developer) do
- # Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
- # d = Developer.new
- # d.name = "Joe"
- # assert !d.valid?
- # assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:non_existent]
- # end
- # end
+ def test_validates_acceptance_of_with_custom_error_using_quotes
+ Person.validates_presence_of :karma, :message=> "This string contains 'single' and \"double\" quotes"
+ p = Person.new
+ assert !p.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", p.errors[:karma].last
+ ensure
+ Person.reset_callbacks(:validate)
+ end
def test_validates_presence_of_for_ruby_class
- repair_validations(Person) do
- Person.validates_presence_of :karma
+ Person.validates_presence_of :karma
- p = Person.new
- assert p.invalid?
+ p = Person.new
+ assert p.invalid?
- assert_equal ["can't be blank"], p.errors[:karma]
+ assert_equal ["can't be blank"], p.errors[:karma]
- p.karma = "Cold"
- assert p.valid?
- end
+ p.karma = "Cold"
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
end
def test_validates_presence_of_for_ruby_class_with_custom_reader
- repair_validations(Person) do
- CustomReader.validates_presence_of :karma
+ CustomReader.validates_presence_of :karma
- p = CustomReader.new
- assert p.invalid?
+ p = CustomReader.new
+ assert p.invalid?
- assert_equal ["can't be blank"], p.errors[:karma]
+ assert_equal ["can't be blank"], p.errors[:karma]
- p[:karma] = "Cold"
- assert p.valid?
- end
+ p[:karma] = "Cold"
+ assert p.valid?
+ ensure
+ CustomReader.reset_callbacks(:validate)
end
end
diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb
index fae87a6188..7540ccb580 100644
--- a/activemodel/test/cases/validations/with_validation_test.rb
+++ b/activemodel/test/cases/validations/with_validation_test.rb
@@ -6,38 +6,51 @@ require 'models/topic'
class ValidatesWithTest < ActiveRecord::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
ERROR_MESSAGE = "Validation error from validator"
OTHER_ERROR_MESSAGE = "Validation error from other validator"
class ValidatorThatAddsErrors < ActiveModel::Validator
- def validate()
+ def validate(record)
record.errors[:base] << ERROR_MESSAGE
end
end
class OtherValidatorThatAddsErrors < ActiveModel::Validator
- def validate()
+ def validate(record)
record.errors[:base] << OTHER_ERROR_MESSAGE
end
end
class ValidatorThatDoesNotAddErrors < ActiveModel::Validator
- def validate()
+ def validate(record)
end
end
class ValidatorThatValidatesOptions < ActiveModel::Validator
- def validate()
+ def validate(record)
if options[:field] == :first_name
record.errors[:base] << ERROR_MESSAGE
end
end
end
+ class ValidatorPerEachAttribute < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ record.errors[attribute] << "Value is #{value}"
+ end
+ end
+
+ class ValidatorCheckValidity < ActiveModel::EachValidator
+ def check_validity!
+ raise "boom!"
+ end
+ end
+
test "vaidation with class that adds errors" do
Topic.validates_with(ValidatorThatAddsErrors)
topic = Topic.new
@@ -98,11 +111,11 @@ class ValidatesWithTest < ActiveRecord::TestCase
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
- test "passes all non-standard configuration options to the validator class" do
+ test "passes all configuration options to the validator class" do
topic = Topic.new
validator = mock()
- validator.expects(:new).with(topic, {:foo => :bar}).returns(validator)
- validator.expects(:validate)
+ validator.expects(:new).with(:foo => :bar, :if => "1 == 1").returns(validator)
+ validator.expects(:validate).with(topic)
Topic.validates_with(validator, :if => "1 == 1", :foo => :bar)
assert topic.valid?
@@ -115,4 +128,39 @@ class ValidatesWithTest < ActiveRecord::TestCase
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
+ test "validates_with each validator" do
+ Topic.validates_with(ValidatorPerEachAttribute, :attributes => [:title, :content])
+ topic = Topic.new :title => "Title", :content => "Content"
+ assert !topic.valid?
+ assert_equal ["Value is Title"], topic.errors[:title]
+ assert_equal ["Value is Content"], topic.errors[:content]
+ end
+
+ test "each validator checks validity" do
+ assert_raise RuntimeError do
+ Topic.validates_with(ValidatorCheckValidity, :attributes => [:title])
+ end
+ end
+
+ test "each validator expects attributes to be given" do
+ assert_raise RuntimeError do
+ Topic.validates_with(ValidatorPerEachAttribute)
+ end
+ end
+
+ test "each validator skip nil values if :allow_nil is set to true" do
+ Topic.validates_with(ValidatorPerEachAttribute, :attributes => [:title, :content], :allow_nil => true)
+ topic = Topic.new :content => ""
+ assert !topic.valid?
+ assert topic.errors[:title].empty?
+ assert_equal ["Value is "], topic.errors[:content]
+ end
+
+ test "each validator skip blank values if :allow_blank is set to true" do
+ Topic.validates_with(ValidatorPerEachAttribute, :attributes => [:title, :content], :allow_blank => true)
+ topic = Topic.new :content => ""
+ assert topic.valid?
+ assert topic.errors[:title].empty?
+ assert topic.errors[:content].empty?
+ end
end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 78565177d8..38a2a716a7 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -9,11 +9,12 @@ require 'models/custom_reader'
class ValidationsTest < ActiveModel::TestCase
include ActiveModel::TestsDatabase
- include ActiveModel::ValidationsRepairHelper
# 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
- repair_validations(Topic)
+ def teardown
+ Topic.reset_callbacks(:validate)
+ end
def test_single_field_validation
r = Reply.new
@@ -70,6 +71,12 @@ class ValidationsTest < ActiveModel::TestCase
assert_equal 2, r.errors.count
end
+ def test_errors_on_nested_attributes_expands_name
+ t = Topic.new
+ t.errors["replies.name"] << "can't be blank"
+ assert_equal ["Replies name can't be blank"], t.errors.full_messages
+ end
+
def test_errors_on_base
r = Reply.new
r.content = "Mismatch"
diff --git a/activemodel/test/models/person.rb b/activemodel/test/models/person.rb
index d98420f900..c83d768379 100644
--- a/activemodel/test/models/person.rb
+++ b/activemodel/test/models/person.rb
@@ -1,5 +1,9 @@
class Person
include ActiveModel::Validations
+ extend ActiveModel::Translation
- attr_accessor :title, :karma
+ attr_accessor :title, :karma, :salary
+end
+
+class Child < Person
end
diff --git a/activemodel/test/models/track_back.rb b/activemodel/test/models/track_back.rb
new file mode 100644
index 0000000000..d137e4ff8f
--- /dev/null
+++ b/activemodel/test/models/track_back.rb
@@ -0,0 +1,4 @@
+class Post
+ class TrackBack
+ end
+end \ No newline at end of file