diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2011-12-22 13:11:44 -0700 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2011-12-22 13:11:44 -0700 |
commit | f2e65b7d54c3034b35a5f248f2482e760cd85cd5 (patch) | |
tree | 90acf9d5eb74b74bc5b9d96cd9b93f0151497d0e /activerecord | |
parent | 2bc3f81c89c1cd7444d476411bef626173172dc0 (diff) | |
parent | 6758941898516deca3d4b9ccf2a9eaf46f2f4080 (diff) | |
download | rails-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')
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 |