mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Revert "Refactoring attributes/types" [#3348 state:open]
This reverts commitf936a1f100
. Conflicts: activerecord/lib/active_record.rb activerecord/lib/active_record/base.rb Revert "Fixed: #without_typecast should only disable typecasting on the duplicated attributes" [#3387 state:open] This reverts commit2831996483
. Reason : It's not generating attribute methods properly, making object.column 5x slower.
This commit is contained in:
parent
a68a3e9af6
commit
6d30002a52
25 changed files with 149 additions and 776 deletions
|
@ -45,7 +45,6 @@ module ActiveRecord
|
||||||
autoload :AssociationPreload
|
autoload :AssociationPreload
|
||||||
autoload :Associations
|
autoload :Associations
|
||||||
autoload :AttributeMethods
|
autoload :AttributeMethods
|
||||||
autoload :Attributes
|
|
||||||
autoload :AutosaveAssociation
|
autoload :AutosaveAssociation
|
||||||
|
|
||||||
autoload :Relation
|
autoload :Relation
|
||||||
|
@ -77,7 +76,6 @@ module ActiveRecord
|
||||||
autoload :StateMachine
|
autoload :StateMachine
|
||||||
autoload :Timestamp
|
autoload :Timestamp
|
||||||
autoload :Transactions
|
autoload :Transactions
|
||||||
autoload :Types
|
|
||||||
autoload :Validations
|
autoload :Validations
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -95,28 +93,6 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module Attributes
|
|
||||||
extend ActiveSupport::Autoload
|
|
||||||
|
|
||||||
eager_autoload do
|
|
||||||
autoload :Aliasing
|
|
||||||
autoload :Store
|
|
||||||
autoload :Typecasting
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module Type
|
|
||||||
extend ActiveSupport::Autoload
|
|
||||||
|
|
||||||
eager_autoload do
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
module Locking
|
module Locking
|
||||||
extend ActiveSupport::Autoload
|
extend ActiveSupport::Autoload
|
||||||
|
|
||||||
|
|
|
@ -8,18 +8,25 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_attribute_before_type_cast(attr_name)
|
def read_attribute_before_type_cast(attr_name)
|
||||||
_attributes.without_typecast[attr_name]
|
@attributes[attr_name]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a hash of attributes before typecasting and deserialization.
|
# Returns a hash of attributes before typecasting and deserialization.
|
||||||
def attributes_before_type_cast
|
def attributes_before_type_cast
|
||||||
_attributes.without_typecast
|
self.attribute_names.inject({}) do |attrs, name|
|
||||||
|
attrs[name] = read_attribute_before_type_cast(name)
|
||||||
|
attrs
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
# Handle *_before_type_cast for method_missing.
|
# Handle *_before_type_cast for method_missing.
|
||||||
def attribute_before_type_cast(attribute_name)
|
def attribute_before_type_cast(attribute_name)
|
||||||
read_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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,23 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def query_attribute(attr_name)
|
def query_attribute(attr_name)
|
||||||
_attributes.has?(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
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -19,5 +35,3 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,11 @@ module ActiveRecord
|
||||||
|
|
||||||
protected
|
protected
|
||||||
def define_method_attribute(attr_name)
|
def define_method_attribute(attr_name)
|
||||||
define_read_method(attr_name.to_sym, attr_name, columns_hash[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
|
||||||
|
|
||||||
if attr_name == primary_key && attr_name != "id"
|
if attr_name == primary_key && attr_name != "id"
|
||||||
define_read_method(:id, attr_name, columns_hash[attr_name])
|
define_read_method(:id, attr_name, columns_hash[attr_name])
|
||||||
|
@ -45,12 +49,18 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
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.
|
# Define an attribute reader method. Cope with nil column.
|
||||||
def define_read_method(symbol, attr_name, column)
|
def define_read_method(symbol, attr_name, column)
|
||||||
access_code = "_attributes['#{attr_name}']"
|
cast_code = column.type_cast_code('v') if column
|
||||||
|
access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
|
||||||
|
|
||||||
unless attr_name.to_s == self.primary_key.to_s
|
unless attr_name.to_s == self.primary_key.to_s
|
||||||
access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless _attributes.key?('#{attr_name}'); ")
|
access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cache_attribute?(attr_name)
|
if cache_attribute?(attr_name)
|
||||||
|
@ -63,7 +73,38 @@ module ActiveRecord
|
||||||
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
# 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)).
|
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
|
||||||
def read_attribute(attr_name)
|
def read_attribute(attr_name)
|
||||||
_attributes[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
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -12,20 +12,48 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
||||||
def cache_attribute?(attr_name)
|
|
||||||
time_zone_aware?(attr_name) || super
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
|
||||||
def time_zone_aware?(attr_name)
|
# This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
|
||||||
column = columns_hash[attr_name]
|
def define_method_attribute(attr_name)
|
||||||
time_zone_aware_attributes &&
|
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
|
||||||
!skip_time_zone_conversion_for_attributes.include?(attr_name.to_sym) &&
|
method_body = <<-EOV
|
||||||
[:datetime, :timestamp].include?(column.type)
|
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
|
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
|
||||||
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,9 +17,14 @@ module ActiveRecord
|
||||||
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
|
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
|
||||||
# columns are turned into +nil+.
|
# columns are turned into +nil+.
|
||||||
def write_attribute(attr_name, value)
|
def write_attribute(attr_name, value)
|
||||||
attr_name = _attributes.unalias(attr_name)
|
attr_name = attr_name.to_s
|
||||||
|
attr_name = self.class.primary_key if attr_name == 'id'
|
||||||
@attributes_cache.delete(attr_name)
|
@attributes_cache.delete(attr_name)
|
||||||
_attributes[attr_name] = value
|
if (column = column_for_attribute(attr_name)) && column.number?
|
||||||
|
@attributes[attr_name] = convert_number_column_value(value)
|
||||||
|
else
|
||||||
|
@attributes[attr_name] = value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
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
|
|
|
@ -1,42 +0,0 @@
|
||||||
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
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
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
|
|
|
@ -1,117 +0,0 @@
|
||||||
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
|
|
|
@ -1200,7 +1200,7 @@ module ActiveRecord #:nodoc:
|
||||||
def instantiate(record)
|
def instantiate(record)
|
||||||
object = find_sti_class(record[inheritance_column]).allocate
|
object = find_sti_class(record[inheritance_column]).allocate
|
||||||
|
|
||||||
object.send(:initialize_attribute_store, record)
|
object.instance_variable_set(:'@attributes', record)
|
||||||
object.instance_variable_set(:'@attributes_cache', {})
|
object.instance_variable_set(:'@attributes_cache', {})
|
||||||
|
|
||||||
object.send(:_run_find_callbacks)
|
object.send(:_run_find_callbacks)
|
||||||
|
@ -1663,7 +1663,7 @@ module ActiveRecord #:nodoc:
|
||||||
# In both instances, valid attribute keys are determined by the column names of the associated table --
|
# 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.
|
# hence you can't have attributes that aren't part of the table columns.
|
||||||
def initialize(attributes = nil)
|
def initialize(attributes = nil)
|
||||||
initialize_attribute_store(attributes_from_column_definition)
|
@attributes = attributes_from_column_definition
|
||||||
@attributes_cache = {}
|
@attributes_cache = {}
|
||||||
@new_record = true
|
@new_record = true
|
||||||
ensure_proper_type
|
ensure_proper_type
|
||||||
|
@ -1694,7 +1694,7 @@ module ActiveRecord #:nodoc:
|
||||||
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
|
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
|
||||||
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
|
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
|
||||||
cloned_attributes.delete(self.class.primary_key)
|
cloned_attributes.delete(self.class.primary_key)
|
||||||
initialize_attribute_store(cloned_attributes)
|
@attributes = cloned_attributes
|
||||||
clear_aggregation_cache
|
clear_aggregation_cache
|
||||||
@attributes_cache = {}
|
@attributes_cache = {}
|
||||||
@new_record = true
|
@new_record = true
|
||||||
|
@ -1924,11 +1924,21 @@ module ActiveRecord #:nodoc:
|
||||||
def reload(options = nil)
|
def reload(options = nil)
|
||||||
clear_aggregation_cache
|
clear_aggregation_cache
|
||||||
clear_association_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 = {}
|
@attributes_cache = {}
|
||||||
self
|
self
|
||||||
end
|
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 value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
# 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)).
|
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
|
||||||
# (Alias for the protected read_attribute method).
|
# (Alias for the protected read_attribute method).
|
||||||
|
@ -2262,7 +2272,7 @@ module ActiveRecord #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def instantiate_time_object(name, values)
|
def instantiate_time_object(name, values)
|
||||||
if self.class.send(:time_zone_aware?, name)
|
if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
|
||||||
Time.zone.local(*values)
|
Time.zone.local(*values)
|
||||||
else
|
else
|
||||||
Time.time_with_datetime_fallback(@@default_timezone, *values)
|
Time.time_with_datetime_fallback(@@default_timezone, *values)
|
||||||
|
@ -2345,6 +2355,22 @@ module ActiveRecord #:nodoc:
|
||||||
comma_pair_list(quote_columns(quoter, hash))
|
comma_pair_list(quote_columns(quoter, hash))
|
||||||
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 object_from_yaml(string)
|
||||||
|
return string unless string.is_a?(String) && string =~ /^---/
|
||||||
|
YAML::load(string) rescue string
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Base.class_eval do
|
Base.class_eval do
|
||||||
|
@ -2359,7 +2385,6 @@ module ActiveRecord #:nodoc:
|
||||||
include AttributeMethods::PrimaryKey
|
include AttributeMethods::PrimaryKey
|
||||||
include AttributeMethods::TimeZoneConversion
|
include AttributeMethods::TimeZoneConversion
|
||||||
include AttributeMethods::Dirty
|
include AttributeMethods::Dirty
|
||||||
include Attributes, Types
|
|
||||||
include Callbacks, ActiveModel::Observing, Timestamp
|
include Callbacks, ActiveModel::Observing, Timestamp
|
||||||
include Associations, AssociationPreload, NamedScope
|
include Associations, AssociationPreload, NamedScope
|
||||||
include ActiveModel::Conversion
|
include ActiveModel::Conversion
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
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
|
|
|
@ -1,30 +0,0 @@
|
||||||
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
|
|
|
@ -1,37 +0,0 @@
|
||||||
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
|
|
|
@ -1,33 +0,0 @@
|
||||||
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
|
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
|
@ -1,37 +0,0 @@
|
||||||
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
|
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
|
@ -1,120 +0,0 @@
|
||||||
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
|
|
|
@ -1,30 +0,0 @@
|
||||||
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
|
|
|
@ -1,24 +0,0 @@
|
||||||
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
|
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
|
@ -1,42 +0,0 @@
|
||||||
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
|
|
|
@ -1,29 +0,0 @@
|
||||||
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
|
|
|
@ -1,32 +0,0 @@
|
||||||
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
|
|
Loading…
Reference in a new issue