提交 f1a534af 编写于 作者: J Jon Leighton

Remove the need for type_cast_attribute.

This is good because it reduces duplication.
上级 47b97a73
......@@ -29,6 +29,10 @@ def attribute_methods_generated?
end
end
def generated_attribute_methods
@generated_attribute_methods ||= (base_class == self ? super : base_class.generated_attribute_methods)
end
def undefine_attribute_methods
if base_class == self
super
......
......@@ -33,7 +33,7 @@ def undefine_attribute_methods
if base_class == self
generated_attribute_methods.module_eval do
public_methods(false).each do |m|
singleton_class.send(:undef_method, m) if m.to_s =~ /^cast_/
singleton_class.send(:undef_method, m) if m.to_s =~ /^attribute_/
end
end
end
......@@ -49,27 +49,27 @@ def undefine_attribute_methods
# The second, slower, branch is necessary to support instances where the database
# returns columns with extra stuff in (like 'my_column(omg)').
def define_method_attribute(attr_name)
access_code = attribute_access_code(attr_name)
cast_code = "v && (#{attribute_cast_code(attr_name)})"
internal = internal_attribute_access_code(attr_name)
external = external_attribute_access_code(attr_name)
if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
def #{attr_name}
#{access_code}
#{internal}
end
def self.cast_#{attr_name}(v)
#{cast_code}
def self.attribute_#{attr_name}(v, attributes_cache, attr_name)
#{external}
end
STR
else
generated_attribute_methods.module_eval do
define_method(attr_name) do
eval(access_code)
eval(internal)
end
singleton_class.send(:define_method, "cast_#{attr_name}") do |v|
eval(cast_code)
singleton_class.send(:define_method, "attribute_#{attr_name}") do |v, attributes_cache, attr_name|
eval(external)
end
end
end
......@@ -80,7 +80,7 @@ def cacheable_column?(column)
attribute_types_cached_by_default.include?(column.type)
end
def attribute_access_code(attr_name)
def internal_attribute_access_code(attr_name)
access_code = "(v=@attributes['#{attr_name}']) && #{attribute_cast_code(attr_name)}"
unless attr_name == self.primary_key
......@@ -94,6 +94,16 @@ def attribute_access_code(attr_name)
access_code
end
def external_attribute_access_code(attr_name)
access_code = "v && #{attribute_cast_code(attr_name)}"
if cache_attribute?(attr_name)
access_code = "attributes_cache[attr_name] ||= (#{access_code})"
end
access_code
end
def attribute_cast_code(attr_name)
columns_hash[attr_name].type_cast_code('v')
end
......@@ -103,36 +113,26 @@ def attribute_cast_code(attr_name)
# "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
caster = "cast_#{attr_name}"
accessor = "attribute_#{attr_name}"
methods = self.class.generated_attribute_methods
if methods.respond_to?(caster)
if methods.respond_to?(accessor)
if @attributes.has_key?(attr_name)
@attributes_cache[attr_name] || methods.send(caster, @attributes[attr_name])
methods.send(accessor, @attributes[attr_name], @attributes_cache, attr_name)
end
elsif !self.class.attribute_methods_generated?
# If we haven't generated the caster methods yet, do that and
# then try again
self.class.define_attribute_methods
read_attribute(attr_name)
else
_read_attribute attr_name
end
end
def _read_attribute(attr_name)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id'
unless @attributes[attr_name].nil?
type_cast_attribute(column_for_attribute(attr_name), @attributes[attr_name])
# If we get here, the attribute has no associated DB column, so
# just return it verbatim.
@attributes[attr_name]
end
end
private
def type_cast_attribute(column, value)
if column
column.type_cast(value)
else
value
end
end
def attribute(attribute_name)
read_attribute(attribute_name)
end
......
......@@ -77,14 +77,6 @@ def set_serialized_attributes
end
end
def type_cast_attribute(column, value)
if column && self.class.serialized_attributes[column.name]
value.unserialized_value
else
super
end
end
def type_cast_attribute_for_write(column, value)
if column && coder = self.class.serialized_attributes[column.name]
Attribute.new(coder, value, :unserialized)
......
......@@ -19,12 +19,15 @@ module ClassMethods
# 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 attribute_access_code(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
def internal_attribute_access_code(attr_name)
column = columns_hash[attr_name]
if create_time_zone_conversion_attribute?(attr_name, column)
<<-CODE
cached = @attributes_cache['#{attr_name}']
return cached if cached
time = _read_attribute('#{attr_name}')
v = @attributes['#{attr_name}']
time = #{column.type_cast_code('v')}
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
CODE
else
......@@ -32,6 +35,16 @@ def attribute_access_code(attr_name)
end
end
def external_attribute_access_code(attr_name)
column = columns_hash[attr_name]
if create_time_zone_conversion_attribute?(attr_name, column)
"attributes_cache[attr_name] ||= (#{attribute_cast_code(attr_name)})"
else
super
end
end
def attribute_cast_code(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
"v.acts_like?(:time) ? v.in_time_zone : v"
......
......@@ -1821,7 +1821,7 @@ def attribute_for_inspect(attr_name)
# Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
# nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
def attribute_present?(attribute)
value = _read_attribute(attribute)
value = read_attribute(attribute)
!value.nil? || (value.respond_to?(:empty?) && !value.empty?)
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册