aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/attribute_methods
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2010-01-22 00:51:45 +0530
committerPratik Naik <pratiknaik@gmail.com>2010-01-22 02:37:07 +0530
commit6d30002a52133bd105adb29084f4cc72b1ee847f (patch)
treee2c194d8b454b66daf4db58d770c1337d0c20399 /activerecord/lib/active_record/attribute_methods
parenta68a3e9af6a02c9ce18d3eec87558241095ce8fb (diff)
downloadrails-6d30002a52133bd105adb29084f4cc72b1ee847f.tar.gz
rails-6d30002a52133bd105adb29084f4cc72b1ee847f.tar.bz2
rails-6d30002a52133bd105adb29084f4cc72b1ee847f.zip
Revert "Refactoring attributes/types" [#3348 state:open]
This reverts commit f936a1f100e75082081e782e5cceb272885c2df7. 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 commit 2831996483c6a045f1f38d8030256eb58d9771c3. Reason : It's not generating attribute methods properly, making object.column 5x slower.
Diffstat (limited to 'activerecord/lib/active_record/attribute_methods')
-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
5 files changed, 117 insertions, 22 deletions
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 74921241f7..a4e144f233 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -8,18 +8,25 @@ module ActiveRecord
end
def read_attribute_before_type_cast(attr_name)
- _attributes.without_typecast[attr_name]
+ @attributes[attr_name]
end
# Returns a hash of attributes before typecasting and deserialization.
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
private
# Handle *_before_type_cast for method_missing.
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
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index 0154ee35f8..a949d80120 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -8,7 +8,23 @@ module ActiveRecord
end
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
private
@@ -19,5 +35,3 @@ 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 97caec7744..3da3d9d8cc 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -37,7 +37,11 @@ module ActiveRecord
protected
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"
define_read_method(:id, attr_name, columns_hash[attr_name])
@@ -45,12 +49,18 @@ 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)
- 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
- 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
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,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
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
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 4ac0c7f608..a8e3e28a7a 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -12,20 +12,48 @@ 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
- 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)
+ # 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
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 37eadbe0a9..e31acac050 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -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
# columns are turned into +nil+.
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[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
private