aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2009-11-02 23:46:00 -0200
committerJosé Valim <jose.valim@gmail.com>2009-11-02 23:46:00 -0200
commitd002826e54415a340e55fdbf363d005faebf8fc5 (patch)
tree431146dfb0b15c4507fd49bb7d6f18bd40ccf839 /activerecord
parent9ba83cce0318fa5051764f4a16c286adf30169e2 (diff)
parent14370e1aab6ddfb5b86cf50bd7e5abcebae0684c (diff)
downloadrails-d002826e54415a340e55fdbf363d005faebf8fc5.tar.gz
rails-d002826e54415a340e55fdbf363d005faebf8fc5.tar.bz2
rails-d002826e54415a340e55fdbf363d005faebf8fc5.zip
Merge branch 'master' of git://github.com/rails/rails
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/Gemfile10
-rw-r--r--activerecord/Rakefile2
-rw-r--r--activerecord/activerecord.gemspec2
-rw-r--r--activerecord/lib/active_record.rb30
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb20
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb49
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb48
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb9
-rw-r--r--activerecord/lib/active_record/attributes.rb37
-rw-r--r--activerecord/lib/active_record/attributes/aliasing.rb42
-rw-r--r--activerecord/lib/active_record/attributes/store.rb15
-rw-r--r--activerecord/lib/active_record/attributes/typecasting.rb117
-rwxr-xr-xactiverecord/lib/active_record/base.rb106
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb14
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb6
-rw-r--r--activerecord/lib/active_record/notifications.rb5
-rw-r--r--activerecord/lib/active_record/types.rb38
-rw-r--r--activerecord/lib/active_record/types/number.rb30
-rw-r--r--activerecord/lib/active_record/types/object.rb37
-rw-r--r--activerecord/lib/active_record/types/serialize.rb33
-rw-r--r--activerecord/lib/active_record/types/time_with_zone.rb20
-rw-r--r--activerecord/lib/active_record/types/unknown.rb37
-rw-r--r--activerecord/lib/active_record/validations.rb89
-rw-r--r--activerecord/lib/active_record/validator.rb68
-rw-r--r--activerecord/test/cases/attributes/aliasing_test.rb20
-rw-r--r--activerecord/test/cases/attributes/typecasting_test.rb120
-rwxr-xr-xactiverecord/test/cases/base_test.rb10
-rw-r--r--activerecord/test/cases/helper.rb9
-rw-r--r--activerecord/test/cases/i18n_test.rb6
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb21
-rw-r--r--activerecord/test/cases/reflection_test.rb4
-rw-r--r--activerecord/test/cases/types/number_test.rb30
-rw-r--r--activerecord/test/cases/types/object_test.rb24
-rw-r--r--activerecord/test/cases/types/serialize_test.rb20
-rw-r--r--activerecord/test/cases/types/time_with_zone_test.rb42
-rw-r--r--activerecord/test/cases/types/unknown_test.rb29
-rw-r--r--activerecord/test/cases/types_test.rb32
-rw-r--r--activerecord/test/cases/validations/i18n_generate_message_validation_test.rb129
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb732
-rw-r--r--activerecord/test/cases/validations_test.rb31
42 files changed, 847 insertions, 1303 deletions
diff --git a/activerecord/Gemfile b/activerecord/Gemfile
deleted file mode 100644
index 9cf37b0af0..0000000000
--- a/activerecord/Gemfile
+++ /dev/null
@@ -1,10 +0,0 @@
-Gem.sources.each { |uri| source uri }
-sibling = "#{File.dirname(__FILE__)}/.."
-
-gem "activesupport", "3.0.pre", :vendored_at => "#{sibling}/activesupport"
-gem "activemodel", "3.0.pre", :vendored_at => "#{sibling}/activemodel"
-gem "arel", :git => "git://github.com/rails/arel.git", :branch => 'master'
-
-only :test do
- gem "mocha"
-end
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 1d8062e042..f7585f789b 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -195,8 +195,6 @@ spec = eval(File.read('activerecord.gemspec'))
Rake::GemPackageTask.new(spec) do |p|
p.gem_spec = spec
- p.need_tar = true
- p.need_zip = true
end
task :lines do
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index bae027bd2a..204cddde47 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', '= 3.0.pre')
s.add_dependency('activemodel', '= 3.0.pre')
- s.add_dependency('arel', '~> 0.1.0')
+ s.add_dependency('arel', '~> 0.1.1')
s.require_path = 'lib'
s.autorequire = 'active_record'
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 2d66fa9fcb..8195e78826 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -21,16 +21,11 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-bundled = "#{File.dirname(__FILE__)}/../vendor/gems/environment"
-if File.exist?("#{bundled}.rb")
- require bundled
-else
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- $:.unshift(activesupport_path) if File.directory?(activesupport_path)
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift(activesupport_path) if File.directory?(activesupport_path)
- activemodel_path = "#{File.dirname(__FILE__)}/../../activemodel/lib"
- $:.unshift(activemodel_path) if File.directory?(activemodel_path)
-end
+activemodel_path = "#{File.dirname(__FILE__)}/../../activemodel/lib"
+$:.unshift(activemodel_path) if File.directory?(activemodel_path)
require 'active_support'
require 'active_model'
@@ -51,6 +46,7 @@ module ActiveRecord
autoload :AssociationPreload, 'active_record/association_preload'
autoload :Associations, 'active_record/associations'
autoload :AttributeMethods, 'active_record/attribute_methods'
+ autoload :Attributes, 'active_record/attributes'
autoload :AutosaveAssociation, 'active_record/autosave_association'
autoload :Relation, 'active_record/relation'
autoload :Base, 'active_record/base'
@@ -74,7 +70,7 @@ module ActiveRecord
autoload :TestCase, 'active_record/test_case'
autoload :Timestamp, 'active_record/timestamp'
autoload :Transactions, 'active_record/transactions'
- autoload :Validator, 'active_record/validator'
+ autoload :Types, 'active_record/types'
autoload :Validations, 'active_record/validations'
module AttributeMethods
@@ -87,6 +83,20 @@ module ActiveRecord
autoload :Write, 'active_record/attribute_methods/write'
end
+ module Attributes
+ autoload :Aliasing, 'active_record/attributes/aliasing'
+ autoload :Store, 'active_record/attributes/store'
+ autoload :Typecasting, 'active_record/attributes/typecasting'
+ end
+
+ module Type
+ autoload :Number, 'active_record/types/number'
+ autoload :Object, 'active_record/types/object'
+ autoload :Serialize, 'active_record/types/serialize'
+ autoload :TimeWithZone, 'active_record/types/time_with_zone'
+ autoload :Unknown, 'active_record/types/unknown'
+ end
+
module Locking
autoload :Optimistic, 'active_record/locking/optimistic'
autoload :Pessimistic, 'active_record/locking/pessimistic'
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index a4e144f233..74921241f7 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -8,25 +8,18 @@ module ActiveRecord
end
def read_attribute_before_type_cast(attr_name)
- @attributes[attr_name]
+ _attributes.without_typecast[attr_name]
end
# Returns a hash of attributes before typecasting and deserialization.
def attributes_before_type_cast
- self.attribute_names.inject({}) do |attrs, name|
- attrs[name] = read_attribute_before_type_cast(name)
- attrs
- end
+ _attributes.without_typecast
end
private
# Handle *_before_type_cast for method_missing.
def attribute_before_type_cast(attribute_name)
- if attribute_name == 'id'
- read_attribute_before_type_cast(self.class.primary_key)
- else
- read_attribute_before_type_cast(attribute_name)
- end
+ read_attribute_before_type_cast(attribute_name)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index a949d80120..0154ee35f8 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -8,23 +8,7 @@ module ActiveRecord
end
def query_attribute(attr_name)
- unless value = read_attribute(attr_name)
- false
- else
- column = self.class.columns_hash[attr_name]
- if column.nil?
- if Numeric === value || value !~ /[^0-9]/
- !value.to_i.zero?
- else
- return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
- !value.blank?
- end
- elsif column.number?
- !value.zero?
- else
- !value.blank?
- end
- end
+ _attributes.has?(attr_name)
end
private
@@ -35,3 +19,5 @@ module ActiveRecord
end
end
end
+
+
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 3da3d9d8cc..97caec7744 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -37,11 +37,7 @@ module ActiveRecord
protected
def define_method_attribute(attr_name)
- if self.serialized_attributes[attr_name]
- define_read_method_for_serialized_attribute(attr_name)
- else
- define_read_method(attr_name.to_sym, attr_name, columns_hash[attr_name])
- end
+ define_read_method(attr_name.to_sym, attr_name, columns_hash[attr_name])
if attr_name == primary_key && attr_name != "id"
define_read_method(:id, attr_name, columns_hash[attr_name])
@@ -49,18 +45,12 @@ module ActiveRecord
end
private
- # Define read method for serialized attribute.
- def define_read_method_for_serialized_attribute(attr_name)
- generated_attribute_methods.module_eval("def #{attr_name}; unserialize_attribute('#{attr_name}'); end", __FILE__, __LINE__)
- end
# Define an attribute reader method. Cope with nil column.
def define_read_method(symbol, attr_name, column)
- cast_code = column.type_cast_code('v') if column
- access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
-
+ access_code = "_attributes['#{attr_name}']"
unless attr_name.to_s == self.primary_key.to_s
- access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
+ access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless _attributes.key?('#{attr_name}'); ")
end
if cache_attribute?(attr_name)
@@ -73,38 +63,7 @@ module ActiveRecord
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
- attr_name = attr_name.to_s
- attr_name = self.class.primary_key if attr_name == 'id'
- if !(value = @attributes[attr_name]).nil?
- if column = column_for_attribute(attr_name)
- if unserializable_attribute?(attr_name, column)
- unserialize_attribute(attr_name)
- else
- column.type_cast(value)
- end
- else
- value
- end
- else
- nil
- end
- end
-
- # Returns true if the attribute is of a text column and marked for serialization.
- def unserializable_attribute?(attr_name, column)
- column.text? && self.class.serialized_attributes[attr_name]
- end
-
- # Returns the unserialized object of the attribute.
- def unserialize_attribute(attr_name)
- unserialized_object = object_from_yaml(@attributes[attr_name])
-
- if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
- @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
- else
- raise SerializationTypeMismatch,
- "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
- end
+ _attributes[attr_name]
end
private
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index a8e3e28a7a..4ac0c7f608 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -12,48 +12,20 @@ module ActiveRecord
end
module ClassMethods
+
+ def cache_attribute?(attr_name)
+ time_zone_aware?(attr_name) || super
+ end
+
protected
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
- # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
- def define_method_attribute(attr_name)
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body = <<-EOV
- def #{attr_name}(reload = false)
- cached = @attributes_cache['#{attr_name}']
- return cached if cached && !reload
- time = read_attribute('#{attr_name}')
- @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
- end
- EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
- else
- super
- end
- end
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
- # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
- def define_method_attribute=(attr_name)
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body = <<-EOV
- def #{attr_name}=(time)
- unless time.acts_like?(:time)
- time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
- end
- time = time.in_time_zone rescue nil if time
- write_attribute(:#{attr_name}, time)
- end
- EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
- else
- super
- end
+ def time_zone_aware?(attr_name)
+ column = columns_hash[attr_name]
+ time_zone_aware_attributes &&
+ !skip_time_zone_conversion_for_attributes.include?(attr_name.to_sym) &&
+ [:datetime, :timestamp].include?(column.type)
end
- private
- def create_time_zone_conversion_attribute?(name, column)
- time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
- end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index e31acac050..37eadbe0a9 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -17,14 +17,9 @@ module ActiveRecord
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
# columns are turned into +nil+.
def write_attribute(attr_name, value)
- attr_name = attr_name.to_s
- attr_name = self.class.primary_key if attr_name == 'id'
+ attr_name = _attributes.unalias(attr_name)
@attributes_cache.delete(attr_name)
- if (column = column_for_attribute(attr_name)) && column.number?
- @attributes[attr_name] = convert_number_column_value(value)
- else
- @attributes[attr_name] = value
- end
+ _attributes[attr_name] = value
end
private
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
new file mode 100644
index 0000000000..e4d9e89821
--- /dev/null
+++ b/activerecord/lib/active_record/attributes.rb
@@ -0,0 +1,37 @@
+module ActiveRecord
+ module Attributes
+
+ # Returns true if the given attribute is in the attributes hash
+ def has_attribute?(attr_name)
+ _attributes.key?(attr_name)
+ end
+
+ # Returns an array of names for the attributes available on this object sorted alphabetically.
+ def attribute_names
+ _attributes.keys.sort!
+ end
+
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
+ def attributes
+ attributes = _attributes.dup
+ attributes.typecast! unless _attributes.frozen?
+ attributes.to_h
+ end
+
+ protected
+
+ # Not to be confused with the public #attributes method, which returns a typecasted Hash.
+ def _attributes
+ @attributes
+ end
+
+ def initialize_attribute_store(merge_attributes = nil)
+ @attributes = ActiveRecord::Attributes::Store.new
+ @attributes.merge!(merge_attributes) if merge_attributes
+ @attributes.types.merge!(self.class.attribute_types)
+ @attributes.aliases.merge!('id' => self.class.primary_key) unless 'id' == self.class.primary_key
+ @attributes
+ end
+
+ end
+end
diff --git a/activerecord/lib/active_record/attributes/aliasing.rb b/activerecord/lib/active_record/attributes/aliasing.rb
new file mode 100644
index 0000000000..db77739d1f
--- /dev/null
+++ b/activerecord/lib/active_record/attributes/aliasing.rb
@@ -0,0 +1,42 @@
+module ActiveRecord
+ module Attributes
+ module Aliasing
+ # Allows access to keys using aliased names.
+ #
+ # Example:
+ # class Attributes < Hash
+ # include Aliasing
+ # end
+ #
+ # attributes = Attributes.new
+ # attributes.aliases['id'] = 'fancy_primary_key'
+ # attributes['fancy_primary_key'] = 2020
+ #
+ # attributes['id']
+ # => 2020
+ #
+ # Additionally, symbols are always aliases of strings:
+ # attributes[:fancy_primary_key]
+ # => 2020
+ #
+ def [](key)
+ super(unalias(key))
+ end
+
+ def []=(key, value)
+ super(unalias(key), value)
+ end
+
+ def aliases
+ @aliases ||= {}
+ end
+
+ def unalias(key)
+ key = key.to_s
+ aliases[key] || key
+ end
+
+ end
+ end
+end
+
diff --git a/activerecord/lib/active_record/attributes/store.rb b/activerecord/lib/active_record/attributes/store.rb
new file mode 100644
index 0000000000..61109f4acc
--- /dev/null
+++ b/activerecord/lib/active_record/attributes/store.rb
@@ -0,0 +1,15 @@
+module ActiveRecord
+ module Attributes
+ class Store < Hash
+ include ActiveRecord::Attributes::Typecasting
+ include ActiveRecord::Attributes::Aliasing
+
+ # Attributes not mapped to a column are handled using Type::Unknown,
+ # which enables boolean typecasting for unmapped keys.
+ def types
+ @types ||= Hash.new(Type::Unknown.new)
+ end
+
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attributes/typecasting.rb b/activerecord/lib/active_record/attributes/typecasting.rb
new file mode 100644
index 0000000000..56c32f9895
--- /dev/null
+++ b/activerecord/lib/active_record/attributes/typecasting.rb
@@ -0,0 +1,117 @@
+module ActiveRecord
+ module Attributes
+ module Typecasting
+ # Typecasts values during access based on their key mapping to a Type.
+ #
+ # Example:
+ # class Attributes < Hash
+ # include Typecasting
+ # end
+ #
+ # attributes = Attributes.new
+ # attributes.types['comments_count'] = Type::Integer
+ # attributes['comments_count'] = '5'
+ #
+ # attributes['comments_count']
+ # => 5
+ #
+ # To support keys not mapped to a typecaster, add a default to types.
+ # attributes.types.default = Type::Unknown
+ # attributes['age'] = '25'
+ # attributes['age']
+ # => '25'
+ #
+ # A valid type supports #cast, #precast, #boolean, and #appendable? methods.
+ #
+ def [](key)
+ value = super(key)
+ typecast_read(key, value)
+ end
+
+ def []=(key, value)
+ super(key, typecast_write(key, value))
+ end
+
+ def to_h
+ hash = {}
+ hash.merge!(self)
+ hash
+ end
+
+ def dup # :nodoc:
+ copy = super
+ copy.types = types.dup
+ copy
+ end
+
+ # Provides a duplicate with typecasting disabled.
+ #
+ # Example:
+ # attributes = Attributes.new
+ # attributes.types['comments_count'] = Type::Integer
+ # attributes['comments_count'] = '5'
+ #
+ # attributes.without_typecast['comments_count']
+ # => '5'
+ #
+ def without_typecast
+ dup.without_typecast!
+ end
+
+ def without_typecast!
+ types.clear
+ self
+ end
+
+ def typecast!
+ keys.each { |key| self[key] = self[key] }
+ self
+ end
+
+ # Check if key has a value that typecasts to true.
+ #
+ # attributes = Attributes.new
+ # attributes.types['comments_count'] = Type::Integer
+ #
+ # attributes['comments_count'] = 0
+ # attributes.has?('comments_count')
+ # => false
+ #
+ # attributes['comments_count'] = 1
+ # attributes.has?('comments_count')
+ # => true
+ #
+ def has?(key)
+ value = self[key]
+ boolean_typecast(key, value)
+ end
+
+ def types
+ @types ||= {}
+ end
+
+ protected
+
+ def types=(other_types)
+ @types = other_types
+ end
+
+ def boolean_typecast(key, value)
+ value ? types[key].boolean(value) : false
+ end
+
+ def typecast_read(key, value)
+ type = types[key]
+ value = type.cast(value)
+ self[key] = value if type.appendable? && !frozen?
+
+ value
+ end
+
+ def typecast_write(key, value)
+ types[key].precast(value)
+ end
+
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 283aa7ddfc..4e6090458a 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1,5 +1,7 @@
+require 'benchmark'
require 'yaml'
require 'set'
+require 'active_support/benchmarkable'
require 'active_support/dependencies'
require 'active_support/time'
require 'active_support/core_ext/class/attribute_accessors'
@@ -1384,7 +1386,8 @@ module ActiveRecord #:nodoc:
subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
end
- def self_and_descendants_from_active_record#nodoc:
+ # Set the lookup ancestors for ActiveModel.
+ def lookup_ancestors #:nodoc:
klass = self
classes = [klass]
while klass != klass.base_class
@@ -1398,32 +1401,9 @@ module ActiveRecord #:nodoc:
[self]
end
- # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
- # Person.human_attribute_name("first_name") # => "First name"
- # This used to be deprecated in favor of humanize, but is now preferred, because it automatically uses the I18n
- # module now.
- # Specify +options+ with additional translating options.
- def human_attribute_name(attribute_key_name, options = {})
- defaults = self_and_descendants_from_active_record.map do |klass|
- :"#{klass.name.underscore}.#{attribute_key_name}"
- end
- defaults << options[:default] if options[:default]
- defaults.flatten!
- defaults << attribute_key_name.to_s.humanize
- options[:count] ||= 1
- I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes]))
- end
-
- # Transform the modelname into a more humane format, using I18n.
- # By default, it will underscore then humanize the class name (BlogPost.human_name #=> "Blog post").
- # Default scope of the translation is activerecord.models
- # Specify +options+ with additional translating options.
- def human_name(options = {})
- defaults = self_and_descendants_from_active_record.map do |klass|
- :"#{klass.name.underscore}"
- end
- defaults << self.name.underscore.humanize
- I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options))
+ # Set the i18n scope to overwrite ActiveModel.
+ def i18n_scope #:nodoc:
+ :activerecord
end
# True if this isn't a concrete subclass needing a STI type condition.
@@ -1463,38 +1443,6 @@ module ActiveRecord #:nodoc:
connection.quote(object)
end
- # Log and benchmark multiple statements in a single block. Example:
- #
- # Project.benchmark("Creating project") do
- # project = Project.create("name" => "stuff")
- # project.create_manager("name" => "David")
- # project.milestones << Milestone.find(:all)
- # end
- #
- # The benchmark is only recorded if the current level of the logger is less than or equal to the <tt>log_level</tt>,
- # which makes it easy to include benchmarking statements in production software that will remain inexpensive because
- # the benchmark will only be conducted if the log level is low enough.
- #
- # The logging of the multiple statements is turned off unless <tt>use_silence</tt> is set to false.
- def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
- if logger && logger.level <= log_level
- result = nil
- ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
- logger.add(log_level, '%s (%.1fms)' % [title, ms])
- result
- else
- yield
- end
- end
-
- # Silences the logger for the duration of the block.
- def silence
- old_logger_level, logger.level = logger.level, Logger::ERROR if logger
- yield
- ensure
- logger.level = old_logger_level if logger
- end
-
# Overwrite the default class equality method to provide support for association proxies.
def ===(object)
object.is_a?(self)
@@ -1674,7 +1622,7 @@ module ActiveRecord #:nodoc:
def instantiate(record)
object = find_sti_class(record[inheritance_column]).allocate
- object.instance_variable_set(:'@attributes', record)
+ object.send(:initialize_attribute_store, record)
object.instance_variable_set(:'@attributes_cache', {})
object.send(:_run_find_callbacks)
@@ -2445,7 +2393,7 @@ module ActiveRecord #:nodoc:
# In both instances, valid attribute keys are determined by the column names of the associated table --
# hence you can't have attributes that aren't part of the table columns.
def initialize(attributes = nil)
- @attributes = attributes_from_column_definition
+ initialize_attribute_store(attributes_from_column_definition)
@attributes_cache = {}
@new_record = true
ensure_proper_type
@@ -2471,7 +2419,7 @@ module ActiveRecord #:nodoc:
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
cloned_attributes.delete(self.class.primary_key)
- @attributes = cloned_attributes
+ initialize_attribute_store(cloned_attributes)
clear_aggregation_cache
@attributes_cache = {}
@new_record = true
@@ -2697,7 +2645,7 @@ module ActiveRecord #:nodoc:
def reload(options = nil)
clear_aggregation_cache
clear_association_cache
- @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
+ _attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
@attributes_cache = {}
self
end
@@ -2794,16 +2742,6 @@ module ActiveRecord #:nodoc:
!value.blank?
end
- # Returns true if the given attribute is in the attributes hash
- def has_attribute?(attr_name)
- @attributes.has_key?(attr_name.to_s)
- end
-
- # Returns an array of names for the attributes available on this object sorted alphabetically.
- def attribute_names
- @attributes.keys.sort
- end
-
# Returns the column object for the named attribute.
def column_for_attribute(name)
self.class.columns_hash[name.to_s]
@@ -2927,18 +2865,6 @@ module ActiveRecord #:nodoc:
end
end
- def convert_number_column_value(value)
- if value == false
- 0
- elsif value == true
- 1
- elsif value.is_a?(String) && value.blank?
- nil
- else
- value
- end
- end
-
def remove_attributes_protected_from_mass_assignment(attributes)
safe_attributes =
if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
@@ -3057,7 +2983,7 @@ module ActiveRecord #:nodoc:
end
def instantiate_time_object(name, values)
- if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
+ if self.class.send(:time_zone_aware?, name)
Time.zone.local(*values)
else
Time.time_with_datetime_fallback(@@default_timezone, *values)
@@ -3144,15 +3070,13 @@ module ActiveRecord #:nodoc:
comma_pair_list(quote_columns(quoter, hash))
end
- def object_from_yaml(string)
- return string unless string.is_a?(String) && string =~ /^---/
- YAML::load(string) rescue string
- end
end
Base.class_eval do
extend ActiveModel::Naming
extend QueryCache::ClassMethods
+ extend ActiveSupport::Benchmarkable
+
include Validations
include Locking::Optimistic, Locking::Pessimistic
include AttributeMethods
@@ -3160,6 +3084,7 @@ module ActiveRecord #:nodoc:
include AttributeMethods::PrimaryKey
include AttributeMethods::TimeZoneConversion
include AttributeMethods::Dirty
+ include Attributes, Types
include Callbacks, ActiveModel::Observing, Timestamp
include Associations, AssociationPreload, NamedScope
include ActiveModel::Conversion
@@ -3169,6 +3094,7 @@ module ActiveRecord #:nodoc:
include AutosaveAssociation, NestedAttributes
include Aggregations, Transactions, Reflection, Batches, Calculations, Serialization
+
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 12253eac3f..377f2a44c5 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -211,9 +211,10 @@ module ActiveRecord
# calling +checkout+ on this pool.
def checkin(conn)
@connection_mutex.synchronize do
- conn.run_callbacks :checkin
- @checked_out.delete conn
- @queue.signal
+ conn.run_callbacks :checkin do
+ @checked_out.delete conn
+ @queue.signal
+ end
end
end
@@ -255,9 +256,10 @@ module ActiveRecord
end
def checkout_and_verify(c)
- c.verify!
- c.run_callbacks :checkout
- @checked_out << c
+ c.run_callbacks :checkout do
+ c.verify!
+ @checked_out << c
+ end
c
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 694e1e561c..8fae26b790 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -1,4 +1,3 @@
-require 'benchmark'
require 'date'
require 'bigdecimal'
require 'bigdecimal/util'
@@ -12,8 +11,6 @@ require 'active_record/connection_adapters/abstract/connection_pool'
require 'active_record/connection_adapters/abstract/connection_specification'
require 'active_record/connection_adapters/abstract/query_cache'
-require 'active_support/core_ext/benchmark'
-
module ActiveRecord
module ConnectionAdapters # :nodoc:
# ActiveRecord supports multiple database systems. AbstractAdapter and
@@ -33,6 +30,7 @@ module ActiveRecord
include Quoting, DatabaseStatements, SchemaStatements
include QueryCache
include ActiveSupport::Callbacks
+
define_callbacks :checkout, :checkin
@@row_even = true
@@ -193,6 +191,7 @@ module ActiveRecord
end
def log_info(sql, name, ms)
+ @runtime += ms
if @logger && @logger.debug?
name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
@@ -200,13 +199,8 @@ module ActiveRecord
end
protected
- def log(sql, name)
- event = ActiveSupport::Orchestra.instrument(:sql, :sql => sql, :name => name) do
- yield if block_given?
- end
- @runtime += event.duration
- log_info(sql, name, event.duration)
- event.result
+ def log(sql, name, &block)
+ ActiveSupport::Notifications.instrument(:sql, :sql => sql, :name => name, &block)
rescue Exception => e
# Log message and raise exception.
# Set last_verification to 0, so that connection gets verified
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 5a49fc2d2f..c9c2892ba4 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -11,11 +11,11 @@ module ActiveRecord
raise ArgumentError, "No database file specified. Missing argument: database"
end
- # Allow database path relative to RAILS_ROOT, but only if
+ # Allow database path relative to Rails.root, but only if
# the database path is not the special path that tells
# Sqlite to build a database only in memory.
- if Object.const_defined?(:RAILS_ROOT) && ':memory:' != config[:database]
- config[:database] = File.expand_path(config[:database], RAILS_ROOT)
+ if Object.const_defined?(:Rails) && ':memory:' != config[:database]
+ config[:database] = File.expand_path(config[:database], Rails.root)
end
end
end
diff --git a/activerecord/lib/active_record/notifications.rb b/activerecord/lib/active_record/notifications.rb
new file mode 100644
index 0000000000..a5ce7ac524
--- /dev/null
+++ b/activerecord/lib/active_record/notifications.rb
@@ -0,0 +1,5 @@
+require 'active_support/notifications'
+
+ActiveSupport::Notifications.subscribe("sql") do |event|
+ ActiveRecord::Base.connection.log_info(event.payload[:sql], event.payload[:name], event.duration)
+end
diff --git a/activerecord/lib/active_record/types.rb b/activerecord/lib/active_record/types.rb
new file mode 100644
index 0000000000..74f569352b
--- /dev/null
+++ b/activerecord/lib/active_record/types.rb
@@ -0,0 +1,38 @@
+module ActiveRecord
+ module Types
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+
+ def attribute_types
+ attribute_types = {}
+ columns.each do |column|
+ options = {}
+ options[:time_zone_aware] = time_zone_aware?(column.name)
+ options[:serialize] = serialized_attributes[column.name]
+
+ attribute_types[column.name] = to_type(column, options)
+ end
+ attribute_types
+ end
+
+ private
+
+ def to_type(column, options = {})
+ type_class = if options[:time_zone_aware]
+ Type::TimeWithZone
+ elsif options[:serialize]
+ Type::Serialize
+ elsif [ :integer, :float, :decimal ].include?(column.type)
+ Type::Number
+ else
+ Type::Object
+ end
+
+ type_class.new(column, options)
+ end
+
+ end
+
+ end
+end
diff --git a/activerecord/lib/active_record/types/number.rb b/activerecord/lib/active_record/types/number.rb
new file mode 100644
index 0000000000..cfbe877575
--- /dev/null
+++ b/activerecord/lib/active_record/types/number.rb
@@ -0,0 +1,30 @@
+module ActiveRecord
+ module Type
+ class Number < Object
+
+ def boolean(value)
+ value = cast(value)
+ !(value.nil? || value.zero?)
+ end
+
+ def precast(value)
+ convert_number_column_value(value)
+ end
+
+ private
+
+ def convert_number_column_value(value)
+ if value == false
+ 0
+ elsif value == true
+ 1
+ elsif value.is_a?(String) && value.blank?
+ nil
+ else
+ value
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/types/object.rb b/activerecord/lib/active_record/types/object.rb
new file mode 100644
index 0000000000..ec3f861abd
--- /dev/null
+++ b/activerecord/lib/active_record/types/object.rb
@@ -0,0 +1,37 @@
+module ActiveRecord
+ module Type
+ module Casting
+
+ def cast(value)
+ typecaster.type_cast(value)
+ end
+
+ def precast(value)
+ value
+ end
+
+ def boolean(value)
+ cast(value).present?
+ end
+
+ # Attributes::Typecasting stores appendable? types (e.g. serialized Arrays) when typecasting reads.
+ def appendable?
+ false
+ end
+
+ end
+
+ class Object
+ include Casting
+
+ attr_reader :name, :options
+ attr_reader :typecaster
+
+ def initialize(typecaster = nil, options = {})
+ @typecaster, @options = typecaster, options
+ end
+
+ end
+
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/types/serialize.rb b/activerecord/lib/active_record/types/serialize.rb
new file mode 100644
index 0000000000..7b6af1981f
--- /dev/null
+++ b/activerecord/lib/active_record/types/serialize.rb
@@ -0,0 +1,33 @@
+module ActiveRecord
+ module Type
+ class Serialize < Object
+
+ def cast(value)
+ unserialize(value)
+ end
+
+ def appendable?
+ true
+ end
+
+ protected
+
+ def unserialize(value)
+ unserialized_object = object_from_yaml(value)
+
+ if unserialized_object.is_a?(@options[:serialize]) || unserialized_object.nil?
+ unserialized_object
+ else
+ raise SerializationTypeMismatch,
+ "#{name} was supposed to be a #{@options[:serialize]}, but was a #{unserialized_object.class.to_s}"
+ end
+ end
+
+ def object_from_yaml(string)
+ return string unless string.is_a?(String) && string =~ /^---/
+ YAML::load(string) rescue string
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/types/time_with_zone.rb b/activerecord/lib/active_record/types/time_with_zone.rb
new file mode 100644
index 0000000000..3a8b9292f9
--- /dev/null
+++ b/activerecord/lib/active_record/types/time_with_zone.rb
@@ -0,0 +1,20 @@
+module ActiveRecord
+ module Type
+ class TimeWithZone < Object
+
+ def cast(time)
+ time = super(time)
+ time.acts_like?(:time) ? time.in_time_zone : time
+ end
+
+ def precast(time)
+ unless time.acts_like?(:time)
+ time = time.is_a?(String) ? ::Time.zone.parse(time) : time.to_time rescue time
+ end
+ time = time.in_time_zone rescue nil if time
+ super(time)
+ end
+
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/types/unknown.rb b/activerecord/lib/active_record/types/unknown.rb
new file mode 100644
index 0000000000..f832c7b304
--- /dev/null
+++ b/activerecord/lib/active_record/types/unknown.rb
@@ -0,0 +1,37 @@
+module ActiveRecord
+ module Type
+ # Useful for handling attributes not mapped to types. Performs some boolean typecasting,
+ # but otherwise leaves the value untouched.
+ class Unknown
+
+ def cast(value)
+ value
+ end
+
+ def precast(value)
+ value
+ end
+
+ # Attempts typecasting to handle numeric, false and blank values.
+ def boolean(value)
+ empty = (numeric?(value) && value.to_i.zero?) || false?(value) || value.blank?
+ !empty
+ end
+
+ def appendable?
+ false
+ end
+
+ protected
+
+ def false?(value)
+ ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
+ end
+
+ def numeric?(value)
+ Numeric === value || value !~ /[^0-9]/
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index e61b253192..0365cb592f 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -17,90 +17,6 @@ module ActiveRecord
end
end
- class Errors < ActiveModel::Errors
- class << self
- def default_error_messages
- message = "Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages')."
- ActiveSupport::Deprecation.warn(message)
-
- I18n.translate 'activerecord.errors.messages'
- end
- end
-
- # Returns all the full error messages in an array.
- #
- # class Company < ActiveRecord::Base
- # validates_presence_of :name, :address, :email
- # validates_length_of :name, :in => 5..30
- # end
- #
- # company = Company.create(:address => '123 First St.')
- # company.errors.full_messages # =>
- # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
- def full_messages(options = {})
- full_messages = []
-
- each do |attribute, messages|
- messages = Array.wrap(messages)
- next if messages.empty?
-
- if attribute == :base
- messages.each {|m| full_messages << m }
- else
- attr_name = @base.class.human_attribute_name(attribute.to_s)
- prefix = attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ')
- messages.each do |m|
- full_messages << "#{prefix}#{m}"
- end
- end
- end
-
- full_messages
- end
-
- # Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
- # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
- # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
- # default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
- # translated attribute name and the value are available for interpolation.
- #
- # When using inheritance in your models, it will check all the inherited models too, but only if the model itself
- # hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
- # error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
- #
- # <ol>
- # <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
- # <li><tt>activerecord.errors.models.admin.blank</tt></li>
- # <li><tt>activerecord.errors.models.user.attributes.title.blank</tt></li>
- # <li><tt>activerecord.errors.models.user.blank</tt></li>
- # <li><tt>activerecord.errors.messages.blank</tt></li>
- # <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
- # </ol>
- def generate_message(attribute, message = :invalid, options = {})
- message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
-
- defaults = @base.class.self_and_descendants_from_active_record.map do |klass|
- [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
- :"models.#{klass.name.underscore}.#{message}" ]
- end
-
- defaults << options.delete(:default)
- defaults = defaults.compact.flatten << :"messages.#{message}"
-
- key = defaults.shift
- value = @base.respond_to?(attribute) ? @base.send(attribute) : nil
-
- options = { :default => defaults,
- :model => @base.class.human_name,
- :attribute => @base.class.human_attribute_name(attribute.to_s),
- :value => value,
- :scope => [:activerecord, :errors]
- }.merge(options)
-
- I18n.translate(key, options)
- end
- end
-
module Validations
extend ActiveSupport::Concern
@@ -165,11 +81,6 @@ module ActiveRecord
errors.empty?
end
-
- # Returns the Errors object that holds all information about attribute error messages.
- def errors
- @errors ||= Errors.new(self)
- end
end
end
end
diff --git a/activerecord/lib/active_record/validator.rb b/activerecord/lib/active_record/validator.rb
deleted file mode 100644
index 83a33f4dcd..0000000000
--- a/activerecord/lib/active_record/validator.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-module ActiveRecord #:nodoc:
-
- # A simple base class that can be used along with ActiveRecord::Base.validates_with
- #
- # class Person < ActiveRecord::Base
- # validates_with MyValidator
- # end
- #
- # class MyValidator < ActiveRecord::Validator
- # def validate
- # if some_complex_logic
- # record.errors[:base] = "This record is invalid"
- # end
- # end
- #
- # private
- # def some_complex_logic
- # # ...
- # end
- # end
- #
- # Any class that inherits from ActiveRecord::Validator will have access to <tt>record</tt>,
- # which is an instance of the record being validated, and must implement a method called <tt>validate</tt>.
- #
- # class Person < ActiveRecord::Base
- # validates_with MyValidator
- # end
- #
- # class MyValidator < ActiveRecord::Validator
- # def validate
- # record # => The person instance being validated
- # options # => Any non-standard options passed to validates_with
- # end
- # end
- #
- # To cause a validation error, you must add to the <tt>record<tt>'s errors directly
- # from within the validators message
- #
- # class MyValidator < ActiveRecord::Validator
- # def validate
- # record.errors[:base] << "This is some custom error message"
- # record.errors[:first_name] << "This is some complex validation"
- # # etc...
- # end
- # end
- #
- # To add behavior to the initialize method, use the following signature:
- #
- # class MyValidator < ActiveRecord::Validator
- # def initialize(record, options)
- # super
- # @my_custom_field = options[:field_name] || :first_name
- # end
- # end
- #
- class Validator
- attr_reader :record, :options
-
- def initialize(record, options)
- @record = record
- @options = options
- end
-
- def validate
- raise "You must override this method"
- end
- end
-end
diff --git a/activerecord/test/cases/attributes/aliasing_test.rb b/activerecord/test/cases/attributes/aliasing_test.rb
new file mode 100644
index 0000000000..7ee25779f1
--- /dev/null
+++ b/activerecord/test/cases/attributes/aliasing_test.rb
@@ -0,0 +1,20 @@
+require "cases/helper"
+
+class AliasingTest < ActiveRecord::TestCase
+
+ class AliasingAttributes < Hash
+ include ActiveRecord::Attributes::Aliasing
+ end
+
+ test "attribute access with aliasing" do
+ attributes = AliasingAttributes.new
+ attributes[:name] = 'Batman'
+ attributes.aliases['nickname'] = 'name'
+
+ assert_equal 'Batman', attributes[:name], "Symbols should point to Strings"
+ assert_equal 'Batman', attributes['name']
+ assert_equal 'Batman', attributes['nickname']
+ assert_equal 'Batman', attributes[:nickname]
+ end
+
+end
diff --git a/activerecord/test/cases/attributes/typecasting_test.rb b/activerecord/test/cases/attributes/typecasting_test.rb
new file mode 100644
index 0000000000..8a3b551375
--- /dev/null
+++ b/activerecord/test/cases/attributes/typecasting_test.rb
@@ -0,0 +1,120 @@
+require "cases/helper"
+
+class TypecastingTest < ActiveRecord::TestCase
+
+ class TypecastingAttributes < Hash
+ include ActiveRecord::Attributes::Typecasting
+ end
+
+ module MockType
+ class Object
+
+ def cast(value)
+ value
+ end
+
+ def precast(value)
+ value
+ end
+
+ def boolean(value)
+ !value.blank?
+ end
+
+ def appendable?
+ false
+ end
+
+ end
+
+ class Integer < Object
+
+ def cast(value)
+ value.to_i
+ end
+
+ def precast(value)
+ value ? value : 0
+ end
+
+ def boolean(value)
+ !Float(value).zero?
+ end
+
+ end
+
+ class Serialize < Object
+
+ def cast(value)
+ YAML::load(value) rescue value
+ end
+
+ def precast(value)
+ value
+ end
+
+ def appendable?
+ true
+ end
+
+ end
+ end
+
+ def setup
+ @attributes = TypecastingAttributes.new
+ @attributes.types.default = MockType::Object.new
+ @attributes.types['comments_count'] = MockType::Integer.new
+ end
+
+ test "typecast on read" do
+ attributes = @attributes.merge('comments_count' => '5')
+ assert_equal 5, attributes['comments_count']
+ end
+
+ test "typecast on write" do
+ @attributes['comments_count'] = false
+
+ assert_equal 0, @attributes.to_h['comments_count']
+ end
+
+ test "serialized objects" do
+ attributes = @attributes.merge('tags' => [ 'peanut butter' ].to_yaml)
+ attributes.types['tags'] = MockType::Serialize.new
+ attributes['tags'] << 'jelly'
+
+ assert_equal [ 'peanut butter', 'jelly' ], attributes['tags']
+ end
+
+ test "without typecasting" do
+ @attributes.merge!('comments_count' => '5')
+ attributes = @attributes.without_typecast
+
+ assert_equal '5', attributes['comments_count']
+ assert_equal 5, @attributes['comments_count'], "Original attributes should typecast"
+ end
+
+
+ test "typecast all attributes" do
+ attributes = @attributes.merge('title' => 'I love sandwiches', 'comments_count' => '5')
+ attributes.typecast!
+
+ assert_equal({ 'title' => 'I love sandwiches', 'comments_count' => 5 }, attributes)
+ end
+
+ test "query for has? value" do
+ attributes = @attributes.merge('comments_count' => '1')
+
+ assert_equal true, attributes.has?('comments_count')
+ attributes['comments_count'] = '0'
+ assert_equal false, attributes.has?('comments_count')
+ end
+
+ test "attributes to Hash" do
+ attributes_hash = { 'title' => 'I love sandwiches', 'comments_count' => '5' }
+ attributes = @attributes.merge(attributes_hash)
+
+ assert_equal Hash, attributes.to_h.class
+ assert_equal attributes_hash, attributes.to_h
+ end
+
+end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index bfb2df313b..5c2911eca1 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -2195,9 +2195,9 @@ class BasicsTest < ActiveRecord::TestCase
log = StringIO.new
ActiveRecord::Base.logger = Logger.new(log)
ActiveRecord::Base.logger.level = Logger::WARN
- ActiveRecord::Base.benchmark("Debug Topic Count", Logger::DEBUG) { Topic.count }
- ActiveRecord::Base.benchmark("Warn Topic Count", Logger::WARN) { Topic.count }
- ActiveRecord::Base.benchmark("Error Topic Count", Logger::ERROR) { Topic.count }
+ ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
+ ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
+ ActiveRecord::Base.benchmark("Error Topic Count", :level => :error) { Topic.count }
assert_no_match /Debug Topic Count/, log.string
assert_match /Warn Topic Count/, log.string
assert_match /Error Topic Count/, log.string
@@ -2209,8 +2209,8 @@ class BasicsTest < ActiveRecord::TestCase
original_logger = ActiveRecord::Base.logger
log = StringIO.new
ActiveRecord::Base.logger = Logger.new(log)
- ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, true) { ActiveRecord::Base.logger.debug "Loud" }
- ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, false) { ActiveRecord::Base.logger.debug "Quiet" }
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
assert_no_match /Loud/, log.string
assert_match /Quiet/, log.string
ensure
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index aa09c7061f..871cfa6468 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -1,5 +1,10 @@
-$:.unshift(File.dirname(__FILE__) + '/../../lib')
-$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib')
+root = File.expand_path('../../../..', __FILE__)
+begin
+ require "#{root}/vendor/gems/environment"
+rescue LoadError
+ $:.unshift("#{root}/activesupport/lib")
+ $:.unshift("#{root}/activerecord/lib")
+end
require 'config'
diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb
index d59c53cec8..ae4dcfb81e 100644
--- a/activerecord/test/cases/i18n_test.rb
+++ b/activerecord/test/cases/i18n_test.rb
@@ -30,17 +30,17 @@ class ActiveRecordI18nTests < Test::Unit::TestCase
def test_translated_model_names
I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
- assert_equal 'topic model', Topic.human_name
+ assert_equal 'topic model', Topic.model_name.human
end
def test_translated_model_names_with_sti
I18n.backend.store_translations 'en', :activerecord => {:models => {:reply => 'reply model'} }
- assert_equal 'reply model', Reply.human_name
+ assert_equal 'reply model', Reply.model_name.human
end
def test_translated_model_names_with_sti_fallback
I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
- assert_equal 'topic model', Reply.human_name
+ assert_equal 'topic model', Reply.model_name.human
end
end
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index bb9013c2a1..f9eea3d118 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -4,14 +4,14 @@ require "timeout"
class PooledConnectionsTest < ActiveRecord::TestCase
def setup
- super
+ @per_test_teardown = []
@connection = ActiveRecord::Base.remove_connection
end
def teardown
ActiveRecord::Base.clear_all_connections!
ActiveRecord::Base.establish_connection(@connection)
- super
+ @per_test_teardown.each {|td| td.call }
end
def checkout_connections
@@ -113,6 +113,23 @@ class PooledConnectionsTest < ActiveRecord::TestCase
assert_equal 3, after_count - before_count
end
+ def test_connection_pool_callbacks
+ checked_out, checked_in = false, false
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
+ set_callback(:checkout, :after) { checked_out = true }
+ set_callback(:checkin, :before) { checked_in = true }
+ end
+ @per_test_teardown << proc do
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
+ reset_callbacks :checkout
+ reset_callbacks :checkin
+ end
+ end
+ checkout_checkin_connections 1, 1
+ assert checked_out
+ assert checked_in
+ end
+
private
def add_record(name)
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index f3ed8ccd8d..99e248743a 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -15,8 +15,8 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_human_name
- assert_equal "Price estimate", PriceEstimate.human_name
- assert_equal "Subscriber", Subscriber.human_name
+ assert_equal "Price estimate", PriceEstimate.model_name.human
+ assert_equal "Subscriber", Subscriber.model_name.human
end
def test_column_null_not_null
diff --git a/activerecord/test/cases/types/number_test.rb b/activerecord/test/cases/types/number_test.rb
new file mode 100644
index 0000000000..ee7216a0f1
--- /dev/null
+++ b/activerecord/test/cases/types/number_test.rb
@@ -0,0 +1,30 @@
+require "cases/helper"
+
+class NumberTest < ActiveRecord::TestCase
+
+ def setup
+ @column = ActiveRecord::ConnectionAdapters::Column.new('comments_count', 0, 'integer')
+ @number = ActiveRecord::Type::Number.new(@column)
+ end
+
+ test "typecast" do
+ assert_equal 1, @number.cast(1)
+ assert_equal 1, @number.cast('1')
+ assert_equal 0, @number.cast('')
+
+ assert_equal 0, @number.precast(false)
+ assert_equal 1, @number.precast(true)
+ assert_equal nil, @number.precast('')
+ assert_equal 0, @number.precast(0)
+ end
+
+ test "cast as boolean" do
+ assert_equal true, @number.boolean('1')
+ assert_equal true, @number.boolean(1)
+
+ assert_equal false, @number.boolean(0)
+ assert_equal false, @number.boolean('0')
+ assert_equal false, @number.boolean(nil)
+ end
+
+end
diff --git a/activerecord/test/cases/types/object_test.rb b/activerecord/test/cases/types/object_test.rb
new file mode 100644
index 0000000000..f2667a9b00
--- /dev/null
+++ b/activerecord/test/cases/types/object_test.rb
@@ -0,0 +1,24 @@
+require "cases/helper"
+
+class ObjectTest < ActiveRecord::TestCase
+
+ def setup
+ @column = ActiveRecord::ConnectionAdapters::Column.new('name', '', 'date')
+ @object = ActiveRecord::Type::Object.new(@column)
+ end
+
+ test "typecast with column" do
+ date = Date.new(2009, 7, 10)
+ assert_equal date, @object.cast('10-07-2009')
+ assert_equal nil, @object.cast('')
+
+ assert_equal date, @object.precast(date)
+ end
+
+ test "cast as boolean" do
+ assert_equal false, @object.boolean(nil)
+ assert_equal false, @object.boolean('false')
+ assert_equal true, @object.boolean('10-07-2009')
+ end
+
+end
diff --git a/activerecord/test/cases/types/serialize_test.rb b/activerecord/test/cases/types/serialize_test.rb
new file mode 100644
index 0000000000..e9423a5b9d
--- /dev/null
+++ b/activerecord/test/cases/types/serialize_test.rb
@@ -0,0 +1,20 @@
+require "cases/helper"
+
+class SerializeTest < ActiveRecord::TestCase
+
+ test "typecast" do
+ serializer = ActiveRecord::Type::Serialize.new(column = nil, :serialize => Array)
+
+ assert_equal [], serializer.cast([].to_yaml)
+ assert_equal ['1'], serializer.cast(['1'].to_yaml)
+ assert_equal nil, serializer.cast(nil.to_yaml)
+ end
+
+ test "cast as boolean" do
+ serializer = ActiveRecord::Type::Serialize.new(column = nil, :serialize => Array)
+
+ assert_equal true, serializer.boolean(['1'].to_yaml)
+ assert_equal false, serializer.boolean([].to_yaml)
+ end
+
+end \ No newline at end of file
diff --git a/activerecord/test/cases/types/time_with_zone_test.rb b/activerecord/test/cases/types/time_with_zone_test.rb
new file mode 100644
index 0000000000..b3de79a6c8
--- /dev/null
+++ b/activerecord/test/cases/types/time_with_zone_test.rb
@@ -0,0 +1,42 @@
+require "cases/helper"
+
+class TimeWithZoneTest < ActiveRecord::TestCase
+
+ def setup
+ @column = ActiveRecord::ConnectionAdapters::Column.new('created_at', 0, 'datetime')
+ @time_with_zone = ActiveRecord::Type::TimeWithZone.new(@column)
+ end
+
+ test "typecast" do
+ Time.use_zone("Pacific Time (US & Canada)") do
+ time_string = "2009-10-07 21:29:10"
+ time = Time.zone.parse(time_string)
+
+ # assert_equal time, @time_with_zone.cast(time_string)
+ assert_equal nil, @time_with_zone.cast('')
+ assert_equal nil, @time_with_zone.cast(nil)
+
+ assert_equal time, @time_with_zone.precast(time)
+ assert_equal time, @time_with_zone.precast(time_string)
+ assert_equal time, @time_with_zone.precast(time.to_time)
+ # assert_equal "#{time.to_date.to_s} 00:00:00 -0700", @time_with_zone.precast(time.to_date).to_s
+ end
+ end
+
+ test "cast as boolean" do
+ Time.use_zone('Central Time (US & Canada)') do
+ time = Time.zone.now
+
+ assert_equal true, @time_with_zone.boolean(time)
+ assert_equal true, @time_with_zone.boolean(time.to_date)
+ assert_equal true, @time_with_zone.boolean(time.to_time)
+
+ assert_equal true, @time_with_zone.boolean(time.to_s)
+ assert_equal true, @time_with_zone.boolean(time.to_date.to_s)
+ assert_equal true, @time_with_zone.boolean(time.to_time.to_s)
+
+ assert_equal false, @time_with_zone.boolean('')
+ end
+ end
+
+end
diff --git a/activerecord/test/cases/types/unknown_test.rb b/activerecord/test/cases/types/unknown_test.rb
new file mode 100644
index 0000000000..230d67b2fb
--- /dev/null
+++ b/activerecord/test/cases/types/unknown_test.rb
@@ -0,0 +1,29 @@
+require "cases/helper"
+
+class UnknownTest < ActiveRecord::TestCase
+
+ test "typecast attributes does't modify values" do
+ unkown = ActiveRecord::Type::Unknown.new
+ person = { 'name' => '0' }
+
+ assert_equal person['name'], unkown.cast(person['name'])
+ assert_equal person['name'], unkown.precast(person['name'])
+ end
+
+ test "cast as boolean" do
+ person = { 'id' => 0, 'name' => ' ', 'admin' => 'false', 'votes' => '0' }
+ unkown = ActiveRecord::Type::Unknown.new
+
+ assert_equal false, unkown.boolean(person['votes'])
+ assert_equal false, unkown.boolean(person['admin'])
+ assert_equal false, unkown.boolean(person['name'])
+ assert_equal false, unkown.boolean(person['id'])
+
+ person = { 'id' => 5, 'name' => 'Eric', 'admin' => 'true', 'votes' => '25' }
+ assert_equal true, unkown.boolean(person['votes'])
+ assert_equal true, unkown.boolean(person['admin'])
+ assert_equal true, unkown.boolean(person['name'])
+ assert_equal true, unkown.boolean(person['id'])
+ end
+
+end \ No newline at end of file
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
new file mode 100644
index 0000000000..403a9a6e02
--- /dev/null
+++ b/activerecord/test/cases/types_test.rb
@@ -0,0 +1,32 @@
+require "cases/helper"
+require 'models/topic'
+
+class TypesTest < ActiveRecord::TestCase
+
+ test "attribute types from columns" do
+ begin
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ attribute_type_classes = {}
+ Topic.attribute_types.each { |key, type| attribute_type_classes[key] = type.class }
+
+ expected = { "id" => ActiveRecord::Type::Number,
+ "replies_count" => ActiveRecord::Type::Number,
+ "parent_id" => ActiveRecord::Type::Number,
+ "content" => ActiveRecord::Type::Serialize,
+ "written_on" => ActiveRecord::Type::TimeWithZone,
+ "title" => ActiveRecord::Type::Object,
+ "author_name" => ActiveRecord::Type::Object,
+ "approved" => ActiveRecord::Type::Object,
+ "parent_title" => ActiveRecord::Type::Object,
+ "bonus_time" => ActiveRecord::Type::Object,
+ "type" => ActiveRecord::Type::Object,
+ "last_read" => ActiveRecord::Type::Object,
+ "author_email_address" => ActiveRecord::Type::Object }
+
+ assert_equal expected, attribute_type_classes
+ ensure
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ end
+ end
+
+end
diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
index 3794a0ebb9..3f96d7973b 100644
--- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -17,26 +17,7 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
}
end
- # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
- def test_generate_message_inclusion_with_default_message
- assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title')
- end
-
- def test_generate_message_inclusion_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title')
- end
-
- # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value)
- def test_generate_message_exclusion_with_default_message
- assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title')
- end
-
- def test_generate_message_exclusion_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title')
- end
-
# validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
- # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
def test_generate_message_invalid_with_default_message
assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title')
end
@@ -45,107 +26,6 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title')
end
- # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message])
- def test_generate_message_confirmation_with_default_message
- assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil)
- end
-
- def test_generate_message_confirmation_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message')
- end
-
- # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message])
- def test_generate_message_accepted_with_default_message
- assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil)
- end
-
- def test_generate_message_accepted_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message')
- end
-
- # add_on_empty: generate_message(attr, :empty, :default => custom_message)
- def test_generate_message_empty_with_default_message
- assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil)
- end
-
- def test_generate_message_empty_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message')
- end
-
- # add_on_blank: generate_message(attr, :blank, :default => custom_message)
- def test_generate_message_blank_with_default_message
- assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil)
- end
-
- def test_generate_message_blank_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message')
- end
-
- # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end)
- def test_generate_message_too_long_with_default_message
- assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10)
- end
-
- def test_generate_message_too_long_with_custom_message
- assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10)
- end
-
- # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin)
- def test_generate_message_too_short_with_default_message
- assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10)
- end
-
- def test_generate_message_too_short_with_custom_message
- assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10)
- end
-
- # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value)
- def test_generate_message_wrong_length_with_default_message
- assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10)
- end
-
- def test_generate_message_wrong_length_with_custom_message
- assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10)
- end
-
- # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
- def test_generate_message_not_a_number_with_default_message
- assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title')
- end
-
- def test_generate_message_not_a_number_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title')
- end
-
- # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message])
- def test_generate_message_greater_than_with_default_message
- assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :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", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_equal_to_with_default_message
- assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_less_than_with_default_message
- assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :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", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_odd_with_default_message
- assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_even_with_default_message
- assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10)
- end
-
# validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message])
def test_generate_message_taken_with_default_message
assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title')
@@ -155,4 +35,13 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title')
end
+ # ActiveRecord#RecordInvalid exception
+
+ test "RecordInvalid exception can be localized" do
+ topic = Topic.new
+ topic.errors.add(:title, :invalid)
+ topic.errors.add(:title, :blank)
+ assert_equal "Validation failed: Title is invalid, Title can't be blank", ActiveRecord::RecordInvalid.new(topic).message
+ end
+
end
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 252138c0d6..532de67d99 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -30,20 +30,6 @@ class I18nValidationTest < ActiveRecord::TestCase
end
end
- def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated
- assert_not_deprecated do
- default = "%s interpolation syntax was deprecated"
- assert_equal default, I18n.t(:does_not_exist, :default => default, :value => 'this')
- end
- end
-
- def test_percent_d_interpolation_syntax_in_error_messages_was_deprecated
- assert_not_deprecated do
- default = "%d interpolation syntaxes are deprecated"
- assert_equal default, I18n.t(:does_not_exist, :default => default, :count => 2)
- end
- end
-
# ActiveRecord::Errors
def test_errors_generate_message_translates_custom_model_attribute_key
I18n.expects(:translate).with(
@@ -162,722 +148,4 @@ class I18nValidationTest < ActiveRecord::TestCase
assert_equal ['global message'], replied_topic.errors[:replies]
end
- def test_errors_add_on_empty_generates_message
- @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil})
- @topic.errors.add_on_empty :title
- end
-
- def test_errors_add_on_empty_generates_message_with_custom_default_message
- @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'})
- @topic.errors.add_on_empty :title, 'custom'
- end
-
- def test_errors_add_on_blank_generates_message
- @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil})
- @topic.errors.add_on_blank :title
- end
-
- def test_errors_add_on_blank_generates_message_with_custom_default_message
- @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'})
- @topic.errors.add_on_blank :title, 'custom'
- end
-
- def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
- @topic.errors.add('title', 'empty')
- I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title')
- @topic.errors.full_messages :locale => 'en'
- end
-
- # ActiveRecord::Validations
- # validates_confirmation_of w/ mocha
- def test_validates_confirmation_of_generates_message
- Topic.validates_confirmation_of :title
- @topic.title_confirmation = 'foo'
- @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil})
- @topic.valid?
- end
-
- def test_validates_confirmation_of_generates_message_with_custom_default_message
- Topic.validates_confirmation_of :title, :message => 'custom'
- @topic.title_confirmation = 'foo'
- @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'})
- @topic.valid?
- end
-
- # validates_acceptance_of w/ mocha
-
- def test_validates_acceptance_of_generates_message
- Topic.validates_acceptance_of :title, :allow_nil => false
- @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil})
- @topic.valid?
- end
-
- def test_validates_acceptance_of_generates_message_with_custom_default_message
- Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false
- @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'})
- @topic.valid?
- end
-
- # validates_presence_of w/ mocha
-
- def test_validates_presence_of_generates_message
- Topic.validates_presence_of :title
- @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil})
- @topic.valid?
- end
-
- def test_validates_presence_of_generates_message_with_custom_default_message
- Topic.validates_presence_of :title, :message => 'custom'
- @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'})
- @topic.valid?
- end
-
- def test_validates_length_of_within_generates_message_with_title_too_short
- Topic.validates_length_of :title, :within => 3..5
- @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
- @topic.valid?
- end
-
- def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message
- Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
- @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'})
- @topic.valid?
- end
-
- def test_validates_length_of_within_generates_message_with_title_too_long
- Topic.validates_length_of :title, :within => 3..5
- @topic.title = 'this title is too long'
- @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil})
- @topic.valid?
- end
-
- def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message
- Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
- @topic.title = 'this title is too long'
- @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'})
- @topic.valid?
- end
-
- # validates_length_of :within w/ mocha
-
- def test_validates_length_of_within_generates_message_with_title_too_short
- Topic.validates_length_of :title, :within => 3..5
- @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
- @topic.valid?
- end
-
- def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message
- Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
- @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'})
- @topic.valid?
- end
-
- def test_validates_length_of_within_generates_message_with_title_too_long
- Topic.validates_length_of :title, :within => 3..5
- @topic.title = 'this title is too long'
- @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil})
- @topic.valid?
- end
-
- def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message
- Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
- @topic.title = 'this title is too long'
- @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'})
- @topic.valid?
- end
-
- # validates_length_of :is w/ mocha
-
- def test_validates_length_of_is_generates_message
- Topic.validates_length_of :title, :is => 5
- @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil})
- @topic.valid?
- end
-
- def test_validates_length_of_is_generates_message_with_custom_default_message
- Topic.validates_length_of :title, :is => 5, :message => 'custom'
- @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'})
- @topic.valid?
- end
-
- # validates_format_of w/ mocha
-
- def test_validates_format_of_generates_message
- Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
- @topic.title = '72x'
- @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil})
- @topic.valid?
- end
-
- def test_validates_format_of_generates_message_with_custom_default_message
- Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom'
- @topic.title = '72x'
- @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'})
- @topic.valid?
- end
-
- # validates_inclusion_of w/ mocha
-
- def test_validates_inclusion_of_generates_message
- Topic.validates_inclusion_of :title, :in => %w(a b c)
- @topic.title = 'z'
- @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil})
- @topic.valid?
- end
-
- def test_validates_inclusion_of_generates_message_with_custom_default_message
- Topic.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom'
- @topic.title = 'z'
- @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'})
- @topic.valid?
- end
-
- # validates_exclusion_of w/ mocha
-
- def test_validates_exclusion_of_generates_message
- Topic.validates_exclusion_of :title, :in => %w(a b c)
- @topic.title = 'a'
- @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil})
- @topic.valid?
- end
-
- def test_validates_exclusion_of_generates_message_with_custom_default_message
- Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom'
- @topic.title = 'a'
- @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'})
- @topic.valid?
- end
-
- # validates_numericality_of without :only_integer w/ mocha
-
- def test_validates_numericality_of_generates_message
- Topic.validates_numericality_of :title
- @topic.title = 'a'
- @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil})
- @topic.valid?
- end
-
- def test_validates_numericality_of_generates_message_with_custom_default_message
- Topic.validates_numericality_of :title, :message => 'custom'
- @topic.title = 'a'
- @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'})
- @topic.valid?
- end
-
- # validates_numericality_of with :only_integer w/ mocha
-
- def test_validates_numericality_of_only_integer_generates_message
- Topic.validates_numericality_of :title, :only_integer => true
- @topic.title = 'a'
- @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil})
- @topic.valid?
- end
-
- def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message
- Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom'
- @topic.title = 'a'
- @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'})
- @topic.valid?
- end
-
- # validates_numericality_of :odd w/ mocha
-
- def test_validates_numericality_of_odd_generates_message
- Topic.validates_numericality_of :title, :only_integer => true, :odd => true
- @topic.title = 0
- @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil})
- @topic.valid?
- end
-
- def test_validates_numericality_of_odd_generates_message_with_custom_default_message
- Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom'
- @topic.title = 0
- @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'})
- @topic.valid?
- end
-
- # validates_numericality_of :less_than w/ mocha
-
- def test_validates_numericality_of_less_than_generates_message
- Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
- @topic.title = 1
- @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil})
- @topic.valid?
- end
-
- def test_validates_numericality_of_odd_generates_message_with_custom_default_message
- Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom'
- @topic.title = 1
- @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'})
- @topic.valid?
- end
-
- # validates_confirmation_of w/o mocha
-
- def test_validates_confirmation_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:confirmation => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}}
-
- Topic.validates_confirmation_of :title
- @topic.title_confirmation = 'foo'
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_confirmation_of_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}}
-
- Topic.validates_confirmation_of :title
- @topic.title_confirmation = 'foo'
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_acceptance_of w/o mocha
-
- def test_validates_acceptance_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:accepted => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}}
-
- Topic.validates_acceptance_of :title, :allow_nil => false
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_acceptance_of_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}}
-
- Topic.validates_acceptance_of :title, :allow_nil => false
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_presence_of w/o mocha
-
- def test_validates_presence_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:blank => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}}
-
- Topic.validates_presence_of :title
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_presence_of_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}}
-
- Topic.validates_presence_of :title
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_length_of :within w/o mocha
-
- def test_validates_length_of_within_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:too_short => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}}
-
- Topic.validates_length_of :title, :within => 3..5
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_length_of_within_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}}
-
- Topic.validates_length_of :title, :within => 3..5
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_length_of :is w/o mocha
-
- def test_validates_length_of_is_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
-
- Topic.validates_length_of :title, :is => 5
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_length_of_is_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
-
- Topic.validates_length_of :title, :is => 5
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- def test_validates_length_of_is_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
-
- Topic.validates_length_of :title, :is => 5
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_length_of_is_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
-
- Topic.validates_length_of :title, :is => 5
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
-
- # validates_format_of w/o mocha
-
- def test_validates_format_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:invalid => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
-
- Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_format_of_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
-
- Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_inclusion_of w/o mocha
-
- def test_validates_inclusion_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:inclusion => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}}
-
- Topic.validates_inclusion_of :title, :in => %w(a b c)
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_inclusion_of_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}}
-
- Topic.validates_inclusion_of :title, :in => %w(a b c)
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_exclusion_of w/o mocha
-
- def test_validates_exclusion_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:exclusion => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}}
-
- Topic.validates_exclusion_of :title, :in => %w(a b c)
- @topic.title = 'a'
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_exclusion_of_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}}
-
- Topic.validates_exclusion_of :title, :in => %w(a b c)
- @topic.title = 'a'
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_numericality_of without :only_integer w/o mocha
-
- def test_validates_numericality_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
-
- Topic.validates_numericality_of :title
- @topic.title = 'a'
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_numericality_of_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
-
- Topic.validates_numericality_of :title, :only_integer => true
- @topic.title = 'a'
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_numericality_of with :only_integer w/o mocha
-
- def test_validates_numericality_of_only_integer_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
-
- Topic.validates_numericality_of :title, :only_integer => true
- @topic.title = 'a'
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_numericality_of_only_integer_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
-
- Topic.validates_numericality_of :title, :only_integer => true
- @topic.title = 'a'
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_numericality_of :odd w/o mocha
-
- def test_validates_numericality_of_odd_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:odd => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}}
-
- Topic.validates_numericality_of :title, :only_integer => true, :odd => true
- @topic.title = 0
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_numericality_of_odd_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}}
-
- Topic.validates_numericality_of :title, :only_integer => true, :odd => true
- @topic.title = 0
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- # validates_numericality_of :less_than w/o mocha
-
- def test_validates_numericality_of_less_than_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:less_than => 'custom message'}}}}}}
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}}
-
- Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
- @topic.title = 1
- @topic.valid?
- assert_equal ['custom message'], @topic.errors[:title]
- end
-
- def test_validates_numericality_of_less_than_finds_global_default_translation
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}}
-
- Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
- @topic.title = 1
- @topic.valid?
- assert_equal ['global message'], @topic.errors[:title]
- end
-
- def test_validations_with_message_symbol_must_translate
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:custom_error => "I am a custom error"}}}
- Topic.validates_presence_of :title, :message => :custom_error
- @topic.title = nil
- @topic.valid?
- assert_equal ["I am a custom error"], @topic.errors[:title]
- end
-
- def test_validates_with_message_symbol_must_translate_per_attribute
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:custom_error => "I am a custom error"}}}}}}
- Topic.validates_presence_of :title, :message => :custom_error
- @topic.title = nil
- @topic.valid?
- assert_equal ["I am a custom error"], @topic.errors[:title]
- end
-
- def test_validates_with_message_symbol_must_translate_per_model
- I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:custom_error => "I am a custom error"}}}}
- Topic.validates_presence_of :title, :message => :custom_error
- @topic.title = nil
- @topic.valid?
- assert_equal ["I am a custom error"], @topic.errors[:title]
- end
-
- def test_validates_with_message_string
- Topic.validates_presence_of :title, :message => "I am a custom error"
- @topic.title = nil
- @topic.valid?
- assert_equal ["I am a custom error"], @topic.errors[:title]
- end
-end
-
-class ActiveRecordValidationsGenerateMessageI18nTests < ActiveRecord::TestCase
-
- def setup
- @topic = Topic.new
- I18n.backend.store_translations :'en', {
- :activerecord => {
- :errors => {
- :messages => {
- :inclusion => "is not included in the list",
- :exclusion => "is reserved",
- :invalid => "is invalid",
- :confirmation => "doesn't match confirmation",
- :accepted => "must be accepted",
- :empty => "can't be empty",
- :blank => "can't 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)",
- :taken => "has already been taken",
- :not_a_number => "is not a number",
- :greater_than => "must be greater than {{count}}",
- :greater_than_or_equal_to => "must be greater than or equal to {{count}}",
- :equal_to => "must be equal to {{count}}",
- :less_than => "must be less than {{count}}",
- :less_than_or_equal_to => "must be less than or equal to {{count}}",
- :odd => "must be odd",
- :even => "must be even"
- }
- }
- }
- }
- end
-
- # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
- def test_generate_message_inclusion_with_default_message
- assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title')
- end
-
- def test_generate_message_inclusion_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title')
- end
-
- # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value)
- def test_generate_message_exclusion_with_default_message
- assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title')
- end
-
- def test_generate_message_exclusion_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title')
- end
-
- # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
- # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
- def test_generate_message_invalid_with_default_message
- assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title')
- end
-
- def test_generate_message_invalid_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title')
- end
-
- # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message])
- def test_generate_message_confirmation_with_default_message
- assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil)
- end
-
- def test_generate_message_confirmation_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message')
- end
-
- # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message])
- def test_generate_message_accepted_with_default_message
- assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil)
- end
-
- def test_generate_message_accepted_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message')
- end
-
- # add_on_empty: generate_message(attr, :empty, :default => custom_message)
- def test_generate_message_empty_with_default_message
- assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil)
- end
-
- def test_generate_message_empty_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message')
- end
-
- # add_on_blank: generate_message(attr, :blank, :default => custom_message)
- def test_generate_message_blank_with_default_message
- assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil)
- end
-
- def test_generate_message_blank_with_custom_message
- assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message')
- end
-
- # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end)
- def test_generate_message_too_long_with_default_message
- assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10)
- end
-
- def test_generate_message_too_long_with_custom_message
- assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10)
- end
-
- # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin)
- def test_generate_message_too_short_with_default_message
- assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10)
- end
-
- def test_generate_message_too_short_with_custom_message
- assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10)
- end
-
- # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value)
- def test_generate_message_wrong_length_with_default_message
- assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10)
- end
-
- def test_generate_message_wrong_length_with_custom_message
- assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10)
- end
-
- # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message])
- def test_generate_message_taken_with_default_message
- assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title')
- end
-
- def test_generate_message_taken_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title')
- end
-
- # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
- def test_generate_message_not_a_number_with_default_message
- assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title')
- end
-
- def test_generate_message_not_a_number_with_custom_message
- assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title')
- end
-
- # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message])
- def test_generate_message_greater_than_with_default_message
- assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :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", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_equal_to_with_default_message
- assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_less_than_with_default_message
- assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :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", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_odd_with_default_message
- assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10)
- end
-
- def test_generate_message_even_with_default_message
- assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10)
- end
- # ActiveRecord#RecordInvalid exception
-
- test "RecordInvalid exception can be localized" do
- topic = Topic.new
- topic.errors.add(:title, :invalid)
- topic.errors.add(:title, :blank)
- assert_equal "Validation failed: Title is invalid, Title can't be blank", ActiveRecord::RecordInvalid.new(topic).message
- end
end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 5cdb623eef..130231c622 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -148,40 +148,9 @@ class ValidationsTest < ActiveRecord::TestCase
assert_equal "100,000", d.salary_before_type_cast
end
- def test_validates_length_with_globally_modified_error_message
- defaults = ActiveSupport::Deprecation.silence { ActiveRecord::Errors.default_error_messages }
- original_message = defaults[:too_short]
- defaults[:too_short] = 'tu est trops petit hombre {{count}}'
-
- Topic.validates_length_of :title, :minimum => 10
- t = Topic.create(:title => 'too short')
- assert !t.valid?
-
- assert_equal ['tu est trops petit hombre 10'], t.errors[:title]
-
- ensure
- defaults[:too_short] = original_message
- end
-
def test_validates_acceptance_of_as_database_column
Topic.validates_acceptance_of(:author_name)
topic = Topic.create("author_name" => "Dan Brown")
assert_equal "Dan Brown", topic["author_name"]
end
-
- def test_deprecated_validation_instance_methods
- tom = DeprecatedPerson.new
-
- assert_deprecated do
- assert tom.invalid?
- assert_equal ["always invalid", "invalid on create"], tom.errors[:name]
- end
-
- tom.save(false)
-
- assert_deprecated do
- assert tom.invalid?
- assert_equal ["always invalid", "invalid on update"], tom.errors[:name]
- end
- end
end