aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorSean Griffin <sean@thoughtbot.com>2014-06-19 11:13:19 -0600
committerSean Griffin <sean@thoughtbot.com>2014-06-19 13:12:52 -0600
commit099af48d31bb2b5fc8e3fff39b825efff7964d55 (patch)
treea125056346c5ffe3bd348349dadb02249c00c81a /activerecord/lib
parentdccf6da66bf4a63971e1f12b98cb1bb1fe5a9015 (diff)
downloadrails-099af48d31bb2b5fc8e3fff39b825efff7964d55.tar.gz
rails-099af48d31bb2b5fc8e3fff39b825efff7964d55.tar.bz2
rails-099af48d31bb2b5fc8e3fff39b825efff7964d55.zip
Introduce an object to aid in creation and management of `@attributes`
Mostly delegation to start, but we can start moving a lot of behavior in bulk to this object.
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb10
-rw-r--r--activerecord/lib/active_record/attribute_set.rb51
-rw-r--r--activerecord/lib/active_record/attributes.rb9
-rw-r--r--activerecord/lib/active_record/core.rb7
-rw-r--r--activerecord/lib/active_record/model_schema.rb10
-rw-r--r--activerecord/lib/active_record/persistence.rb6
7 files changed, 71 insertions, 23 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 53fa132219..ab85414277 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -32,6 +32,7 @@ module ActiveRecord
extend ActiveSupport::Autoload
autoload :Attribute
+ autoload :AttributeSet
autoload :Base
autoload :Callbacks
autoload :Core
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index b4d75d6556..268cec6160 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -230,7 +230,7 @@ module ActiveRecord
# For queries selecting a subset of columns, return false for unselected columns.
# We check defined?(@attributes) not to issue warnings if called on objects that
# have been allocated but not yet initialized.
- if defined?(@attributes) && @attributes.any? && self.class.column_names.include?(name)
+ if defined?(@attributes) && self.class.column_names.include?(name)
return has_attribute?(name)
end
@@ -247,7 +247,7 @@ module ActiveRecord
# person.has_attribute?('age') # => true
# person.has_attribute?(:nothing) # => false
def has_attribute?(attr_name)
- @attributes.has_key?(attr_name.to_s)
+ @attributes.include?(attr_name.to_s)
end
# Returns an array of names for the attributes available on this object.
@@ -367,12 +367,6 @@ module ActiveRecord
protected
- def clone_attributes # :nodoc:
- @attributes.each_with_object({}) do |(name, attr), h|
- h[name] = attr.dup
- end
- end
-
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
value = send(reader_method, attribute_name)
value.duplicable? ? value.clone : value
diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb
new file mode 100644
index 0000000000..102ef17e16
--- /dev/null
+++ b/activerecord/lib/active_record/attribute_set.rb
@@ -0,0 +1,51 @@
+module ActiveRecord
+ class AttributeSet # :nodoc:
+ delegate :[], :[]=, :fetch, :include?, :keys, :each_with_object, to: :attributes
+
+ def initialize(attributes)
+ @attributes = attributes
+ end
+
+ def update(other)
+ attributes.update(other.attributes)
+ end
+
+ def freeze
+ @attributes.freeze
+ super
+ end
+
+ def initialize_dup(_)
+ @attributes = attributes.dup
+ attributes.each do |key, attr|
+ attributes[key] = attr.dup
+ end
+
+ super
+ end
+
+ def initialize_clone(_)
+ @attributes = attributes.clone
+ super
+ end
+
+ class Builder
+ def initialize(types)
+ @types = types
+ end
+
+ def build_from_database(values, additional_types = {})
+ attributes = values.each_with_object({}) do |(name, value), hash|
+ type = additional_types.fetch(name, @types[name])
+ hash[name] = Attribute.from_database(value, type)
+ end
+ AttributeSet.new(attributes)
+ end
+ end
+
+ protected
+
+ attr_reader :attributes
+
+ end
+end
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 9c80121d70..d0287140c8 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -109,13 +109,14 @@ module ActiveRecord
end
def clear_caches_calculated_from_columns
- @columns = nil
- @columns_hash = nil
- @column_types = nil
+ @attributes_builder = nil
@column_defaults = nil
- @raw_column_defaults = nil
@column_names = nil
+ @column_types = nil
+ @columns = nil
+ @columns_hash = nil
@content_columns = nil
+ @raw_column_defaults = nil
end
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index d39e5fddfe..e8e165a7c8 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -251,11 +251,10 @@ module ActiveRecord
def initialize(attributes = nil, options = {})
defaults = {}
self.class.raw_column_defaults.each do |k, v|
- default = v.duplicable? ? v.dup : v
- defaults[k] = Attribute.from_database(default, type_for_attribute(k))
+ defaults[k] = v.duplicable? ? v.dup : v
end
- @attributes = defaults
+ @attributes = self.class.attributes_builder.build_from_database(defaults)
@column_types = self.class.column_types
init_internals
@@ -325,7 +324,7 @@ module ActiveRecord
##
def initialize_dup(other) # :nodoc:
pk = self.class.primary_key
- @attributes = other.clone_attributes
+ @attributes = @attributes.dup
@attributes[pk] = Attribute.from_database(nil, type_for_attribute(pk))
run_callbacks(:initialize) unless _initialize_callbacks.empty?
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index fad7eae461..ffd30eb87d 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -219,12 +219,18 @@ module ActiveRecord
connection.schema_cache.table_exists?(table_name)
end
+ def attributes_builder # :nodoc:
+ @attributes_builder ||= AttributeSet::Builder.new(column_types)
+ end
+
def column_types # :nodoc:
- @column_types ||= Hash[columns.map { |column| [column.name, column.cast_type] }]
+ @column_types ||= Hash.new(Type::Value.new).tap do |column_types|
+ columns.each { |column| column_types[column.name] = column.cast_type }
+ end
end
def type_for_attribute(attr_name) # :nodoc:
- column_types.fetch(attr_name) { Type::Value.new }
+ column_types[attr_name]
end
# Returns a hash where the keys are column names and the values are
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 5c744762d9..6707f12489 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -48,11 +48,7 @@ module ActiveRecord
# how this "single-table" inheritance mapping is implemented.
def instantiate(attributes, column_types = {})
klass = discriminate_class_for_record(attributes)
-
- attributes = attributes.each_with_object({}) do |(name, value), h|
- type = column_types.fetch(name) { klass.type_for_attribute(name) }
- h[name] = Attribute.from_database(value, type)
- end
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
end