提交 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? ...@@ -29,6 +29,10 @@ def attribute_methods_generated?
end end
end end
def generated_attribute_methods
@generated_attribute_methods ||= (base_class == self ? super : base_class.generated_attribute_methods)
end
def undefine_attribute_methods def undefine_attribute_methods
if base_class == self if base_class == self
super super
......
...@@ -33,7 +33,7 @@ def undefine_attribute_methods ...@@ -33,7 +33,7 @@ def undefine_attribute_methods
if base_class == self if base_class == self
generated_attribute_methods.module_eval do generated_attribute_methods.module_eval do
public_methods(false).each do |m| 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 end
end end
...@@ -49,27 +49,27 @@ def undefine_attribute_methods ...@@ -49,27 +49,27 @@ def undefine_attribute_methods
# The second, slower, branch is necessary to support instances where the database # The second, slower, branch is necessary to support instances where the database
# returns columns with extra stuff in (like 'my_column(omg)'). # returns columns with extra stuff in (like 'my_column(omg)').
def define_method_attribute(attr_name) def define_method_attribute(attr_name)
access_code = attribute_access_code(attr_name) internal = internal_attribute_access_code(attr_name)
cast_code = "v && (#{attribute_cast_code(attr_name)})" external = external_attribute_access_code(attr_name)
if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
def #{attr_name} def #{attr_name}
#{access_code} #{internal}
end end
def self.cast_#{attr_name}(v) def self.attribute_#{attr_name}(v, attributes_cache, attr_name)
#{cast_code} #{external}
end end
STR STR
else else
generated_attribute_methods.module_eval do generated_attribute_methods.module_eval do
define_method(attr_name) do define_method(attr_name) do
eval(access_code) eval(internal)
end end
singleton_class.send(:define_method, "cast_#{attr_name}") do |v| singleton_class.send(:define_method, "attribute_#{attr_name}") do |v, attributes_cache, attr_name|
eval(cast_code) eval(external)
end end
end end
end end
...@@ -80,7 +80,7 @@ def cacheable_column?(column) ...@@ -80,7 +80,7 @@ def cacheable_column?(column)
attribute_types_cached_by_default.include?(column.type) attribute_types_cached_by_default.include?(column.type)
end 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)}" access_code = "(v=@attributes['#{attr_name}']) && #{attribute_cast_code(attr_name)}"
unless attr_name == self.primary_key unless attr_name == self.primary_key
...@@ -94,6 +94,16 @@ def attribute_access_code(attr_name) ...@@ -94,6 +94,16 @@ def attribute_access_code(attr_name)
access_code access_code
end 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) def attribute_cast_code(attr_name)
columns_hash[attr_name].type_cast_code('v') columns_hash[attr_name].type_cast_code('v')
end end
...@@ -103,36 +113,26 @@ def attribute_cast_code(attr_name) ...@@ -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)). # "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)
attr_name = attr_name.to_s attr_name = attr_name.to_s
caster = "cast_#{attr_name}" accessor = "attribute_#{attr_name}"
methods = self.class.generated_attribute_methods methods = self.class.generated_attribute_methods
if methods.respond_to?(caster) if methods.respond_to?(accessor)
if @attributes.has_key?(attr_name) 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 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 else
_read_attribute attr_name # If we get here, the attribute has no associated DB column, so
end # just return it verbatim.
end @attributes[attr_name]
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])
end end
end end
private private
def type_cast_attribute(column, value)
if column
column.type_cast(value)
else
value
end
end
def attribute(attribute_name) def attribute(attribute_name)
read_attribute(attribute_name) read_attribute(attribute_name)
end end
......
...@@ -77,14 +77,6 @@ def set_serialized_attributes ...@@ -77,14 +77,6 @@ def set_serialized_attributes
end end
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) def type_cast_attribute_for_write(column, value)
if column && coder = self.class.serialized_attributes[column.name] if column && coder = self.class.serialized_attributes[column.name]
Attribute.new(coder, value, :unserialized) Attribute.new(coder, value, :unserialized)
......
...@@ -19,12 +19,15 @@ module ClassMethods ...@@ -19,12 +19,15 @@ module ClassMethods
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled. # 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 # This enhanced read method automatically converts the UTC time stored in the database to the time
# zone stored in Time.zone. # zone stored in Time.zone.
def attribute_access_code(attr_name) def internal_attribute_access_code(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) column = columns_hash[attr_name]
if create_time_zone_conversion_attribute?(attr_name, column)
<<-CODE <<-CODE
cached = @attributes_cache['#{attr_name}'] cached = @attributes_cache['#{attr_name}']
return cached if cached 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 @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
CODE CODE
else else
...@@ -32,6 +35,16 @@ def attribute_access_code(attr_name) ...@@ -32,6 +35,16 @@ def attribute_access_code(attr_name)
end end
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) def attribute_cast_code(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
"v.acts_like?(:time) ? v.in_time_zone : v" "v.acts_like?(:time) ? v.in_time_zone : v"
......
...@@ -1821,7 +1821,7 @@ def attribute_for_inspect(attr_name) ...@@ -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 # 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). # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
def attribute_present?(attribute) def attribute_present?(attribute)
value = _read_attribute(attribute) value = read_attribute(attribute)
!value.nil? || (value.respond_to?(:empty?) && !value.empty?) !value.nil? || (value.respond_to?(:empty?) && !value.empty?)
end end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册