aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2011-12-22 13:11:44 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2011-12-22 13:11:44 -0700
commitf2e65b7d54c3034b35a5f248f2482e760cd85cd5 (patch)
tree90acf9d5eb74b74bc5b9d96cd9b93f0151497d0e /activerecord
parent2bc3f81c89c1cd7444d476411bef626173172dc0 (diff)
parent6758941898516deca3d4b9ccf2a9eaf46f2f4080 (diff)
downloadrails-f2e65b7d54c3034b35a5f248f2482e760cd85cd5.tar.gz
rails-f2e65b7d54c3034b35a5f248f2482e760cd85cd5.tar.bz2
rails-f2e65b7d54c3034b35a5f248f2482e760cd85cd5.zip
Merge branch '3-2-stable' of github.com:rails/rails into 3-2-stable
* '3-2-stable' of github.com:rails/rails: Use sass-rails >= 3.2.2 for new apps Make ActiveRecord::Relation#pluck work with serialized attributes Make read_attribute code path accessible at the class level
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb40
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb20
-rw-r--r--activerecord/lib/active_record/base.rb9
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb27
-rw-r--r--activerecord/lib/active_record/persistence.rb8
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb8
-rw-r--r--activerecord/test/cases/calculations_test.rb9
7 files changed, 60 insertions, 61 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 77bf9d0905..a83d944d1b 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -39,6 +39,26 @@ module ActiveRecord
super
end
+ def type_cast_attribute(attr_name, attributes, cache = {}) #:nodoc:
+ return unless attr_name
+ attr_name = attr_name.to_s
+
+ if generated_external_attribute_methods.method_defined?(attr_name)
+ if attributes.has_key?(attr_name) || attr_name == 'id'
+ generated_external_attribute_methods.send(attr_name, attributes[attr_name], attributes, cache, attr_name)
+ end
+ elsif !attribute_methods_generated?
+ # If we haven't generated the caster methods yet, do that and
+ # then try again
+ define_attribute_methods
+ type_cast_attribute(attr_name, attributes, cache)
+ else
+ # If we get here, the attribute has no associated DB column, so
+ # just return it verbatim.
+ attributes[attr_name]
+ end
+ end
+
protected
# We want to generate the methods via module_eval rather than define_method,
# because define_method is slower on dispatch and uses more memory (because it
@@ -105,25 +125,7 @@ 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)
- return unless attr_name
-
- attr_name = attr_name.to_s
- methods = self.class.generated_external_attribute_methods
-
- if methods.method_defined?(attr_name)
- if @attributes.has_key?(attr_name) || attr_name == 'id'
- methods.send(attr_name, @attributes[attr_name], @attributes, @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
- # If we get here, the attribute has no associated DB column, so
- # just return it verbatim.
- @attributes[attr_name]
- end
+ self.class.type_cast_attribute(attr_name, @attributes, @attributes_cache)
end
private
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 0a4432506f..2ffd91f796 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -58,6 +58,18 @@ module ActiveRecord
self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
end
+ def initialize_attributes(attributes) #:nodoc:
+ super
+
+ serialized_attributes.each do |key, coder|
+ if attributes.key?(key)
+ attributes[key] = Attribute.new(coder, attributes[key], :serialized)
+ end
+ end
+
+ attributes
+ end
+
private
def attribute_cast_code(attr_name)
@@ -69,14 +81,6 @@ module ActiveRecord
end
end
- def set_serialized_attributes
- self.class.serialized_attributes.each do |key, coder|
- if @attributes.key?(key)
- @attributes[key] = Attribute.new(coder, @attributes[key], :serialized)
- end
- end
- end
-
def type_cast_attribute_for_write(column, value)
if column && coder = self.class.serialized_attributes[column.name]
Attribute.new(coder, value, :unserialized)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 432a40ea54..fa5846de39 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -469,7 +469,7 @@ module ActiveRecord #:nodoc:
# # Instantiates a single new object bypassing mass-assignment security
# User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
def initialize(attributes = nil, options = {})
- @attributes = attributes_from_column_definition
+ @attributes = self.class.initialize_attributes(self.class.column_defaults.dup)
@association_cache = {}
@aggregation_cache = {}
@attributes_cache = {}
@@ -482,7 +482,6 @@ module ActiveRecord #:nodoc:
@relation = nil
ensure_proper_type
- set_serialized_attributes
populate_with_current_scope_attributes
@@ -503,11 +502,9 @@ module ActiveRecord #:nodoc:
# post.init_with('attributes' => { 'title' => 'hello world' })
# post.title # => 'hello world'
def init_with(coder)
- @attributes = coder['attributes']
+ @attributes = self.class.initialize_attributes(coder['attributes'])
@relation = nil
- set_serialized_attributes
-
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
@association_cache = {}
@aggregation_cache = {}
@@ -534,7 +531,7 @@ module ActiveRecord #:nodoc:
_run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
@changed_attributes = {}
- attributes_from_column_definition.each do |attr, orig_value|
+ self.class.column_defaults.each do |attr, orig_value|
@changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index ce0a165660..0e33cdb617 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -64,21 +64,6 @@ module ActiveRecord
send(lock_col + '=', previous_lock_value + 1)
end
- def attributes_from_column_definition
- result = self.class.column_defaults.dup
-
- # If the locking column has no default value set,
- # start the lock version at zero. Note we can't use
- # <tt>locking_enabled?</tt> at this point as
- # <tt>@attributes</tt> may not have been initialized yet.
-
- if result.key?(self.class.locking_column) && lock_optimistically
- result[self.class.locking_column] ||= 0
- end
-
- result
- end
-
def update(attribute_names = @attributes.keys) #:nodoc:
return super unless locking_enabled?
return 0 if attribute_names.empty?
@@ -180,6 +165,18 @@ module ActiveRecord
counters = counters.merge(locking_column => 1) if locking_enabled?
super
end
+
+ # If the locking column has no default value set,
+ # start the lock version at zero. Note we can't use
+ # <tt>locking_enabled?</tt> at this point as
+ # <tt>@attributes</tt> may not have been initialized yet.
+ def initialize_attributes(attributes) #:nodoc:
+ if attributes.key?(locking_column) && lock_optimistically
+ attributes[locking_column] ||= 0
+ end
+
+ attributes
+ end
end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index a2fe21043f..038355deaa 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -368,13 +368,5 @@ module ActiveRecord
@new_record = false
id
end
-
- # Initializes the attributes array with keys matching the columns from the linked table and
- # the values matching the corresponding default value of that column, so
- # that a new instance, or one populated from a passed-in Hash, still has all the attributes
- # that instances loaded from the database would.
- def attributes_from_column_definition
- self.class.column_defaults.dup
- end
end
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 0f57e9831d..0d60810cd2 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -168,7 +168,7 @@ module ActiveRecord
# This method is designed to perform select by a single column as direct SQL query
# Returns <tt>Array</tt> with values of the specified column name
- # The values has same data type as column.
+ # The values has same data type as column.
#
# Examples:
#
@@ -177,9 +177,9 @@ module ActiveRecord
# Person.where(:confirmed => true).limit(5).pluck(:id)
#
def pluck(column_name)
- scope = self.select(column_name)
- self.connection.select_values(scope.to_sql).map! do |value|
- type_cast_using_column(value, column_for(column_name))
+ column_name = column_name.to_s
+ klass.connection.select_all(select(column_name).arel).map! do |attributes|
+ klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 5abf3d1af4..66c801ca75 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -458,7 +458,6 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [ topic.approved ], relation.pluck(:approved)
assert_equal [ topic.last_read ], relation.pluck(:last_read)
assert_equal [ topic.written_on ], relation.pluck(:written_on)
-
end
def test_pluck_and_uniq
@@ -471,4 +470,12 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [contract.id], company.contracts.pluck(:id)
end
+ def test_pluck_with_serialization
+ t = Topic.create!(:content => { :foo => :bar })
+ assert_equal [{:foo => :bar}], Topic.where(:id => t.id).pluck(:content)
+ end
+
+ def test_pluck_with_qualified_column_name
+ assert_equal [1,2,3,4], Topic.order(:id).pluck("topics.id")
+ end
end