aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/CHANGELOG.md124
-rw-r--r--activemodel/MIT-LICENSE2
-rw-r--r--activemodel/README.rdoc106
-rw-r--r--activemodel/Rakefile7
-rw-r--r--activemodel/activemodel.gemspec6
-rw-r--r--activemodel/examples/validations.rb29
-rw-r--r--activemodel/lib/active_model.rb8
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb109
-rw-r--r--activemodel/lib/active_model/callbacks.rb48
-rw-r--r--activemodel/lib/active_model/conversion.rb27
-rw-r--r--activemodel/lib/active_model/deprecated_mass_assignment_security.rb21
-rw-r--r--activemodel/lib/active_model/dirty.rb137
-rw-r--r--activemodel/lib/active_model/errors.rb105
-rw-r--r--activemodel/lib/active_model/forbidden_attributes_protection.rb1
-rw-r--r--activemodel/lib/active_model/gem_version.rb15
-rw-r--r--activemodel/lib/active_model/lint.rb15
-rw-r--r--activemodel/lib/active_model/locale/en.yml12
-rw-r--r--activemodel/lib/active_model/model.rb24
-rw-r--r--activemodel/lib/active_model/naming.rb36
-rw-r--r--activemodel/lib/active_model/secure_password.rb92
-rw-r--r--activemodel/lib/active_model/serialization.rb18
-rw-r--r--activemodel/lib/active_model/serializers/json.rb14
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb16
-rw-r--r--activemodel/lib/active_model/translation.rb2
-rw-r--r--activemodel/lib/active_model/validations.rb92
-rw-r--r--activemodel/lib/active_model/validations/absence.rb2
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb12
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb17
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb34
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb20
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb8
-rw-r--r--activemodel/lib/active_model/validations/format.rb42
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb13
-rw-r--r--activemodel/lib/active_model/validations/length.rb6
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb67
-rw-r--r--activemodel/lib/active_model/validations/presence.rb2
-rw-r--r--activemodel/lib/active_model/validations/validates.rb18
-rw-r--r--activemodel/lib/active_model/validations/with.rb12
-rw-r--r--activemodel/lib/active_model/validator.rb23
-rw-r--r--activemodel/lib/active_model/version.rb12
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb93
-rw-r--r--activemodel/test/cases/callbacks_test.rb27
-rw-r--r--activemodel/test/cases/conversion_test.rb18
-rw-r--r--activemodel/test/cases/deprecated_mass_assignment_security_test.rb16
-rw-r--r--activemodel/test/cases/dirty_test.rb108
-rw-r--r--activemodel/test/cases/errors_test.rb187
-rw-r--r--activemodel/test/cases/helper.rb10
-rw-r--r--activemodel/test/cases/model_test.rb51
-rw-r--r--activemodel/test/cases/naming_test.rb8
-rw-r--r--activemodel/test/cases/railtie_test.rb5
-rw-r--r--activemodel/test/cases/secure_password_test.rb207
-rw-r--r--activemodel/test/cases/serialization_test.rb34
-rw-r--r--activemodel/test/cases/serializers/json_serialization_test.rb76
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb133
-rw-r--r--activemodel/test/cases/translation_test.rb47
-rw-r--r--activemodel/test/cases/validations/absence_validation_test.rb13
-rw-r--r--activemodel/test/cases/validations/acceptance_validation_test.rb8
-rw-r--r--activemodel/test/cases/validations/callbacks_test.rb36
-rw-r--r--activemodel/test/cases/validations/conditional_validation_test.rb50
-rw-r--r--activemodel/test/cases/validations/confirmation_validation_test.rb66
-rw-r--r--activemodel/test/cases/validations/exclusion_validation_test.rb18
-rw-r--r--activemodel/test/cases/validations/format_validation_test.rb36
-rw-r--r--activemodel/test/cases/validations/i18n_generate_message_validation_test.rb70
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb122
-rw-r--r--activemodel/test/cases/validations/inclusion_validation_test.rb55
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb121
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb67
-rw-r--r--activemodel/test/cases/validations/presence_validation_test.rb6
-rw-r--r--activemodel/test/cases/validations/validates_test.rb43
-rw-r--r--activemodel/test/cases/validations/validations_context_test.rb29
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb63
-rw-r--r--activemodel/test/cases/validations_test.rb141
-rw-r--r--activemodel/test/models/administrator.rb11
-rw-r--r--activemodel/test/models/automobile.rb12
-rw-r--r--activemodel/test/models/contact.rb17
-rw-r--r--activemodel/test/models/oauthed_user.rb11
-rw-r--r--activemodel/test/models/reply.rb6
-rw-r--r--activemodel/test/models/topic.rb2
-rw-r--r--activemodel/test/models/user.rb3
-rw-r--r--activemodel/test/models/visitor.rb1
80 files changed, 1989 insertions, 1392 deletions
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 09e6ede064..b86e988841 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,123 +1 @@
-## Rails 4.0.0 (unreleased) ##
-
-* Add `ActiveModel::Validations::AbsenceValidator`, a validator to check the
- absence of attributes.
-
- class Person
- include ActiveModel::Validations
-
- attr_accessor :first_name
- validates_absence_of :first_name
- end
-
- person = Person.new
- person.first_name = "John"
- person.valid?
- # => false
- person.errors.messages
- # => {:first_name=>["must be blank"]}
-
- *Roberto Vasquez Angel*
-
-* `[attribute]_changed?` now returns `false` after a call to `reset_[attribute]!`
-
- *Renato Mascarenhas*
-
-* Observers was extracted from Active Model as `rails-observers` gem.
-
- *Rafael Mendonça França*
-
-* Specify type of singular association during serialization *Steve Klabnik*
-
-* Fixed length validator to correctly handle nil values. Fixes #7180.
-
- *Michal Zima*
-
-* Removed dispensable `require` statements. Make sure to require `active_model` before requiring
- individual parts of the framework.
-
- *Yves Senn*
-
-* Use BCrypt's `MIN_COST` in the test environment for speedier tests when using `has_secure_pasword`.
-
- *Brian Cardarella + Jeremy Kemper + Trevor Turk*
-
-* Add `ActiveModel::ForbiddenAttributesProtection`, a simple module to
- protect attributes from mass assignment when non-permitted attributes are passed.
-
- *DHH + Guillermo Iguaran*
-
-* `ActiveModel::MassAssignmentSecurity` has been extracted from Active Model and the
- `protected_attributes` gem should be added to Gemfile in order to use
- `attr_accessible` and `attr_protected` macros in your models.
-
- *Guillermo Iguaran*
-
-* Due to a change in builder, nil values and empty strings now generates
- closed tags, so instead of this:
-
- <pseudonyms nil=\"true\"></pseudonyms>
-
- It generates this:
-
- <pseudonyms nil=\"true\"/>
-
- *Carlos Antonio da Silva*
-
-* Changed inclusion and exclusion validators to accept a symbol for `:in` option.
-
- This allows to use dynamic inclusion/exclusion values using methods, besides the current lambda/proc support.
-
- *Gabriel Sobrinho*
-
-* `AM::Validation#validates` ability to pass custom exception to `:strict` option.
-
- *Bogdan Gusiev*
-
-* Changed `ActiveModel::Serializers::Xml::Serializer#add_associations` to by default
- propagate `:skip_types, :dasherize, :camelize` keys to included associations.
- It can be overriden on each association by explicitly specifying the option on one
- or more associations
-
- *Anthony Alberto*
-
-* Changed `AM::Serializers::JSON.include_root_in_json' default value to false.
- Now, AM Serializers and AR objects have the same default behaviour. Fixes #6578.
-
- class User < ActiveRecord::Base; end
-
- class Person
- include ActiveModel::Model
- include ActiveModel::AttributeMethods
- include ActiveModel::Serializers::JSON
-
- attr_accessor :name, :age
-
- def attributes
- instance_values
- end
- end
-
- user.as_json
- => {"id"=>1, "name"=>"Konata Izumi", "age"=>16, "awesome"=>true}
- # root is not included
-
- person.as_json
- => {"name"=>"Francesco", "age"=>22}
- # root is not included
-
- *Francesco Rodriguez*
-
-* Passing false hash values to `validates` will no longer enable the corresponding validators *Steve Purcell*
-
-* `ConfirmationValidator` error messages will attach to `:#{attribute}_confirmation` instead of `attribute` *Brian Cardarella*
-
-* Added ActiveModel::Model, a mixin to make Ruby objects work with AP out of box *Guillermo Iguaran*
-
-* `AM::Errors#to_json`: support `:full_messages` parameter *Bogdan Gusiev*
-
-* Trim down Active Model API by removing `valid?` and `errors.full_messages` *José Valim*
-
-* When `^` or `$` are used in the regular expression provided to `validates_format_of` and the :multiline option is not set to true, an exception will be raised. This is to prevent security vulnerabilities when using `validates_format_of`. The problem is described in detail in the Rails security guide *Jan Berdajs + Egor Homakov*
-
-Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activemodel/CHANGELOG.md) for previous changes.
+Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activemodel/CHANGELOG.md) for previous changes.
diff --git a/activemodel/MIT-LICENSE b/activemodel/MIT-LICENSE
index 5c668d9624..3ec7a617cf 100644
--- a/activemodel/MIT-LICENSE
+++ b/activemodel/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2013 David Heinemeier Hansson
+Copyright (c) 2004-2015 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index 1b1fe2fa2b..f6beff14e1 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -2,7 +2,7 @@
Active Model provides a known set of interfaces for usage in model classes.
They allow for Action Pack helpers to interact with non-Active Record models,
-for example. Active Model also helps building custom ORMs for use outside of
+for example. Active Model also helps with building custom ORMs for use outside of
the Rails framework.
Prior to Rails 3.0, if a plugin or gem developer wanted to have an object
@@ -24,8 +24,8 @@ to integrate with Action Pack out of the box: <tt>ActiveModel::Model</tt>.
end
person = Person.new(name: 'bob', age: '18')
- person.name # => 'bob'
- person.age # => '18'
+ person.name # => 'bob'
+ person.age # => '18'
person.valid? # => true
It includes model name introspections, conversions, translations and
@@ -49,7 +49,8 @@ behavior out of the box:
send("#{attr}=", nil)
end
end
-
+
+ person = Person.new
person.clear_name
person.clear_age
@@ -78,16 +79,31 @@ behavior out of the box:
class Person
include ActiveModel::Dirty
- attr_accessor :name
+ define_attribute_methods :name
+
+ def name
+ @name
+ end
+
+ def name=(val)
+ name_will_change! unless val == @name
+ @name = val
+ end
+
+ def save
+ # do persistence work
+ changes_applied
+ end
end
person = Person.new
- person.name # => nil
- person.changed? # => false
+ person.name # => nil
+ person.changed? # => false
person.name = 'bob'
- person.changed? # => true
- person.changed # => ['name']
- person.changes # => { 'name' => [nil, 'bob'] }
+ person.changed? # => true
+ person.changed # => ['name']
+ person.changes # => { 'name' => [nil, 'bob'] }
+ person.save
person.name = 'robert'
person.save
person.previous_changes # => {'name' => ['bob, 'robert']}
@@ -109,16 +125,19 @@ behavior out of the box:
attr_reader :errors
def validate!
- errors.add(:name, "can not be nil") if name.nil?
+ errors.add(:name, "cannot be nil") if name.nil?
end
def self.human_attribute_name(attr, options = {})
"Name"
end
end
-
+
+ person = Person.new
+ person.name = nil
+ person.validate!
person.errors.full_messages
- # => ["Name can not be nil"]
+ # => ["Name cannot be nil"]
{Learn more}[link:classes/ActiveModel/Errors.html]
@@ -128,7 +147,7 @@ behavior out of the box:
extend ActiveModel::Naming
end
- NamedPerson.model_name # => "NamedPerson"
+ NamedPerson.model_name.name # => "NamedPerson"
NamedPerson.model_name.human # => "Named person"
{Learn more}[link:classes/ActiveModel/Naming.html]
@@ -180,41 +199,41 @@ behavior out of the box:
* Validation support
- class Person
- include ActiveModel::Validations
+ class Person
+ include ActiveModel::Validations
- attr_accessor :first_name, :last_name
+ attr_accessor :first_name, :last_name
- validates_each :first_name, :last_name do |record, attr, value|
- record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
- end
- end
+ validates_each :first_name, :last_name do |record, attr, value|
+ record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
+ end
+ end
- person = Person.new
- person.first_name = 'zoolander'
- person.valid? # => false
+ person = Person.new
+ person.first_name = 'zoolander'
+ person.valid? # => false
{Learn more}[link:classes/ActiveModel/Validations.html]
* Custom validators
+
+ class HasNameValidator < ActiveModel::Validator
+ def validate(record)
+ record.errors[:name] = "must exist" if record.name.blank?
+ end
+ end
- class ValidatorPerson
- include ActiveModel::Validations
- validates_with HasNameValidator
- attr_accessor :name
- end
-
- class HasNameValidator < ActiveModel::Validator
- def validate(record)
- record.errors[:name] = "must exist" if record.name.blank?
- end
- end
+ class ValidatorPerson
+ include ActiveModel::Validations
+ validates_with HasNameValidator
+ attr_accessor :name
+ end
- p = ValidatorPerson.new
- p.valid? # => false
- p.errors.full_messages # => ["Name must exist"]
- p.name = "Bob"
- p.valid? # => true
+ p = ValidatorPerson.new
+ p.valid? # => false
+ p.errors.full_messages # => ["Name must exist"]
+ p.name = "Bob"
+ p.valid? # => true
{Learn more}[link:classes/ActiveModel/Validator.html]
@@ -243,6 +262,11 @@ API documentation is at
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/activemodel/Rakefile b/activemodel/Rakefile
index fc5aaf9f8f..c30a559ef5 100644
--- a/activemodel/Rakefile
+++ b/activemodel/Rakefile
@@ -9,18 +9,17 @@ Rake::TestTask.new do |t|
t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort
t.warning = true
t.verbose = true
+ t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
namespace :test do
task :isolated do
- ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
Dir.glob("#{dir}/test/**/*_test.rb").all? do |file|
- sh(ruby, '-w', "-I#{dir}/lib", "-I#{dir}/test", file)
+ sh(Gem.ruby, '-w', "-I#{dir}/lib", "-I#{dir}/test", file)
end or raise "Failures"
end
end
-require 'rake/packagetask'
require 'rubygems/package_task'
spec = eval(File.read("#{dir}/activemodel.gemspec"))
@@ -29,7 +28,7 @@ Gem::PackageTask.new(spec) do |p|
p.gem_spec = spec
end
-desc "Release to gemcutter"
+desc "Release to rubygems"
task :release => :package do
require 'rake/gemcutter'
Rake::Gemcutter::Tasks.new(spec).define
diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec
index 51655fe3da..3c6eb56296 100644
--- a/activemodel/activemodel.gemspec
+++ b/activemodel/activemodel.gemspec
@@ -5,9 +5,9 @@ Gem::Specification.new do |s|
s.name = 'activemodel'
s.version = version
s.summary = 'A toolkit for building modeling frameworks (part of Rails).'
- s.description = 'A toolkit for building modeling frameworks like Active Record. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.'
+ s.description = 'A toolkit for building modeling frameworks like Active Record. Rich support for attributes, callbacks, validations, serialization, internationalization, and testing.'
- s.required_ruby_version = '>= 1.9.3'
+ s.required_ruby_version = '>= 2.2.0'
s.license = 'MIT'
@@ -20,5 +20,5 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
- s.add_dependency 'builder', '~> 3.1.0'
+ s.add_dependency 'builder', '~> 3.1'
end
diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb
deleted file mode 100644
index a56ec4db39..0000000000
--- a/activemodel/examples/validations.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'active_model'
-
-class Person
- include ActiveModel::Conversion
- include ActiveModel::Validations
-
- validates_presence_of :name
-
- attr_accessor :name
-
- def initialize(attributes = {})
- @name = attributes[:name]
- end
-
- def persist
- @persisted = true
- end
-
- def persisted?
- @persisted
- end
-end
-
-person1 = Person.new
-p person1.valid? # => false
-p person1.errors.messages # => {:name=>["can't be blank"]}
-
-person2 = Person.new(:name => "matz")
-p person2.valid? # => true
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 3bd5531356..46d60db756 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2013 David Heinemeier Hansson
+# Copyright (c) 2004-2015 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -37,7 +37,6 @@ module ActiveModel
autoload :ForbiddenAttributesProtection
autoload :Lint
autoload :Model
- autoload :DeprecatedMassAssignmentSecurity
autoload :Name, 'active_model/naming'
autoload :Naming
autoload :SecurePassword
@@ -49,6 +48,7 @@ module ActiveModel
eager_autoload do
autoload :Errors
+ autoload :StrictValidationFailed, 'active_model/errors'
end
module Serializers
@@ -60,9 +60,9 @@ module ActiveModel
end
end
- def eager_load!
+ def self.eager_load!
super
- ActiveModel::Serializer.eager_load!
+ ActiveModel::Serializers.eager_load!
end
end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 6d11c0fbdc..96be551264 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -1,4 +1,5 @@
require 'thread_safe'
+require 'mutex_m'
module ActiveModel
# Raised when an attribute is not defined.
@@ -12,19 +13,23 @@ module ActiveModel
# # => ActiveModel::MissingAttributeError: missing attribute: user_id
class MissingAttributeError < NoMethodError
end
- # == Active \Model Attribute Methods
+
+ # == Active \Model \Attribute \Methods
#
- # <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and
- # suffixes to your methods as well as handling the creation of Active Record
- # like class methods such as +table_name+.
+ # Provides a way to add prefixes and suffixes to your methods as
+ # well as handling the creation of <tt>ActiveRecord::Base</tt>-like
+ # class methods such as +table_name+.
#
- # The requirements to implement ActiveModel::AttributeMethods are to:
+ # The requirements to implement <tt>ActiveModel::AttributeMethods</tt> are to:
#
- # * <tt>include ActiveModel::AttributeMethods</tt> in your object.
- # * Call each Attribute Method module method you want to add, such as
- # +attribute_method_suffix+ or +attribute_method_prefix+.
+ # * <tt>include ActiveModel::AttributeMethods</tt> in your class.
+ # * Call each of its method you want to add, such as +attribute_method_suffix+
+ # or +attribute_method_prefix+.
# * Call +define_attribute_methods+ after the other methods are called.
# * Define the various generic +_attribute+ methods that you have declared.
+ # * Define an +attributes+ method which returns a hash with each
+ # attribute name in your model as hash key and the attribute value as hash value.
+ # Hash keys must be strings.
#
# A minimal implementation could be:
#
@@ -38,6 +43,10 @@ module ActiveModel
#
# attr_accessor :name
#
+ # def attributes
+ # { 'name' => @name }
+ # end
+ #
# private
#
# def attribute_contrived?(attr)
@@ -52,13 +61,6 @@ module ActiveModel
# send("#{attr}=", 'Default Name')
# end
# end
- #
- # Note that whenever you include ActiveModel::AttributeMethods in your class,
- # it requires you to implement an +attributes+ method which returns a hash
- # with each attribute name in your model as hash key and the attribute value as
- # hash value.
- #
- # Hash keys must be strings.
module AttributeMethods
extend ActiveSupport::Concern
@@ -166,20 +168,19 @@ module ActiveModel
# private
#
# def reset_attribute_to_default!(attr)
- # ...
+ # send("#{attr}=", 'Default Name')
# end
# end
#
# person = Person.new
# person.name # => 'Gem'
# person.reset_name_to_default!
- # person.name # => 'Gemma'
+ # person.name # => 'Default Name'
def attribute_method_affix(*affixes)
self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new prefix: affix[:prefix], suffix: affix[:suffix] }
undefine_attribute_methods
end
-
# Allows you to make aliases for attributes.
#
# class Person
@@ -213,6 +214,16 @@ module ActiveModel
end
end
+ # Is +new_name+ an alias?
+ def attribute_alias?(new_name)
+ attribute_aliases.key? new_name.to_s
+ end
+
+ # Returns the original name for the alias +name+
+ def attribute_alias(name)
+ attribute_aliases[name.to_s]
+ end
+
# Declares the attributes that should be prefixed and suffixed by
# ActiveModel::AttributeMethods.
#
@@ -234,7 +245,7 @@ module ActiveModel
# private
#
# def clear_attribute(attr)
- # ...
+ # send("#{attr}=", nil)
# end
# end
def define_attribute_methods(*attr_names)
@@ -317,9 +328,10 @@ module ActiveModel
attribute_method_matchers_cache.clear
end
- # Returns true if the attribute methods defined have been generated.
def generated_attribute_methods #:nodoc:
- @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
+ @generated_attribute_methods ||= Module.new {
+ extend Mutex_m
+ }.tap { |mod| include mod }
end
protected
@@ -332,23 +344,21 @@ module ActiveModel
# invoked often in a typical rails, both of which invoke the method
# +match_attribute_method?+. The latter method iterates through an
# array doing regular expression matches, which results in a lot of
- # object creations. Most of the times it returns a +nil+ match. As the
+ # object creations. Most of the time it returns a +nil+ match. As the
# match result is always the same given a +method_name+, this cache is
# used to alleviate the GC, which ultimately also speeds up the app
# significantly (in our case our test suite finishes 10% faster with
# this cache).
def attribute_method_matchers_cache #:nodoc:
- @attribute_method_matchers_cache ||= ThreadSafe::Cache.new(:initial_capacity => 4)
+ @attribute_method_matchers_cache ||= ThreadSafe::Cache.new(initial_capacity: 4)
end
- def attribute_method_matcher(method_name) #:nodoc:
+ def attribute_method_matchers_matching(method_name) #:nodoc:
attribute_method_matchers_cache.compute_if_absent(method_name) do
# Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix
# will match every time.
matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
- match = nil
- matchers.detect { |method| match = method.match(method_name) }
- match
+ matchers.map { |method| method.match(method_name) }.compact
end
end
@@ -383,14 +393,6 @@ module ActiveModel
AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name)
def initialize(options = {})
- if options[:prefix] == '' || options[:suffix] == ''
- message = "Specifying an empty prefix/suffix for an attribute method is no longer " \
- "necessary. If the un-prefixed/suffixed version of the method has not been " \
- "defined when `define_attribute_methods` is called, it will be defined " \
- "automatically."
- ActiveSupport::Deprecation.warn message
- end
-
@prefix, @suffix = options.fetch(:prefix, ''), options.fetch(:suffix, '')
@regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
@method_missing_target = "#{@prefix}attribute#{@suffix}"
@@ -413,17 +415,16 @@ module ActiveModel
end
end
- # Allows access to the object attributes, which are held in the
- # <tt>@attributes</tt> hash, as though they were first-class methods. So a
- # Person class with a name attribute can use Person#name and Person#name=
- # and never directly use the attributes hash -- except for multiple assigns
- # with ActiveRecord#attributes=. A Milestone class can also ask
- # Milestone#completed? to test that the completed attribute is not +nil+
- # or 0.
+ # Allows access to the object attributes, which are held in the hash
+ # returned by <tt>attributes</tt>, as though they were first-class
+ # methods. So a +Person+ class with a +name+ attribute can for example use
+ # <tt>Person#name</tt> and <tt>Person#name=</tt> and never directly use
+ # the attributes hash -- except for multiple assigns with
+ # <tt>ActiveRecord::Base#attributes=</tt>.
#
- # It's also possible to instantiate related objects, so a Client class
- # belonging to the clients table with a +master_id+ foreign key can
- # instantiate master through Client#master.
+ # It's also possible to instantiate related objects, so a <tt>Client</tt>
+ # class belonging to the +clients+ table with a +master_id+ foreign key
+ # can instantiate master through <tt>Client#master</tt>.
def method_missing(method, *args, &block)
if respond_to_without_attributes?(method, true)
super
@@ -433,17 +434,17 @@ module ActiveModel
end
end
- # attribute_missing is like method_missing, but for attributes. When method_missing is
- # called we check to see if there is a matching attribute method. If so, we call
- # attribute_missing to dispatch the attribute. This method can be overloaded to
- # customize the behavior.
+ # +attribute_missing+ is like +method_missing+, but for attributes. When
+ # +method_missing+ is called we check to see if there is a matching
+ # attribute method. If so, we tell +attribute_missing+ to dispatch the
+ # attribute. This method can be overloaded to customize the behavior.
def attribute_missing(match, *args, &block)
__send__(match.target, match.attr_name, *args, &block)
end
- # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
- # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
- # which will all return +true+.
+ # A +Person+ instance with a +name+ attribute can ask
+ # <tt>person.respond_to?(:name)</tt>, <tt>person.respond_to?(:name=)</tt>,
+ # and <tt>person.respond_to?(:name?)</tt> which will all return +true+.
alias :respond_to_without_attributes? :respond_to?
def respond_to?(method, include_private_methods = false)
if super
@@ -466,8 +467,8 @@ module ActiveModel
# Returns a struct representing the matching attribute method.
# The struct's attributes are prefix, base and suffix.
def match_attribute_method?(method_name)
- match = self.class.send(:attribute_method_matcher, method_name)
- match if match && attribute_method?(match.attr_name)
+ matches = self.class.send(:attribute_method_matchers_matching, method_name)
+ matches.detect { |match| attribute_method?(match.attr_name) }
end
def missing_attribute(attr_name, stack)
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index c52e4947ae..b3d70dc515 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/extract_options'
+
module ActiveModel
# == Active \Model \Callbacks
#
@@ -28,7 +30,7 @@ module ActiveModel
# end
#
# Then in your class, you can use the +before_create+, +after_create+ and
- # +around_create+ methods, just as you would in an Active Record module.
+ # +around_create+ methods, just as you would in an Active Record model.
#
# before_create :action_before_create
#
@@ -95,13 +97,16 @@ module ActiveModel
# # obj is the MyModel instance that the callback is being called on
# end
# end
+ #
+ # NOTE: +method_name+ passed to `define_model_callbacks` must not end with
+ # `!`, `?` or `=`.
def define_model_callbacks(*callbacks)
options = callbacks.extract_options!
options = {
- :terminator => "result == false",
- :skip_after_callbacks_if_terminated => true,
- :scope => [:kind, :name],
- :only => [:before, :around, :after]
+ terminator: ->(_,result) { result == false },
+ skip_after_callbacks_if_terminated: true,
+ scope: [:kind, :name],
+ only: [:before, :around, :after]
}.merge!(options)
types = Array(options.delete(:only))
@@ -118,30 +123,27 @@ module ActiveModel
private
def _define_before_model_callback(klass, callback) #:nodoc:
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
- def self.before_#{callback}(*args, &block)
- set_callback(:#{callback}, :before, *args, &block)
- end
- CALLBACK
+ klass.define_singleton_method("before_#{callback}") do |*args, &block|
+ set_callback(:"#{callback}", :before, *args, &block)
+ end
end
def _define_around_model_callback(klass, callback) #:nodoc:
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
- def self.around_#{callback}(*args, &block)
- set_callback(:#{callback}, :around, *args, &block)
- end
- CALLBACK
+ klass.define_singleton_method("around_#{callback}") do |*args, &block|
+ set_callback(:"#{callback}", :around, *args, &block)
+ end
end
def _define_after_model_callback(klass, callback) #:nodoc:
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
- def self.after_#{callback}(*args, &block)
- options = args.extract_options!
- options[:prepend] = true
- options[:if] = Array(options[:if]) << "value != false"
- set_callback(:#{callback}, :after, *(args << options), &block)
- end
- CALLBACK
+ klass.define_singleton_method("after_#{callback}") do |*args, &block|
+ options = args.extract_options!
+ options[:prepend] = true
+ conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
+ v != false
+ }
+ options[:if] = Array(options[:if]) << conditional
+ set_callback(:"#{callback}", :after, *(args << options), &block)
+ end
end
end
end
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index 1f5d23dd8e..9c9b6f4a77 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -1,5 +1,5 @@
module ActiveModel
- # == Active \Model Conversions
+ # == Active \Model \Conversion
#
# Handles default conversions: to_model, to_key, to_param, and to_partial_path.
#
@@ -40,13 +40,15 @@ module ActiveModel
self
end
- # Returns an Enumerable of all key attributes if any is set, regardless if
- # the object is persisted or not. If there no key attributes, returns +nil+.
+ # Returns an Array of all key attributes if any is set, regardless if
+ # the object is persisted or not. Returns +nil+ if there are no key attributes.
#
- # class Person < ActiveRecord::Base
+ # class Person
+ # include ActiveModel::Conversion
+ # attr_accessor :id
# end
#
- # person = Person.create
+ # person = Person.create(id: 1)
# person.to_key # => [1]
def to_key
key = respond_to?(:id) && id
@@ -56,13 +58,18 @@ module ActiveModel
# Returns a +string+ representing the object's key suitable for use in URLs,
# or +nil+ if <tt>persisted?</tt> is +false+.
#
- # class Person < ActiveRecord::Base
+ # class Person
+ # include ActiveModel::Conversion
+ # attr_accessor :id
+ # def persisted?
+ # true
+ # end
# end
#
- # person = Person.create
+ # person = Person.create(id: 1)
# person.to_param # => "1"
def to_param
- persisted? ? to_key.join('-') : nil
+ (persisted? && key = to_key) ? key.join('-') : nil
end
# Returns a +string+ identifying the path associated with the object.
@@ -83,8 +90,8 @@ module ActiveModel
# internal method and should not be accessed directly.
def _to_partial_path #:nodoc:
@_to_partial_path ||= begin
- element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self))
- collection = ActiveSupport::Inflector.tableize(self)
+ element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
+ collection = ActiveSupport::Inflector.tableize(name)
"#{collection}/#{element}".freeze
end
end
diff --git a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb b/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
deleted file mode 100644
index 1f409c87b9..0000000000
--- a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module ActiveModel
- module DeprecatedMassAssignmentSecurity # :nodoc:
- extend ActiveSupport::Concern
-
- module ClassMethods # :nodoc:
- def attr_protected(*args)
- raise "`attr_protected` is extracted out of Rails into a gem. " \
- "Please use new recommended protection model for params" \
- "(strong_parameters) or add `protected_attributes` to your " \
- "Gemfile to use old one."
- end
-
- def attr_accessible(*args)
- raise "`attr_accessible` is extracted out of Rails into a gem. " \
- "Please use new recommended protection model for params" \
- "(strong_parameters) or add `protected_attributes` to your " \
- "Gemfile to use old one."
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 6e67cd2285..337b61c55c 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -1,5 +1,6 @@
require 'active_support/hash_with_indifferent_access'
require 'active_support/core_ext/object/duplicable'
+require 'active_support/core_ext/string/filters'
module ActiveModel
# == Active \Model \Dirty
@@ -12,15 +13,12 @@ module ActiveModel
# * <tt>include ActiveModel::Dirty</tt> in your object.
# * Call <tt>define_attribute_methods</tt> passing each method you want to
# track.
- # * Call <tt>attr_name_will_change!</tt> before each change to the tracked
+ # * Call <tt>[attr_name]_will_change!</tt> before each change to the tracked
# attribute.
- #
- # If you wish to also track previous changes on save or update, you need to
- # add:
- #
- # @previously_changed = changes
- #
- # inside of your save or update method.
+ # * Call <tt>changes_applied</tt> after the changes are persisted.
+ # * Call <tt>clear_changes_information</tt> when you want to reset the changes
+ # information.
+ # * Call <tt>restore_attributes</tt> when you want to restore previous data.
#
# A minimal implementation could be:
#
@@ -39,14 +37,25 @@ module ActiveModel
# end
#
# def save
- # @previously_changed = changes
- # @changed_attributes.clear
+ # # do persistence work
+ #
+ # changes_applied
+ # end
+ #
+ # def reload!
+ # # get the values from the persistence layer
+ #
+ # clear_changes_information
+ # end
+ #
+ # def rollback!
+ # restore_attributes
# end
# end
#
# A newly instantiated object is unchanged:
#
- # person = Person.find_by_name('Uncle Bob')
+ # person = Person.find_by(name: 'Uncle Bob')
# person.changed? # => false
#
# Change the name:
@@ -54,6 +63,7 @@ module ActiveModel
# person.name = 'Bob'
# person.changed? # => true
# person.name_changed? # => true
+ # person.name_changed?(from: "Uncle Bob", to: "Bob") # => true
# person.name_was # => "Uncle Bob"
# person.name_change # => ["Uncle Bob", "Bob"]
# person.name = 'Bill'
@@ -65,6 +75,19 @@ module ActiveModel
# person.changed? # => false
# person.name_changed? # => false
#
+ # Reset the changes:
+ #
+ # person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
+ # person.reload!
+ # person.previous_changes # => {}
+ #
+ # Rollback the changes:
+ #
+ # person.name = "Uncle Bob"
+ # person.rollback!
+ # person.name # => "Bill"
+ # person.name_changed? # => false
+ #
# Assigning the same value leaves the attribute unchanged:
#
# person.name = 'Bill'
@@ -77,9 +100,11 @@ module ActiveModel
# person.changed # => ["name"]
# person.changes # => {"name" => ["Bill", "Bob"]}
#
- # If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
- # to mark that the attribute is changing. Otherwise ActiveModel can't track
- # changes to in-place attributes.
+ # If an attribute is modified in-place then make use of
+ # +[attribute_name]_will_change!+ to mark that the attribute is changing.
+ # Otherwise Active Model can't track changes to in-place attributes. Note
+ # that Active Record can detect in-place modifications automatically. You do
+ # not need to call +[attribute_name]_will_change!+ on Active Record models.
#
# person.name_will_change!
# person.name_change # => ["Bill", "Bill"]
@@ -91,7 +116,8 @@ module ActiveModel
included do
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
- attribute_method_affix :prefix => 'reset_', :suffix => '!'
+ attribute_method_affix prefix: 'reset_', suffix: '!'
+ attribute_method_affix prefix: 'restore_', suffix: '!'
end
# Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
@@ -129,7 +155,7 @@ module ActiveModel
# person.save
# person.previous_changes # => {"name" => ["bob", "robert"]}
def previous_changes
- @previously_changed
+ @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
end
# Returns a hash of the attributes with unsaved changes indicating their original
@@ -139,14 +165,52 @@ module ActiveModel
# person.name = 'robert'
# person.changed_attributes # => {"name" => "bob"}
def changed_attributes
- @changed_attributes ||= {}
+ @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
+ end
+
+ # Handle <tt>*_changed?</tt> for +method_missing+.
+ def attribute_changed?(attr, options = {}) #:nodoc:
+ result = changes_include?(attr)
+ result &&= options[:to] == __send__(attr) if options.key?(:to)
+ result &&= options[:from] == changed_attributes[attr] if options.key?(:from)
+ result
+ end
+
+ # Handle <tt>*_was</tt> for +method_missing+.
+ def attribute_was(attr) # :nodoc:
+ attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
+ end
+
+ # Restore all previous data of the provided attributes.
+ def restore_attributes(attributes = changed)
+ attributes.each { |attr| restore_attribute! attr }
end
private
- # Handle <tt>*_changed?</tt> for +method_missing+.
- def attribute_changed?(attr)
- changed_attributes.include?(attr)
+ def changes_include?(attr_name)
+ attributes_changed_by_setter.include?(attr_name)
+ end
+
+ # Removes current changes and makes them accessible through +previous_changes+.
+ def changes_applied # :doc:
+ @previously_changed = changes
+ @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
+ end
+
+ # Clear all dirty data: current changes and previous changes.
+ def clear_changes_information # :doc:
+ @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
+ @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
+ end
+
+ def reset_changes
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `#reset_changes` is deprecated and will be removed on Rails 5.
+ Please use `#clear_changes_information` instead.
+ MSG
+
+ clear_changes_information
end
# Handle <tt>*_change</tt> for +method_missing+.
@@ -154,11 +218,6 @@ module ActiveModel
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
end
- # Handle <tt>*_was</tt> for +method_missing+.
- def attribute_was(attr)
- attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
- end
-
# Handle <tt>*_will_change!</tt> for +method_missing+.
def attribute_will_change!(attr)
return if attribute_changed?(attr)
@@ -169,15 +228,39 @@ module ActiveModel
rescue TypeError, NoMethodError
end
- changed_attributes[attr] = value
+ set_attribute_was(attr, value)
end
# Handle <tt>reset_*!</tt> for +method_missing+.
def reset_attribute!(attr)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `#reset_#{attr}!` is deprecated and will be removed on Rails 5.
+ Please use `#restore_#{attr}!` instead.
+ MSG
+
+ restore_attribute!(attr)
+ end
+
+ # Handle <tt>restore_*!</tt> for +method_missing+.
+ def restore_attribute!(attr)
if attribute_changed?(attr)
__send__("#{attr}=", changed_attributes[attr])
- changed_attributes.delete(attr)
+ clear_attribute_changes([attr])
end
end
+
+ # This is necessary because `changed_attributes` might be overridden in
+ # other implemntations (e.g. in `ActiveRecord`)
+ alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc:
+
+ # Force an attribute to have a particular "before" value
+ def set_attribute_was(attr, old_value)
+ attributes_changed_by_setter[attr] = old_value
+ end
+
+ # Remove changes information for the provided attributes.
+ def clear_attribute_changes(attributes) # :doc:
+ attributes_changed_by_setter.except!(*attributes)
+ end
end
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 963e52bff3..55687cb3c7 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -7,12 +7,11 @@ module ActiveModel
# == Active \Model \Errors
#
# Provides a modified +Hash+ that you can include in your object
- # for handling error messages and interacting with Action Pack helpers.
+ # for handling error messages and interacting with Action View helpers.
#
# A minimal implementation could be:
#
# class Person
- #
# # Required dependency for ActiveModel::Errors
# extend ActiveModel::Naming
#
@@ -24,7 +23,7 @@ module ActiveModel
# attr_reader :errors
#
# def validate!
- # errors.add(:name, "can not be nil") if name == nil
+ # errors.add(:name, "cannot be nil") if name.nil?
# end
#
# # The following methods are needed to be minimally implemented
@@ -40,7 +39,6 @@ module ActiveModel
# def Person.lookup_ancestors
# [self]
# end
- #
# end
#
# The last three methods are required in your object for Errors to be
@@ -52,9 +50,9 @@ module ActiveModel
#
# The above allows you to do:
#
- # p = Person.new
- # person.validate! # => ["can not be nil"]
- # person.errors.full_messages # => ["name can not be nil"]
+ # person = Person.new
+ # person.validate! # => ["cannot be nil"]
+ # person.errors.full_messages # => ["name cannot be nil"]
# # etc..
class Errors
include Enumerable
@@ -82,7 +80,7 @@ module ActiveModel
# Clear the error messages.
#
- # person.errors.full_messages # => ["name can not be nil"]
+ # person.errors.full_messages # => ["name cannot be nil"]
# person.errors.clear
# person.errors.full_messages # => []
def clear
@@ -92,19 +90,21 @@ module ActiveModel
# Returns +true+ if the error messages include an error for the given key
# +attribute+, +false+ otherwise.
#
- # person.errors.messages # => {:name=>["can not be nil"]}
+ # person.errors.messages # => {:name=>["cannot be nil"]}
# person.errors.include?(:name) # => true
# person.errors.include?(:age) # => false
def include?(attribute)
- (v = messages[attribute]) && v.any?
+ messages[attribute].present?
end
# aliases include?
alias :has_key? :include?
+ # aliases include?
+ alias :key? :include?
# Get messages for +key+.
#
- # person.errors.messages # => {:name=>["can not be nil"]}
- # person.errors.get(:name) # => ["can not be nil"]
+ # person.errors.messages # => {:name=>["cannot be nil"]}
+ # person.errors.get(:name) # => ["cannot be nil"]
# person.errors.get(:age) # => nil
def get(key)
messages[key]
@@ -112,7 +112,7 @@ module ActiveModel
# Set messages for +key+ to +value+.
#
- # person.errors.get(:name) # => ["can not be nil"]
+ # person.errors.get(:name) # => ["cannot be nil"]
# person.errors.set(:name, ["can't be nil"])
# person.errors.get(:name) # => ["can't be nil"]
def set(key, value)
@@ -121,8 +121,8 @@ module ActiveModel
# Delete messages for +key+. Returns the deleted messages.
#
- # person.errors.get(:name) # => ["can not be nil"]
- # person.errors.delete(:name) # => ["can not be nil"]
+ # person.errors.get(:name) # => ["cannot be nil"]
+ # person.errors.delete(:name) # => ["cannot be nil"]
# person.errors.get(:name) # => nil
def delete(key)
messages.delete(key)
@@ -131,8 +131,8 @@ module ActiveModel
# When passed a symbol or a name of a method, returns an array of errors
# for the method.
#
- # person.errors[:name] # => ["can not be nil"]
- # person.errors['name'] # => ["can not be nil"]
+ # person.errors[:name] # => ["cannot be nil"]
+ # person.errors['name'] # => ["cannot be nil"]
def [](attribute)
get(attribute.to_sym) || set(attribute.to_sym, [])
end
@@ -177,15 +177,15 @@ module ActiveModel
# Returns all message values.
#
- # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
- # person.errors.values # => [["can not be nil", "must be specified"]]
+ # person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
+ # person.errors.values # => [["cannot be nil", "must be specified"]]
def values
messages.values
end
# Returns all message keys.
#
- # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
+ # person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
# person.errors.keys # => [:name]
def keys
messages.keys
@@ -213,7 +213,7 @@ module ActiveModel
# Returns +true+ if no errors are found, +false+ otherwise.
# If the error message is a string it can be empty.
#
- # person.errors.full_messages # => ["name can not be nil"]
+ # person.errors.full_messages # => ["name cannot be nil"]
# person.errors.empty? # => false
def empty?
all? { |k, v| v && v.empty? && !v.is_a?(String) }
@@ -233,15 +233,15 @@ module ActiveModel
# # <error>name must be specified</error>
# # </errors>
def to_xml(options={})
- to_a.to_xml({ :root => "errors", :skip_types => true }.merge!(options))
+ to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
end
# Returns a Hash that can be used as the JSON representation for this
# object. You can pass the <tt>:full_messages</tt> option. This determines
# if the json object should contain full messages or not (false by default).
#
- # person.as_json # => {:name=>["can not be nil"]}
- # person.as_json(full_messages: true) # => {:name=>["name can not be nil"]}
+ # person.errors.as_json # => {:name=>["cannot be nil"]}
+ # person.errors.as_json(full_messages: true) # => {:name=>["name cannot be nil"]}
def as_json(options=nil)
to_hash(options && options[:full_messages])
end
@@ -249,15 +249,13 @@ module ActiveModel
# Returns a Hash of attributes with their error messages. If +full_messages+
# is +true+, it will contain full messages (see +full_message+).
#
- # person.to_hash # => {:name=>["can not be nil"]}
- # person.to_hash(true) # => {:name=>["name can not be nil"]}
+ # person.errors.to_hash # => {:name=>["cannot be nil"]}
+ # person.errors.to_hash(true) # => {:name=>["name cannot be nil"]}
def to_hash(full_messages = false)
if full_messages
- messages = {}
- self.messages.each do |attribute, array|
+ self.messages.each_with_object({}) do |(attribute, array), messages|
messages[attribute] = array.map { |message| full_message(attribute, message) }
end
- messages
else
self.messages.dup
end
@@ -281,17 +279,24 @@ module ActiveModel
# If +message+ is a proc, it will be called, allowing for things like
# <tt>Time.now</tt> to be used within an error.
#
- # If the <tt>:strict</tt> option is set to true will raise
+ # If the <tt>:strict</tt> option is set to +true+, it will raise
# ActiveModel::StrictValidationFailed instead of adding the error.
# <tt>:strict</tt> option can also be set to any other exception.
#
- # person.errors.add(:name, nil, strict: true)
+ # person.errors.add(:name, :invalid, strict: true)
# # => ActiveModel::StrictValidationFailed: name is invalid
- # person.errors.add(:name, nil, strict: NameIsInvalid)
+ # person.errors.add(:name, :invalid, strict: NameIsInvalid)
# # => NameIsInvalid: name is invalid
#
# person.errors.messages # => {}
- def add(attribute, message = nil, options = {})
+ #
+ # +attribute+ should be set to <tt>:base</tt> if the error is not
+ # directly associated with a single attribute.
+ #
+ # person.errors.add(:base, "either name or email must be present")
+ # person.errors.messages
+ # # => {:base=>["either name or email must be present"]}
+ def add(attribute, message = :invalid, options = {})
message = normalize_message(attribute, message, options)
if exception = options[:strict]
exception = ActiveModel::StrictValidationFailed if exception == true
@@ -333,7 +338,7 @@ module ActiveModel
#
# person.errors.add :name, :blank
# person.errors.added? :name, :blank # => true
- def added?(attribute, message = nil, options = {})
+ def added?(attribute, message = :invalid, options = {})
message = normalize_message(attribute, message, options)
self[attribute].include? message
end
@@ -352,17 +357,31 @@ module ActiveModel
map { |attribute, message| full_message(attribute, message) }
end
+ # Returns all the full error messages for a given attribute in an array.
+ #
+ # class Person
+ # validates_presence_of :name, :email
+ # validates_length_of :name, in: 5..30
+ # end
+ #
+ # person = Person.create()
+ # person.errors.full_messages_for(:name)
+ # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
+ def full_messages_for(attribute)
+ (get(attribute) || []).map { |message| full_message(attribute, message) }
+ end
+
# Returns a full message for a given attribute.
#
# person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
def full_message(attribute, message)
return message if attribute == :base
attr_name = attribute.to_s.tr('.', '_').humanize
- attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
+ attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
I18n.t(:"errors.format", {
- :default => "%{attribute} %{message}",
- :attribute => attr_name,
- :message => message
+ default: "%{attribute} %{message}",
+ attribute: attr_name,
+ message: message
})
end
@@ -414,10 +433,10 @@ module ActiveModel
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
options = {
- :default => defaults,
- :model => @base.class.model_name.human,
- :attribute => @base.class.human_attribute_name(attribute),
- :value => value
+ default: defaults,
+ model: @base.model_name.human,
+ attribute: @base.class.human_attribute_name(attribute),
+ value: value
}.merge!(options)
I18n.translate(key, options)
@@ -425,8 +444,6 @@ module ActiveModel
private
def normalize_message(attribute, message, options)
- message ||= :invalid
-
case message
when Symbol
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
diff --git a/activemodel/lib/active_model/forbidden_attributes_protection.rb b/activemodel/lib/active_model/forbidden_attributes_protection.rb
index 7468f95548..b4fa378601 100644
--- a/activemodel/lib/active_model/forbidden_attributes_protection.rb
+++ b/activemodel/lib/active_model/forbidden_attributes_protection.rb
@@ -23,5 +23,6 @@ module ActiveModel
attributes
end
end
+ alias :sanitize_forbidden_attributes :sanitize_for_mass_assignment
end
end
diff --git a/activemodel/lib/active_model/gem_version.rb b/activemodel/lib/active_model/gem_version.rb
new file mode 100644
index 0000000000..2403242ce6
--- /dev/null
+++ b/activemodel/lib/active_model/gem_version.rb
@@ -0,0 +1,15 @@
+module ActiveModel
+ # Returns the version of the currently loaded Active Model as a <tt>Gem::Version</tt>
+ def self.gem_version
+ Gem::Version.new VERSION::STRING
+ end
+
+ module VERSION
+ MAJOR = 5
+ MINOR = 0
+ TINY = 0
+ PRE = "alpha"
+
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
+ end
+end
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index 1be2913f0b..38087521a2 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -13,7 +13,7 @@ module ActiveModel
#
# These tests do not attempt to determine the semantic correctness of the
# returned values. For instance, you could implement <tt>valid?</tt> to
- # always return true, and the tests would pass. It is up to you to ensure
+ # always return +true+, and the tests would pass. It is up to you to ensure
# that the values are semantically meaningful.
#
# Objects you pass in are expected to return a compliant object from a call
@@ -73,16 +73,19 @@ module ActiveModel
# == \Naming
#
- # Model.model_name must return a string with some convenience methods:
- # <tt>:human</tt>, <tt>:singular</tt> and <tt>:plural</tt>. Check
- # ActiveModel::Naming for more information.
+ # Model.model_name and Model#model_name must return a string with some
+ # convenience methods: # <tt>:human</tt>, <tt>:singular</tt> and
+ # <tt>:plural</tt>. Check ActiveModel::Naming for more information.
def test_model_naming
- assert model.class.respond_to?(:model_name), "The model should respond to model_name"
+ assert model.class.respond_to?(:model_name), "The model class should respond to model_name"
model_name = model.class.model_name
assert model_name.respond_to?(:to_str)
assert model_name.human.respond_to?(:to_str)
assert model_name.singular.respond_to?(:to_str)
assert model_name.plural.respond_to?(:to_str)
+
+ assert model.respond_to?(:model_name), "The model instance should respond to model_name"
+ assert_equal model.model_name, model.class.model_name
end
# == \Errors Testing
@@ -98,7 +101,7 @@ module ActiveModel
private
def model
- assert @model.respond_to?(:to_model), "The object should respond_to to_model"
+ assert @model.respond_to?(:to_model), "The object should respond to to_model"
@model.to_model
end
diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml
index 540e8132d3..bf07945fe1 100644
--- a/activemodel/lib/active_model/locale/en.yml
+++ b/activemodel/lib/active_model/locale/en.yml
@@ -14,9 +14,15 @@ en:
empty: "can't be empty"
blank: "can't be blank"
present: "must be blank"
- too_long: "is too long (maximum is %{count} characters)"
- too_short: "is too short (minimum is %{count} characters)"
- wrong_length: "is the wrong length (should be %{count} characters)"
+ too_long:
+ one: "is too long (maximum is 1 character)"
+ other: "is too long (maximum is %{count} characters)"
+ too_short:
+ one: "is too short (minimum is 1 character)"
+ other: "is too short (minimum is %{count} characters)"
+ wrong_length:
+ one: "is the wrong length (should be 1 character)"
+ other: "is the wrong length (should be %{count} characters)"
not_a_number: "is not a number"
not_an_integer: "must be an integer"
greater_than: "must be greater than %{count}"
diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb
index 62383a03e8..d51d6ddcc9 100644
--- a/activemodel/lib/active_model/model.rb
+++ b/activemodel/lib/active_model/model.rb
@@ -1,6 +1,6 @@
module ActiveModel
- # == Active \Model Basic \Model
+ # == Active \Model \Basic \Model
#
# Includes the required interface for an object to interact with
# <tt>ActionPack</tt>, using different <tt>ActiveModel</tt> modules.
@@ -16,8 +16,8 @@ module ActiveModel
# end
#
# person = Person.new(name: 'bob', age: '18')
- # person.name # => 'bob'
- # person.age # => 18
+ # person.name # => "bob"
+ # person.age # => "18"
#
# Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
# to return +false+, which is the most common case. You may want to override
@@ -56,13 +56,13 @@ module ActiveModel
# refer to the specific modules included in <tt>ActiveModel::Model</tt>
# (see below).
module Model
- def self.included(base) #:nodoc:
- base.class_eval do
- extend ActiveModel::Naming
- extend ActiveModel::Translation
- include ActiveModel::Validations
- include ActiveModel::Conversion
- end
+ extend ActiveSupport::Concern
+ include ActiveModel::Validations
+ include ActiveModel::Conversion
+
+ included do
+ extend ActiveModel::Naming
+ extend ActiveModel::Translation
end
# Initializes a new model with the given +params+.
@@ -74,11 +74,13 @@ module ActiveModel
#
# person = Person.new(name: 'bob', age: '18')
# person.name # => "bob"
- # person.age # => 18
+ # person.age # => "18"
def initialize(params={})
params.each do |attr, value|
self.public_send("#{attr}=", value)
end if params
+
+ super()
end
# Indicates if the model is persisted. Default is +false+.
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 6887f6d781..4e6b02c246 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -129,7 +129,7 @@ module ActiveModel
#
# Equivalent to +to_s+.
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
- :to_str, :to => :name
+ :to_str, to: :name
# Returns a new ActiveModel::Name instance. By default, the +namespace+
# and +name+ option will take the namespace and name of the given class
@@ -183,7 +183,7 @@ module ActiveModel
defaults << options[:default] if options[:default]
defaults << @human
- options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default))
+ options = { scope: [@klass.i18n_scope, :models], count: 1, default: defaults }.merge!(options.except(:default))
I18n.translate(defaults.shift, options)
end
@@ -204,7 +204,7 @@ module ActiveModel
# extend ActiveModel::Naming
# end
#
- # BookCover.model_name # => "BookCover"
+ # BookCover.model_name.name # => "BookCover"
# BookCover.model_name.human # => "Book cover"
#
# BookCover.model_name.i18n_key # => :book_cover
@@ -214,14 +214,20 @@ module ActiveModel
# is required to pass the Active Model Lint test. So either extending the
# provided method below, or rolling your own is required.
module Naming
+ def self.extended(base) #:nodoc:
+ base.remove_possible_method :model_name
+ base.delegate :model_name, to: :class
+ end
+
# Returns an ActiveModel::Name object for module. It can be
# used to retrieve all kinds of naming-related information
# (See ActiveModel::Name for more information).
#
- # class Person < ActiveModel::Model
+ # class Person
+ # include ActiveModel::Model
# end
#
- # Person.model_name # => Person
+ # Person.model_name.name # => "Person"
# Person.model_name.class # => ActiveModel::Name
# Person.model_name.singular # => "person"
# Person.model_name.plural # => "people"
@@ -262,10 +268,10 @@ module ActiveModel
# namespaced models regarding whether it's inside isolated engine.
#
# # For isolated engine:
- # ActiveModel::Naming.singular_route_key(Blog::Post) #=> post
+ # ActiveModel::Naming.singular_route_key(Blog::Post) # => "post"
#
# # For shared engine:
- # ActiveModel::Naming.singular_route_key(Blog::Post) #=> blog_post
+ # ActiveModel::Naming.singular_route_key(Blog::Post) # => "blog_post"
def self.singular_route_key(record_or_class)
model_name_from_record_or_class(record_or_class).singular_route_key
end
@@ -274,10 +280,10 @@ module ActiveModel
# namespaced models regarding whether it's inside isolated engine.
#
# # For isolated engine:
- # ActiveModel::Naming.route_key(Blog::Post) #=> posts
+ # ActiveModel::Naming.route_key(Blog::Post) # => "posts"
#
# # For shared engine:
- # ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
+ # ActiveModel::Naming.route_key(Blog::Post) # => "blog_posts"
#
# The route key also considers if the noun is uncountable and, in
# such cases, automatically appends _index.
@@ -289,21 +295,19 @@ module ActiveModel
# namespaced models regarding whether it's inside isolated engine.
#
# # For isolated engine:
- # ActiveModel::Naming.param_key(Blog::Post) #=> post
+ # ActiveModel::Naming.param_key(Blog::Post) # => "post"
#
# # For shared engine:
- # ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
+ # ActiveModel::Naming.param_key(Blog::Post) # => "blog_post"
def self.param_key(record_or_class)
model_name_from_record_or_class(record_or_class).param_key
end
def self.model_name_from_record_or_class(record_or_class) #:nodoc:
- if record_or_class.respond_to?(:model_name)
- record_or_class.model_name
- elsif record_or_class.respond_to?(:to_model)
- record_or_class.to_model.class.model_name
+ if record_or_class.respond_to?(:to_model)
+ record_or_class.to_model.model_name
else
- record_or_class.class.model_name
+ record_or_class.model_name
end
end
private_class_method :model_name_from_record_or_class
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 6644b60609..96e88f1b6c 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -2,25 +2,36 @@ module ActiveModel
module SecurePassword
extend ActiveSupport::Concern
- class << self; attr_accessor :min_cost; end
+ # BCrypt hash function can handle maximum 72 characters, and if we pass
+ # password of length more than 72 characters it ignores extra characters.
+ # Hence need to put a restriction on password length.
+ MAX_PASSWORD_LENGTH_ALLOWED = 72
+
+ class << self
+ attr_accessor :min_cost # :nodoc:
+ end
self.min_cost = false
module ClassMethods
# Adds methods to set and authenticate against a BCrypt password.
- # This mechanism requires you to have a password_digest attribute.
+ # This mechanism requires you to have a +password_digest+ attribute.
+ #
+ # The following validations are added automatically:
+ # * Password must be present on creation
+ # * Password length should be less than or equal to 72 characters
+ # * Confirmation of password (using a +password_confirmation+ attribute)
#
- # Validations for presence of password on create, confirmation of password
- # (using a +password_confirmation+ attribute) are automatically added. If
- # you wish to turn off validations, pass <tt>validations: false</tt> as an
- # argument. You can add more validations by hand if need be.
+ # If password confirmation validation is not needed, simply leave out the
+ # value for +password_confirmation+ (i.e. don't provide a form field for
+ # it). When this attribute has a +nil+ value, the validation will not be
+ # triggered.
#
- # If you don't need the confirmation validation, just don't set any
- # value to the password_confirmation attribute and the the validation
- # will not be triggered.
+ # For further customizability, it is possible to supress the default
+ # validations by passing <tt>validations: false</tt> as an argument.
#
- # You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use #has_secure_password:
+ # Add bcrypt (~> 3.1.7) to Gemfile to use #has_secure_password:
#
- # gem 'bcrypt-ruby', '~> 3.0.0'
+ # gem 'bcrypt', '~> 3.1.7'
#
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
#
@@ -30,33 +41,44 @@ module ActiveModel
# end
#
# user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
- # user.save # => false, password required
+ # user.save # => false, password required
# user.password = 'mUc3m00RsqyRe'
- # user.save # => false, confirmation doesn't match
+ # user.save # => false, confirmation doesn't match
# user.password_confirmation = 'mUc3m00RsqyRe'
- # user.save # => true
- # user.authenticate('notright') # => false
- # user.authenticate('mUc3m00RsqyRe') # => user
- # User.find_by_name('david').try(:authenticate, 'notright') # => false
- # User.find_by_name('david').try(:authenticate, 'mUc3m00RsqyRe') # => user
+ # user.save # => true
+ # user.authenticate('notright') # => false
+ # user.authenticate('mUc3m00RsqyRe') # => user
+ # User.find_by(name: 'david').try(:authenticate, 'notright') # => false
+ # User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
def has_secure_password(options = {})
- # Load bcrypt-ruby only when has_secure_password is used.
+ # Load bcrypt gem only when has_secure_password is used.
# This is to avoid ActiveModel (and by extension the entire framework)
# being dependent on a binary library.
- gem 'bcrypt-ruby', '~> 3.0.0'
- require 'bcrypt'
+ begin
+ require 'bcrypt'
+ rescue LoadError
+ $stderr.puts "You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install"
+ raise
+ end
- attr_reader :password
+ include InstanceMethodsOnActivation
if options.fetch(:validations, true)
- validates_confirmation_of :password
- validates_presence_of :password, :on => :create
+ include ActiveModel::Validations
- before_create { raise "Password digest missing on new record" if password_digest.blank? }
- end
+ # This ensures the model has a password by checking whether the password_digest
+ # is present, so that this works with both new and existing records. However,
+ # when there is an error, the message is added to the password attribute instead
+ # so that the error message will make sense to the end-user.
+ validate do |record|
+ record.errors.add(:password, :blank) unless record.password_digest.present?
+ end
- include InstanceMethodsOnActivation
+ validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
+ validates_confirmation_of :password, allow_blank: true
+ end
+ # This code is necessary as long as the protected_attributes gem is supported.
if respond_to?(:attributes_protected_by_default)
def self.attributes_protected_by_default #:nodoc:
super + ['password_digest']
@@ -77,11 +99,13 @@ module ActiveModel
# user.authenticate('notright') # => false
# user.authenticate('mUc3m00RsqyRe') # => user
def authenticate(unencrypted_password)
- BCrypt::Password.new(password_digest) == unencrypted_password && self
+ BCrypt::Password.new(password_digest).is_password?(unencrypted_password) && self
end
+ attr_reader :password
+
# Encrypts the password into the +password_digest+ attribute, only if the
- # new password is not blank.
+ # new password is not empty.
#
# class User < ActiveRecord::Base
# has_secure_password validations: false
@@ -93,12 +117,18 @@ module ActiveModel
# user.password = 'mUc3m00RsqyRe'
# user.password_digest # => "$2a$10$4LEA7r4YmNHtvlAvHhsYAeZmk/xeUVtMTYqwIvYY76EW5GUqDiP4."
def password=(unencrypted_password)
- unless unencrypted_password.blank?
+ if unencrypted_password.nil?
+ self.password_digest = nil
+ elsif !unencrypted_password.empty?
@password = unencrypted_password
- cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine::DEFAULT_COST
+ cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
end
end
+
+ def password_confirmation=(unencrypted_password)
+ @password_confirmation = unencrypted_password
+ end
end
end
end
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index fdb06aebb9..976f50b13e 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/hash/slice'
module ActiveModel
# == Active \Model \Serialization
#
- # Provides a basic serialization to a serializable_hash for your object.
+ # Provides a basic serialization to a serializable_hash for your objects.
#
# A minimal implementation could be:
#
@@ -25,14 +25,14 @@ module ActiveModel
# person.name = "Bob"
# person.serializable_hash # => {"name"=>"Bob"}
#
- # You need to declare an attributes hash which contains the attributes you
- # want to serialize. Attributes must be strings, not symbols. When called,
- # serializable hash will use instance methods that match the name of the
- # attributes hash's keys. In order to override this behavior, take a look at
- # the private method +read_attribute_for_serialization+.
+ # An +attributes+ hash must be defined and should contain any attributes you
+ # need to be serialized. Attributes must be strings, not symbols.
+ # When called, serializable hash will use instance methods that match the name
+ # of the attributes hash's keys. In order to override this behavior, take a look
+ # at the private method +read_attribute_for_serialization+.
#
- # Most of the time though, you will want to include the JSON or XML
- # serializations. Both of these modules automatically include the
+ # Most of the time though, either the JSON or XML serializations are needed.
+ # Both of these modules automatically include the
# <tt>ActiveModel::Serialization</tt> module, so there is no need to
# explicitly include it.
#
@@ -128,7 +128,7 @@ module ActiveModel
# retrieve the value for a given attribute differently:
#
# class MyClass
- # include ActiveModel::Validations
+ # include ActiveModel::Serialization
#
# def initialize(data = {})
# @data = data
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index 9d984b7a18..b66dbf1afe 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -2,7 +2,7 @@ require 'active_support/json'
module ActiveModel
module Serializers
- # == Active Model JSON Serializer
+ # == Active \Model \JSON \Serializer
module JSON
extend ActiveSupport::Concern
include ActiveModel::Serialization
@@ -93,7 +93,7 @@ module ActiveModel
end
if root
- root = self.class.model_name.element if root == true
+ root = model_name.element if root == true
{ root => serializable_hash(options) }
else
serializable_hash(options)
@@ -109,7 +109,7 @@ module ActiveModel
#
# def attributes=(hash)
# hash.each do |key, value|
- # instance_variable_set("@#{key}", value)
+ # send("#{key}=", value)
# end
# end
#
@@ -130,10 +130,10 @@ module ActiveModel
#
# json = { person: { name: 'bob', age: 22, awesome:true } }.to_json
# person = Person.new
- # person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
- # person.name # => "bob"
- # person.age # => 22
- # person.awesome # => true
+ # person.from_json(json, true) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
+ # person.name # => "bob"
+ # person.age # => 22
+ # person.awesome # => true
def from_json(json, include_root=include_root_in_json)
hash = ActiveSupport::JSON.decode(json)
hash = hash.values.first if include_root
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 648ae7ce3d..3ad3bf30ad 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/hash/conversions'
require 'active_support/core_ext/hash/slice'
@@ -79,17 +79,17 @@ module ActiveModel
require 'builder' unless defined? ::Builder
options[:indent] ||= 2
- options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
+ options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
@builder = options[:builder]
@builder.instruct! unless options[:skip_instruct]
- root = (options[:root] || @serializable.class.model_name.element).to_s
+ root = (options[:root] || @serializable.model_name.element).to_s
root = ActiveSupport::XmlMini.rename_key(root, options)
args = [root]
- args << {:xmlns => options[:namespace]} if options[:namespace]
- args << {:type => options[:type]} if options[:type] && !options[:skip_types]
+ args << { xmlns: options[:namespace] } if options[:namespace]
+ args << { type: options[:type] } if options[:type] && !options[:skip_types]
@builder.tag!(*args) do
add_attributes_and_methods
@@ -132,7 +132,7 @@ module ActiveModel
records = records.to_ary
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
- type = options[:skip_types] ? { } : {:type => "array"}
+ type = options[:skip_types] ? { } : { type: "array" }
association_name = association.to_s.singularize
merged_options[:root] = association_name
@@ -145,7 +145,7 @@ module ActiveModel
record_type = {}
else
record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
- record_type = {:type => record_class}
+ record_type = { type: record_class }
end
record.to_xml merged_options.merge(record_type)
@@ -205,7 +205,7 @@ module ActiveModel
Serializer.new(self, options).serialize(&block)
end
- # Sets the model +attributes+ from a JSON string. Returns +self+.
+ # Sets the model +attributes+ from an XML string. Returns +self+.
#
# class Person
# include ActiveModel::Serializers::Xml
diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb
index 0d098ba93d..8470915abb 100644
--- a/activemodel/lib/active_model/translation.rb
+++ b/activemodel/lib/active_model/translation.rb
@@ -41,7 +41,7 @@ module ActiveModel
#
# Specify +options+ with additional translating options.
def human_attribute_name(attribute, options = {})
- options = { :count => 1 }.merge!(options)
+ options = { count: 1 }.merge!(options)
parts = attribute.to_s.split(".")
attribute = parts.pop
namespace = parts.join("/") unless parts.empty?
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 2db4a25f61..6a2668b8f7 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/hash/except'
module ActiveModel
- # == Active \Model Validations
+ # == Active \Model \Validations
#
# Provides a full validation framework to your objects.
#
@@ -39,6 +39,7 @@ module ActiveModel
extend ActiveSupport::Concern
included do
+ extend ActiveModel::Naming
extend ActiveModel::Callbacks
extend ActiveModel::Translation
@@ -46,7 +47,7 @@ module ActiveModel
include HelperMethods
attr_accessor :validation_context
- define_callbacks :validate, :scope => :name
+ define_callbacks :validate, scope: :name
class_attribute :_validators
self._validators = Hash.new { |h,k| h[k] = [] }
@@ -66,8 +67,11 @@ module ActiveModel
# end
#
# Options:
- # * <tt>:on</tt> - Specifies the context where this validation is active
- # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>)
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</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
@@ -83,6 +87,8 @@ module ActiveModel
validates_with BlockValidator, _merge_attributes(attr_names), &block
end
+ VALID_OPTIONS_FOR_VALIDATE = [:on, :if, :unless, :prepend].freeze
+
# Adds a validation method or block to the class. This is useful when
# overriding the +validate+ instance method becomes too unwieldy and
# you're looking for more descriptive declaration of your validations.
@@ -124,10 +130,11 @@ module ActiveModel
# end
#
# Options:
- # * <tt>:on</tt> - Specifies the context where this validation is active
- # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>)
- # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
- # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</tt>)
# * <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,
@@ -139,11 +146,23 @@ module ActiveModel
# value.
def validate(*args, &block)
options = args.extract_options!
+
+ if args.all? { |arg| arg.is_a?(Symbol) }
+ options.each_key do |k|
+ unless VALID_OPTIONS_FOR_VALIDATE.include?(k)
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{VALID_OPTIONS_FOR_VALIDATE.map(&:inspect).join(', ')}. Perhaps you meant to call `validates` instead of `validate`?")
+ end
+ end
+ end
+
if options.key?(:on)
options = options.dup
options[:if] = Array(options[:if])
- options[:if].unshift("validation_context == :#{options[:on]}")
+ options[:if].unshift ->(o) {
+ Array(options[:on]).include?(o.validation_context)
+ }
end
+
args << options
set_callback(:validate, *args, &block)
end
@@ -169,6 +188,49 @@ module ActiveModel
_validators.values.flatten.uniq
end
+ # Clears all of the validators and validations.
+ #
+ # Note that this will clear anything that is being used to validate
+ # the model for both the +validates_with+ and +validate+ methods.
+ # It clears the validators that are created with an invocation of
+ # +validates_with+ and the callbacks that are set by an invocation
+ # of +validate+.
+ #
+ # class Person
+ # include ActiveModel::Validations
+ #
+ # validates_with MyValidator
+ # validates_with OtherValidator, on: :create
+ # validates_with StrictValidator, strict: true
+ # validate :cannot_be_robot
+ #
+ # def cannot_be_robot
+ # errors.add(:base, 'A person cannot be a robot') if person_is_robot
+ # end
+ # end
+ #
+ # Person.validators
+ # # => [
+ # # #<MyValidator:0x007fbff403e808 @options={}>,
+ # # #<OtherValidator:0x007fbff403d930 @options={on: :create}>,
+ # # #<StrictValidator:0x007fbff3204a30 @options={strict:true}>
+ # # ]
+ #
+ # If one runs <tt>Person.clear_validators!</tt> and then checks to see what
+ # validators this class has, you would obtain:
+ #
+ # Person.validators # => []
+ #
+ # Also, the callback set by <tt>validate :cannot_be_robot</tt> will be erased
+ # so that:
+ #
+ # Person._validate_callbacks.empty? # => true
+ #
+ def clear_validators!
+ reset_callbacks(:validate)
+ _validators.clear
+ end
+
# List all validators that are being used to validate a specific attribute.
#
# class Person
@@ -183,7 +245,6 @@ module ActiveModel
# Person.validators_on(:name)
# # => [
# # #<ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
- # # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={in:0..99}>
# # ]
def validators_on(*attributes)
attributes.flat_map do |attribute|
@@ -239,6 +300,8 @@ module ActiveModel
# Runs all the specified validations and returns +true+ if no errors were
# added otherwise +false+.
#
+ # Aliased as validate.
+ #
# class Person
# include ActiveModel::Validations
#
@@ -273,6 +336,8 @@ module ActiveModel
self.validation_context = current_context
end
+ alias_method :validate, :valid?
+
# Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
# added, +false+ otherwise.
#
@@ -327,13 +392,10 @@ module ActiveModel
protected
def run_validations! #:nodoc:
- run_callbacks :validate
+ _run_validate_callbacks
errors.empty?
end
end
end
-Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
- filename = File.basename(path)
- require "active_model/validations/#{filename}"
-end
+Dir[File.dirname(__FILE__) + "/validations/*.rb"].each { |file| require file }
diff --git a/activemodel/lib/active_model/validations/absence.rb b/activemodel/lib/active_model/validations/absence.rb
index 1a1863370b..9b5416fb1d 100644
--- a/activemodel/lib/active_model/validations/absence.rb
+++ b/activemodel/lib/active_model/validations/absence.rb
@@ -21,7 +21,7 @@ module ActiveModel
# * <tt>:message</tt> - A custom error message (default is: "must be blank").
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+.
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
# See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_absence_of(*attr_names)
validates_with AbsenceValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 0935ad0d2a..ac5e79859b 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -3,7 +3,8 @@ module ActiveModel
module Validations
class AcceptanceValidator < EachValidator # :nodoc:
def initialize(options)
- super({ :allow_nil => true, :accept => "1" }.merge!(options))
+ super({ allow_nil: true, accept: "1" }.merge!(options))
+ setup!(options[:class])
end
def validate_each(record, attribute, value)
@@ -12,7 +13,8 @@ module ActiveModel
end
end
- def setup(klass)
+ private
+ def setup!(klass)
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
klass.send(:attr_reader, *attr_readers)
@@ -36,8 +38,6 @@ module ActiveModel
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "must be
# accepted").
- # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default
- # is +true+).
# * <tt>:accept</tt> - Specifies value that is considered accepted.
# The default value is a string "1", which makes it easy to relate to
# an HTML checkbox. This should be set to +true+ if you are validating
@@ -45,8 +45,8 @@ module ActiveModel
# before validation.
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+.
- # See <tt>ActiveModel::Validation#validates</tt> for more information
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information.
def validates_acceptance_of(*attr_names)
validates_with AcceptanceValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb
index e28ad2841b..25ccabd66b 100644
--- a/activemodel/lib/active_model/validations/callbacks.rb
+++ b/activemodel/lib/active_model/validations/callbacks.rb
@@ -1,6 +1,6 @@
module ActiveModel
module Validations
- # == Active \Model Validation Callbacks
+ # == Active \Model \Validation \Callbacks
#
# Provides an interface for any class to have +before_validation+ and
# +after_validation+ callbacks.
@@ -22,7 +22,10 @@ module ActiveModel
included do
include ActiveSupport::Callbacks
- define_callbacks :validation, :terminator => "result == false", :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name]
+ define_callbacks :validation,
+ terminator: ->(_,result) { result == false },
+ skip_after_callbacks_if_terminated: true,
+ scope: [:kind, :name]
end
module ClassMethods
@@ -55,7 +58,9 @@ module ActiveModel
if options.is_a?(Hash) && options[:on]
options[:if] = Array(options[:if])
options[:on] = Array(options[:on])
- options[:if].unshift("#{options[:on]}.include? self.validation_context")
+ options[:if].unshift ->(o) {
+ options[:on].include? o.validation_context
+ }
end
set_callback(:validation, :before, *args, &block)
end
@@ -93,7 +98,9 @@ module ActiveModel
options[:if] = Array(options[:if])
if options[:on]
options[:on] = Array(options[:on])
- options[:if].unshift("#{options[:on]}.include? self.validation_context")
+ options[:if].unshift ->(o) {
+ options[:on].include? o.validation_context
+ }
end
set_callback(:validation, :after, *(args << options), &block)
end
@@ -103,7 +110,7 @@ module ActiveModel
# Overwrite run validations to include callbacks.
def run_validations! #:nodoc:
- run_callbacks(:validation) { super }
+ _run_validation_callbacks { super }
end
end
end
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index 49df98d6c1..bad9e4f9a9 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -15,26 +15,36 @@ module ActiveModel
private
def include?(record, value)
- exclusions = if delimiter.respond_to?(:call)
- delimiter.call(record)
- elsif delimiter.respond_to?(:to_sym)
- record.send(delimiter)
- else
- delimiter
- end
+ members = if delimiter.respond_to?(:call)
+ delimiter.call(record)
+ elsif delimiter.respond_to?(:to_sym)
+ record.send(delimiter)
+ else
+ delimiter
+ end
- exclusions.send(inclusion_method(exclusions), value)
+ members.send(inclusion_method(members), value)
end
def delimiter
@delimiter ||= options[:in] || options[:within]
end
- # In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
- # range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
- # uses the previous logic of comparing a value with the range endpoints.
+ # In Ruby 1.9 <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
+ # possible values in the range for equality, which is slower but more accurate.
+ # <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
+ # endpoints, which is fast but is only accurate on Numeric, Time, or DateTime ranges.
def inclusion_method(enumerable)
- enumerable.is_a?(Range) ? :cover? : :include?
+ if enumerable.is_a? Range
+ case enumerable.first
+ when Numeric, Time, DateTime
+ :cover?
+ else
+ :include?
+ end
+ else
+ :include?
+ end
end
end
end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index 3a3abce364..1b11c28087 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -2,17 +2,27 @@ module ActiveModel
module Validations
class ConfirmationValidator < EachValidator # :nodoc:
+ def initialize(options)
+ super
+ setup!(options[:class])
+ end
+
def validate_each(record, attribute, value)
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
human_attribute_name = record.class.human_attribute_name(attribute)
- record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(:attribute => human_attribute_name))
+ record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(attribute: human_attribute_name))
end
end
- def setup(klass)
- klass.send(:attr_accessor, *attributes.map do |attribute|
+ private
+ def setup!(klass)
+ klass.send(:attr_reader, *attributes.map do |attribute|
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
end.compact)
+
+ klass.send(:attr_writer, *attributes.map do |attribute|
+ :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
+ end.compact)
end
end
@@ -44,10 +54,10 @@ module ActiveModel
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "doesn't match
- # confirmation").
+ # <tt>%{translated_attribute_name}</tt>").
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+.
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
# See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_confirmation_of(*attr_names)
validates_with ConfirmationValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index b7f38e48f5..f342d27275 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -8,7 +8,7 @@ module ActiveModel
def validate_each(record, attribute, value)
if include?(record, value)
- record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(:value => value))
+ record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(value: value))
end
end
end
@@ -34,13 +34,9 @@ module ActiveModel
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
# reserved").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the
- # attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
- # attribute is blank(default is +false+).
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+.
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
# See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_exclusion_of(*attr_names)
validates_with ExclusionValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 9398b7e66e..02478dd5b6 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -17,8 +17,8 @@ module ActiveModel
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
end
- check_options_validity(options, :with)
- check_options_validity(options, :without)
+ check_options_validity :with
+ check_options_validity :without
end
private
@@ -29,30 +29,32 @@ module ActiveModel
end
def record_error(record, attribute, name, value)
- record.errors.add(attribute, :invalid, options.except(name).merge!(:value => value))
+ record.errors.add(attribute, :invalid, options.except(name).merge!(value: value))
end
- def regexp_using_multiline_anchors?(regexp)
- regexp.source.start_with?("^") ||
- (regexp.source.end_with?("$") && !regexp.source.end_with?("\\$"))
+ def check_options_validity(name)
+ if option = options[name]
+ if option.is_a?(Regexp)
+ if options[:multiline] != true && regexp_using_multiline_anchors?(option)
+ raise ArgumentError, "The provided regular expression is using multiline anchors (^ or $), " \
+ "which may present a security risk. Did you mean to use \\A and \\z, or forgot to add the " \
+ ":multiline => true option?"
+ end
+ elsif !option.respond_to?(:call)
+ raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
+ end
+ end
end
- def check_options_validity(options, name)
- option = options[name]
- if option && !option.is_a?(Regexp) && !option.respond_to?(:call)
- raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
- elsif option && option.is_a?(Regexp) &&
- regexp_using_multiline_anchors?(option) && options[:multiline] != true
- raise ArgumentError, "The provided regular expression is using multiline anchors (^ or $), " \
- "which may present a security risk. Did you mean to use \\A and \\z, or forgot to add the " \
- ":multiline => true option?"
- end
+ def regexp_using_multiline_anchors?(regexp)
+ source = regexp.source
+ source.start_with?("^") || (source.end_with?("$") && !source.end_with?("\\$"))
end
end
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
+ # form, going by the regular expression provided. You can require that the
# attribute matches the regular expression:
#
# class Person < ActiveRecord::Base
@@ -89,10 +91,6 @@ module ActiveModel
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the
- # attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
- # attribute is blank (default is +false+).
# * <tt>:with</tt> - Regular expression that if the attribute matches will
# result in a successful validation. This can be provided as a proc or
# lambda returning regular expression which will be called at runtime.
@@ -105,7 +103,7 @@ module ActiveModel
# beginning or end of the string. These anchors are <tt>^</tt> and <tt>$</tt>.
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+.
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
# See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_format_of(*attr_names)
validates_with FormatValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 5e45a04c2c..c84025f083 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -8,7 +8,7 @@ module ActiveModel
def validate_each(record, attribute, value)
unless include?(record, value)
- record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(:value => value))
+ record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(value: value))
end
end
end
@@ -28,18 +28,15 @@ module ActiveModel
# Configuration options:
# * <tt>:in</tt> - An enumerable object of available items. This can be
# supplied as a proc, lambda or symbol which returns an enumerable. If the
- # enumerable is a range the test is performed with <tt>Range#cover?</tt>,
- # otherwise with <tt>include?</tt>.
+ # enumerable is a numerical range the test is performed with <tt>Range#cover?</tt>,
+ # otherwise with <tt>include?</tt>. When using a proc or lambda the instance
+ # under validation is passed as an argument.
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
# not included in the list").
- # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
- # attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
- # attribute is blank (default is +false+).
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+.
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
# See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_inclusion_of(*attr_names)
validates_with InclusionValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 675fb5f1e5..a96b30cadd 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -1,10 +1,10 @@
module ActiveModel
- # == Active \Model Length \Validator
+ # == Active \Model Length Validator
module Validations
class LengthValidator < EachValidator # :nodoc:
- MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
- CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
+ MESSAGES = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
+ CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 085532c35b..4ba4e3e8f7 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -2,17 +2,18 @@ module ActiveModel
module Validations
class NumericalityValidator < EachValidator # :nodoc:
- CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
- :equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
- :odd => :odd?, :even => :even?, :other_than => :!= }.freeze
+ CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
+ equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
+ odd: :odd?, even: :even?, other_than: :!= }.freeze
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
def check_validity!
keys = CHECKS.keys - [:odd, :even]
options.slice(*keys).each do |option, value|
- next if value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
- 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)
+ raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
+ end
end
end
@@ -22,6 +23,10 @@ module ActiveModel
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast)
raw_value ||= value
+ if record_attribute_changed_in_place?(record, attr_name)
+ raw_value = value
+ end
+
return if options[:allow_nil] && raw_value.nil?
unless value = parse_raw_value_as_a_number(raw_value)
@@ -29,7 +34,7 @@ module ActiveModel
return
end
- if options[:only_integer]
+ if allow_only_integer?(record)
unless value = parse_raw_value_as_an_integer(raw_value)
record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
return
@@ -43,11 +48,15 @@ module ActiveModel
record.errors.add(attr_name, option, filtered_options(value))
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)
+ case option_value
+ when Proc
+ option_value = option_value.call(record)
+ when Symbol
+ option_value = record.send(option_value)
+ end
unless value.send(CHECKS[option], option_value)
- record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value))
+ record.errors.add(attr_name, option, filtered_options(value).merge!(count: option_value))
end
end
end
@@ -56,24 +65,37 @@ module ActiveModel
protected
def parse_raw_value_as_a_number(raw_value)
- case raw_value
- when /\A0[xX]/
- nil
- else
- begin
- Kernel.Float(raw_value)
- rescue ArgumentError, TypeError
- nil
- end
- end
+ Kernel.Float(raw_value) if raw_value !~ /\A0[xX]/
+ rescue ArgumentError, TypeError
+ nil
end
def parse_raw_value_as_an_integer(raw_value)
- raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
+ raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\z/
end
def filtered_options(value)
- options.except(*RESERVED_OPTIONS).merge!(:value => value)
+ filtered = options.except(*RESERVED_OPTIONS)
+ filtered[:value] = value
+ filtered
+ end
+
+ def allow_only_integer?(record)
+ case options[:only_integer]
+ when Symbol
+ record.send(options[:only_integer])
+ when Proc
+ options[:only_integer].call(record)
+ else
+ options[:only_integer]
+ end
+ end
+
+ private
+
+ def record_attribute_changed_in_place?(record, attr_name)
+ record.respond_to?(:attribute_changed_in_place?) &&
+ record.attribute_changed_in_place?(attr_name.to_s)
end
end
@@ -110,7 +132,7 @@ module ActiveModel
# * <tt>:even</tt> - Specifies the value must be an even number.
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+ .
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+ .
# See <tt>ActiveModel::Validation#validates</tt> for more information
#
# The following checks can also be supplied with a proc or a symbol which
@@ -121,6 +143,7 @@ module ActiveModel
# * <tt>:equal_to</tt>
# * <tt>:less_than</tt>
# * <tt>:less_than_or_equal_to</tt>
+ # * <tt>:only_integer</tt>
#
# For example:
#
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index ab8c8359fc..5d593274eb 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -29,7 +29,7 @@ module ActiveModel
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
#
# There is also a list of default options supported by every validator:
- # +:if+, +:unless+, +:on+ and +:strict+.
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
# See <tt>ActiveModel::Validation#validates</tt> for more information
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 1eb0716891..bda436d8d0 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -13,7 +13,7 @@ module ActiveModel
# validates :terms, acceptance: true
# validates :password, confirmation: true
# validates :username, exclusion: { in: %w(admin superuser) }
- # validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, on: :create }
+ # validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
# validates :age, inclusion: { in: 0..9 }
# validates :first_name, length: { maximum: 30 }
# validates :age, numericality: true
@@ -71,9 +71,11 @@ module ActiveModel
#
# There is also a list of options that could be used along with validators:
#
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</tt>)
# * <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,
@@ -83,7 +85,9 @@ module ActiveModel
# 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.
- # * <tt>:strict</tt> - if the <tt>:strict</tt> option is set to true
+ # * <tt>:allow_nil</tt> - Skip validation if the attribute is +nil+.
+ # * <tt>:allow_blank</tt> - Skip validation if the attribute is blank.
+ # * <tt>:strict</tt> - If the <tt>:strict</tt> option is set to true
# will raise ActiveModel::StrictValidationFailed instead of adding the error.
# <tt>:strict</tt> option can also be set to any other exception.
#
@@ -159,9 +163,9 @@ module ActiveModel
when Hash
options
when Range, Array
- { :in => options }
+ { in: options }
else
- { :with => options }
+ { with: options }
end
end
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 2ae335d0f4..a2531327bf 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -52,8 +52,11 @@ module ActiveModel
# end
#
# Configuration options:
- # * <tt>:on</tt> - Specifies when this validation is active
- # (<tt>:create</tt> or <tt>:update</tt>.
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</tt>)
# * <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>).
@@ -83,9 +86,10 @@ module ActiveModel
# end
def validates_with(*args, &block)
options = args.extract_options!
+ options[:class] = self
+
args.each do |klass|
validator = klass.new(options, &block)
- validator.setup(self) if validator.respond_to?(:setup)
if validator.respond_to?(:attributes) && !validator.attributes.empty?
validator.attributes.each do |attribute|
@@ -138,6 +142,8 @@ module ActiveModel
# class version of this method for more information.
def validates_with(*args, &block)
options = args.extract_options!
+ options[:class] = self.class
+
args.each do |klass|
validator = klass.new(options, &block)
validator.validate(self)
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index f989b21140..0116de68ab 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -60,6 +60,9 @@ module ActiveModel
# end
# end
#
+ # Note that the validator is initialized only once for the whole application
+ # life cycle, and not on each validation run.
+ #
# The easiest way to add custom validators for validating individual attributes
# is with the convenient <tt>ActiveModel::EachValidator</tt>.
#
@@ -76,21 +79,19 @@ module ActiveModel
# include ActiveModel::Validations
# attr_accessor :title
#
- # validates :title, presence: true
+ # validates :title, presence: true, title: true
# end
#
- # Validator may also define a +setup+ instance method which will get called
- # with the class that using that validator as its argument. This can be
- # useful when there are prerequisites such as an +attr_accessor+ being present.
+ # It can be useful to access the class that is using that validator when there are prerequisites such
+ # as an +attr_accessor+ being present. This class is accessible via +options[:class]+ in the constructor.
+ # To setup your validator override the constructor.
#
# class MyValidator < ActiveModel::Validator
- # def setup(klass)
- # klass.send :attr_accessor, :custom_attribute
+ # def initialize(options={})
+ # super
+ # options[:class].send :attr_accessor, :custom_attribute
# end
# end
- #
- # This setup method is only called when used with validation macros or the
- # class level <tt>validates_with</tt> method.
class Validator
attr_reader :options
@@ -104,10 +105,10 @@ module ActiveModel
# Accepts options that will be made available through the +options+ reader.
def initialize(options = {})
- @options = options.freeze
+ @options = options.except(:class).freeze
end
- # Return the kind for this validator.
+ # Returns the kind for this validator.
#
# PresenceValidator.new.kind # => :presence
# UniquenessValidator.new.kind # => :uniqueness
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index e195c12a4d..b1f9082ea7 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -1,10 +1,8 @@
-module ActiveModel
- module VERSION #:nodoc:
- MAJOR = 4
- MINOR = 0
- TINY = 0
- PRE = "beta"
+require_relative 'gem_version'
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
+module ActiveModel
+ # Returns the version of the currently loaded ActiveModel as a <tt>Gem::Version</tt>
+ def self.version
+ gem_version
end
end
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb
index baaf842222..e81b7ac424 100644
--- a/activemodel/test/cases/attribute_methods_test.rb
+++ b/activemodel/test/cases/attribute_methods_test.rb
@@ -10,7 +10,7 @@ class ModelWithAttributes
end
def attributes
- { :foo => 'value of foo', :baz => 'value of baz' }
+ { foo: 'value of foo', baz: 'value of baz' }
end
private
@@ -80,7 +80,7 @@ class ModelWithRubyKeywordNamedAttributes
include ActiveModel::AttributeMethods
def attributes
- { :begin => 'value of begin', :end => 'value of end' }
+ { begin: 'value of begin', end: 'value of end' }
end
private
@@ -104,10 +104,14 @@ class AttributeMethodsTest < ActiveModel::TestCase
end
test '#define_attribute_method generates attribute method' do
- ModelWithAttributes.define_attribute_method(:foo)
+ begin
+ ModelWithAttributes.define_attribute_method(:foo)
- assert_respond_to ModelWithAttributes.new, :foo
- assert_equal "value of foo", ModelWithAttributes.new.foo
+ assert_respond_to ModelWithAttributes.new, :foo
+ assert_equal "value of foo", ModelWithAttributes.new.foo
+ ensure
+ ModelWithAttributes.undefine_attribute_methods
+ end
end
test '#define_attribute_method does not generate attribute method if already defined in attribute module' do
@@ -134,24 +138,36 @@ class AttributeMethodsTest < ActiveModel::TestCase
end
test '#define_attribute_method generates attribute method with invalid identifier characters' do
- ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b')
+ begin
+ ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b')
- assert_respond_to ModelWithWeirdNamesAttributes.new, :'a?b'
- assert_equal "value of a?b", ModelWithWeirdNamesAttributes.new.send('a?b')
+ assert_respond_to ModelWithWeirdNamesAttributes.new, :'a?b'
+ assert_equal "value of a?b", ModelWithWeirdNamesAttributes.new.send('a?b')
+ ensure
+ ModelWithWeirdNamesAttributes.undefine_attribute_methods
+ end
end
test '#define_attribute_methods works passing multiple arguments' do
- ModelWithAttributes.define_attribute_methods(:foo, :baz)
+ begin
+ ModelWithAttributes.define_attribute_methods(:foo, :baz)
- assert_equal "value of foo", ModelWithAttributes.new.foo
- assert_equal "value of baz", ModelWithAttributes.new.baz
+ assert_equal "value of foo", ModelWithAttributes.new.foo
+ assert_equal "value of baz", ModelWithAttributes.new.baz
+ ensure
+ ModelWithAttributes.undefine_attribute_methods
+ end
end
test '#define_attribute_methods generates attribute methods' do
- ModelWithAttributes.define_attribute_methods(:foo)
+ begin
+ ModelWithAttributes.define_attribute_methods(:foo)
- assert_respond_to ModelWithAttributes.new, :foo
- assert_equal "value of foo", ModelWithAttributes.new.foo
+ assert_respond_to ModelWithAttributes.new, :foo
+ assert_equal "value of foo", ModelWithAttributes.new.foo
+ ensure
+ ModelWithAttributes.undefine_attribute_methods
+ end
end
test '#alias_attribute generates attribute_aliases lookup hash' do
@@ -164,26 +180,38 @@ class AttributeMethodsTest < ActiveModel::TestCase
end
test '#define_attribute_methods generates attribute methods with spaces in their names' do
- ModelWithAttributesWithSpaces.define_attribute_methods(:'foo bar')
+ begin
+ ModelWithAttributesWithSpaces.define_attribute_methods(:'foo bar')
- assert_respond_to ModelWithAttributesWithSpaces.new, :'foo bar'
- assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.send(:'foo bar')
+ assert_respond_to ModelWithAttributesWithSpaces.new, :'foo bar'
+ assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.send(:'foo bar')
+ ensure
+ ModelWithAttributesWithSpaces.undefine_attribute_methods
+ end
end
test '#alias_attribute works with attributes with spaces in their names' do
- ModelWithAttributesWithSpaces.define_attribute_methods(:'foo bar')
- ModelWithAttributesWithSpaces.alias_attribute(:'foo_bar', :'foo bar')
+ begin
+ ModelWithAttributesWithSpaces.define_attribute_methods(:'foo bar')
+ ModelWithAttributesWithSpaces.alias_attribute(:'foo_bar', :'foo bar')
- assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.foo_bar
+ assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.foo_bar
+ ensure
+ ModelWithAttributesWithSpaces.undefine_attribute_methods
+ end
end
test '#alias_attribute works with attributes named as a ruby keyword' do
- ModelWithRubyKeywordNamedAttributes.define_attribute_methods([:begin, :end])
- ModelWithRubyKeywordNamedAttributes.alias_attribute(:from, :begin)
- ModelWithRubyKeywordNamedAttributes.alias_attribute(:to, :end)
-
- assert_equal "value of begin", ModelWithRubyKeywordNamedAttributes.new.from
- assert_equal "value of end", ModelWithRubyKeywordNamedAttributes.new.to
+ begin
+ ModelWithRubyKeywordNamedAttributes.define_attribute_methods([:begin, :end])
+ ModelWithRubyKeywordNamedAttributes.alias_attribute(:from, :begin)
+ ModelWithRubyKeywordNamedAttributes.alias_attribute(:to, :end)
+
+ assert_equal "value of begin", ModelWithRubyKeywordNamedAttributes.new.from
+ assert_equal "value of end", ModelWithRubyKeywordNamedAttributes.new.to
+ ensure
+ ModelWithRubyKeywordNamedAttributes.undefine_attribute_methods
+ end
end
test '#undefine_attribute_methods removes attribute methods' do
@@ -194,7 +222,7 @@ class AttributeMethodsTest < ActiveModel::TestCase
assert_raises(NoMethodError) { ModelWithAttributes.new.foo }
end
- test 'acessing a suffixed attribute' do
+ test 'accessing a suffixed attribute' do
m = ModelWithAttributes2.new
m.attributes = { 'foo' => 'bar' }
@@ -202,17 +230,6 @@ class AttributeMethodsTest < ActiveModel::TestCase
assert_equal 'bar', m.foo_test
end
- test 'explicitly specifying an empty prefix/suffix is deprecated' do
- klass = Class.new(ModelWithAttributes)
-
- assert_deprecated { klass.attribute_method_suffix '' }
- assert_deprecated { klass.attribute_method_prefix '' }
-
- klass.define_attribute_methods(:foo)
-
- assert_equal 'value of foo', klass.new.foo
- end
-
test 'should not interfere with method_missing if the attr has a private/protected method' do
m = ModelWithAttributes2.new
m.attributes = { 'private_method' => '<3', 'protected_method' => 'O_o' }
diff --git a/activemodel/test/cases/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb
index 086e7266ff..2ac681b8d8 100644
--- a/activemodel/test/cases/callbacks_test.rb
+++ b/activemodel/test/cases/callbacks_test.rb
@@ -7,6 +7,7 @@ class CallbacksTest < ActiveModel::TestCase
model.callbacks << :before_around_create
yield
model.callbacks << :after_around_create
+ false
end
end
@@ -15,25 +16,29 @@ class CallbacksTest < ActiveModel::TestCase
extend ActiveModel::Callbacks
define_model_callbacks :create
- define_model_callbacks :initialize, :only => :after
- define_model_callbacks :multiple, :only => [:before, :around]
- define_model_callbacks :empty, :only => []
+ define_model_callbacks :initialize, only: :after
+ define_model_callbacks :multiple, only: [:before, :around]
+ define_model_callbacks :empty, only: []
before_create :before_create
around_create CallbackValidator.new
after_create do |model|
model.callbacks << :after_create
+ false
end
after_create "@callbacks << :final_callback"
- def initialize(valid=true)
- @callbacks, @valid = [], valid
+ def initialize(options = {})
+ @callbacks = []
+ @valid = options[:valid]
+ @before_create_returns = options[:before_create_returns]
end
def before_create
@callbacks << :before_create
+ @before_create_returns
end
def create
@@ -51,14 +56,20 @@ class CallbacksTest < ActiveModel::TestCase
:after_around_create, :after_create, :final_callback]
end
- test "after callbacks are always appended" do
+ test "the callback chain is not halted when around or after callbacks return false" do
model = ModelCallbacks.new
model.create
assert_equal model.callbacks.last, :final_callback
end
+ test "the callback chain is halted when a before callback returns false" do
+ model = ModelCallbacks.new(before_create_returns: false)
+ model.create
+ assert_equal model.callbacks.last, :before_create
+ end
+
test "after callbacks are not executed if the block returns false" do
- model = ModelCallbacks.new(false)
+ model = ModelCallbacks.new(valid: false)
model.create
assert_equal model.callbacks, [ :before_create, :before_around_create,
:create, :after_around_create]
@@ -107,7 +118,7 @@ class CallbacksTest < ActiveModel::TestCase
test "after_create callbacks with both callbacks declared in one line" do
assert_equal ["callback1", "callback2"], Violin1.new.create.history
end
- test "after_create callbacks with both callbacks declared in differnt lines" do
+ test "after_create callbacks with both callbacks declared in different lines" do
assert_equal ["callback1", "callback2"], Violin2.new.create.history
end
diff --git a/activemodel/test/cases/conversion_test.rb b/activemodel/test/cases/conversion_test.rb
index a037666cbc..800cad6d9a 100644
--- a/activemodel/test/cases/conversion_test.rb
+++ b/activemodel/test/cases/conversion_test.rb
@@ -13,7 +13,7 @@ class ConversionTest < ActiveModel::TestCase
end
test "to_key default implementation returns the id in an array for persisted records" do
- assert_equal [1], Contact.new(:id => 1).to_key
+ assert_equal [1], Contact.new(id: 1).to_key
end
test "to_param default implementation returns nil for new records" do
@@ -21,7 +21,21 @@ class ConversionTest < ActiveModel::TestCase
end
test "to_param default implementation returns a string of ids for persisted records" do
- assert_equal "1", Contact.new(:id => 1).to_param
+ assert_equal "1", Contact.new(id: 1).to_param
+ end
+
+ test "to_param returns the string joined by '-'" do
+ assert_equal "abc-xyz", Contact.new(id: ["abc", "xyz"]).to_param
+ end
+
+ test "to_param returns nil if to_key is nil" do
+ klass = Class.new(Contact) do
+ def persisted?
+ true
+ end
+ end
+
+ assert_nil klass.new.to_param
end
test "to_partial_path default implementation returns a string giving a relative path" do
diff --git a/activemodel/test/cases/deprecated_mass_assignment_security_test.rb b/activemodel/test/cases/deprecated_mass_assignment_security_test.rb
deleted file mode 100644
index c1fe8822cd..0000000000
--- a/activemodel/test/cases/deprecated_mass_assignment_security_test.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require 'cases/helper'
-require 'models/project'
-
-class DeprecatedMassAssignmentSecurityTest < ActiveModel::TestCase
- def test_attr_accessible_raise_error
- assert_raise RuntimeError, /protected_attributes/ do
- Project.attr_accessible :username
- end
- end
-
- def test_attr_protected_raise_error
- assert_raise RuntimeError, /protected_attributes/ do
- Project.attr_protected :username
- end
- end
-end
diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb
index ba45089cca..db2cd885e2 100644
--- a/activemodel/test/cases/dirty_test.rb
+++ b/activemodel/test/cases/dirty_test.rb
@@ -3,11 +3,12 @@ require "cases/helper"
class DirtyTest < ActiveModel::TestCase
class DirtyModel
include ActiveModel::Dirty
- define_attribute_methods :name, :color
+ define_attribute_methods :name, :color, :size
def initialize
@name = nil
@color = nil
+ @size = nil
end
def name
@@ -28,9 +29,25 @@ class DirtyTest < ActiveModel::TestCase
@color = val
end
+ def size
+ @size
+ end
+
+ def size=(val)
+ attribute_will_change!(:size) unless val == @size
+ @size = val
+ end
+
def save
- @previously_changed = changes
- @changed_attributes.clear
+ changes_applied
+ end
+
+ def reload
+ clear_changes_information
+ end
+
+ def deprecated_reload
+ reset_changes
end
end
@@ -58,12 +75,30 @@ class DirtyTest < ActiveModel::TestCase
assert_equal [nil, "John"], @model.changes['name']
end
+ test "checking if an attribute has changed to a particular value" do
+ @model.name = "Ringo"
+ assert @model.name_changed?(from: nil, to: "Ringo")
+ assert_not @model.name_changed?(from: "Pete", to: "Ringo")
+ assert @model.name_changed?(to: "Ringo")
+ assert_not @model.name_changed?(to: "Pete")
+ assert @model.name_changed?(from: nil)
+ assert_not @model.name_changed?(from: "Pete")
+ end
+
test "changes accessible through both strings and symbols" do
@model.name = "David"
assert_not_nil @model.changes[:name]
assert_not_nil @model.changes['name']
end
+ test "be consistent with symbols arguments after the changes are applied" do
+ @model.name = "David"
+ assert @model.attribute_changed?(:name)
+ @model.save
+ @model.name = 'Rafael'
+ assert @model.attribute_changed?(:name)
+ end
+
test "attribute mutation" do
@model.instance_variable_set("@name", "Yam")
assert !@model.name_changed?
@@ -76,7 +111,7 @@ class DirtyTest < ActiveModel::TestCase
test "resetting attribute" do
@model.name = "Bob"
- @model.reset_name!
+ @model.restore_name!
assert_nil @model.name
assert !@model.name_changed?
end
@@ -125,4 +160,69 @@ class DirtyTest < ActiveModel::TestCase
assert_equal ["Otto", "Mr. Manfredgensonton"], @model.name_change
assert_equal @model.name_was, "Otto"
end
+
+ test "using attribute_will_change! with a symbol" do
+ @model.size = 1
+ assert @model.size_changed?
+ end
+
+ test "reload should reset all changes" do
+ @model.name = 'Dmitry'
+ @model.name_changed?
+ @model.save
+ @model.name = 'Bob'
+
+ assert_equal [nil, 'Dmitry'], @model.previous_changes['name']
+ assert_equal 'Dmitry', @model.changed_attributes['name']
+
+ @model.reload
+
+ assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.previous_changes
+ assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.changed_attributes
+ end
+
+ test "reset_changes is deprecated" do
+ @model.name = 'Dmitry'
+ @model.name_changed?
+ @model.save
+ @model.name = 'Bob'
+
+ assert_equal [nil, 'Dmitry'], @model.previous_changes['name']
+ assert_equal 'Dmitry', @model.changed_attributes['name']
+
+ assert_deprecated do
+ @model.deprecated_reload
+ end
+
+ assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.previous_changes
+ assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.changed_attributes
+ end
+
+ test "restore_attributes should restore all previous data" do
+ @model.name = 'Dmitry'
+ @model.color = 'Red'
+ @model.save
+ @model.name = 'Bob'
+ @model.color = 'White'
+
+ @model.restore_attributes
+
+ assert_not @model.changed?
+ assert_equal 'Dmitry', @model.name
+ assert_equal 'Red', @model.color
+ end
+
+ test "restore_attributes can restore only some attributes" do
+ @model.name = 'Dmitry'
+ @model.color = 'Red'
+ @model.save
+ @model.name = 'Bob'
+ @model.color = 'White'
+
+ @model.restore_attributes(['name'])
+
+ assert @model.changed?
+ assert_equal 'Dmitry', @model.name
+ assert_equal 'White', @model.color
+ end
end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index cc0c3f16d2..efedd9055f 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -11,7 +11,7 @@ class ErrorsTest < ActiveModel::TestCase
attr_reader :errors
def validate!
- errors.add(:name, "can not be nil") if name == nil
+ errors.add(:name, "cannot be nil") if name == nil
end
def read_attribute_for_validation(attr)
@@ -51,10 +51,26 @@ class ErrorsTest < ActiveModel::TestCase
def test_has_key?
errors = ActiveModel::Errors.new(self)
errors[:foo] = 'omg'
- assert errors.has_key?(:foo), 'errors should have key :foo'
+ assert_equal true, errors.has_key?(:foo), 'errors should have key :foo'
end
- test "should be able to clear the errors" do
+ def test_has_no_key
+ errors = ActiveModel::Errors.new(self)
+ assert_equal false, errors.has_key?(:name), 'errors should not have key :name'
+ end
+
+ def test_key?
+ errors = ActiveModel::Errors.new(self)
+ errors[:foo] = 'omg'
+ assert_equal true, errors.key?(:foo), 'errors should have key :foo'
+ end
+
+ def test_no_key
+ errors = ActiveModel::Errors.new(self)
+ assert_equal false, errors.key?(:name), 'errors should not have key :name'
+ end
+
+ test "clear errors" do
person = Person.new
person.validate!
@@ -63,7 +79,7 @@ class ErrorsTest < ActiveModel::TestCase
assert person.errors.empty?
end
- test "get returns the error by the provided key" do
+ test "get returns the errors for the provided key" do
errors = ActiveModel::Errors.new(self)
errors[:foo] = "omg"
@@ -77,6 +93,13 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal({ foo: "omg" }, errors.messages)
end
+ test "error access is indifferent" do
+ errors = ActiveModel::Errors.new(self)
+ errors[:foo] = "omg"
+
+ assert_equal ["omg"], errors["foo"]
+ end
+
test "values returns an array of messages" do
errors = ActiveModel::Errors.new(self)
errors.set(:foo, "omg")
@@ -93,21 +116,7 @@ class ErrorsTest < ActiveModel::TestCase
assert_equal [:foo, :baz], errors.keys
end
- test "as_json returns a json formatted representation of the errors hash" do
- person = Person.new
- person.validate!
-
- assert_equal({ name: ["can not be nil"] }, person.errors.as_json)
- end
-
- test "as_json with :full_messages option" do
- person = Person.new
- person.validate!
-
- assert_equal({ name: ["name can not be nil"] }, person.errors.as_json(full_messages: true))
- end
-
- test "should return true if no errors" do
+ test "detecting whether there are errors with empty?, blank?, include?" do
person = Person.new
person.errors[:foo]
assert person.errors.empty?
@@ -115,139 +124,154 @@ class ErrorsTest < ActiveModel::TestCase
assert !person.errors.include?(:foo)
end
- test "method validate! should work" do
+ test "adding errors using conditionals with Person#validate!" do
person = Person.new
person.validate!
- assert_equal ["name can not be nil"], person.errors.full_messages
- assert_equal ["can not be nil"], person.errors[:name]
+ assert_equal ["name cannot be nil"], person.errors.full_messages
+ assert_equal ["cannot be nil"], person.errors[:name]
end
- test 'should be able to assign error' do
+ test "assign error" do
person = Person.new
person.errors[:name] = 'should not be nil'
assert_equal ["should not be nil"], person.errors[:name]
end
- test 'should be able to add an error on an attribute' do
+ test "add an error message on a specific attribute" do
person = Person.new
- person.errors.add(:name, "can not be blank")
- assert_equal ["can not be blank"], person.errors[:name]
+ person.errors.add(:name, "cannot be blank")
+ assert_equal ["cannot be blank"], person.errors[:name]
end
- test "should be able to add an error with a symbol" do
+ test "add an error with a symbol" do
person = Person.new
person.errors.add(:name, :blank)
message = person.errors.generate_message(:name, :blank)
assert_equal [message], person.errors[:name]
end
- test "should be able to add an error with a proc" do
+ test "add an error with a proc" do
person = Person.new
- message = Proc.new { "can not be blank" }
+ message = Proc.new { "cannot be blank" }
person.errors.add(:name, message)
- assert_equal ["can not be blank"], person.errors[:name]
+ assert_equal ["cannot be blank"], person.errors[:name]
end
- test "added? should be true if that error was added" do
+ test "added? detects if a specific error was added to the object" do
person = Person.new
- person.errors.add(:name, "can not be blank")
- assert person.errors.added?(:name, "can not be blank")
+ person.errors.add(:name, "cannot be blank")
+ assert person.errors.added?(:name, "cannot be blank")
end
- test "added? should handle when message is a symbol" do
+ test "added? handles symbol message" do
person = Person.new
person.errors.add(:name, :blank)
assert person.errors.added?(:name, :blank)
end
- test "added? should handle when message is a proc" do
+ test "added? handles proc messages" do
person = Person.new
- message = Proc.new { "can not be blank" }
+ message = Proc.new { "cannot be blank" }
person.errors.add(:name, message)
assert person.errors.added?(:name, message)
end
- test "added? should default message to :invalid" do
+ test "added? defaults message to :invalid" do
person = Person.new
person.errors.add(:name)
assert person.errors.added?(:name)
end
- test "added? should be true when several errors are present, and we ask for one of them" do
+ test "added? matches the given message when several errors are present for the same attribute" do
person = Person.new
- person.errors.add(:name, "can not be blank")
+ person.errors.add(:name, "cannot be blank")
person.errors.add(:name, "is invalid")
- assert person.errors.added?(:name, "can not be blank")
+ assert person.errors.added?(:name, "cannot be blank")
end
- test "added? should be false if no errors are present" do
+ test "added? returns false when no errors are present" do
person = Person.new
assert !person.errors.added?(:name)
end
- test "added? should be false when an error is present, but we check for another error" do
+ test "added? returns false when checking a nonexisting error and other errors are present for the given attribute" do
person = Person.new
person.errors.add(:name, "is invalid")
- assert !person.errors.added?(:name, "can not be blank")
+ assert !person.errors.added?(:name, "cannot be blank")
end
- test 'should respond to size' do
+ test "size calculates the number of error messages" do
person = Person.new
- person.errors.add(:name, "can not be blank")
+ person.errors.add(:name, "cannot be blank")
assert_equal 1, person.errors.size
end
- test 'to_a should return an array' do
+ test "to_a returns the list of errors with complete messages containing the attribute names" do
+ person = Person.new
+ person.errors.add(:name, "cannot be blank")
+ person.errors.add(:name, "cannot be nil")
+ assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.to_a
+ end
+
+ test "to_hash returns the error messages hash" do
person = Person.new
- person.errors.add(:name, "can not be blank")
- person.errors.add(:name, "can not be nil")
- assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a
+ person.errors.add(:name, "cannot be blank")
+ assert_equal({ name: ["cannot be blank"] }, person.errors.to_hash)
end
- test 'to_hash should return a hash' do
+ test "full_messages creates a list of error messages with the attribute name included" do
person = Person.new
- person.errors.add(:name, "can not be blank")
- assert_instance_of ::Hash, person.errors.to_hash
+ person.errors.add(:name, "cannot be blank")
+ person.errors.add(:name, "cannot be nil")
+ assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.full_messages
end
- test 'full_messages should return an array of error messages, with the attribute name included' do
+ test "full_messages_for contains all the error messages for the given attribute" do
person = Person.new
- person.errors.add(:name, "can not be blank")
- person.errors.add(:name, "can not be nil")
- assert_equal ["name can not be blank", "name can not be nil"], person.errors.full_messages
+ person.errors.add(:name, "cannot be blank")
+ person.errors.add(:name, "cannot be nil")
+ assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.full_messages_for(:name)
end
- test 'full_message should return the given message if attribute equals :base' do
+ test "full_messages_for does not contain error messages from other attributes" do
+ person = Person.new
+ person.errors.add(:name, "cannot be blank")
+ person.errors.add(:email, "cannot be blank")
+ assert_equal ["name cannot be blank"], person.errors.full_messages_for(:name)
+ end
+
+ test "full_messages_for returns an empty list in case there are no errors for the given attribute" do
+ person = Person.new
+ person.errors.add(:name, "cannot be blank")
+ assert_equal [], person.errors.full_messages_for(:email)
+ end
+
+ test "full_message returns the given message when attribute is :base" do
person = Person.new
assert_equal "press the button", person.errors.full_message(:base, "press the button")
end
- test 'full_message should return the given message with the attribute name included' do
+ test "full_message returns the given message with the attribute name included" do
person = Person.new
- assert_equal "name can not be blank", person.errors.full_message(:name, "can not be blank")
+ assert_equal "name cannot be blank", person.errors.full_message(:name, "cannot be blank")
+ assert_equal "name_test cannot be blank", person.errors.full_message(:name_test, "cannot be blank")
end
- test 'should return a JSON hash representation of the errors' do
+ test "as_json creates a json formatted representation of the errors hash" do
person = Person.new
- person.errors.add(:name, "can not be blank")
- person.errors.add(:name, "can not be nil")
- person.errors.add(:email, "is invalid")
- hash = person.errors.as_json
- assert_equal ["can not be blank", "can not be nil"], hash[:name]
- assert_equal ["is invalid"], hash[:email]
+ person.validate!
+
+ assert_equal({ name: ["cannot be nil"] }, person.errors.as_json)
end
- test 'should return a JSON hash representation of the errors with full messages' do
+ test "as_json with :full_messages option creates a json formatted representation of the errors containing complete messages" do
person = Person.new
- person.errors.add(:name, "can not be blank")
- person.errors.add(:name, "can not be nil")
- person.errors.add(:email, "is invalid")
- hash = person.errors.as_json(:full_messages => true)
- assert_equal ["name can not be blank", "name can not be nil"], hash[:name]
- assert_equal ["email is invalid"], hash[:email]
+ person.validate!
+
+ assert_equal({ name: ["name cannot be nil"] }, person.errors.as_json(full_messages: true))
end
- test "generate_message should work without i18n_scope" do
+ test "generate_message works without i18n_scope" do
person = Person.new
assert !Person.respond_to?(:i18n_scope)
assert_nothing_raised {
@@ -270,8 +294,15 @@ class ErrorsTest < ActiveModel::TestCase
test "add_on_empty generates message with custom default message" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :empty, {:message => 'custom'})
- person.errors.add_on_empty :name, :message => 'custom'
+ person.errors.expects(:generate_message).with(:name, :empty, { message: 'custom' })
+ person.errors.add_on_empty :name, message: 'custom'
+ end
+
+ test "add_on_empty generates message with empty string value" do
+ person = Person.new
+ person.name = ''
+ person.errors.expects(:generate_message).with(:name, :empty, {})
+ person.errors.add_on_empty :name
end
test "add_on_blank generates message" do
@@ -289,7 +320,7 @@ class ErrorsTest < ActiveModel::TestCase
test "add_on_blank generates message with custom default message" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :blank, {:message => 'custom'})
- person.errors.add_on_blank :name, :message => 'custom'
+ person.errors.expects(:generate_message).with(:name, :blank, { message: 'custom' })
+ person.errors.add_on_blank :name, message: 'custom'
end
end
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index 7a63674757..4ce6103593 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -7,4 +7,14 @@ require 'active_support/core_ext/string/access'
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
+# Disable available locale checks to avoid warnings running the test suite.
+I18n.enforce_available_locales = false
+
require 'active_support/testing/autorun'
+
+require 'mocha/setup' # FIXME: stop using mocha
+
+# FIXME: we have tests that depend on run order, we should fix that and
+# remove this method call.
+require 'active_support/test_case'
+ActiveSupport::TestCase.test_order = :sorted
diff --git a/activemodel/test/cases/model_test.rb b/activemodel/test/cases/model_test.rb
index 588d8e661e..ee0fa26546 100644
--- a/activemodel/test/cases/model_test.rb
+++ b/activemodel/test/cases/model_test.rb
@@ -3,7 +3,30 @@ require 'cases/helper'
class ModelTest < ActiveModel::TestCase
include ActiveModel::Lint::Tests
+ module DefaultValue
+ def self.included(klass)
+ klass.class_eval { attr_accessor :hello }
+ end
+
+ def initialize(*args)
+ @attr ||= 'default value'
+ super
+ end
+ end
+
class BasicModel
+ include DefaultValue
+ include ActiveModel::Model
+ attr_accessor :attr
+ end
+
+ class BasicModelWithReversedMixins
+ include ActiveModel::Model
+ include DefaultValue
+ attr_accessor :attr
+ end
+
+ class SimpleModel
include ActiveModel::Model
attr_accessor :attr
end
@@ -13,20 +36,40 @@ class ModelTest < ActiveModel::TestCase
end
def test_initialize_with_params
- object = BasicModel.new(:attr => "value")
- assert_equal object.attr, "value"
+ object = BasicModel.new(attr: "value")
+ assert_equal "value", object.attr
+ end
+
+ def test_initialize_with_params_and_mixins_reversed
+ object = BasicModelWithReversedMixins.new(attr: "value")
+ assert_equal "value", object.attr
end
def test_initialize_with_nil_or_empty_hash_params_does_not_explode
assert_nothing_raised do
BasicModel.new()
- BasicModel.new nil
+ BasicModel.new(nil)
BasicModel.new({})
+ SimpleModel.new(attr: 'value')
end
end
def test_persisted_is_always_false
- object = BasicModel.new(:attr => "value")
+ object = BasicModel.new(attr: "value")
assert object.persisted? == false
end
+
+ def test_mixin_inclusion_chain
+ object = BasicModel.new
+ assert_equal 'default value', object.attr
+ end
+
+ def test_mixin_initializer_when_args_exist
+ object = BasicModel.new(hello: 'world')
+ assert_equal 'world', object.hello
+ end
+
+ def test_mixin_initializer_when_args_dont_exist
+ assert_raises(NoMethodError) { SimpleModel.new(hello: 'world') }
+ end
end
diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb
index 38ba3cc152..7b8287edbf 100644
--- a/activemodel/test/cases/naming_test.rb
+++ b/activemodel/test/cases/naming_test.rb
@@ -245,7 +245,7 @@ class NamingHelpersTest < ActiveModel::TestCase
end
def test_uncountable
- assert uncountable?(@uncountable), "Expected 'sheep' to be uncoutable"
+ assert uncountable?(@uncountable), "Expected 'sheep' to be uncountable"
assert !uncountable?(@klass), "Expected 'contact' to be countable"
end
@@ -272,3 +272,9 @@ class NameWithAnonymousClassTest < ActiveModel::TestCase
assert_equal "Anonymous", model_name
end
end
+
+class NamingMethodDelegationTest < ActiveModel::TestCase
+ def test_model_name
+ assert_equal Blog::Post.model_name, Blog::Post.new.model_name
+ end
+end
diff --git a/activemodel/test/cases/railtie_test.rb b/activemodel/test/cases/railtie_test.rb
index a0cd1402b1..96b3b07e50 100644
--- a/activemodel/test/cases/railtie_test.rb
+++ b/activemodel/test/cases/railtie_test.rb
@@ -7,9 +7,12 @@ class RailtieTest < ActiveModel::TestCase
def setup
require 'active_model/railtie'
+ # Set a fake logger to avoid creating the log directory automatically
+ fake_logger = Logger.new(nil)
+
@app ||= Class.new(::Rails::Application) do
config.eager_load = false
- config.logger = Logger.new(STDOUT)
+ config.logger = fake_logger
end
end
diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb
index 7783bb25d5..6d56c8344a 100644
--- a/activemodel/test/cases/secure_password_test.rb
+++ b/activemodel/test/cases/secure_password_test.rb
@@ -1,78 +1,192 @@
require 'cases/helper'
require 'models/user'
-require 'models/oauthed_user'
require 'models/visitor'
-require 'models/administrator'
class SecurePasswordTest < ActiveModel::TestCase
setup do
+ # Used only to speed up tests
+ @original_min_cost = ActiveModel::SecurePassword.min_cost
ActiveModel::SecurePassword.min_cost = true
@user = User.new
@visitor = Visitor.new
- @oauthed_user = OauthedUser.new
+
+ # Simulate loading an existing user from the DB
+ @existing_user = User.new
+ @existing_user.password_digest = BCrypt::Password.create('password', cost: BCrypt::Engine::MIN_COST)
end
teardown do
- ActiveModel::SecurePassword.min_cost = false
+ ActiveModel::SecurePassword.min_cost = @original_min_cost
+ end
+
+ test "automatically include ActiveModel::Validations when validations are enabled" do
+ assert_respond_to @user, :valid?
end
- test "blank password" do
- @user.password = @visitor.password = ''
+ test "don't include ActiveModel::Validations when validations are disabled" do
+ assert_not_respond_to @visitor, :valid?
+ end
+
+ test "create a new user with validations and valid password/confirmation" do
+ @user.password = 'password'
+ @user.password_confirmation = 'password'
+
+ assert @user.valid?(:create), 'user should be valid'
+
+ @user.password = 'a' * 72
+ @user.password_confirmation = 'a' * 72
+
+ assert @user.valid?(:create), 'user should be valid'
+ end
+
+ test "create a new user with validation and a spaces only password" do
+ @user.password = ' ' * 72
+ assert @user.valid?(:create), 'user should be valid'
+ end
+
+ test "create a new user with validation and a blank password" do
+ @user.password = ''
assert !@user.valid?(:create), 'user should be invalid'
- assert @visitor.valid?(:create), 'visitor should be valid'
+ assert_equal 1, @user.errors.count
+ assert_equal ["can't be blank"], @user.errors[:password]
end
- test "nil password" do
- @user.password = @visitor.password = nil
+ test "create a new user with validation and a nil password" do
+ @user.password = nil
assert !@user.valid?(:create), 'user should be invalid'
- assert @visitor.valid?(:create), 'visitor should be valid'
+ assert_equal 1, @user.errors.count
+ assert_equal ["can't be blank"], @user.errors[:password]
end
- test "blank password doesn't override previous password" do
- @user.password = 'test'
- @user.password = ''
- assert_equal @user.password, 'test'
+ test 'create a new user with validation and password length greater than 72' do
+ @user.password = 'a' * 73
+ @user.password_confirmation = 'a' * 73
+ assert !@user.valid?(:create), 'user should be invalid'
+ assert_equal 1, @user.errors.count
+ assert_equal ["is too long (maximum is 72 characters)"], @user.errors[:password]
+ end
+
+ test "create a new user with validation and a blank password confirmation" do
+ @user.password = 'password'
+ @user.password_confirmation = ''
+ assert !@user.valid?(:create), 'user should be invalid'
+ assert_equal 1, @user.errors.count
+ assert_equal ["doesn't match Password"], @user.errors[:password_confirmation]
end
- test "password must be present" do
- assert !@user.valid?(:create)
- assert_equal 1, @user.errors.size
+ test "create a new user with validation and a nil password confirmation" do
+ @user.password = 'password'
+ @user.password_confirmation = nil
+ assert @user.valid?(:create), 'user should be valid'
end
- test "match confirmation" do
- @user.password = @visitor.password = "thiswillberight"
- @user.password_confirmation = @visitor.password_confirmation = "wrong"
+ test "create a new user with validation and an incorrect password confirmation" do
+ @user.password = 'password'
+ @user.password_confirmation = 'something else'
+ assert !@user.valid?(:create), 'user should be invalid'
+ assert_equal 1, @user.errors.count
+ assert_equal ["doesn't match Password"], @user.errors[:password_confirmation]
+ end
+
+ test "update an existing user with validation and no change in password" do
+ assert @existing_user.valid?(:update), 'user should be valid'
+ end
- assert !@user.valid?
- assert @visitor.valid?
+ test "update an existing user with validations and valid password/confirmation" do
+ @existing_user.password = 'password'
+ @existing_user.password_confirmation = 'password'
- @user.password_confirmation = "thiswillberight"
+ assert @existing_user.valid?(:update), 'user should be valid'
- assert @user.valid?
+ @existing_user.password = 'a' * 72
+ @existing_user.password_confirmation = 'a' * 72
+
+ assert @existing_user.valid?(:update), 'user should be valid'
end
- test "authenticate" do
- @user.password = "secret"
+ test "updating an existing user with validation and a blank password" do
+ @existing_user.password = ''
+ assert @existing_user.valid?(:update), 'user should be valid'
+ end
- assert !@user.authenticate("wrong")
- assert @user.authenticate("secret")
+ test "updating an existing user with validation and a spaces only password" do
+ @user.password = ' ' * 72
+ assert @user.valid?(:update), 'user should be valid'
end
- test "User should not be created with blank digest" do
- assert_raise RuntimeError do
- @user.run_callbacks :create
- end
- @user.password = "supersecretpassword"
- assert_nothing_raised do
- @user.run_callbacks :create
- end
+ test "updating an existing user with validation and a blank password and password_confirmation" do
+ @existing_user.password = ''
+ @existing_user.password_confirmation = ''
+ assert @existing_user.valid?(:update), 'user should be valid'
end
- test "Oauthed user can be created with blank digest" do
- assert_nothing_raised do
- @oauthed_user.run_callbacks :create
- end
+ test "updating an existing user with validation and a nil password" do
+ @existing_user.password = nil
+ assert !@existing_user.valid?(:update), 'user should be invalid'
+ assert_equal 1, @existing_user.errors.count
+ assert_equal ["can't be blank"], @existing_user.errors[:password]
+ end
+
+ test 'updating an existing user with validation and password length greater than 72' do
+ @existing_user.password = 'a' * 73
+ @existing_user.password_confirmation = 'a' * 73
+ assert !@existing_user.valid?(:update), 'user should be invalid'
+ assert_equal 1, @existing_user.errors.count
+ assert_equal ["is too long (maximum is 72 characters)"], @existing_user.errors[:password]
+ end
+
+ test "updating an existing user with validation and a blank password confirmation" do
+ @existing_user.password = 'password'
+ @existing_user.password_confirmation = ''
+ assert !@existing_user.valid?(:update), 'user should be invalid'
+ assert_equal 1, @existing_user.errors.count
+ assert_equal ["doesn't match Password"], @existing_user.errors[:password_confirmation]
+ end
+
+ test "updating an existing user with validation and a nil password confirmation" do
+ @existing_user.password = 'password'
+ @existing_user.password_confirmation = nil
+ assert @existing_user.valid?(:update), 'user should be valid'
+ end
+
+ test "updating an existing user with validation and an incorrect password confirmation" do
+ @existing_user.password = 'password'
+ @existing_user.password_confirmation = 'something else'
+ assert !@existing_user.valid?(:update), 'user should be invalid'
+ assert_equal 1, @existing_user.errors.count
+ assert_equal ["doesn't match Password"], @existing_user.errors[:password_confirmation]
+ end
+
+ test "updating an existing user with validation and a blank password digest" do
+ @existing_user.password_digest = ''
+ assert !@existing_user.valid?(:update), 'user should be invalid'
+ assert_equal 1, @existing_user.errors.count
+ assert_equal ["can't be blank"], @existing_user.errors[:password]
+ end
+
+ test "updating an existing user with validation and a nil password digest" do
+ @existing_user.password_digest = nil
+ assert !@existing_user.valid?(:update), 'user should be invalid'
+ assert_equal 1, @existing_user.errors.count
+ assert_equal ["can't be blank"], @existing_user.errors[:password]
+ end
+
+ test "setting a blank password should not change an existing password" do
+ @existing_user.password = ''
+ assert @existing_user.password_digest == 'password'
+ end
+
+ test "setting a nil password should clear an existing password" do
+ @existing_user.password = nil
+ assert_equal nil, @existing_user.password_digest
+ end
+
+ test "authenticate" do
+ @user.password = "secret"
+
+ assert !@user.authenticate("wrong")
+ assert @user.authenticate("secret")
end
test "Password digest cost defaults to bcrypt default cost when min_cost is false" do
@@ -82,6 +196,19 @@ class SecurePasswordTest < ActiveModel::TestCase
assert_equal BCrypt::Engine::DEFAULT_COST, @user.password_digest.cost
end
+ test "Password digest cost honors bcrypt cost attribute when min_cost is false" do
+ begin
+ original_bcrypt_cost = BCrypt::Engine.cost
+ ActiveModel::SecurePassword.min_cost = false
+ BCrypt::Engine.cost = 5
+
+ @user.password = "secret"
+ assert_equal BCrypt::Engine.cost, @user.password_digest.cost
+ ensure
+ BCrypt::Engine.cost = original_bcrypt_cost
+ end
+ end
+
test "Password digest cost can be set to bcrypt min cost to speed up tests" do
ActiveModel::SecurePassword.min_cost = true
diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb
index d2ba9fd95d..4ae41aa19c 100644
--- a/activemodel/test/cases/serialization_test.rb
+++ b/activemodel/test/cases/serialization_test.rb
@@ -49,32 +49,32 @@ class SerializationTest < ActiveModel::TestCase
def test_method_serializable_hash_should_work_with_only_option
expected = {"name"=>"David"}
- assert_equal expected, @user.serializable_hash(:only => [:name])
+ assert_equal expected, @user.serializable_hash(only: [:name])
end
def test_method_serializable_hash_should_work_with_except_option
expected = {"gender"=>"male", "email"=>"david@example.com"}
- assert_equal expected, @user.serializable_hash(:except => [:name])
+ assert_equal expected, @user.serializable_hash(except: [:name])
end
def test_method_serializable_hash_should_work_with_methods_option
expected = {"name"=>"David", "gender"=>"male", "foo"=>"i_am_foo", "email"=>"david@example.com"}
- assert_equal expected, @user.serializable_hash(:methods => [:foo])
+ assert_equal expected, @user.serializable_hash(methods: [:foo])
end
def test_method_serializable_hash_should_work_with_only_and_methods
expected = {"foo"=>"i_am_foo"}
- assert_equal expected, @user.serializable_hash(:only => [], :methods => [:foo])
+ assert_equal expected, @user.serializable_hash(only: [], methods: [:foo])
end
def test_method_serializable_hash_should_work_with_except_and_methods
expected = {"gender"=>"male", "foo"=>"i_am_foo"}
- assert_equal expected, @user.serializable_hash(:except => [:name, :email], :methods => [:foo])
+ assert_equal expected, @user.serializable_hash(except: [:name, :email], methods: [:foo])
end
def test_should_not_call_methods_that_dont_respond
expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"}
- assert_equal expected, @user.serializable_hash(:methods => [:bar])
+ assert_equal expected, @user.serializable_hash(methods: [:bar])
end
def test_should_use_read_attribute_for_serialization
@@ -83,26 +83,26 @@ class SerializationTest < ActiveModel::TestCase
end
expected = { "name" => "Jon" }
- assert_equal expected, @user.serializable_hash(:only => :name)
+ assert_equal expected, @user.serializable_hash(only: :name)
end
def test_include_option_with_singular_association
expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com",
"address"=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}}
- assert_equal expected, @user.serializable_hash(:include => :address)
+ assert_equal expected, @user.serializable_hash(include: :address)
end
def test_include_option_with_plural_association
expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
"friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
{"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
- assert_equal expected, @user.serializable_hash(:include => :friends)
+ assert_equal expected, @user.serializable_hash(include: :friends)
end
def test_include_option_with_empty_association
@user.friends = []
expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", "friends"=>[]}
- assert_equal expected, @user.serializable_hash(:include => :friends)
+ assert_equal expected, @user.serializable_hash(include: :friends)
end
class FriendList
@@ -120,7 +120,7 @@ class SerializationTest < ActiveModel::TestCase
expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
"friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
{"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
- assert_equal expected, @user.serializable_hash(:include => :friends)
+ assert_equal expected, @user.serializable_hash(include: :friends)
end
def test_multiple_includes
@@ -128,13 +128,13 @@ class SerializationTest < ActiveModel::TestCase
"address"=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111},
"friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
{"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
- assert_equal expected, @user.serializable_hash(:include => [:address, :friends])
+ assert_equal expected, @user.serializable_hash(include: [:address, :friends])
end
def test_include_with_options
expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
"address"=>{"street"=>"123 Lane"}}
- assert_equal expected, @user.serializable_hash(:include => {:address => {:only => "street"}})
+ assert_equal expected, @user.serializable_hash(include: { address: { only: "street" } })
end
def test_nested_include
@@ -143,19 +143,19 @@ class SerializationTest < ActiveModel::TestCase
"friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male',
"friends"=> [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]},
{"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', "friends"=> []}]}
- assert_equal expected, @user.serializable_hash(:include => {:friends => {:include => :friends}})
+ assert_equal expected, @user.serializable_hash(include: { friends: { include: :friends } })
end
def test_only_include
expected = {"name"=>"David", "friends" => [{"name" => "Joe"}, {"name" => "Sue"}]}
- assert_equal expected, @user.serializable_hash(:only => :name, :include => {:friends => {:only => :name}})
+ assert_equal expected, @user.serializable_hash(only: :name, include: { friends: { only: :name } })
end
def test_except_include
expected = {"name"=>"David", "email"=>"david@example.com",
"friends"=> [{"name" => 'Joe', "email" => 'joe@example.com'},
{"name" => "Sue", "email" => 'sue@example.com'}]}
- assert_equal expected, @user.serializable_hash(:except => :gender, :include => {:friends => {:except => :gender}})
+ assert_equal expected, @user.serializable_hash(except: :gender, include: { friends: { except: :gender } })
end
def test_multiple_includes_with_options
@@ -163,6 +163,6 @@ class SerializationTest < ActiveModel::TestCase
"address"=>{"street"=>"123 Lane"},
"friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
{"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
- assert_equal expected, @user.serializable_hash(:include => [{:address => {:only => "street"}}, :friends])
+ assert_equal expected, @user.serializable_hash(include: [{ address: {only: "street" } }, :friends])
end
end
diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb
index 9134c4980c..e2eb91eeb0 100644
--- a/activemodel/test/cases/serializers/json_serialization_test.rb
+++ b/activemodel/test/cases/serializers/json_serialization_test.rb
@@ -1,25 +1,7 @@
require 'cases/helper'
require 'models/contact'
-require 'models/automobile'
require 'active_support/core_ext/object/instance_variables'
-class Contact
- include ActiveModel::Serializers::JSON
- include ActiveModel::Validations
-
- def attributes=(hash)
- hash.each do |k, v|
- instance_variable_set("@#{k}", v)
- end
- end
-
- remove_method :attributes if method_defined?(:attributes)
-
- def attributes
- instance_values
- end
-end
-
class JsonSerializationTest < ActiveModel::TestCase
def setup
@contact = Contact.new
@@ -30,11 +12,6 @@ class JsonSerializationTest < ActiveModel::TestCase
@contact.preferences = { 'shows' => 'anime' }
end
- def teardown
- # set to the default value
- Contact.include_root_in_json = false
- end
-
test "should not include root in json (class method)" do
json = @contact.to_json
@@ -47,19 +24,25 @@ class JsonSerializationTest < ActiveModel::TestCase
end
test "should include root in json if include_root_in_json is true" do
- Contact.include_root_in_json = true
- json = @contact.to_json
-
- assert_match %r{^\{"contact":\{}, json
- assert_match %r{"name":"Konata Izumi"}, json
- assert_match %r{"age":16}, json
- assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
- assert_match %r{"awesome":true}, json
- assert_match %r{"preferences":\{"shows":"anime"\}}, json
+ begin
+ original_include_root_in_json = Contact.include_root_in_json
+ Contact.include_root_in_json = true
+ json = @contact.to_json
+
+ assert_match %r{^\{"contact":\{}, json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"age":16}, json
+ assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
+ assert_match %r{"awesome":true}, json
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
+ ensure
+ Contact.include_root_in_json = original_include_root_in_json
+ end
end
test "should include root in json (option) even if the default is set to false" do
json = @contact.to_json(root: true)
+
assert_match %r{^\{"contact":\{}, json
end
@@ -91,7 +74,7 @@ class JsonSerializationTest < ActiveModel::TestCase
end
test "should allow attribute filtering with only" do
- json = @contact.to_json(:only => [:name, :age])
+ json = @contact.to_json(only: [:name, :age])
assert_match %r{"name":"Konata Izumi"}, json
assert_match %r{"age":16}, json
@@ -145,22 +128,21 @@ class JsonSerializationTest < ActiveModel::TestCase
end
test "as_json should return a hash if include_root_in_json is true" do
- Contact.include_root_in_json = true
- json = @contact.as_json
-
- assert_kind_of Hash, json
- assert_kind_of Hash, json['contact']
- %w(name age created_at awesome preferences).each do |field|
- assert_equal @contact.send(field), json['contact'][field]
+ begin
+ original_include_root_in_json = Contact.include_root_in_json
+ Contact.include_root_in_json = true
+ json = @contact.as_json
+
+ assert_kind_of Hash, json
+ assert_kind_of Hash, json['contact']
+ %w(name age created_at awesome preferences).each do |field|
+ assert_equal @contact.send(field), json['contact'][field]
+ end
+ ensure
+ Contact.include_root_in_json = original_include_root_in_json
end
end
- test "as_json should keep the default order in the hash" do
- json = @contact.as_json
-
- assert_equal %w(name age created_at awesome preferences), json.keys
- end
-
test "from_json should work without a root (class attribute)" do
json = @contact.to_json
result = Contact.new.from_json(json)
@@ -204,7 +186,7 @@ class JsonSerializationTest < ActiveModel::TestCase
assert_no_match %r{"preferences":}, json
end
- test "custom as_json options should be extendible" do
+ test "custom as_json options should be extensible" do
def @contact.as_json(options = {}); super(options.merge(only: [:name])); end
json = @contact.to_json
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index 99a9c1fe33..22fca5bd17 100644
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -3,18 +3,6 @@ require 'models/contact'
require 'active_support/core_ext/object/instance_variables'
require 'ostruct'
-class Contact
- include ActiveModel::Serializers::Xml
-
- attr_accessor :address, :friends, :contact
-
- remove_method :attributes if method_defined?(:attributes)
-
- def attributes
- instance_values.except("address", "friends", "contact")
- end
-end
-
module Admin
class Contact < ::Contact
end
@@ -35,7 +23,7 @@ end
class SerializableContact < Contact
def serializable_hash(options={})
- super(options.merge(:only => [:name, :age]))
+ super(options.merge(only: [:name, :age]))
end
end
@@ -50,60 +38,55 @@ class XmlSerializationTest < ActiveModel::TestCase
customer.name = "John"
@contact.preferences = customer
@contact.address = Address.new
- @contact.address.street = "123 Lane"
@contact.address.city = "Springfield"
- @contact.address.state = "CA"
- @contact.address.zip = 11111
@contact.address.apt_number = 35
@contact.friends = [Contact.new, Contact.new]
- @related_contact = SerializableContact.new
- @related_contact.name = "related"
- @contact.contact = @related_contact
+ @contact.contact = SerializableContact.new
end
test "should serialize default root" do
- @xml = @contact.to_xml
- assert_match %r{^<contact>}, @xml
- assert_match %r{</contact>$}, @xml
+ xml = @contact.to_xml
+ assert_match %r{^<contact>}, xml
+ assert_match %r{</contact>$}, xml
end
test "should serialize namespaced root" do
- @xml = Admin::Contact.new(@contact.attributes).to_xml
- assert_match %r{^<contact>}, @xml
- assert_match %r{</contact>$}, @xml
+ xml = Admin::Contact.new(@contact.attributes).to_xml
+ assert_match %r{^<contact>}, xml
+ assert_match %r{</contact>$}, xml
end
test "should serialize default root with namespace" do
- @xml = @contact.to_xml :namespace => "http://xml.rubyonrails.org/contact"
- assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, @xml
- assert_match %r{</contact>$}, @xml
+ xml = @contact.to_xml namespace: "http://xml.rubyonrails.org/contact"
+ assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, xml
+ assert_match %r{</contact>$}, xml
end
test "should serialize custom root" do
- @xml = @contact.to_xml :root => 'xml_contact'
- assert_match %r{^<xml-contact>}, @xml
- assert_match %r{</xml-contact>$}, @xml
+ xml = @contact.to_xml root: 'xml_contact'
+ assert_match %r{^<xml-contact>}, xml
+ assert_match %r{</xml-contact>$}, xml
end
test "should allow undasherized tags" do
- @xml = @contact.to_xml :root => 'xml_contact', :dasherize => false
- assert_match %r{^<xml_contact>}, @xml
- assert_match %r{</xml_contact>$}, @xml
- assert_match %r{<created_at}, @xml
+ xml = @contact.to_xml root: 'xml_contact', dasherize: false
+ assert_match %r{^<xml_contact>}, xml
+ assert_match %r{</xml_contact>$}, xml
+ assert_match %r{<created_at}, xml
end
test "should allow camelized tags" do
- @xml = @contact.to_xml :root => 'xml_contact', :camelize => true
- assert_match %r{^<XmlContact>}, @xml
- assert_match %r{</XmlContact>$}, @xml
- assert_match %r{<CreatedAt}, @xml
+ xml = @contact.to_xml root: 'xml_contact', camelize: true
+ assert_match %r{^<XmlContact>}, xml
+ assert_match %r{</XmlContact>$}, xml
+ assert_match %r{<CreatedAt}, xml
end
test "should allow lower-camelized tags" do
- @xml = @contact.to_xml :root => 'xml_contact', :camelize => :lower
- assert_match %r{^<xmlContact>}, @xml
- assert_match %r{</xmlContact>$}, @xml
- assert_match %r{<createdAt}, @xml
+ xml = @contact.to_xml root: 'xml_contact', camelize: :lower
+ assert_match %r{^<xmlContact>}, xml
+ assert_match %r{</xmlContact>$}, xml
+ assert_match %r{<createdAt}, xml
end
test "should use serializable hash" do
@@ -111,22 +94,22 @@ class XmlSerializationTest < ActiveModel::TestCase
@contact.name = 'aaron stack'
@contact.age = 25
- @xml = @contact.to_xml
- assert_match %r{<name>aaron stack</name>}, @xml
- assert_match %r{<age type="integer">25</age>}, @xml
- assert_no_match %r{<awesome>}, @xml
+ xml = @contact.to_xml
+ assert_match %r{<name>aaron stack</name>}, xml
+ assert_match %r{<age type="integer">25</age>}, xml
+ assert_no_match %r{<awesome>}, xml
end
test "should allow skipped types" do
- @xml = @contact.to_xml :skip_types => true
- assert_match %r{<age>25</age>}, @xml
+ xml = @contact.to_xml skip_types: true
+ assert_match %r{<age>25</age>}, xml
end
test "should include yielded additions" do
- @xml = @contact.to_xml do |xml|
+ xml_output = @contact.to_xml do |xml|
xml.creator "David"
end
- assert_match %r{<creator>David</creator>}, @xml
+ assert_match %r{<creator>David</creator>}, xml_output
end
test "should serialize string" do
@@ -134,7 +117,7 @@ class XmlSerializationTest < ActiveModel::TestCase
end
test "should serialize nil" do
- assert_match %r{<pseudonyms nil=\"true\"/>}, @contact.to_xml(:methods => :pseudonyms)
+ assert_match %r{<pseudonyms nil="true"/>}, @contact.to_xml(methods: :pseudonyms)
end
test "should serialize integer" do
@@ -142,50 +125,50 @@ class XmlSerializationTest < ActiveModel::TestCase
end
test "should serialize datetime" do
- assert_match %r{<created-at type=\"dateTime\">2006-08-01T00:00:00Z</created-at>}, @contact.to_xml
+ assert_match %r{<created-at type="dateTime">2006-08-01T00:00:00Z</created-at>}, @contact.to_xml
end
test "should serialize boolean" do
- assert_match %r{<awesome type=\"boolean\">false</awesome>}, @contact.to_xml
+ assert_match %r{<awesome type="boolean">false</awesome>}, @contact.to_xml
end
test "should serialize array" do
- assert_match %r{<social type=\"array\">\s*<social>twitter</social>\s*<social>github</social>\s*</social>}, @contact.to_xml(:methods => :social)
+ assert_match %r{<social type="array">\s*<social>twitter</social>\s*<social>github</social>\s*</social>}, @contact.to_xml(methods: :social)
end
test "should serialize hash" do
- assert_match %r{<network>\s*<git type=\"symbol\">github</git>\s*</network>}, @contact.to_xml(:methods => :network)
+ assert_match %r{<network>\s*<git type="symbol">github</git>\s*</network>}, @contact.to_xml(methods: :network)
end
test "should serialize yaml" do
- assert_match %r{<preferences type=\"yaml\">--- !ruby/struct:Customer(\s*)\nname: John\n</preferences>}, @contact.to_xml
+ assert_match %r{<preferences type="yaml">--- !ruby/struct:Customer(\s*)\nname: John\n</preferences>}, @contact.to_xml
end
test "should call proc on object" do
proc = Proc.new { |options| options[:builder].tag!('nationality', 'unknown') }
- xml = @contact.to_xml(:procs => [ proc ])
+ xml = @contact.to_xml(procs: [ proc ])
assert_match %r{<nationality>unknown</nationality>}, xml
end
- test 'should supply serializable to second proc argument' do
+ test "should supply serializable to second proc argument" do
proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
- xml = @contact.to_xml(:procs => [ proc ])
+ xml = @contact.to_xml(procs: [ proc ])
assert_match %r{<name-reverse>kcats noraa</name-reverse>}, xml
end
test "should serialize string correctly when type passed" do
- xml = @contact.to_xml :type => 'Contact'
+ xml = @contact.to_xml type: 'Contact'
assert_match %r{<contact type="Contact">}, xml
assert_match %r{<name>aaron stack</name>}, xml
end
test "include option with singular association" do
- xml = @contact.to_xml :include => :address, :indent => 0
- assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true))
+ xml = @contact.to_xml include: :address, indent: 0
+ assert xml.include?(@contact.address.to_xml(indent: 0, skip_instruct: true))
end
test "include option with plural association" do
- xml = @contact.to_xml :include => :friends, :indent => 0
+ xml = @contact.to_xml include: :friends, indent: 0
assert_match %r{<friends type="array">}, xml
assert_match %r{<friend type="Contact">}, xml
end
@@ -202,60 +185,60 @@ class XmlSerializationTest < ActiveModel::TestCase
test "include option with ary" do
@contact.friends = FriendList.new(@contact.friends)
- xml = @contact.to_xml :include => :friends, :indent => 0
+ xml = @contact.to_xml include: :friends, indent: 0
assert_match %r{<friends type="array">}, xml
assert_match %r{<friend type="Contact">}, xml
end
test "multiple includes" do
- xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => [ :address, :friends ]
- assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true))
+ xml = @contact.to_xml indent: 0, skip_instruct: true, include: [ :address, :friends ]
+ assert xml.include?(@contact.address.to_xml(indent: 0, skip_instruct: true))
assert_match %r{<friends type="array">}, xml
assert_match %r{<friend type="Contact">}, xml
end
test "include with options" do
- xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => { :address => { :only => :city } }
+ xml = @contact.to_xml indent: 0, skip_instruct: true, include: { address: { only: :city } }
assert xml.include?(%(><address><city>Springfield</city></address>))
end
test "propagates skip_types option to included associations" do
- xml = @contact.to_xml :include => :friends, :indent => 0, :skip_types => true
+ xml = @contact.to_xml include: :friends, indent: 0, skip_types: true
assert_match %r{<friends>}, xml
assert_match %r{<friend>}, xml
end
test "propagates skip-types option to included associations and attributes" do
- xml = @contact.to_xml :skip_types => true, :include => :address, :indent => 0
+ xml = @contact.to_xml skip_types: true, include: :address, indent: 0
assert_match %r{<address>}, xml
assert_match %r{<apt-number>}, xml
end
test "propagates camelize option to included associations and attributes" do
- xml = @contact.to_xml :camelize => true, :include => :address, :indent => 0
+ xml = @contact.to_xml camelize: true, include: :address, indent: 0
assert_match %r{<Address>}, xml
assert_match %r{<AptNumber type="integer">}, xml
end
test "propagates dasherize option to included associations and attributes" do
- xml = @contact.to_xml :dasherize => false, :include => :address, :indent => 0
+ xml = @contact.to_xml dasherize: false, include: :address, indent: 0
assert_match %r{<apt_number type="integer">}, xml
end
test "don't propagate skip_types if skip_types is defined at the included association level" do
- xml = @contact.to_xml :skip_types => true, :include => { :address => { :skip_types => false } }, :indent => 0
+ xml = @contact.to_xml skip_types: true, include: { address: { skip_types: false } }, indent: 0
assert_match %r{<address>}, xml
assert_match %r{<apt-number type="integer">}, xml
end
test "don't propagate camelize if camelize is defined at the included association level" do
- xml = @contact.to_xml :camelize => true, :include => { :address => { :camelize => false } }, :indent => 0
+ xml = @contact.to_xml camelize: true, include: { address: { camelize: false } }, indent: 0
assert_match %r{<address>}, xml
assert_match %r{<apt-number type="integer">}, xml
end
test "don't propagate dasherize if dasherize is defined at the included association level" do
- xml = @contact.to_xml :dasherize => false, :include => { :address => { :dasherize => true } }, :indent => 0
+ xml = @contact.to_xml dasherize: false, include: { address: { dasherize: true } }, indent: 0
assert_match %r{<address>}, xml
assert_match %r{<apt-number type="integer">}, xml
end
diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb
index fd833cdd06..cedc812ec7 100644
--- a/activemodel/test/cases/translation_test.rb
+++ b/activemodel/test/cases/translation_test.rb
@@ -7,23 +7,27 @@ class ActiveModelI18nTests < ActiveModel::TestCase
I18n.backend = I18n::Backend::Simple.new
end
+ def teardown
+ I18n.backend.reload!
+ end
+
def test_translated_model_attributes
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } }
+ 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_default
- I18n.backend.store_translations 'en', :attributes => { :name => 'name default attribute' }
+ I18n.backend.store_translations 'en', attributes: { name: 'name default attribute' }
assert_equal 'name default attribute', Person.human_attribute_name('name')
end
def test_translated_model_attributes_using_default_option
- assert_equal 'name default attribute', Person.human_attribute_name('name', :default => "name default attribute")
+ assert_equal 'name default attribute', Person.human_attribute_name('name', default: "name default attribute")
end
def test_translated_model_attributes_using_default_option_as_symbol
- I18n.backend.store_translations 'en', :default_name => 'name default attribute'
- assert_equal 'name default attribute', Person.human_attribute_name('name', :default => :default_name)
+ I18n.backend.store_translations 'en', default_name: 'name default attribute'
+ assert_equal 'name default attribute', Person.human_attribute_name('name', default: :default_name)
end
def test_translated_model_attributes_falling_back_to_default
@@ -31,71 +35,74 @@ class ActiveModelI18nTests < ActiveModel::TestCase
end
def test_translated_model_attributes_using_default_option_as_symbol_and_falling_back_to_default
- assert_equal 'Name', Person.human_attribute_name('name', :default => :default_name)
+ assert_equal 'Name', Person.human_attribute_name('name', default: :default_name)
end
def test_translated_model_attributes_with_symbols
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:name => 'person name attribute'} } }
+ 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 => {:child => {:name => 'child name attribute'} } }
+ 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 => {:person => {:name => 'person name attribute'} } }
+ 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_attributes_with_attribute_matching_namespaced_model_name
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:gender => 'person gender'}, :"person/gender" => {:attribute => 'person gender attribute'}}}
+ I18n.backend.store_translations 'en', activemodel: { attributes: {
+ person: { gender: 'person gender'},
+ :"person/gender" => { attribute: 'person gender attribute' }
+ } }
assert_equal 'person gender', Person.human_attribute_name('gender')
assert_equal 'person gender attribute', Person::Gender.human_attribute_name('attribute')
end
def test_translated_deeply_nested_model_attributes
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:"person/contacts/addresses" => {:street => 'Deeply Nested Address Street'}}}
+ I18n.backend.store_translations 'en', activemodel: { attributes: { :"person/contacts/addresses" => { street: 'Deeply Nested Address Street' } } }
assert_equal 'Deeply Nested Address Street', Person.human_attribute_name('contacts.addresses.street')
end
def test_translated_nested_model_attributes
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:"person/addresses" => {:street => 'Person Address Street'}}}
+ I18n.backend.store_translations 'en', activemodel: { attributes: { :"person/addresses" => { street: 'Person Address Street' } } }
assert_equal 'Person Address Street', Person.human_attribute_name('addresses.street')
end
def test_translated_nested_model_attributes_with_namespace_fallback
- I18n.backend.store_translations 'en', :activemodel => {:attributes => {:addresses => {:street => 'Cool Address Street'}}}
+ I18n.backend.store_translations 'en', activemodel: { attributes: { addresses: { street: 'Cool Address Street' } } }
assert_equal 'Cool Address Street', Person.human_attribute_name('addresses.street')
end
def test_translated_model_names
- I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'person model'} }
+ 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 => {:child => 'child model'} }
+ 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 => {:person => 'person model'} }
+ I18n.backend.store_translations 'en', activemodel: { models: { person: 'person model' } }
assert_equal 'person model', Child.model_name.human
end
def test_human_does_not_modify_options
- options = { :default => 'person model' }
+ options = { default: 'person model' }
Person.model_name.human(options)
- assert_equal({ :default => 'person model' }, options)
+ assert_equal({ default: 'person model' }, options)
end
def test_human_attribute_name_does_not_modify_options
- options = { :default => 'Cool gender' }
+ options = { default: 'Cool gender' }
Person.human_attribute_name('gender', options)
- assert_equal({ :default => 'Cool gender' }, options)
+ assert_equal({ default: 'Cool gender' }, options)
end
end
diff --git a/activemodel/test/cases/validations/absence_validation_test.rb b/activemodel/test/cases/validations/absence_validation_test.rb
index c05d71de5a..ebfe1cf4e4 100644
--- a/activemodel/test/cases/validations/absence_validation_test.rb
+++ b/activemodel/test/cases/validations/absence_validation_test.rb
@@ -6,12 +6,12 @@ require 'models/custom_reader'
class AbsenceValidationTest < ActiveModel::TestCase
teardown do
- Topic.reset_callbacks(:validate)
- Person.reset_callbacks(:validate)
- CustomReader.reset_callbacks(:validate)
+ Topic.clear_validators!
+ Person.clear_validators!
+ CustomReader.clear_validators!
end
- def test_validate_absences
+ def test_validates_absence_of
Topic.validates_absence_of(:title, :content)
t = Topic.new
t.title = "foo"
@@ -23,11 +23,12 @@ class AbsenceValidationTest < ActiveModel::TestCase
t.content = "something"
assert t.invalid?
assert_equal ["must be blank"], t.errors[:content]
+ assert_equal [], t.errors[:title]
t.content = ""
assert t.valid?
end
- def test_accepts_array_arguments
+ def test_validates_absence_of_with_array_arguments
Topic.validates_absence_of %w(title content)
t = Topic.new
t.title = "foo"
@@ -37,7 +38,7 @@ class AbsenceValidationTest < ActiveModel::TestCase
assert_equal ["must be blank"], t.errors[:content]
end
- def test_validates_acceptance_of_with_custom_error_using_quotes
+ def test_validates_absence_of_with_custom_error_using_quotes
Person.validates_absence_of :karma, message: "This string contains 'single' and \"double\" quotes"
p = Person.new
p.karma = "good"
diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb
index de04e11258..e78aa1adaf 100644
--- a/activemodel/test/cases/validations/acceptance_validation_test.rb
+++ b/activemodel/test/cases/validations/acceptance_validation_test.rb
@@ -8,7 +8,7 @@ require 'models/person'
class AcceptanceValidationTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_terms_of_service_agreement_no_acceptance
@@ -30,7 +30,7 @@ class AcceptanceValidationTest < ActiveModel::TestCase
end
def test_eula
- Topic.validates_acceptance_of(:eula, :message => "must be abided")
+ Topic.validates_acceptance_of(:eula, message: "must be abided")
t = Topic.new("title" => "We should be confirmed","eula" => "")
assert t.invalid?
@@ -41,7 +41,7 @@ class AcceptanceValidationTest < ActiveModel::TestCase
end
def test_terms_of_service_agreement_with_accept_value
- Topic.validates_acceptance_of(:terms_of_service, :accept => "I agree.")
+ Topic.validates_acceptance_of(:terms_of_service, accept: "I agree.")
t = Topic.new("title" => "We should be confirmed", "terms_of_service" => "")
assert t.invalid?
@@ -63,6 +63,6 @@ class AcceptanceValidationTest < ActiveModel::TestCase
p.karma = "1"
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
end
diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb
index 0015b3c196..5d6d48b824 100644
--- a/activemodel/test/cases/validations/callbacks_test.rb
+++ b/activemodel/test/cases/validations/callbacks_test.rb
@@ -30,18 +30,44 @@ class DogWithTwoValidators < Dog
before_validation { self.history << 'before_validation_marker2' }
end
-class DogValidatorReturningFalse < Dog
+class DogBeforeValidatorReturningFalse < Dog
before_validation { false }
before_validation { self.history << 'before_validation_marker2' }
end
+class DogAfterValidatorReturningFalse < Dog
+ after_validation { false }
+ after_validation { self.history << 'after_validation_marker' }
+end
+
class DogWithMissingName < Dog
before_validation { self.history << 'before_validation_marker' }
validates_presence_of :name
end
+class DogValidatorWithIfCondition < Dog
+ before_validation :set_before_validation_marker1, if: -> { true }
+ before_validation :set_before_validation_marker2, if: -> { false }
+
+ after_validation :set_after_validation_marker1, if: -> { true }
+ after_validation :set_after_validation_marker2, if: -> { false }
+
+ def set_before_validation_marker1; self.history << 'before_validation_marker1'; end
+ def set_before_validation_marker2; self.history << 'before_validation_marker2' ; end
+
+ def set_after_validation_marker1; self.history << 'after_validation_marker1'; end
+ def set_after_validation_marker2; self.history << 'after_validation_marker2' ; end
+end
+
+
class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
+ def test_if_condition_is_respected_for_before_validation
+ d = DogValidatorWithIfCondition.new
+ d.valid?
+ assert_equal ["before_validation_marker1", "after_validation_marker1"], d.history
+ end
+
def test_before_validation_and_after_validation_callbacks_should_be_called
d = DogWithMethodCallbacks.new
d.valid?
@@ -61,12 +87,18 @@ class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
end
def test_further_callbacks_should_not_be_called_if_before_validation_returns_false
- d = DogValidatorReturningFalse.new
+ d = DogBeforeValidatorReturningFalse.new
output = d.valid?
assert_equal [], d.history
assert_equal false, output
end
+ def test_further_callbacks_should_be_called_if_after_validation_returns_false
+ d = DogAfterValidatorReturningFalse.new
+ d.valid?
+ assert_equal ['after_validation_marker'], d.history
+ end
+
def test_validation_test_should_be_done
d = DogWithMissingName.new
output = d.valid?
diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb
index e06b04af19..1261937b56 100644
--- a/activemodel/test/cases/validations/conditional_validation_test.rb
+++ b/activemodel/test/cases/validations/conditional_validation_test.rb
@@ -6,12 +6,12 @@ require 'models/topic'
class ConditionalValidationTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_if_validation_using_method_true
# When the method returns true
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => :condition_is_true )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: :condition_is_true)
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -20,23 +20,23 @@ class ConditionalValidationTest < ActiveModel::TestCase
def test_unless_validation_using_method_true
# When the method returns true
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => :condition_is_true )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: :condition_is_true)
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_if_validation_using_method_false
# When the method returns false
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => :condition_is_true_but_its_not )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: :condition_is_true_but_its_not)
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_unless_validation_using_method_false
# When the method returns false
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => :condition_is_true_but_its_not )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: :condition_is_true_but_its_not)
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -45,7 +45,7 @@ class ConditionalValidationTest < ActiveModel::TestCase
def test_if_validation_using_string_true
# When the evaluated string returns true
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => "a = 1; a == 1" )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: "a = 1; a == 1")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -54,23 +54,23 @@ class ConditionalValidationTest < ActiveModel::TestCase
def test_unless_validation_using_string_true
# When the evaluated string returns true
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => "a = 1; a == 1" )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: "a = 1; a == 1")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_if_validation_using_string_false
# When the evaluated string returns false
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => "false")
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", if: "false")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_unless_validation_using_string_false
# When the evaluated string returns false
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => "false")
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}", unless: "false")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -79,8 +79,8 @@ class ConditionalValidationTest < ActiveModel::TestCase
def test_if_validation_using_block_true
# When the block returns true
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}",
- :if => Proc.new { |r| r.content.size > 4 } )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}",
+ if: Proc.new { |r| r.content.size > 4 })
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -89,26 +89,26 @@ class ConditionalValidationTest < ActiveModel::TestCase
def test_unless_validation_using_block_true
# When the block returns true
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}",
- :unless => Proc.new { |r| r.content.size > 4 } )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}",
+ unless: Proc.new { |r| r.content.size > 4 })
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_if_validation_using_block_false
# When the block returns false
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}",
- :if => Proc.new { |r| r.title != "uhohuhoh"} )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}",
+ if: Proc.new { |r| r.title != "uhohuhoh"})
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
- assert t.errors[:title].empty?
+ assert_empty t.errors[:title]
end
def test_unless_validation_using_block_false
# When the block returns false
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}",
- :unless => Proc.new { |r| r.title != "uhohuhoh"} )
+ Topic.validates_length_of(:title, maximum: 5, too_long: "hoo %{count}",
+ unless: Proc.new { |r| r.title != "uhohuhoh"} )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -120,11 +120,11 @@ class ConditionalValidationTest < ActiveModel::TestCase
# ensure that it works correctly
def test_validation_with_if_as_string
Topic.validates_presence_of(:title)
- Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
+ Topic.validates_presence_of(:author_name, if: "title.to_s.match('important')")
t = Topic.new
assert t.invalid?, "A topic without a title should not be valid"
- assert t.errors[:author_name].empty?, "A topic without an 'important' title should not require an author"
+ assert_empty t.errors[:author_name], "A topic without an 'important' title should not require an author"
t.title = "Just a title"
assert t.valid?, "A topic with a basic title should be valid"
diff --git a/activemodel/test/cases/validations/confirmation_validation_test.rb b/activemodel/test/cases/validations/confirmation_validation_test.rb
index f7556a249f..65a2a1eb49 100644
--- a/activemodel/test/cases/validations/confirmation_validation_test.rb
+++ b/activemodel/test/cases/validations/confirmation_validation_test.rb
@@ -7,13 +7,13 @@ require 'models/person'
class ConfirmationValidationTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_no_title_confirmation
Topic.validates_confirmation_of(:title)
- t = Topic.new(:author_name => "Plutarch")
+ t = Topic.new(author_name: "Plutarch")
assert t.valid?
t.title_confirmation = "Parallel Lives"
@@ -49,26 +49,60 @@ class ConfirmationValidationTest < ActiveModel::TestCase
p.karma = "None"
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
def test_title_confirmation_with_i18n_attribute
- @old_load_path, @old_backend = I18n.load_path.dup, I18n.backend
- I18n.load_path.clear
- I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations('en', {
- :errors => {:messages => {:confirmation => "doesn't match %{attribute}"}},
- :activemodel => {:attributes => {:topic => {:title => 'Test Title'}}}
- })
+ begin
+ @old_load_path, @old_backend = I18n.load_path.dup, I18n.backend
+ I18n.load_path.clear
+ I18n.backend = I18n::Backend::Simple.new
+ I18n.backend.store_translations('en', {
+ errors: { messages: { confirmation: "doesn't match %{attribute}" } },
+ activemodel: { attributes: { topic: { title: 'Test Title'} } }
+ })
+
+ Topic.validates_confirmation_of(:title)
+
+ t = Topic.new("title" => "We should be confirmed","title_confirmation" => "")
+ assert t.invalid?
+ assert_equal ["doesn't match Test Title"], t.errors[:title_confirmation]
+ ensure
+ I18n.load_path.replace @old_load_path
+ I18n.backend = @old_backend
+ I18n.backend.reload!
+ end
+ end
- Topic.validates_confirmation_of(:title)
+ test "does not override confirmation reader if present" do
+ klass = Class.new do
+ include ActiveModel::Validations
- t = Topic.new("title" => "We should be confirmed","title_confirmation" => "")
- assert t.invalid?
- assert_equal ["doesn't match Test Title"], t.errors[:title_confirmation]
+ def title_confirmation
+ "expected title"
+ end
+
+ validates_confirmation_of :title
+ end
- I18n.load_path.replace @old_load_path
- I18n.backend = @old_backend
+ assert_equal "expected title", klass.new.title_confirmation,
+ "confirmation validation should not override the reader"
end
+ test "does not override confirmation writer if present" do
+ klass = Class.new do
+ include ActiveModel::Validations
+
+ def title_confirmation=(value)
+ @title_confirmation = "expected title"
+ end
+
+ validates_confirmation_of :title
+ end
+
+ model = klass.new
+ model.title_confirmation = "new title"
+ assert_equal "expected title", model.title_confirmation,
+ "confirmation validation should not override the writer"
+ end
end
diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb
index 7d5af27f3d..1ce41f9bc9 100644
--- a/activemodel/test/cases/validations/exclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/exclusion_validation_test.rb
@@ -7,18 +7,18 @@ require 'models/person'
class ExclusionValidationTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_validates_exclusion_of
- Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
+ Topic.validates_exclusion_of(:title, in: %w( abe monkey ))
assert Topic.new("title" => "something", "content" => "abc").valid?
assert Topic.new("title" => "monkey", "content" => "abc").invalid?
end
def test_validates_exclusion_of_with_formatted_message
- Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %{value} is restricted" )
+ Topic.validates_exclusion_of(:title, in: %w( abe monkey ), message: "option %{value} is restricted")
assert Topic.new("title" => "something", "content" => "abc")
@@ -29,7 +29,7 @@ class ExclusionValidationTest < ActiveModel::TestCase
end
def test_validates_exclusion_of_with_within_option
- Topic.validates_exclusion_of( :title, :within => %w( abe monkey ) )
+ Topic.validates_exclusion_of(:title, within: %w( abe monkey ))
assert Topic.new("title" => "something", "content" => "abc")
@@ -39,7 +39,7 @@ class ExclusionValidationTest < ActiveModel::TestCase
end
def test_validates_exclusion_of_for_ruby_class
- Person.validates_exclusion_of :karma, :in => %w( abe monkey )
+ Person.validates_exclusion_of :karma, in: %w( abe monkey )
p = Person.new
p.karma = "abe"
@@ -50,11 +50,11 @@ class ExclusionValidationTest < ActiveModel::TestCase
p.karma = "Lifo"
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
def test_validates_exclusion_of_with_lambda
- Topic.validates_exclusion_of :title, :in => lambda{ |topic| topic.author_name == "sikachu" ? %w( monkey elephant ) : %w( abe wasabi ) }
+ Topic.validates_exclusion_of :title, in: lambda { |topic| topic.author_name == "sikachu" ? %w( monkey elephant ) : %w( abe wasabi ) }
t = Topic.new
t.title = "elephant"
@@ -66,7 +66,7 @@ class ExclusionValidationTest < ActiveModel::TestCase
end
def test_validates_inclusion_of_with_symbol
- Person.validates_exclusion_of :karma, :in => :reserved_karmas
+ Person.validates_exclusion_of :karma, in: :reserved_karmas
p = Person.new
p.karma = "abe"
@@ -87,6 +87,6 @@ class ExclusionValidationTest < ActiveModel::TestCase
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
end
diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb
index 308a3c6cef..0f91b73cd7 100644
--- a/activemodel/test/cases/validations/format_validation_test.rb
+++ b/activemodel/test/cases/validations/format_validation_test.rb
@@ -7,11 +7,11 @@ require 'models/person'
class PresenceValidationTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_validate_format
- Topic.validates_format_of(:title, :content, :with => /\AValidation\smacros \w+!\z/, :message => "is bad data")
+ Topic.validates_format_of(:title, :content, with: /\AValidation\smacros \w+!\z/, message: "is bad data")
t = Topic.new("title" => "i'm incorrect", "content" => "Validation macros rule!")
assert t.invalid?, "Shouldn't be valid"
@@ -27,7 +27,7 @@ class PresenceValidationTest < ActiveModel::TestCase
end
def test_validate_format_with_allow_blank
- Topic.validates_format_of(:title, :with => /\AValidation\smacros \w+!\z/, :allow_blank => true)
+ Topic.validates_format_of(:title, with: /\AValidation\smacros \w+!\z/, allow_blank: true)
assert Topic.new("title" => "Shouldn't be valid").invalid?
assert Topic.new("title" => "").valid?
assert Topic.new("title" => nil).valid?
@@ -36,7 +36,7 @@ class PresenceValidationTest < ActiveModel::TestCase
# testing ticket #3142
def test_validate_format_numeric
- Topic.validates_format_of(:title, :content, :with => /\A[1-9][0-9]*\z/, :message => "is bad data")
+ Topic.validates_format_of(:title, :content, with: /\A[1-9][0-9]*\z/, message: "is bad data")
t = Topic.new("title" => "72x", "content" => "6789")
assert t.invalid?, "Shouldn't be valid"
@@ -63,24 +63,24 @@ class PresenceValidationTest < ActiveModel::TestCase
end
def test_validate_format_with_formatted_message
- Topic.validates_format_of(:title, :with => /\AValid Title\z/, :message => "can't be %{value}")
- t = Topic.new(:title => 'Invalid title')
+ Topic.validates_format_of(:title, with: /\AValid Title\z/, message: "can't be %{value}")
+ t = Topic.new(title: 'Invalid title')
assert t.invalid?
assert_equal ["can't be Invalid title"], t.errors[:title]
end
-
+
def test_validate_format_of_with_multiline_regexp_should_raise_error
- assert_raise(ArgumentError) { Topic.validates_format_of(:title, :with => /^Valid Title$/) }
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, with: /^Valid Title$/) }
end
-
+
def test_validate_format_of_with_multiline_regexp_and_option
assert_nothing_raised(ArgumentError) do
- Topic.validates_format_of(:title, :with => /^Valid Title$/, :multiline => true)
+ Topic.validates_format_of(:title, with: /^Valid Title$/, multiline: true)
end
end
def test_validate_format_with_not_option
- Topic.validates_format_of(:title, :without => /foo/, :message => "should not contain foo")
+ Topic.validates_format_of(:title, without: /foo/, message: "should not contain foo")
t = Topic.new
t.title = "foobar"
@@ -97,19 +97,19 @@ class PresenceValidationTest < ActiveModel::TestCase
end
def test_validates_format_of_with_both_regexps_should_raise_error
- assert_raise(ArgumentError) { Topic.validates_format_of(:title, :with => /this/, :without => /that/) }
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, with: /this/, without: /that/) }
end
def test_validates_format_of_when_with_isnt_a_regexp_should_raise_error
- assert_raise(ArgumentError) { Topic.validates_format_of(:title, :with => "clearly not a regexp") }
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, with: "clearly not a regexp") }
end
def test_validates_format_of_when_not_isnt_a_regexp_should_raise_error
- assert_raise(ArgumentError) { Topic.validates_format_of(:title, :without => "clearly not a regexp") }
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, without: "clearly not a regexp") }
end
def test_validates_format_of_with_lambda
- Topic.validates_format_of :content, :with => lambda{ |topic| topic.title == "digit" ? /\A\d+\Z/ : /\A\S+\Z/ }
+ Topic.validates_format_of :content, with: lambda { |topic| topic.title == "digit" ? /\A\d+\Z/ : /\A\S+\Z/ }
t = Topic.new
t.title = "digit"
@@ -121,7 +121,7 @@ class PresenceValidationTest < ActiveModel::TestCase
end
def test_validates_format_of_without_lambda
- Topic.validates_format_of :content, :without => lambda{ |topic| topic.title == "characters" ? /\A\d+\Z/ : /\A\S+\Z/ }
+ Topic.validates_format_of :content, without: lambda { |topic| topic.title == "characters" ? /\A\d+\Z/ : /\A\S+\Z/ }
t = Topic.new
t.title = "characters"
@@ -133,7 +133,7 @@ class PresenceValidationTest < ActiveModel::TestCase
end
def test_validates_format_of_for_ruby_class
- Person.validates_format_of :karma, :with => /\A\d+\Z/
+ Person.validates_format_of :karma, with: /\A\d+\Z/
p = Person.new
p.karma = "Pixies"
@@ -144,6 +144,6 @@ class PresenceValidationTest < ActiveModel::TestCase
p.karma = "1234"
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
end
diff --git a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb
index 302cbe9761..3eeb80a48b 100644
--- a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -4,35 +4,35 @@ require 'models/person'
class I18nGenerateMessageValidationTest < ActiveModel::TestCase
def setup
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
@person = Person.new
end
# validates_inclusion_of: generate_message(attr_name, :inclusion, message: custom_message, value: value)
def test_generate_message_inclusion_with_default_message
- assert_equal 'is not included in the list', @person.errors.generate_message(:title, :inclusion, :value => 'title')
+ assert_equal 'is not included in the list', @person.errors.generate_message(:title, :inclusion, value: 'title')
end
def test_generate_message_inclusion_with_custom_message
- assert_equal 'custom message title', @person.errors.generate_message(:title, :inclusion, :message => 'custom message %{value}', :value => 'title')
+ assert_equal 'custom message title', @person.errors.generate_message(:title, :inclusion, message: 'custom message %{value}', value: 'title')
end
# validates_exclusion_of: generate_message(attr_name, :exclusion, message: custom_message, value: value)
def test_generate_message_exclusion_with_default_message
- assert_equal 'is reserved', @person.errors.generate_message(:title, :exclusion, :value => 'title')
+ assert_equal 'is reserved', @person.errors.generate_message(:title, :exclusion, value: 'title')
end
def test_generate_message_exclusion_with_custom_message
- assert_equal 'custom message title', @person.errors.generate_message(:title, :exclusion, :message => 'custom message %{value}', :value => 'title')
+ assert_equal 'custom message title', @person.errors.generate_message(:title, :exclusion, message: 'custom message %{value}', value: 'title')
end
# validates_format_of: generate_message(attr_name, :invalid, message: custom_message, value: value)
def test_generate_message_invalid_with_default_message
- assert_equal 'is invalid', @person.errors.generate_message(:title, :invalid, :value => 'title')
+ assert_equal 'is invalid', @person.errors.generate_message(:title, :invalid, value: 'title')
end
def test_generate_message_invalid_with_custom_message
- assert_equal 'custom message title', @person.errors.generate_message(:title, :invalid, :message => 'custom message %{value}', :value => 'title')
+ assert_equal 'custom message title', @person.errors.generate_message(:title, :invalid, message: 'custom message %{value}', value: 'title')
end
# validates_confirmation_of: generate_message(attr_name, :confirmation, message: custom_message)
@@ -41,7 +41,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase
end
def test_generate_message_confirmation_with_custom_message
- assert_equal 'custom message', @person.errors.generate_message(:title, :confirmation, :message => 'custom message')
+ assert_equal 'custom message', @person.errors.generate_message(:title, :confirmation, message: 'custom message')
end
# validates_acceptance_of: generate_message(attr_name, :accepted, message: custom_message)
@@ -50,7 +50,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase
end
def test_generate_message_accepted_with_custom_message
- assert_equal 'custom message', @person.errors.generate_message(:title, :accepted, :message => 'custom message')
+ assert_equal 'custom message', @person.errors.generate_message(:title, :accepted, message: 'custom message')
end
# add_on_empty: generate_message(attr, :empty, message: custom_message)
@@ -59,7 +59,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase
end
def test_generate_message_empty_with_custom_message
- assert_equal 'custom message', @person.errors.generate_message(:title, :empty, :message => 'custom message')
+ assert_equal 'custom message', @person.errors.generate_message(:title, :empty, message: 'custom message')
end
# add_on_blank: generate_message(attr, :blank, message: custom_message)
@@ -68,71 +68,83 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase
end
def test_generate_message_blank_with_custom_message
- assert_equal 'custom message', @person.errors.generate_message(:title, :blank, :message => 'custom message')
+ assert_equal 'custom message', @person.errors.generate_message(:title, :blank, message: 'custom message')
end
# validates_length_of: generate_message(attr, :too_long, message: custom_message, count: option_value.end)
- def test_generate_message_too_long_with_default_message
- assert_equal "is too long (maximum is 10 characters)", @person.errors.generate_message(:title, :too_long, :count => 10)
+ def test_generate_message_too_long_with_default_message_plural
+ assert_equal "is too long (maximum is 10 characters)", @person.errors.generate_message(:title, :too_long, count: 10)
+ end
+
+ def test_generate_message_too_long_with_default_message_singular
+ assert_equal "is too long (maximum is 1 character)", @person.errors.generate_message(:title, :too_long, count: 1)
end
def test_generate_message_too_long_with_custom_message
- assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_long, :message => 'custom message %{count}', :count => 10)
+ assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_long, message: 'custom message %{count}', count: 10)
end
# validates_length_of: generate_message(attr, :too_short, default: custom_message, count: option_value.begin)
- def test_generate_message_too_short_with_default_message
- assert_equal "is too short (minimum is 10 characters)", @person.errors.generate_message(:title, :too_short, :count => 10)
+ def test_generate_message_too_short_with_default_message_plural
+ assert_equal "is too short (minimum is 10 characters)", @person.errors.generate_message(:title, :too_short, count: 10)
+ end
+
+ def test_generate_message_too_short_with_default_message_singular
+ assert_equal "is too short (minimum is 1 character)", @person.errors.generate_message(:title, :too_short, count: 1)
end
def test_generate_message_too_short_with_custom_message
- assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_short, :message => 'custom message %{count}', :count => 10)
+ assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_short, message: 'custom message %{count}', count: 10)
end
# validates_length_of: generate_message(attr, :wrong_length, message: custom_message, count: option_value)
- def test_generate_message_wrong_length_with_default_message
- assert_equal "is the wrong length (should be 10 characters)", @person.errors.generate_message(:title, :wrong_length, :count => 10)
+ def test_generate_message_wrong_length_with_default_message_plural
+ assert_equal "is the wrong length (should be 10 characters)", @person.errors.generate_message(:title, :wrong_length, count: 10)
+ end
+
+ def test_generate_message_wrong_length_with_default_message_singular
+ assert_equal "is the wrong length (should be 1 character)", @person.errors.generate_message(:title, :wrong_length, count: 1)
end
def test_generate_message_wrong_length_with_custom_message
- assert_equal 'custom message 10', @person.errors.generate_message(:title, :wrong_length, :message => 'custom message %{count}', :count => 10)
+ assert_equal 'custom message 10', @person.errors.generate_message(:title, :wrong_length, message: 'custom message %{count}', count: 10)
end
# validates_numericality_of: generate_message(attr_name, :not_a_number, value: raw_value, message: custom_message)
def test_generate_message_not_a_number_with_default_message
- assert_equal "is not a number", @person.errors.generate_message(:title, :not_a_number, :value => 'title')
+ assert_equal "is not a number", @person.errors.generate_message(:title, :not_a_number, value: 'title')
end
def test_generate_message_not_a_number_with_custom_message
- assert_equal 'custom message title', @person.errors.generate_message(:title, :not_a_number, :message => 'custom message %{value}', :value => 'title')
+ assert_equal 'custom message title', @person.errors.generate_message(:title, :not_a_number, message: 'custom message %{value}', value: 'title')
end
# validates_numericality_of: generate_message(attr_name, option, value: raw_value, default: custom_message)
def test_generate_message_greater_than_with_default_message
- assert_equal "must be greater than 10", @person.errors.generate_message(:title, :greater_than, :value => 'title', :count => 10)
+ assert_equal "must be greater than 10", @person.errors.generate_message(:title, :greater_than, value: 'title', count: 10)
end
def test_generate_message_greater_than_or_equal_to_with_default_message
- assert_equal "must be greater than or equal to 10", @person.errors.generate_message(:title, :greater_than_or_equal_to, :value => 'title', :count => 10)
+ assert_equal "must be greater than or equal to 10", @person.errors.generate_message(:title, :greater_than_or_equal_to, value: 'title', count: 10)
end
def test_generate_message_equal_to_with_default_message
- assert_equal "must be equal to 10", @person.errors.generate_message(:title, :equal_to, :value => 'title', :count => 10)
+ assert_equal "must be equal to 10", @person.errors.generate_message(:title, :equal_to, value: 'title', count: 10)
end
def test_generate_message_less_than_with_default_message
- assert_equal "must be less than 10", @person.errors.generate_message(:title, :less_than, :value => 'title', :count => 10)
+ assert_equal "must be less than 10", @person.errors.generate_message(:title, :less_than, value: 'title', count: 10)
end
def test_generate_message_less_than_or_equal_to_with_default_message
- assert_equal "must be less than or equal to 10", @person.errors.generate_message(:title, :less_than_or_equal_to, :value => 'title', :count => 10)
+ assert_equal "must be less than or equal to 10", @person.errors.generate_message(:title, :less_than_or_equal_to, value: 'title', count: 10)
end
def test_generate_message_odd_with_default_message
- assert_equal "must be odd", @person.errors.generate_message(:title, :odd, :value => 'title', :count => 10)
+ assert_equal "must be odd", @person.errors.generate_message(:title, :odd, value: 'title', count: 10)
end
def test_generate_message_even_with_default_message
- assert_equal "must be even", @person.errors.generate_message(:title, :even, :value => 'title', :count => 10)
+ assert_equal "must be even", @person.errors.generate_message(:title, :even, value: 'title', count: 10)
end
end
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index 4c01b47608..96084a32ba 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -6,37 +6,38 @@ require 'models/person'
class I18nValidationTest < ActiveModel::TestCase
def setup
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
@person = Person.new
@old_load_path, @old_backend = I18n.load_path.dup, I18n.backend
I18n.load_path.clear
I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations('en', :errors => {:messages => {:custom => nil}})
+ I18n.backend.store_translations('en', errors: { messages: { custom: nil } })
end
def teardown
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
I18n.load_path.replace @old_load_path
I18n.backend = @old_backend
+ I18n.backend.reload!
end
def test_full_message_encoding
- I18n.backend.store_translations('en', :errors => {
- :messages => { :too_short => '猫舌' }})
- Person.validates_length_of :title, :within => 3..5
+ I18n.backend.store_translations('en', errors: {
+ messages: { too_short: '猫舌' } })
+ Person.validates_length_of :title, within: 3..5
@person.valid?
assert_equal ['Title 猫舌'], @person.errors.full_messages
end
def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
@person.errors.add(:name, 'not found')
- Person.expects(:human_attribute_name).with(:name, :default => 'Name').returns("Person's name")
+ Person.expects(:human_attribute_name).with(:name, default: 'Name').returns("Person's name")
assert_equal ["Person's name not found"], @person.errors.full_messages
end
def test_errors_full_messages_uses_format
- I18n.backend.store_translations('en', :errors => {:format => "Field %{attribute} %{message}"})
+ I18n.backend.store_translations('en', errors: { format: "Field %{attribute} %{message}" })
@person.errors.add('name', 'empty')
assert_equal ["Field Name empty"], @person.errors.full_messages
end
@@ -49,10 +50,10 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES = [
# [ case, validation_options, generate_message_options]
[ "given no options", {}, {}],
- [ "given custom message", {:message => "custom"}, {:message => "custom"}],
- [ "given if condition", {:if => lambda { true }}, {}],
- [ "given unless condition", {:unless => lambda { false }}, {}],
- [ "given option that is not reserved", {:format => "jpg"}, {:format => "jpg" }]
+ [ "given custom message", { message: "custom" }, { message: "custom" }],
+ [ "given if condition", { if: lambda { true }}, {}],
+ [ "given unless condition", { unless: lambda { false }}, {}],
+ [ "given option that is not reserved", { format: "jpg" }, { format: "jpg" }]
]
# validates_confirmation_of w/ mocha
@@ -61,7 +62,7 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_confirmation_of on generated message #{name}" do
Person.validates_confirmation_of :title, validation_options
@person.title_confirmation = 'foo'
- @person.errors.expects(:generate_message).with(:title_confirmation, :confirmation, generate_message_options.merge(:attribute => 'Title'))
+ @person.errors.expects(:generate_message).with(:title_confirmation, :confirmation, generate_message_options.merge(attribute: 'Title'))
@person.valid?
end
end
@@ -70,7 +71,7 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_acceptance_of on generated message #{name}" do
- Person.validates_acceptance_of :title, validation_options.merge(:allow_nil => false)
+ Person.validates_acceptance_of :title, validation_options.merge(allow_nil: false)
@person.errors.expects(:generate_message).with(:title, :accepted, generate_message_options)
@person.valid?
end
@@ -90,8 +91,8 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :withing on generated message when too short #{name}" do
- Person.validates_length_of :title, validation_options.merge(:within => 3..5)
- @person.errors.expects(:generate_message).with(:title, :too_short, generate_message_options.merge(:count => 3))
+ Person.validates_length_of :title, validation_options.merge(within: 3..5)
+ @person.errors.expects(:generate_message).with(:title, :too_short, generate_message_options.merge(count: 3))
@person.valid?
end
end
@@ -100,9 +101,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :too_long generated message #{name}" do
- Person.validates_length_of :title, validation_options.merge(:within => 3..5)
+ Person.validates_length_of :title, validation_options.merge(within: 3..5)
@person.title = 'this title is too long'
- @person.errors.expects(:generate_message).with(:title, :too_long, generate_message_options.merge(:count => 5))
+ @person.errors.expects(:generate_message).with(:title, :too_long, generate_message_options.merge(count: 5))
@person.valid?
end
end
@@ -111,8 +112,8 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :is on generated message #{name}" do
- Person.validates_length_of :title, validation_options.merge(:is => 5)
- @person.errors.expects(:generate_message).with(:title, :wrong_length, generate_message_options.merge(:count => 5))
+ Person.validates_length_of :title, validation_options.merge(is: 5)
+ @person.errors.expects(:generate_message).with(:title, :wrong_length, generate_message_options.merge(count: 5))
@person.valid?
end
end
@@ -121,9 +122,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_format_of on generated message #{name}" do
- Person.validates_format_of :title, validation_options.merge(:with => /\A[1-9][0-9]*\z/)
+ Person.validates_format_of :title, validation_options.merge(with: /\A[1-9][0-9]*\z/)
@person.title = '72x'
- @person.errors.expects(:generate_message).with(:title, :invalid, generate_message_options.merge(:value => '72x'))
+ @person.errors.expects(:generate_message).with(:title, :invalid, generate_message_options.merge(value: '72x'))
@person.valid?
end
end
@@ -132,9 +133,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_inclusion_of on generated message #{name}" do
- Person.validates_inclusion_of :title, validation_options.merge(:in => %w(a b c))
+ Person.validates_inclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = 'z'
- @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(:value => 'z'))
+ @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(value: 'z'))
@person.valid?
end
end
@@ -143,9 +144,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_inclusion_of using :within on generated message #{name}" do
- Person.validates_inclusion_of :title, validation_options.merge(:within => %w(a b c))
+ Person.validates_inclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = 'z'
- @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(:value => 'z'))
+ @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(value: 'z'))
@person.valid?
end
end
@@ -154,9 +155,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_exclusion_of generated message #{name}" do
- Person.validates_exclusion_of :title, validation_options.merge(:in => %w(a b c))
+ Person.validates_exclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = 'a'
- @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(:value => 'a'))
+ @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(value: 'a'))
@person.valid?
end
end
@@ -165,9 +166,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_exclusion_of using :within generated message #{name}" do
- Person.validates_exclusion_of :title, validation_options.merge(:within => %w(a b c))
+ Person.validates_exclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = 'a'
- @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(:value => 'a'))
+ @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(value: 'a'))
@person.valid?
end
end
@@ -178,7 +179,7 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of generated message #{name}" do
Person.validates_numericality_of :title, validation_options
@person.title = 'a'
- @person.errors.expects(:generate_message).with(:title, :not_a_number, generate_message_options.merge(:value => 'a'))
+ @person.errors.expects(:generate_message).with(:title, :not_a_number, generate_message_options.merge(value: 'a'))
@person.valid?
end
end
@@ -187,9 +188,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_numericality_of for :only_integer on generated message #{name}" do
- Person.validates_numericality_of :title, validation_options.merge(:only_integer => true)
+ Person.validates_numericality_of :title, validation_options.merge(only_integer: true)
@person.title = '0.0'
- @person.errors.expects(:generate_message).with(:title, :not_an_integer, generate_message_options.merge(:value => '0.0'))
+ @person.errors.expects(:generate_message).with(:title, :not_an_integer, generate_message_options.merge(value: '0.0'))
@person.valid?
end
end
@@ -198,9 +199,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_numericality_of for :odd on generated message #{name}" do
- Person.validates_numericality_of :title, validation_options.merge(:only_integer => true, :odd => true)
+ Person.validates_numericality_of :title, validation_options.merge(only_integer: true, odd: true)
@person.title = 0
- @person.errors.expects(:generate_message).with(:title, :odd, generate_message_options.merge(:value => 0))
+ @person.errors.expects(:generate_message).with(:title, :odd, generate_message_options.merge(value: 0))
@person.valid?
end
end
@@ -209,9 +210,9 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_numericality_of for :less_than on generated message #{name}" do
- Person.validates_numericality_of :title, validation_options.merge(:only_integer => true, :less_than => 0)
+ Person.validates_numericality_of :title, validation_options.merge(only_integer: true, less_than: 0)
@person.title = 1
- @person.errors.expects(:generate_message).with(:title, :less_than, generate_message_options.merge(:value => 1, :count => 0))
+ @person.errors.expects(:generate_message).with(:title, :less_than, generate_message_options.merge(value: 1, count: 0))
@person.valid?
end
end
@@ -226,8 +227,8 @@ class I18nValidationTest < ActiveModel::TestCase
end
# test "validates_confirmation_of finds custom model key translation when blank"
test "#{validation} finds custom model key translation when #{error_type}" do
- I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {attribute => {error_type => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :errors => {:messages => {error_type => 'global message'}}
+ I18n.backend.store_translations 'en', activemodel: { errors: { models: { person: { attributes: { attribute => { error_type => 'custom message' } } } } } }
+ I18n.backend.store_translations 'en', errors: { messages: { error_type => 'global message'}}
yield(@person, {})
@person.valid?
@@ -236,17 +237,17 @@ class I18nValidationTest < ActiveModel::TestCase
# test "validates_confirmation_of finds custom model key translation with interpolation when blank"
test "#{validation} finds custom model key translation with interpolation when #{error_type}" do
- I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {attribute => {error_type => 'custom message with %{extra}'}}}}}}
- I18n.backend.store_translations 'en', :errors => {:messages => {error_type => 'global message'}}
+ I18n.backend.store_translations 'en', activemodel: { errors: { models: { person: { attributes: { attribute => { error_type => 'custom message with %{extra}' } } } } } }
+ I18n.backend.store_translations 'en', errors: { messages: {error_type => 'global message'} }
- yield(@person, {:extra => "extra information"})
+ yield(@person, { extra: "extra information" })
@person.valid?
assert_equal ['custom message with extra information'], @person.errors[attribute]
end
# test "validates_confirmation_of finds global default key translation when blank"
test "#{validation} finds global default key translation when #{error_type}" do
- I18n.backend.store_translations 'en', :errors => {:messages => {error_type => 'global message'}}
+ I18n.backend.store_translations 'en', errors: { messages: {error_type => 'global message'} }
yield(@person, {})
@person.valid?
@@ -264,7 +265,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_acceptance_of w/o mocha
set_expectations_for_validation "validates_acceptance_of", :accepted do |person, options_to_merge|
- Person.validates_acceptance_of :title, options_to_merge.merge(:allow_nil => false)
+ Person.validates_acceptance_of :title, options_to_merge.merge(allow_nil: false)
end
# validates_presence_of w/o mocha
@@ -276,36 +277,36 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_length_of :within w/o mocha
set_expectations_for_validation "validates_length_of", :too_short do |person, options_to_merge|
- Person.validates_length_of :title, options_to_merge.merge(:within => 3..5)
+ Person.validates_length_of :title, options_to_merge.merge(within: 3..5)
end
set_expectations_for_validation "validates_length_of", :too_long do |person, options_to_merge|
- Person.validates_length_of :title, options_to_merge.merge(:within => 3..5)
+ Person.validates_length_of :title, options_to_merge.merge(within: 3..5)
person.title = "too long"
end
# validates_length_of :is w/o mocha
set_expectations_for_validation "validates_length_of", :wrong_length do |person, options_to_merge|
- Person.validates_length_of :title, options_to_merge.merge(:is => 5)
+ Person.validates_length_of :title, options_to_merge.merge(is: 5)
end
# validates_format_of w/o mocha
set_expectations_for_validation "validates_format_of", :invalid do |person, options_to_merge|
- Person.validates_format_of :title, options_to_merge.merge(:with => /\A[1-9][0-9]*\z/)
+ Person.validates_format_of :title, options_to_merge.merge(with: /\A[1-9][0-9]*\z/)
end
# validates_inclusion_of w/o mocha
set_expectations_for_validation "validates_inclusion_of", :inclusion do |person, options_to_merge|
- Person.validates_inclusion_of :title, options_to_merge.merge(:in => %w(a b c))
+ Person.validates_inclusion_of :title, options_to_merge.merge(in: %w(a b c))
end
# validates_exclusion_of w/o mocha
set_expectations_for_validation "validates_exclusion_of", :exclusion do |person, options_to_merge|
- Person.validates_exclusion_of :title, options_to_merge.merge(:in => %w(a b c))
+ Person.validates_exclusion_of :title, options_to_merge.merge(in: %w(a b c))
person.title = 'a'
end
@@ -319,55 +320,54 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_numericality_of with :only_integer w/o mocha
set_expectations_for_validation "validates_numericality_of", :not_an_integer do |person, options_to_merge|
- Person.validates_numericality_of :title, options_to_merge.merge(:only_integer => true)
+ Person.validates_numericality_of :title, options_to_merge.merge(only_integer: true)
person.title = '1.0'
end
# validates_numericality_of :odd w/o mocha
set_expectations_for_validation "validates_numericality_of", :odd do |person, options_to_merge|
- Person.validates_numericality_of :title, options_to_merge.merge(:only_integer => true, :odd => true)
+ Person.validates_numericality_of :title, options_to_merge.merge(only_integer: true, odd: true)
person.title = 0
end
# validates_numericality_of :less_than w/o mocha
set_expectations_for_validation "validates_numericality_of", :less_than do |person, options_to_merge|
- Person.validates_numericality_of :title, options_to_merge.merge(:only_integer => true, :less_than => 0)
+ Person.validates_numericality_of :title, options_to_merge.merge(only_integer: true, less_than: 0)
person.title = 1
end
# test with validates_with
def test_validations_with_message_symbol_must_translate
- I18n.backend.store_translations 'en', :errors => {:messages => {:custom_error => "I am a custom error"}}
- Person.validates_presence_of :title, :message => :custom_error
+ I18n.backend.store_translations 'en', errors: { messages: { custom_error: "I am a custom error" } }
+ Person.validates_presence_of :title, message: :custom_error
@person.title = nil
@person.valid?
assert_equal ["I am a custom error"], @person.errors[:title]
end
def test_validates_with_message_symbol_must_translate_per_attribute
- I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:custom_error => "I am a custom error"}}}}}}
- Person.validates_presence_of :title, :message => :custom_error
+ I18n.backend.store_translations 'en', activemodel: { errors: { models: { person: { attributes: { title: { custom_error: "I am a custom error" } } } } } }
+ Person.validates_presence_of :title, message: :custom_error
@person.title = nil
@person.valid?
assert_equal ["I am a custom error"], @person.errors[:title]
end
def test_validates_with_message_symbol_must_translate_per_model
- I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:custom_error => "I am a custom error"}}}}
- Person.validates_presence_of :title, :message => :custom_error
+ I18n.backend.store_translations 'en', activemodel: { errors: { models: { person: { custom_error: "I am a custom error" } } } }
+ Person.validates_presence_of :title, message: :custom_error
@person.title = nil
@person.valid?
assert_equal ["I am a custom error"], @person.errors[:title]
end
def test_validates_with_message_string
- Person.validates_presence_of :title, :message => "I am a custom error"
+ Person.validates_presence_of :title, message: "I am a custom error"
@person.title = nil
@person.valid?
assert_equal ["I am a custom error"], @person.errors[:title]
end
-
end
diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb
index 117e9109fc..3a8f3080e1 100644
--- a/activemodel/test/cases/validations/inclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/inclusion_validation_test.rb
@@ -1,5 +1,6 @@
# encoding: utf-8
require 'cases/helper'
+require 'active_support/all'
require 'models/topic'
require 'models/person'
@@ -7,20 +8,42 @@ require 'models/person'
class InclusionValidationTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_validates_inclusion_of_range
- Topic.validates_inclusion_of( :title, :in => 'aaa'..'bbb' )
+ Topic.validates_inclusion_of(:title, in: 'aaa'..'bbb')
assert Topic.new("title" => "bbc", "content" => "abc").invalid?
assert Topic.new("title" => "aa", "content" => "abc").invalid?
+ assert Topic.new("title" => "aaab", "content" => "abc").invalid?
assert Topic.new("title" => "aaa", "content" => "abc").valid?
assert Topic.new("title" => "abc", "content" => "abc").valid?
assert Topic.new("title" => "bbb", "content" => "abc").valid?
end
+ def test_validates_inclusion_of_time_range
+ Topic.validates_inclusion_of(:created_at, in: 1.year.ago..Time.now)
+ assert Topic.new(title: 'aaa', created_at: 2.years.ago).invalid?
+ assert Topic.new(title: 'aaa', created_at: 3.months.ago).valid?
+ assert Topic.new(title: 'aaa', created_at: 37.weeks.from_now).invalid?
+ end
+
+ def test_validates_inclusion_of_date_range
+ Topic.validates_inclusion_of(:created_at, in: 1.year.until(Date.today)..Date.today)
+ assert Topic.new(title: 'aaa', created_at: 2.years.until(Date.today)).invalid?
+ assert Topic.new(title: 'aaa', created_at: 3.months.until(Date.today)).valid?
+ assert Topic.new(title: 'aaa', created_at: 37.weeks.since(Date.today)).invalid?
+ end
+
+ def test_validates_inclusion_of_date_time_range
+ Topic.validates_inclusion_of(:created_at, in: 1.year.until(DateTime.current)..DateTime.current)
+ assert Topic.new(title: 'aaa', created_at: 2.years.until(DateTime.current)).invalid?
+ assert Topic.new(title: 'aaa', created_at: 3.months.until(DateTime.current)).valid?
+ assert Topic.new(title: 'aaa', created_at: 37.weeks.since(DateTime.current)).invalid?
+ end
+
def test_validates_inclusion_of
- Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
+ Topic.validates_inclusion_of(:title, in: %w( a b c d e f g ))
assert Topic.new("title" => "a!", "content" => "abc").invalid?
assert Topic.new("title" => "a b", "content" => "abc").invalid?
@@ -33,16 +56,16 @@ class InclusionValidationTest < ActiveModel::TestCase
assert t.errors[:title].any?
assert_equal ["is not included in the list"], t.errors[:title]
- assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
- assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of(:title, in: nil) }
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of(:title, in: 0) }
- assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
- assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
- assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of(:title, in: "hi!") }
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of(:title, in: {}) }
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of(:title, in: []) }
end
def test_validates_inclusion_of_with_allow_nil
- Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil => true )
+ Topic.validates_inclusion_of(:title, in: %w( a b c d e f g ), allow_nil: true)
assert Topic.new("title" => "a!", "content" => "abc").invalid?
assert Topic.new("title" => "", "content" => "abc").invalid?
@@ -50,7 +73,7 @@ class InclusionValidationTest < ActiveModel::TestCase
end
def test_validates_inclusion_of_with_formatted_message
- Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %{value} is not in the list" )
+ Topic.validates_inclusion_of(:title, in: %w( a b c d e f g ), message: "option %{value} is not in the list")
assert Topic.new("title" => "a", "content" => "abc").valid?
@@ -61,7 +84,7 @@ class InclusionValidationTest < ActiveModel::TestCase
end
def test_validates_inclusion_of_with_within_option
- Topic.validates_inclusion_of( :title, :within => %w( a b c d e f g ) )
+ Topic.validates_inclusion_of(:title, within: %w( a b c d e f g ))
assert Topic.new("title" => "a", "content" => "abc").valid?
@@ -71,7 +94,7 @@ class InclusionValidationTest < ActiveModel::TestCase
end
def test_validates_inclusion_of_for_ruby_class
- Person.validates_inclusion_of :karma, :in => %w( abe monkey )
+ Person.validates_inclusion_of :karma, in: %w( abe monkey )
p = Person.new
p.karma = "Lifo"
@@ -82,11 +105,11 @@ class InclusionValidationTest < ActiveModel::TestCase
p.karma = "monkey"
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
def test_validates_inclusion_of_with_lambda
- Topic.validates_inclusion_of :title, :in => lambda{ |topic| topic.author_name == "sikachu" ? %w( monkey elephant ) : %w( abe wasabi ) }
+ Topic.validates_inclusion_of :title, in: lambda{ |topic| topic.author_name == "sikachu" ? %w( monkey elephant ) : %w( abe wasabi ) }
t = Topic.new
t.title = "wasabi"
@@ -98,7 +121,7 @@ class InclusionValidationTest < ActiveModel::TestCase
end
def test_validates_inclusion_of_with_symbol
- Person.validates_inclusion_of :karma, :in => :available_karmas
+ Person.validates_inclusion_of :karma, in: :available_karmas
p = Person.new
p.karma = "Lifo"
@@ -119,6 +142,6 @@ class InclusionValidationTest < ActiveModel::TestCase
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
end
diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb
index 1a40ca8efc..046ffcb16f 100644
--- a/activemodel/test/cases/validations/length_validation_test.rb
+++ b/activemodel/test/cases/validations/length_validation_test.rb
@@ -5,13 +5,12 @@ require 'models/topic'
require 'models/person'
class LengthValidationTest < ActiveModel::TestCase
-
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_validates_length_of_with_allow_nil
- Topic.validates_length_of( :title, :is => 5, :allow_nil => true )
+ Topic.validates_length_of( :title, is: 5, allow_nil: true )
assert Topic.new("title" => "ab").invalid?
assert Topic.new("title" => "").invalid?
@@ -20,7 +19,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_with_allow_blank
- Topic.validates_length_of( :title, :is => 5, :allow_blank => true )
+ Topic.validates_length_of( :title, is: 5, allow_blank: true )
assert Topic.new("title" => "ab").invalid?
assert Topic.new("title" => "").valid?
@@ -29,7 +28,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_minimum
- Topic.validates_length_of :title, :minimum => 5
+ Topic.validates_length_of :title, minimum: 5
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -51,13 +50,13 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum_should_allow_nil
- Topic.validates_length_of :title, :maximum => 10
+ Topic.validates_length_of :title, maximum: 10
t = Topic.new
assert t.valid?
end
def test_optionally_validates_length_of_using_minimum
- Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
+ Topic.validates_length_of :title, minimum: 5, allow_nil: true
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -67,7 +66,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum
- Topic.validates_length_of :title, :maximum => 5
+ Topic.validates_length_of :title, maximum: 5
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -82,7 +81,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_maximum
- Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
+ Topic.validates_length_of :title, maximum: 5, allow_nil: true
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -92,7 +91,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_within
- Topic.validates_length_of(:title, :content, :within => 3..5)
+ Topic.validates_length_of(:title, :content, within: 3..5)
t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
assert t.invalid?
@@ -111,7 +110,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_within_with_exclusive_range
- Topic.validates_length_of(:title, :within => 4...10)
+ Topic.validates_length_of(:title, within: 4...10)
t = Topic.new("title" => "9 chars!!")
assert t.valid?
@@ -125,7 +124,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_within
- Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
+ Topic.validates_length_of :title, :content, within: 3..5, allow_nil: true
t = Topic.new('title' => 'abc', 'content' => 'abcd')
assert t.valid?
@@ -135,7 +134,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_is
- Topic.validates_length_of :title, :is => 5
+ Topic.validates_length_of :title, is: 5
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -153,7 +152,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_is
- Topic.validates_length_of :title, :is => 5, :allow_nil => true
+ Topic.validates_length_of :title, is: 5, allow_nil: true
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -167,25 +166,25 @@ class LengthValidationTest < ActiveModel::TestCase
bigmax = 2 ** 32
bigrange = bigmin...bigmax
assert_nothing_raised do
- Topic.validates_length_of :title, :is => bigmin + 5
- Topic.validates_length_of :title, :within => bigrange
- Topic.validates_length_of :title, :in => bigrange
- Topic.validates_length_of :title, :minimum => bigmin
- Topic.validates_length_of :title, :maximum => bigmax
+ Topic.validates_length_of :title, is: bigmin + 5
+ Topic.validates_length_of :title, within: bigrange
+ Topic.validates_length_of :title, in: bigrange
+ Topic.validates_length_of :title, minimum: bigmin
+ Topic.validates_length_of :title, maximum: bigmax
end
end
def test_validates_length_of_nasty_params
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is => -6) }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within => 6) }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum => "a") }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum => "a") }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within => "a") }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is => "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, is: -6) }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, within: 6) }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, minimum: "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, maximum: "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, within: "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, is: "a") }
end
def test_validates_length_of_custom_errors_for_minimum_with_message
- Topic.validates_length_of( :title, :minimum => 5, :message => "boo %{count}" )
+ Topic.validates_length_of( :title, minimum: 5, message: "boo %{count}" )
t = Topic.new("title" => "uhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -193,7 +192,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_minimum_with_too_short
- Topic.validates_length_of( :title, :minimum => 5, :too_short => "hoo %{count}" )
+ Topic.validates_length_of( :title, minimum: 5, too_short: "hoo %{count}" )
t = Topic.new("title" => "uhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -201,7 +200,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_maximum_with_message
- Topic.validates_length_of( :title, :maximum => 5, :message => "boo %{count}" )
+ Topic.validates_length_of( :title, maximum: 5, message: "boo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -209,7 +208,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_in
- Topic.validates_length_of(:title, :in => 10..20, :message => "hoo %{count}")
+ Topic.validates_length_of(:title, in: 10..20, message: "hoo %{count}")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -222,7 +221,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_maximum_with_too_long
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}" )
+ Topic.validates_length_of( :title, maximum: 5, too_long: "hoo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -230,21 +229,21 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_both_too_short_and_too_long
- Topic.validates_length_of :title, :minimum => 3, :maximum => 5, :too_short => 'too short', :too_long => 'too long'
+ Topic.validates_length_of :title, minimum: 3, maximum: 5, too_short: 'too short', too_long: 'too long'
- t = Topic.new(:title => 'a')
+ t = Topic.new(title: 'a')
assert t.invalid?
assert t.errors[:title].any?
assert_equal ['too short'], t.errors['title']
- t = Topic.new(:title => 'aaaaaa')
+ t = Topic.new(title: 'aaaaaa')
assert t.invalid?
assert t.errors[:title].any?
assert_equal ['too long'], t.errors['title']
end
def test_validates_length_of_custom_errors_for_is_with_message
- Topic.validates_length_of( :title, :is => 5, :message => "boo %{count}" )
+ Topic.validates_length_of( :title, is: 5, message: "boo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -252,7 +251,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_is_with_wrong_length
- Topic.validates_length_of( :title, :is => 5, :wrong_length => "hoo %{count}" )
+ Topic.validates_length_of( :title, is: 5, wrong_length: "hoo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -260,7 +259,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_minimum_utf8
- Topic.validates_length_of :title, :minimum => 5
+ Topic.validates_length_of :title, minimum: 5
t = Topic.new("title" => "一二三四五", "content" => "whatever")
assert t.valid?
@@ -272,7 +271,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum_utf8
- Topic.validates_length_of :title, :maximum => 5
+ Topic.validates_length_of :title, maximum: 5
t = Topic.new("title" => "一二三四五", "content" => "whatever")
assert t.valid?
@@ -284,7 +283,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_within_utf8
- Topic.validates_length_of(:title, :content, :within => 3..5)
+ Topic.validates_length_of(:title, :content, within: 3..5)
t = Topic.new("title" => "一二", "content" => "12三四五六七")
assert t.invalid?
@@ -296,12 +295,12 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_within_utf8
- Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
+ Topic.validates_length_of :title, within: 3..5, allow_nil: true
- t = Topic.new(:title => "一二三四五")
+ t = Topic.new(title: "一二三四五")
assert t.valid?, t.errors.inspect
- t = Topic.new(:title => "一二三")
+ t = Topic.new(title: "一二三")
assert t.valid?, t.errors.inspect
t.title = nil
@@ -309,7 +308,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_is_utf8
- Topic.validates_length_of :title, :is => 5
+ Topic.validates_length_of :title, is: 5
t = Topic.new("title" => "一二345", "content" => "whatever")
assert t.valid?
@@ -321,9 +320,9 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_with_block
- Topic.validates_length_of :content, :minimum => 5, :too_short => "Your essay must be at least %{count} words.",
- :tokenizer => lambda {|str| str.scan(/\w+/) }
- t = Topic.new(:content => "this content should be long enough")
+ Topic.validates_length_of :content, minimum: 5, too_short: "Your essay must be at least %{count} words.",
+ tokenizer: lambda {|str| str.scan(/\w+/) }
+ t = Topic.new(content: "this content should be long enough")
assert t.valid?
t.content = "not long enough"
@@ -333,18 +332,18 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_for_fixnum
- Topic.validates_length_of(:approved, :is => 4)
+ Topic.validates_length_of(:approved, is: 4)
- t = Topic.new("title" => "uhohuhoh", "content" => "whatever", :approved => 1)
+ t = Topic.new("title" => "uhohuhoh", "content" => "whatever", approved: 1)
assert t.invalid?
assert t.errors[:approved].any?
- t = Topic.new("title" => "uhohuhoh", "content" => "whatever", :approved => 1234)
+ t = Topic.new("title" => "uhohuhoh", "content" => "whatever", approved: 1234)
assert t.valid?
end
def test_validates_length_of_for_ruby_class
- Person.validates_length_of :karma, :minimum => 5
+ Person.validates_length_of :karma, minimum: 5
p = Person.new
p.karma = "Pix"
@@ -355,11 +354,11 @@ class LengthValidationTest < ActiveModel::TestCase
p.karma = "The Smiths"
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
def test_validates_length_of_for_infinite_maxima
- Topic.validates_length_of(:title, :within => 5..Float::INFINITY)
+ Topic.validates_length_of(:title, within: 5..Float::INFINITY)
t = Topic.new("title" => "1234")
assert t.invalid?
@@ -368,7 +367,7 @@ class LengthValidationTest < ActiveModel::TestCase
t.title = "12345"
assert t.valid?
- Topic.validates_length_of(:author_name, :maximum => Float::INFINITY)
+ Topic.validates_length_of(:author_name, maximum: Float::INFINITY)
assert t.valid?
@@ -377,13 +376,13 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum_should_not_allow_nil_when_nil_not_allowed
- Topic.validates_length_of :title, :maximum => 10, :allow_nil => false
+ Topic.validates_length_of :title, maximum: 10, allow_nil: false
t = Topic.new
assert t.invalid?
end
def test_validates_length_of_using_maximum_should_not_allow_nil_and_empty_string_when_blank_not_allowed
- Topic.validates_length_of :title, :maximum => 10, :allow_blank => false
+ Topic.validates_length_of :title, maximum: 10, allow_blank: false
t = Topic.new
assert t.invalid?
@@ -392,13 +391,13 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_both_minimum_and_maximum_should_not_allow_nil
- Topic.validates_length_of :title, :minimum => 5, :maximum => 10
+ Topic.validates_length_of :title, minimum: 5, maximum: 10
t = Topic.new
assert t.invalid?
end
def test_validates_length_of_using_minimum_0_should_not_allow_nil
- Topic.validates_length_of :title, :minimum => 0
+ Topic.validates_length_of :title, minimum: 0
t = Topic.new
assert t.invalid?
@@ -407,11 +406,19 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_is_0_should_not_allow_nil
- Topic.validates_length_of :title, :is => 0
+ Topic.validates_length_of :title, is: 0
t = Topic.new
assert t.invalid?
t.title = ""
assert t.valid?
end
+
+ def test_validates_with_diff_in_option
+ Topic.validates_length_of(:title, is: 5)
+ Topic.validates_length_of(:title, is: 5, if: Proc.new { false } )
+
+ assert Topic.new("title" => "david").valid?
+ assert Topic.new("title" => "david2").invalid?
+ end
end
diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb
index 6742a4bab0..12a22f9c40 100644
--- a/activemodel/test/cases/validations/numericality_validation_test.rb
+++ b/activemodel/test/cases/validations/numericality_validation_test.rb
@@ -9,7 +9,7 @@ require 'bigdecimal'
class NumericalityValidationTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
NIL = [nil]
@@ -30,84 +30,99 @@ class NumericalityValidationTest < ActiveModel::TestCase
end
def test_validates_numericality_of_with_nil_allowed
- Topic.validates_numericality_of :approved, :allow_nil => true
+ Topic.validates_numericality_of :approved, allow_nil: true
invalid!(JUNK + BLANK)
valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
end
def test_validates_numericality_of_with_integer_only
- Topic.validates_numericality_of :approved, :only_integer => true
+ Topic.validates_numericality_of :approved, only_integer: true
invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
valid!(INTEGERS)
end
def test_validates_numericality_of_with_integer_only_and_nil_allowed
- Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
+ Topic.validates_numericality_of :approved, only_integer: true, allow_nil: true
invalid!(JUNK + BLANK + FLOATS + BIGDECIMAL + INFINITY)
valid!(NIL + INTEGERS)
end
+ def test_validates_numericality_of_with_integer_only_and_symbol_as_value
+ Topic.validates_numericality_of :approved, only_integer: :condition_is_true_but_its_not
+
+ invalid!(NIL + BLANK + JUNK)
+ valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
+ end
+
+ def test_validates_numericality_of_with_integer_only_and_proc_as_value
+ Topic.send(:define_method, :allow_only_integers?, lambda { false })
+ Topic.validates_numericality_of :approved, only_integer: Proc.new(&:allow_only_integers?)
+
+ invalid!(NIL + BLANK + JUNK)
+ valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
+ end
+
def test_validates_numericality_with_greater_than
- Topic.validates_numericality_of :approved, :greater_than => 10
+ Topic.validates_numericality_of :approved, greater_than: 10
invalid!([-10, 10], 'must be greater than 10')
valid!([11])
end
def test_validates_numericality_with_greater_than_or_equal
- Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
+ Topic.validates_numericality_of :approved, greater_than_or_equal_to: 10
invalid!([-9, 9], 'must be greater than or equal to 10')
valid!([10])
end
def test_validates_numericality_with_equal_to
- Topic.validates_numericality_of :approved, :equal_to => 10
+ Topic.validates_numericality_of :approved, equal_to: 10
invalid!([-10, 11] + INFINITY, 'must be equal to 10')
valid!([10])
end
def test_validates_numericality_with_less_than
- Topic.validates_numericality_of :approved, :less_than => 10
+ Topic.validates_numericality_of :approved, less_than: 10
invalid!([10], 'must be less than 10')
valid!([-9, 9])
end
def test_validates_numericality_with_less_than_or_equal_to
- Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
+ Topic.validates_numericality_of :approved, less_than_or_equal_to: 10
invalid!([11], 'must be less than or equal to 10')
valid!([-10, 10])
end
def test_validates_numericality_with_odd
- Topic.validates_numericality_of :approved, :odd => true
+ Topic.validates_numericality_of :approved, odd: true
invalid!([-2, 2], 'must be odd')
valid!([-1, 1])
end
def test_validates_numericality_with_even
- Topic.validates_numericality_of :approved, :even => true
+ Topic.validates_numericality_of :approved, even: true
invalid!([-1, 1], 'must be even')
valid!([-2, 2])
end
def test_validates_numericality_with_greater_than_less_than_and_even
- Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
+ Topic.validates_numericality_of :approved, greater_than: 1, less_than: 4, even: true
invalid!([1, 3, 4])
valid!([2])
end
def test_validates_numericality_with_other_than
- Topic.validates_numericality_of :approved, :other_than => 0
+ Topic.validates_numericality_of :approved, other_than: 0
invalid!([0, 0.0])
valid!([-1, 42])
@@ -115,30 +130,32 @@ class NumericalityValidationTest < ActiveModel::TestCase
def test_validates_numericality_with_proc
Topic.send(:define_method, :min_approved, lambda { 5 })
- Topic.validates_numericality_of :approved, :greater_than_or_equal_to => Proc.new {|topic| topic.min_approved }
+ Topic.validates_numericality_of :approved, greater_than_or_equal_to: Proc.new(&:min_approved)
invalid!([3, 4])
valid!([5, 6])
+ ensure
Topic.send(:remove_method, :min_approved)
end
def test_validates_numericality_with_symbol
Topic.send(:define_method, :max_approved, lambda { 5 })
- Topic.validates_numericality_of :approved, :less_than_or_equal_to => :max_approved
+ Topic.validates_numericality_of :approved, less_than_or_equal_to: :max_approved
invalid!([6])
valid!([4, 5])
+ ensure
Topic.send(:remove_method, :max_approved)
end
def test_validates_numericality_with_numeric_message
- Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than %{count}"
+ Topic.validates_numericality_of :approved, less_than: 4, message: "smaller than %{count}"
topic = Topic.new("title" => "numeric test", "approved" => 10)
assert !topic.valid?
assert_equal ["smaller than 4"], topic.errors[:approved]
- Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than %{count}"
+ Topic.validates_numericality_of :approved, greater_than: 4, message: "greater than %{count}"
topic = Topic.new("title" => "numeric test", "approved" => 1)
assert !topic.valid?
@@ -146,7 +163,7 @@ class NumericalityValidationTest < ActiveModel::TestCase
end
def test_validates_numericality_of_for_ruby_class
- Person.validates_numericality_of :karma, :allow_nil => false
+ Person.validates_numericality_of :karma, allow_nil: false
p = Person.new
p.karma = "Pix"
@@ -157,15 +174,15 @@ class NumericalityValidationTest < ActiveModel::TestCase
p.karma = "1234"
assert p.valid?
ensure
- Person.reset_callbacks(:validate)
+ Person.clear_validators!
end
def test_validates_numericality_with_invalid_args
- assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, :greater_than_or_equal_to => "foo" }
- assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, :less_than_or_equal_to => "foo" }
- assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, :greater_than => "foo" }
- assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, :less_than => "foo" }
- assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, :equal_to => "foo" }
+ assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, greater_than_or_equal_to: "foo" }
+ assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, less_than_or_equal_to: "foo" }
+ assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, greater_than: "foo" }
+ assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, less_than: "foo" }
+ assert_raise(ArgumentError){ Topic.validates_numericality_of :approved, equal_to: "foo" }
end
private
@@ -185,7 +202,7 @@ class NumericalityValidationTest < ActiveModel::TestCase
end
def with_each_topic_approved_value(values)
- topic = Topic.new(:title => "numeric test", :content => "whatever")
+ topic = Topic.new(title: "numeric test", content: "whatever")
values.each do |value|
topic.approved = value
yield topic, value
diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb
index 2f228cfa83..ecf16d1e16 100644
--- a/activemodel/test/cases/validations/presence_validation_test.rb
+++ b/activemodel/test/cases/validations/presence_validation_test.rb
@@ -8,9 +8,9 @@ require 'models/custom_reader'
class PresenceValidationTest < ActiveModel::TestCase
teardown do
- Topic.reset_callbacks(:validate)
- Person.reset_callbacks(:validate)
- CustomReader.reset_callbacks(:validate)
+ Topic.clear_validators!
+ Person.clear_validators!
+ CustomReader.clear_validators!
end
def test_validate_presences
diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb
index 144532d6f4..8d4b74ee49 100644
--- a/activemodel/test/cases/validations/validates_test.rb
+++ b/activemodel/test/cases/validations/validates_test.rb
@@ -3,7 +3,6 @@ require 'cases/helper'
require 'models/person'
require 'models/topic'
require 'models/person_with_validator'
-require 'validators/email_validator'
require 'validators/namespace/email_validator'
class ValidatesTest < ActiveModel::TestCase
@@ -11,26 +10,26 @@ class ValidatesTest < ActiveModel::TestCase
teardown :reset_callbacks
def reset_callbacks
- Person.reset_callbacks(:validate)
- Topic.reset_callbacks(:validate)
- PersonWithValidator.reset_callbacks(:validate)
+ Person.clear_validators!
+ Topic.clear_validators!
+ PersonWithValidator.clear_validators!
end
def test_validates_with_messages_empty
- Person.validates :title, :presence => {:message => "" }
+ Person.validates :title, presence: { message: "" }
person = Person.new
assert !person.valid?, 'person should not be valid.'
end
def test_validates_with_built_in_validation
- Person.validates :title, :numericality => true
+ Person.validates :title, numericality: true
person = Person.new
person.valid?
assert_equal ['is not a number'], person.errors[:title]
end
def test_validates_with_attribute_specified_as_string
- Person.validates "title", :numericality => true
+ Person.validates "title", numericality: true
person = Person.new
person.valid?
assert_equal ['is not a number'], person.errors[:title]
@@ -41,14 +40,14 @@ class ValidatesTest < ActiveModel::TestCase
end
def test_validates_with_built_in_validation_and_options
- Person.validates :salary, :numericality => { :message => 'my custom message' }
+ Person.validates :salary, numericality: { message: 'my custom message' }
person = Person.new
person.valid?
assert_equal ['my custom message'], person.errors[:salary]
end
def test_validates_with_validator_class
- Person.validates :karma, :email => true
+ Person.validates :karma, email: true
person = Person.new
person.valid?
assert_equal ['is not an email'], person.errors[:karma]
@@ -62,33 +61,33 @@ class ValidatesTest < ActiveModel::TestCase
end
def test_validates_with_if_as_local_conditions
- Person.validates :karma, :presence => true, :email => { :unless => :condition_is_true }
+ Person.validates :karma, presence: true, email: { unless: :condition_is_true }
person = Person.new
person.valid?
assert_equal ["can't be blank"], person.errors[:karma]
end
def test_validates_with_if_as_shared_conditions
- Person.validates :karma, :presence => true, :email => true, :if => :condition_is_true
+ Person.validates :karma, presence: true, email: true, if: :condition_is_true
person = Person.new
person.valid?
assert_equal ["can't be blank", "is not an email"], person.errors[:karma].sort
end
def test_validates_with_unless_shared_conditions
- Person.validates :karma, :presence => true, :email => true, :unless => :condition_is_true
+ Person.validates :karma, presence: true, email: true, unless: :condition_is_true
person = Person.new
assert person.valid?
end
def test_validates_with_allow_nil_shared_conditions
- Person.validates :karma, :length => { :minimum => 20 }, :email => true, :allow_nil => true
+ Person.validates :karma, length: { minimum: 20 }, email: true, allow_nil: true
person = Person.new
assert person.valid?
end
def test_validates_with_regexp
- Person.validates :karma, :format => /positive|negative/
+ Person.validates :karma, format: /positive|negative/
person = Person.new
assert person.invalid?
assert_equal ['is invalid'], person.errors[:karma]
@@ -97,7 +96,7 @@ class ValidatesTest < ActiveModel::TestCase
end
def test_validates_with_array
- Person.validates :gender, :inclusion => %w(m f)
+ Person.validates :gender, inclusion: %w(m f)
person = Person.new
assert person.invalid?
assert_equal ['is not included in the list'], person.errors[:gender]
@@ -106,7 +105,7 @@ class ValidatesTest < ActiveModel::TestCase
end
def test_validates_with_range
- Person.validates :karma, :length => 6..20
+ Person.validates :karma, length: 6..20
person = Person.new
assert person.invalid?
assert_equal ['is too short (minimum is 6 characters)'], person.errors[:karma]
@@ -115,25 +114,25 @@ class ValidatesTest < ActiveModel::TestCase
end
def test_validates_with_validator_class_and_options
- Person.validates :karma, :email => { :message => 'my custom message' }
+ Person.validates :karma, email: { message: 'my custom message' }
person = Person.new
person.valid?
assert_equal ['my custom message'], person.errors[:karma]
end
def test_validates_with_unknown_validator
- assert_raise(ArgumentError) { Person.validates :karma, :unknown => true }
+ assert_raise(ArgumentError) { Person.validates :karma, unknown: true }
end
def test_validates_with_included_validator
- PersonWithValidator.validates :title, :presence => true
+ PersonWithValidator.validates :title, presence: true
person = PersonWithValidator.new
person.valid?
assert_equal ['Local validator'], person.errors[:title]
end
def test_validates_with_included_validator_and_options
- PersonWithValidator.validates :title, :presence => { :custom => ' please' }
+ PersonWithValidator.validates :title, presence: { custom: ' please' }
person = PersonWithValidator.new
person.valid?
assert_equal ['Local validator please'], person.errors[:title]
@@ -141,7 +140,7 @@ class ValidatesTest < ActiveModel::TestCase
def test_validates_with_included_validator_and_wildcard_shortcut
# Shortcut for PersonWithValidator.validates :title, like: { with: "Mr." }
- PersonWithValidator.validates :title, :like => "Mr."
+ PersonWithValidator.validates :title, like: "Mr."
person = PersonWithValidator.new
person.title = "Ms. Pacman"
person.valid?
@@ -149,7 +148,7 @@ class ValidatesTest < ActiveModel::TestCase
end
def test_defining_extra_default_keys_for_validates
- Topic.validates :title, :confirmation => true, :message => 'Y U NO CONFIRM'
+ Topic.validates :title, confirmation: true, message: 'Y U NO CONFIRM'
topic = Topic.new
topic.title = "What's happening"
topic.title_confirmation = "Not this"
diff --git a/activemodel/test/cases/validations/validations_context_test.rb b/activemodel/test/cases/validations/validations_context_test.rb
index 15a49e38dd..005bf118c6 100644
--- a/activemodel/test/cases/validations/validations_context_test.rb
+++ b/activemodel/test/cases/validations/validations_context_test.rb
@@ -4,10 +4,8 @@ require 'cases/helper'
require 'models/topic'
class ValidationsContextTest < ActiveModel::TestCase
-
def teardown
- Topic.reset_callbacks(:validate)
- Topic._validators.clear
+ Topic.clear_validators!
end
ERROR_MESSAGE = "Validation error from validator"
@@ -18,22 +16,35 @@ class ValidationsContextTest < ActiveModel::TestCase
end
end
- test "with a class that adds errors on update and validating a new model with no arguments" do
- Topic.validates_with(ValidatorThatAddsErrors, :on => :create)
+ test "with a class that adds errors on create and validating a new model with no arguments" do
+ Topic.validates_with(ValidatorThatAddsErrors, on: :create)
topic = Topic.new
- assert topic.valid?, "Validation doesn't run on create if 'on' is set to update"
+ assert topic.valid?, "Validation doesn't run on valid? if 'on' is set to create"
end
test "with a class that adds errors on update and validating a new model" do
- Topic.validates_with(ValidatorThatAddsErrors, :on => :update)
+ Topic.validates_with(ValidatorThatAddsErrors, on: :update)
topic = Topic.new
assert topic.valid?(:create), "Validation doesn't run on create if 'on' is set to update"
end
test "with a class that adds errors on create and validating a new model" do
- Topic.validates_with(ValidatorThatAddsErrors, :on => :create)
+ Topic.validates_with(ValidatorThatAddsErrors, on: :create)
topic = Topic.new
assert topic.invalid?(:create), "Validation does run on create if 'on' is set to create"
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
-end \ No newline at end of file
+
+ test "with a class that adds errors on multiple contexts and validating a new model" do
+ Topic.validates_with(ValidatorThatAddsErrors, on: [:context1, :context2])
+
+ topic = Topic.new
+ assert topic.valid?, "Validation ran with no context given when 'on' is set to context1 and context2"
+
+ assert topic.invalid?(:context1), "Validation did not run on context1 when 'on' is set to context1 and context2"
+ assert topic.errors[:base].include?(ERROR_MESSAGE)
+
+ assert topic.invalid?(:context2), "Validation did not run on context2 when 'on' is set to context1 and context2"
+ assert topic.errors[:base].include?(ERROR_MESSAGE)
+ end
+end
diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb
index 457f553661..736c2deea8 100644
--- a/activemodel/test/cases/validations/with_validation_test.rb
+++ b/activemodel/test/cases/validations/with_validation_test.rb
@@ -6,8 +6,7 @@ require 'models/topic'
class ValidatesWithTest < ActiveModel::TestCase
def teardown
- Topic.reset_callbacks(:validate)
- Topic._validators.clear
+ Topic.clear_validators!
end
ERROR_MESSAGE = "Validation error from validator"
@@ -50,7 +49,7 @@ class ValidatesWithTest < ActiveModel::TestCase
end
end
- test "vaidation with class that adds errors" do
+ test "validation with class that adds errors" do
Topic.validates_with(ValidatorThatAddsErrors)
topic = Topic.new
assert topic.invalid?, "A class that adds errors causes the record to be invalid"
@@ -72,26 +71,26 @@ class ValidatesWithTest < ActiveModel::TestCase
end
test "with if statements that return false" do
- Topic.validates_with(ValidatorThatAddsErrors, :if => "1 == 2")
+ Topic.validates_with(ValidatorThatAddsErrors, if: "1 == 2")
topic = Topic.new
assert topic.valid?
end
test "with if statements that return true" do
- Topic.validates_with(ValidatorThatAddsErrors, :if => "1 == 1")
+ Topic.validates_with(ValidatorThatAddsErrors, if: "1 == 1")
topic = Topic.new
assert topic.invalid?
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
test "with unless statements that return true" do
- Topic.validates_with(ValidatorThatAddsErrors, :unless => "1 == 1")
+ Topic.validates_with(ValidatorThatAddsErrors, unless: "1 == 1")
topic = Topic.new
assert topic.valid?
end
test "with unless statements that returns false" do
- Topic.validates_with(ValidatorThatAddsErrors, :unless => "1 == 2")
+ Topic.validates_with(ValidatorThatAddsErrors, unless: "1 == 2")
topic = Topic.new
assert topic.invalid?
assert topic.errors[:base].include?(ERROR_MESSAGE)
@@ -100,45 +99,23 @@ class ValidatesWithTest < ActiveModel::TestCase
test "passes all configuration options to the validator class" do
topic = Topic.new
validator = mock()
- validator.expects(:new).with(:foo => :bar, :if => "1 == 1").returns(validator)
+ validator.expects(:new).with(foo: :bar, if: "1 == 1", class: Topic).returns(validator)
validator.expects(:validate).with(topic)
- Topic.validates_with(validator, :if => "1 == 1", :foo => :bar)
- assert topic.valid?
- end
-
- test "calls setup method of validator passing in self when validator has setup method" do
- topic = Topic.new
- validator = stub_everything
- validator.stubs(:new).returns(validator)
- validator.stubs(:validate)
- validator.stubs(:respond_to?).with(:setup).returns(true)
- validator.expects(:setup).with(Topic).once
- Topic.validates_with(validator)
- assert topic.valid?
- end
-
- test "doesn't call setup method of validator when validator has no setup method" do
- topic = Topic.new
- validator = stub_everything
- validator.stubs(:new).returns(validator)
- validator.stubs(:validate)
- validator.stubs(:respond_to?).with(:setup).returns(false)
- validator.expects(:setup).with(Topic).never
- Topic.validates_with(validator)
+ Topic.validates_with(validator, if: "1 == 1", foo: :bar)
assert topic.valid?
end
test "validates_with with options" do
- Topic.validates_with(ValidatorThatValidatesOptions, :field => :first_name)
+ Topic.validates_with(ValidatorThatValidatesOptions, field: :first_name)
topic = Topic.new
assert topic.invalid?
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"
+ Topic.validates_with(ValidatorPerEachAttribute, attributes: [:title, :content])
+ topic = Topic.new title: "Title", content: "Content"
assert topic.invalid?
assert_equal ["Value is Title"], topic.errors[:title]
assert_equal ["Value is Content"], topic.errors[:content]
@@ -146,7 +123,7 @@ class ValidatesWithTest < ActiveModel::TestCase
test "each validator checks validity" do
assert_raise RuntimeError do
- Topic.validates_with(ValidatorCheckValidity, :attributes => [:title])
+ Topic.validates_with(ValidatorCheckValidity, attributes: [:title])
end
end
@@ -157,25 +134,25 @@ class ValidatesWithTest < ActiveModel::TestCase
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 => ""
+ Topic.validates_with(ValidatorPerEachAttribute, attributes: [:title, :content], allow_nil: true)
+ topic = Topic.new content: ""
assert topic.invalid?
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 => ""
+ 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
test "validates_with can validate with an instance method" do
- Topic.validates :title, :with => :my_validation
+ Topic.validates :title, with: :my_validation
- topic = Topic.new :title => "foo"
+ topic = Topic.new title: "foo"
assert topic.valid?
assert topic.errors[:title].empty?
@@ -185,9 +162,9 @@ class ValidatesWithTest < ActiveModel::TestCase
end
test "optionally pass in the attribute being validated when validating with an instance method" do
- Topic.validates :title, :content, :with => :my_validation_with_arg
+ Topic.validates :title, :content, with: :my_validation_with_arg
- topic = Topic.new :title => "foo"
+ topic = Topic.new title: "foo"
assert !topic.valid?
assert topic.errors[:title].empty?
assert_equal ['is missing'], topic.errors[:content]
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index a9d32808da..2b932683ea 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -4,33 +4,25 @@ require 'cases/helper'
require 'models/topic'
require 'models/reply'
require 'models/custom_reader'
-require 'models/automobile'
require 'active_support/json'
require 'active_support/xml_mini'
class ValidationsTest < ActiveModel::TestCase
-
class CustomStrictValidationException < StandardError; end
- def setup
- Topic._validators.clear
- end
-
- # 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
def teardown
- Topic.reset_callbacks(:validate)
+ Topic.clear_validators!
end
def test_single_field_validation
r = Reply.new
r.title = "There's no content!"
- assert r.invalid?, "A reply without content shouldn't be saveable"
+ assert r.invalid?, "A reply without content should be invalid"
assert r.after_validation_performed, "after_validation callback should be called"
r.content = "Messa content!"
- assert r.valid?, "A reply with content should be saveable"
+ assert r.valid?, "A reply with content should be valid"
assert r.after_validation_performed, "after_validation callback should be called"
end
@@ -146,6 +138,8 @@ class ValidationsTest < ActiveModel::TestCase
assert_equal 4, hits
assert_equal %w(gotcha gotcha), t.errors[:title]
assert_equal %w(gotcha gotcha), t.errors[:content]
+ ensure
+ CustomReader.clear_validators!
end
def test_validate_block
@@ -166,12 +160,56 @@ class ValidationsTest < ActiveModel::TestCase
def test_invalid_validator
Topic.validate :i_dont_exist
- assert_raise(NameError) do
+ assert_raises(NoMethodError) do
t = Topic.new
t.valid?
end
end
+ def test_invalid_options_to_validate
+ error = assert_raises(ArgumentError) do
+ # A common mistake -- we meant to call 'validates'
+ Topic.validate :title, presence: true
+ end
+ message = 'Unknown key: :presence. Valid keys are: :on, :if, :unless, :prepend. Perhaps you meant to call `validates` instead of `validate`?'
+ assert_equal message, error.message
+ end
+
+ def test_callback_options_to_validate
+ klass = Class.new(Topic) do
+ attr_reader :call_sequence
+
+ def initialize(*)
+ super
+ @call_sequence = []
+ end
+
+ private
+ def validator_a
+ @call_sequence << :a
+ end
+
+ def validator_b
+ @call_sequence << :b
+ end
+
+ def validator_c
+ @call_sequence << :c
+ end
+ end
+
+ assert_nothing_raised do
+ klass.validate :validator_a, if: ->{ true }
+ klass.validate :validator_b, prepend: true
+ klass.validate :validator_c, unless: ->{ true }
+ end
+
+ t = klass.new
+
+ assert_predicate t, :valid?
+ assert_equal [:b, :a], t.call_sequence
+ end
+
def test_errors_conversions
Topic.validates_presence_of %w(title content)
t = Topic.new
@@ -190,16 +228,16 @@ class ValidationsTest < ActiveModel::TestCase
def test_validation_order
Topic.validates_presence_of :title
- Topic.validates_length_of :title, :minimum => 2
+ Topic.validates_length_of :title, minimum: 2
t = Topic.new("title" => "")
assert t.invalid?
assert_equal "can't be blank", t.errors["title"].first
Topic.validates_presence_of :title, :author_name
Topic.validate {errors.add('author_email_address', 'will never be valid')}
- Topic.validates_length_of :title, :content, :minimum => 2
+ Topic.validates_length_of :title, :content, minimum: 2
- t = Topic.new :title => ''
+ t = Topic.new title: ''
assert t.invalid?
assert_equal :title, key = t.errors.keys[0]
@@ -213,10 +251,10 @@ class ValidationsTest < ActiveModel::TestCase
assert_equal 'is too short (minimum is 2 characters)', t.errors[key][0]
end
- def test_validaton_with_if_and_on
- Topic.validates_presence_of :title, :if => Proc.new{|x| x.author_name = "bad"; true }, :on => :update
+ def test_validation_with_if_and_on
+ Topic.validates_presence_of :title, if: Proc.new{|x| x.author_name = "bad"; true }, on: :update
- t = Topic.new(:title => "")
+ t = Topic.new(title: "")
# If block should not fire
assert t.valid?
@@ -239,7 +277,7 @@ class ValidationsTest < ActiveModel::TestCase
end
def test_validation_with_message_as_proc
- Topic.validates_presence_of(:title, :message => proc { "no blanks here".upcase })
+ Topic.validates_presence_of(:title, message: proc { "no blanks here".upcase })
t = Topic.new
assert t.invalid?
@@ -248,7 +286,7 @@ class ValidationsTest < ActiveModel::TestCase
def test_list_of_validators_for_model
Topic.validates_presence_of :title
- Topic.validates_length_of :title, :minimum => 2
+ Topic.validates_length_of :title, minimum: 2
assert_equal 2, Topic.validators.count
assert_equal [:presence, :length], Topic.validators.map(&:kind)
@@ -256,7 +294,7 @@ class ValidationsTest < ActiveModel::TestCase
def test_list_of_validators_on_an_attribute
Topic.validates_presence_of :title, :content
- Topic.validates_length_of :title, :minimum => 2
+ Topic.validates_length_of :title, minimum: 2
assert_equal 2, Topic.validators_on(:title).count
assert_equal [:presence, :length], Topic.validators_on(:title).map(&:kind)
@@ -265,13 +303,13 @@ class ValidationsTest < ActiveModel::TestCase
end
def test_accessing_instance_of_validator_on_an_attribute
- Topic.validates_length_of :title, :minimum => 10
+ Topic.validates_length_of :title, minimum: 10
assert_equal 10, Topic.validators_on(:title).first.options[:minimum]
end
def test_list_of_validators_on_multiple_attributes
- Topic.validates :title, :length => { :minimum => 10 }
- Topic.validates :author_name, :presence => true, :format => /a/
+ Topic.validates :title, length: { minimum: 10 }
+ Topic.validates :author_name, presence: true, format: /a/
validators = Topic.validators_on(:title, :author_name)
@@ -279,73 +317,88 @@ class ValidationsTest < ActiveModel::TestCase
ActiveModel::Validations::FormatValidator,
ActiveModel::Validations::LengthValidator,
ActiveModel::Validations::PresenceValidator
- ], validators.map { |v| v.class }.sort_by { |c| c.to_s }
+ ], validators.map(&:class).sort_by(&:to_s)
end
def test_list_of_validators_will_be_empty_when_empty
- Topic.validates :title, :length => { :minimum => 10 }
+ Topic.validates :title, length: { minimum: 10 }
assert_equal [], Topic.validators_on(:author_name)
end
def test_validations_on_the_instance_level
- auto = Automobile.new
+ Topic.validates :title, :author_name, presence: true
+ Topic.validates :content, length: { minimum: 10 }
- assert auto.invalid?
- assert_equal 2, auto.errors.size
+ topic = Topic.new
+ assert topic.invalid?
+ assert_equal 3, topic.errors.size
+
+ topic.title = 'Some Title'
+ topic.author_name = 'Some Author'
+ topic.content = 'Some Content Whose Length is more than 10.'
+ assert topic.valid?
+ end
+
+ def test_validate
+ Topic.validate do
+ validates_presence_of :title, :author_name
+ validates_length_of :content, minimum: 10
+ end
- auto.make = 'Toyota'
- auto.model = 'Corolla'
+ topic = Topic.new
+ assert_empty topic.errors
- assert auto.valid?
+ topic.validate
+ assert_not_empty topic.errors
end
def test_strict_validation_in_validates
- Topic.validates :title, :strict => true, :presence => true
+ Topic.validates :title, strict: true, presence: true
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
def test_strict_validation_not_fails
- Topic.validates :title, :strict => true, :presence => true
- assert Topic.new(:title => "hello").valid?
+ Topic.validates :title, strict: true, presence: true
+ assert Topic.new(title: "hello").valid?
end
def test_strict_validation_particular_validator
- Topic.validates :title, :presence => { :strict => true }
+ Topic.validates :title, presence: { strict: true }
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
def test_strict_validation_in_custom_validator_helper
- Topic.validates_presence_of :title, :strict => true
+ Topic.validates_presence_of :title, strict: true
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
def test_strict_validation_custom_exception
- Topic.validates_presence_of :title, :strict => CustomStrictValidationException
+ Topic.validates_presence_of :title, strict: CustomStrictValidationException
assert_raises CustomStrictValidationException do
Topic.new.valid?
end
end
def test_validates_with_bang
- Topic.validates! :title, :presence => true
+ Topic.validates! :title, presence: true
assert_raises ActiveModel::StrictValidationFailed do
Topic.new.valid?
end
end
def test_validates_with_false_hash_value
- Topic.validates :title, :presence => false
+ Topic.validates :title, presence: false
assert Topic.new.valid?
end
def test_strict_validation_error_message
- Topic.validates :title, :strict => true, :presence => true
+ Topic.validates :title, strict: true, presence: true
exception = assert_raises(ActiveModel::StrictValidationFailed) do
Topic.new.valid?
@@ -354,14 +407,14 @@ class ValidationsTest < ActiveModel::TestCase
end
def test_does_not_modify_options_argument
- options = { :presence => true }
+ options = { presence: true }
Topic.validates :title, options
- assert_equal({ :presence => true }, options)
+ assert_equal({ presence: true }, options)
end
def test_dup_validity_is_independent
Topic.validates_presence_of :title
- topic = Topic.new("title" => "Litterature")
+ topic = Topic.new("title" => "Literature")
topic.valid?
duped = topic.dup
diff --git a/activemodel/test/models/administrator.rb b/activemodel/test/models/administrator.rb
deleted file mode 100644
index 2f3aff290c..0000000000
--- a/activemodel/test/models/administrator.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class Administrator
- extend ActiveModel::Callbacks
- include ActiveModel::Validations
- include ActiveModel::SecurePassword
-
- define_model_callbacks :create
-
- attr_accessor :name, :password_digest
-
- has_secure_password
-end
diff --git a/activemodel/test/models/automobile.rb b/activemodel/test/models/automobile.rb
deleted file mode 100644
index 021ea61c80..0000000000
--- a/activemodel/test/models/automobile.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-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 \ No newline at end of file
diff --git a/activemodel/test/models/contact.rb b/activemodel/test/models/contact.rb
index 7bfc542afb..bcfd267a34 100644
--- a/activemodel/test/models/contact.rb
+++ b/activemodel/test/models/contact.rb
@@ -1,15 +1,20 @@
class Contact
extend ActiveModel::Naming
include ActiveModel::Conversion
+ include ActiveModel::Validations
+
+ include ActiveModel::Serializers::JSON
+ include ActiveModel::Serializers::Xml
attr_accessor :id, :name, :age, :created_at, :awesome, :preferences
+ attr_accessor :address, :friends, :contact
def social
%w(twitter github)
end
def network
- {:git => :github}
+ { git: :github }
end
def initialize(options = {})
@@ -23,4 +28,14 @@ class Contact
def persisted?
id
end
+
+ def attributes=(hash)
+ hash.each do |k, v|
+ instance_variable_set("@#{k}", v)
+ end
+ end
+
+ def attributes
+ instance_values.except("address", "friends", "contact")
+ end
end
diff --git a/activemodel/test/models/oauthed_user.rb b/activemodel/test/models/oauthed_user.rb
deleted file mode 100644
index 9750bc19d4..0000000000
--- a/activemodel/test/models/oauthed_user.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class OauthedUser
- extend ActiveModel::Callbacks
- include ActiveModel::Validations
- include ActiveModel::SecurePassword
-
- define_model_callbacks :create
-
- has_secure_password(validations: false)
-
- attr_accessor :password_digest, :password_salt
-end
diff --git a/activemodel/test/models/reply.rb b/activemodel/test/models/reply.rb
index ec1efeac19..b77910e671 100644
--- a/activemodel/test/models/reply.rb
+++ b/activemodel/test/models/reply.rb
@@ -2,11 +2,11 @@ require 'models/topic'
class Reply < Topic
validate :errors_on_empty_content
- validate :title_is_wrong_create, :on => :create
+ validate :title_is_wrong_create, on: :create
validate :check_empty_title
- validate :check_content_mismatch, :on => :create
- validate :check_wrong_update, :on => :update
+ validate :check_content_mismatch, on: :create
+ validate :check_wrong_update, on: :update
def check_empty_title
errors[:title] << "is Empty" unless title && title.size > 0
diff --git a/activemodel/test/models/topic.rb b/activemodel/test/models/topic.rb
index c9af78f595..1411a093e9 100644
--- a/activemodel/test/models/topic.rb
+++ b/activemodel/test/models/topic.rb
@@ -6,7 +6,7 @@ class Topic
super | [ :message ]
end
- attr_accessor :title, :author_name, :content, :approved
+ attr_accessor :title, :author_name, :content, :approved, :created_at
attr_accessor :after_validation_performed
after_validation :perform_after_validation
diff --git a/activemodel/test/models/user.rb b/activemodel/test/models/user.rb
index 4b11df12bf..1ec6001c48 100644
--- a/activemodel/test/models/user.rb
+++ b/activemodel/test/models/user.rb
@@ -1,11 +1,10 @@
class User
extend ActiveModel::Callbacks
- include ActiveModel::Validations
include ActiveModel::SecurePassword
define_model_callbacks :create
has_secure_password
- attr_accessor :password_digest, :password_salt
+ attr_accessor :password_digest
end
diff --git a/activemodel/test/models/visitor.rb b/activemodel/test/models/visitor.rb
index 4d7f4be097..22ad1a3c3d 100644
--- a/activemodel/test/models/visitor.rb
+++ b/activemodel/test/models/visitor.rb
@@ -1,6 +1,5 @@
class Visitor
extend ActiveModel::Callbacks
- include ActiveModel::Validations
include ActiveModel::SecurePassword
define_model_callbacks :create