[DRE-commits] [SCM] ruby-activerecord-3.2.git branch, master, updated. debian/3.2.6-5-27-g2ee0c93

Ondřej Surý ondrej at sury.org
Wed May 29 09:06:41 UTC 2013


The following commit has been merged in the master branch:
commit e9977191854161ae1a523f2d190fdb2a8446f250
Author: Ondřej Surý <ondrej at sury.org>
Date:   Tue Mar 19 10:09:54 2013 +0100

    Imported Upstream version 3.2.13

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 93c5aba..ec017f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,289 @@
-## Rails 3.2.12 (unreleased) ##
+## Rails 3.2.13 (Feb 17, 2013) ##
+
+*   Reverted 921a296a3390192a71abeec6d9a035cc6d1865c8, 'Quote numeric values
+    compared to string columns.' This caused several regressions.
+
+    *Steve Klabnik*
+
+*   Fix overriding of attributes by default_scope on `ActiveRecord::Base#dup`.
+
+    *Hiroshige UMINO*
+
+*   Fix issue with overriding Active Record reader methods with a composed object
+    and using that attribute as the scope of a `uniqueness_of` validation.
+    Backport #7072.
+
+    *Peter Brown*
+
+*   Sqlite now preserves custom primary keys when copying or altering tables.
+    Fixes #9367.
+    Backport #2312.
+
+    *Sean Scally + Yves Senn*
+
+*   Preloading `has_many :through` associations with conditions won't
+    cache the `:through` association. This will prevent invalid
+    subsets to be cached.
+    Fixes #8423.
+    Backport #9252.
+
+    Example:
+
+        class User
+          has_many :posts
+          has_many :recent_comments, -> { where('created_at > ?', 1.week.ago) }, :through => :posts
+        end
+
+        a_user = User.includes(:recent_comments).first
+
+        # this is preloaded
+        a_user.recent_comments
+
+        # fetching the recent_comments through the posts association won't preload it.
+        a_user.posts
+
+    *Yves Senn*
+
+*   Fix handling of dirty time zone aware attributes
+
+    Previously, when `time_zone_aware_attributes` were enabled, after
+    changing a datetime or timestamp attribute and then changing it back
+    to the original value, `changed_attributes` still tracked the
+    attribute as changed. This caused `[attribute]_changed?` and
+    `changed?` methods to return true incorrectly.
+
+    Example:
+
+        in_time_zone 'Paris' do
+          order = Order.new
+          original_time = Time.local(2012, 10, 10)
+          order.shipped_at = original_time
+          order.save
+          order.changed? # => false
+
+          # changing value
+          order.shipped_at = Time.local(2013, 1, 1)
+          order.changed? # => true
+
+          # reverting to original value
+          order.shipped_at = original_time
+          order.changed? # => false, used to return true
+        end
+
+    Backport of #9073
+    Fixes #8898
+
+    *Lilibeth De La Cruz*
+
+*   Fix counter cache columns not updated when replacing `has_many :through`
+    associations.
+    Backport #8400.
+    Fix #7630.
+
+    *Matthew Robertson*
+
+*   Don't update `column_defaults` when calling destructive methods on column with default value.
+    Backport c517602.
+    Fix #6115.
+
+    *Piotr Sarnacki + Aleksey Magusev + Alan Daud*
+
+*   When `#count` is used in conjunction with `#uniq` we perform `count(:distinct => true)`.
+    Fix #6865.
+
+    Example:
+
+      relation.uniq.count # => SELECT COUNT(DISTINCT *)
+
+    *Yves Senn + Kaspar Schiess*
+
+*   Fix `ActiveRecord::Relation#pluck` when columns or tables are reserved words.
+    Backport #7536.
+    Fix #8968.
+
+    *Ian Lesperance + Yves Senn + Kaspar Schiess*
+
+*   Don't run explain on slow queries for database adapters that don't support it.
+    Backport #6197.
+
+    *Blake Smith*
+
+*   Revert round usec when comparing timestamp attributes in the dirty tracking.
+    Fixes #8460.
+
+    *Andrew White*
+
+*   Revert creation of through association models when using `collection=[]`
+    on a `has_many :through` association from an unsaved model.
+    Fix #7661, #8269.
+
+    *Ernie Miller*
+
+*   Fix undefined method `to_i` when calling `new` on a scope that uses an
+    Array; Fix FloatDomainError when setting integer column to NaN.
+    Fixes #8718, #8734, #8757.
+
+    *Jason Stirk + Tristan Harward*
+
+*   Serialized attributes can be serialized in integer columns.
+    Fix #8575.
+
+    *Rafael Mendonça França*
+
+*   Keep index names when using `alter_table` with sqlite3.
+    Fix #3489.
+    Backport #8522.
+
+    *Yves Senn*
+
+*   Recognize migrations placed in directories containing numbers and 'rb'.
+    Fix #8492.
+    Backport of #8500.
+
+    *Yves Senn*
+
+*   Add `ActiveRecord::Base.cache_timestamp_format` class attribute to control
+    the format of the timestamp value in the cache key.
+    This allows users to improve the precision of the cache key.
+    Fixes #8195.
+
+    *Rafael Mendonça França*
+
+*   Add `:nsec` date format. This can be used to improve the precision of cache key.
+    Please note that this format only works with Ruby 1.9, Ruby 1.8 will ignore it completely.
+
+    *Jamie Gaskins*
+
+*   Unscope `update_column(s)` query to ignore default scope.
+
+    When applying `default_scope` to a class with a where clause, using
+    `update_column(s)` could generate a query that would not properly update
+    the record due to the where clause from the `default_scope` being applied
+    to the update query.
+
+        class User < ActiveRecord::Base
+          default_scope where(active: true)
+        end
+
+        user = User.first
+        user.active = false
+        user.save!
+
+        user.update_column(:active, true) # => false
+
+    In this situation we want to skip the default_scope clause and just
+    update the record based on the primary key. With this change:
+
+        user.update_column(:active, true) # => true
+
+    Backport of #8436 fix.
+
+    *Carlos Antonio da Silva*
+
+*   Fix performance problem with primary_key method in PostgreSQL adapter when having many schemas.
+    Uses pg_constraint table instead of pg_depend table which has many records in general.
+    Fix #8414
+
+    *kennyj*
+
+*   Do not instantiate intermediate Active Record objects when eager loading.
+    These records caused `after_find` to run more than expected.
+    Fix #3313
+    Backport of #8403
+
+    *Yves Senn*
+
+*   Fix `pluck` to work with joins. Backport of #4942.
+
+    *Carlos Antonio da Silva*
+
+*   Fix a problem with `translate_exception` method in a non English environment.
+    Backport of #6397.
+
+    *kennyj*
+
+*   Fix dirty attribute checks for TimeZoneConversion with nil and blank
+    datetime attributes. Setting a nil datetime to a blank string should not
+    result in a change being flagged.
+    Fixes #8310.
+    Backport of #8311.
+
+    *Alisdair McDiarmid*
+
+*   Prevent mass assignment to the type column of polymorphic associations when using `build`.
+    Fixes #8265.
+    Backport of #8291.
+
+    *Yves Senn*
+
+*   When running migrations on Postgresql, the `:limit` option for `binary` and `text` columns is
+    silently dropped.
+    Previously, these migrations caused sql exceptions, because Postgresql doesn't support limits
+    on these types.
+
+    *Victor Costan*
+
+*   Use `nil?` instead of `blank?` to check whether dynamic finder with a bang
+    should raise RecordNotFound.
+    Fixes #7238.
+
+    *Nikita Afanasenko*
+
+*   Fix deleting from a HABTM join table upon destroying an object of a model
+    with optimistic locking enabled.
+    Fixes #5332.
+
+    *Nick Rogers*
+
+*   Use query cache/uncache when using ENV["DATABASE_URL"].
+    Fixes #6951.
+    Backport of #8074.
+
+    *kennyj*
+
+*   Do not create useless database transaction when building `has_one` association.
+
+    Example:
+
+        User.has_one :profile
+        User.new.build_profile
+
+    Backport of #8154.
+
+    *Bogdan Gusiev*
+
+*   `AR::Base#attributes_before_type_cast` now returns unserialized values for serialized attributes.
+
+    *Nikita Afanasenko*
+
+*   Fix issue that raises `NameError` when overriding the `accepts_nested_attributes` in child classes.
+
+    Before:
+
+        class Shared::Person < ActiveRecord::Base
+          has_one :address
+
+          accepts_nested_attributes :address, :reject_if => :all_blank
+        end
+
+        class Person < Shared::Person
+          accepts_nested_attributes :address
+        end
+
+        Person
+        #=> NameError: method `address_attributes=' not defined in Person
+
+    After:
+
+        Person
+        #=> Person(id: integer, ...)
+
+    Fixes #8131.
+
+    *Gabriel Sobrinho, Ricardo Henrique*
+
+
+## Rails 3.2.12 (Feb 11, 2013) ##
 
 *   Quote numeric values being compared to non-numeric columns. Otherwise,
     in some database, the string column values will be coerced to a numeric
@@ -10,17 +295,24 @@
 
     *Dylan Smith*
 
+
 ## Rails 3.2.11 (Jan 8, 2013) ##
 
 *   Fix querying with an empty hash *Damien Mathieu* [CVE-2013-0155]
 
-## Rails 3.2.10 ##
+
+## Rails 3.2.10 (Jan 2, 2013) ##
 
 *   CVE-2012-5664 options hashes should only be extracted if there are extra
     parameters
 
+
 ## Rails 3.2.9 (Nov 12, 2012) ##
 
+*   Fix `find_in_batches` crashing when IDs are strings and start option is not specified.
+
+    *Alexis Bernard*
+
 *   Fix issue with collection associations calling first(n)/last(n) and attempting
     to set the inverse association when `:inverse_of` was used. Fixes #8087.
 
diff --git a/checksums.yaml.gz b/checksums.yaml.gz
index abc9b2c..b8116f7 100644
Binary files a/checksums.yaml.gz and b/checksums.yaml.gz differ
diff --git a/lib/active_record/associations/association.rb b/lib/active_record/associations/association.rb
index 59c1bad..ab0d888 100644
--- a/lib/active_record/associations/association.rb
+++ b/lib/active_record/associations/association.rb
@@ -231,7 +231,8 @@ module ActiveRecord
 
         def build_record(attributes, options)
           reflection.build_association(attributes, options) do |record|
-            attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
+            skip_assign = [reflection.foreign_key, reflection.type].compact
+            attributes = create_scope.except(*(record.changed - skip_assign))
             record.assign_attributes(attributes, :without_protection => true)
           end
         end
diff --git a/lib/active_record/associations/has_many_through_association.rb b/lib/active_record/associations/has_many_through_association.rb
index ce81333..2b9e7ea 100644
--- a/lib/active_record/associations/has_many_through_association.rb
+++ b/lib/active_record/associations/has_many_through_association.rb
@@ -38,20 +38,6 @@ module ActiveRecord
         super
       end
 
-      def concat_records(records)
-        ensure_not_nested
-
-        records = super
-
-        if owner.new_record? && records
-          records.flatten.each do |record|
-            build_through_record(record)
-          end
-        end
-
-        records
-      end
-
       def insert_record(record, validate = true, raise = false)
         ensure_not_nested
 
@@ -153,6 +139,11 @@ module ActiveRecord
 
           delete_through_records(records)
 
+          if source_reflection.options[:counter_cache]
+            counter = source_reflection.counter_cache_column
+            klass.decrement_counter counter, records.map(&:id)
+          end
+
           if through_reflection.macro == :has_many && update_through_counter?(method)
             update_counter(-count, through_reflection)
           end
diff --git a/lib/active_record/associations/has_one_association.rb b/lib/active_record/associations/has_one_association.rb
index 501ebe7..56f9013 100644
--- a/lib/active_record/associations/has_one_association.rb
+++ b/lib/active_record/associations/has_one_association.rb
@@ -11,7 +11,7 @@ module ActiveRecord
         # If target and record are nil, or target is equal to record,
         # we don't need to have transaction.
         if (target || record) && target != record
-          reflection.klass.transaction do
+          transaction_if(save) do
             remove_target!(options[:dependent]) if target && !target.destroyed?
   
             if record
@@ -70,6 +70,14 @@ module ActiveRecord
         def nullify_owner_attributes(record)
           record[reflection.foreign_key] = nil
         end
+
+        def transaction_if(value)
+          if value
+            reflection.klass.transaction { yield }
+          else
+            yield
+          end
+        end
     end
   end
 end
diff --git a/lib/active_record/associations/preloader/through_association.rb b/lib/active_record/associations/preloader/through_association.rb
index ad6374d..4cb7b56 100644
--- a/lib/active_record/associations/preloader/through_association.rb
+++ b/lib/active_record/associations/preloader/through_association.rb
@@ -37,7 +37,8 @@ module ActiveRecord
             through_records = Array.wrap(owner.send(through_reflection.name))
 
             # Dont cache the association - we would only be caching a subset
-            if reflection.options[:source_type] && through_reflection.collection?
+            if (preload_options != through_options) ||
+               (reflection.options[:source_type] && through_reflection.collection?)
               owner.association(through_reflection.name).reset
             end
 
diff --git a/lib/active_record/attribute_methods/serialization.rb b/lib/active_record/attribute_methods/serialization.rb
index 00023b0..deb89f3 100644
--- a/lib/active_record/attribute_methods/serialization.rb
+++ b/lib/active_record/attribute_methods/serialization.rb
@@ -90,6 +90,14 @@ module ActiveRecord
         end
       end
 
+      def _field_changed?(attr, old, value)
+        if self.class.serialized_attributes.include?(attr)
+          old != value
+        else
+          super
+        end
+      end
+
       def read_attribute_before_type_cast(attr_name)
         if serialized_attributes.include?(attr_name)
           super.unserialized_value
@@ -97,6 +105,16 @@ module ActiveRecord
           super
         end
       end
+
+      def attributes_before_type_cast
+        super.dup.tap do |attributes|
+          self.class.serialized_attributes.each_key do |key|
+            if attributes.key?(key)
+              attributes[key] = attributes[key].unserialized_value
+            end
+          end
+        end
+      end
     end
   end
 end
diff --git a/lib/active_record/attribute_methods/time_zone_conversion.rb b/lib/active_record/attribute_methods/time_zone_conversion.rb
index 4c3d5ee..6f3112e 100644
--- a/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -37,18 +37,16 @@ module ActiveRecord
             if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
               method_body, line = <<-EOV, __LINE__ + 1
                 def #{attr_name}=(original_time)
+                  original_time = nil if original_time.blank?
                   time = original_time
                   unless time.acts_like?(:time)
                     time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
                   end
-                  zoned_time   = time && time.in_time_zone rescue nil
-                  rounded_time = round_usec(zoned_time)
-                  rounded_value = round_usec(read_attribute("#{attr_name}"))
-                  if (rounded_value != rounded_time) || (!rounded_value && original_time)
-                    write_attribute("#{attr_name}", original_time)
-                    #{attr_name}_will_change!
-                    @attributes_cache["#{attr_name}"] = zoned_time
-                  end
+                  time = time.in_time_zone rescue nil if time
+                  previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
+                  write_attribute(:#{attr_name}, original_time)
+                  #{attr_name}_will_change! if previous_time != time
+                  @attributes_cache["#{attr_name}"] = time
                 end
               EOV
               generated_attribute_methods.module_eval(method_body, __FILE__, line)
@@ -62,12 +60,6 @@ module ActiveRecord
             time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp])
           end
       end
-
-      private
-      def round_usec(value)
-        return unless value
-        value.change(:usec => 0)
-      end
     end
   end
 end
diff --git a/lib/active_record/attribute_methods/write.rb b/lib/active_record/attribute_methods/write.rb
index 8c6fa90..16aed73 100644
--- a/lib/active_record/attribute_methods/write.rb
+++ b/lib/active_record/attribute_methods/write.rb
@@ -54,12 +54,13 @@ module ActiveRecord
         end
 
         def convert_number_column_value(value)
-          if value == false
+          case value
+          when FalseClass
             0
-          elsif value == true
+          when TrueClass
             1
-          elsif value.is_a?(String) && value.blank?
-            nil
+          when String
+            value.presence
           else
             value
           end
diff --git a/lib/active_record/base.rb b/lib/active_record/base.rb
index 62c8110..5bd1721 100644
--- a/lib/active_record/base.rb
+++ b/lib/active_record/base.rb
@@ -479,7 +479,8 @@ 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 = self.class.initialize_attributes(self.class.column_defaults.dup)
+        defaults = Hash[self.class.column_defaults.map { |k, v| [k, v.duplicable? ? v.dup : v] }]
+        @attributes = self.class.initialize_attributes(defaults)
         @association_cache = {}
         @aggregation_cache = {}
         @attributes_cache = {}
@@ -552,12 +553,11 @@ module ActiveRecord #:nodoc:
         @new_record  = true
 
         ensure_proper_type
-        populate_with_current_scope_attributes
         super
       end
 
       # Backport dup from 1.9 so that initialize_dup() gets called
-      unless Object.respond_to?(:initialize_dup)
+      unless Object.respond_to?(:initialize_dup, true)
         def dup # :nodoc:
           copy = super
           copy.initialize_dup(self)
diff --git a/lib/active_record/connection_adapters/abstract/database_statements.rb b/lib/active_record/connection_adapters/abstract/database_statements.rb
index db99c3f..2703c2e 100644
--- a/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -266,7 +266,7 @@ module ActiveRecord
       # Inserts the given fixture into the table. Overridden in adapters that require
       # something beyond a simple insert (eg. Oracle).
       def insert_fixture(fixture, table_name)
-        columns = Hash[columns(table_name).map { |c| [c.name, c] }]
+        columns = schema_cache.columns_hash(table_name)
 
         key_list   = []
         value_list = fixture.map do |name, value|
diff --git a/lib/active_record/connection_adapters/abstract/quoting.rb b/lib/active_record/connection_adapters/abstract/quoting.rb
index fe0b195..f93c7cd 100644
--- a/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -25,19 +25,13 @@ module ActiveRecord
         when true, false
           if column && column.type == :integer
             value ? '1' : '0'
-          elsif column && [:text, :string, :binary].include?(column.type)
-            value ? "'1'" : "'0'"
           else
             value ? quoted_true : quoted_false
           end
           # BigDecimals need to be put in a non-normalized form and quoted.
         when nil        then "NULL"
-        when Numeric, ActiveSupport::Duration
-          value = BigDecimal === value ? value.to_s('F') : value.to_s
-          if column && ![:integer, :float, :decimal].include?(column.type)
-            value = "'#{value}'"
-          end
-          value
+        when BigDecimal then value.to_s('F')
+        when Numeric    then value.to_s
         when Date, Time then "'#{quoted_date(value)}'"
         when Symbol     then "'#{quote_string(value.to_s)}'"
         else
diff --git a/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 61c5e80..abccc3a 100644
--- a/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -199,6 +199,8 @@ module ActiveRecord
         if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
           s = column.class.string_to_binary(value).unpack("H*")[0]
           "x'#{s}'"
+        elsif value.kind_of?(BigDecimal)
+          value.to_s("F")
         else
           super
         end
diff --git a/lib/active_record/connection_adapters/column.rb b/lib/active_record/connection_adapters/column.rb
index e6269c7..e22d9f7 100644
--- a/lib/active_record/connection_adapters/column.rb
+++ b/lib/active_record/connection_adapters/column.rb
@@ -175,7 +175,7 @@ module ActiveRecord
           when TrueClass, FalseClass
             value ? 1 : 0
           else
-            value.to_i
+            value.to_i rescue nil
           end
         end
 
diff --git a/lib/active_record/connection_adapters/mysql_adapter.rb b/lib/active_record/connection_adapters/mysql_adapter.rb
index c132692..4850b68 100644
--- a/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -2,7 +2,7 @@ require 'active_record/connection_adapters/abstract_mysql_adapter'
 require 'active_record/connection_adapters/statement_pool'
 require 'active_support/core_ext/hash/keys'
 
-gem 'mysql', '~> 2.8.1'
+gem 'mysql', '~> 2.8'
 require 'mysql'
 
 class Mysql
diff --git a/lib/active_record/connection_adapters/postgresql_adapter.rb b/lib/active_record/connection_adapters/postgresql_adapter.rb
index 80a0c6d..8806693 100644
--- a/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -315,8 +315,6 @@ module ActiveRecord
           @visitor = BindSubstitution.new self
         end
 
-        connection_parameters.delete :prepared_statements
-
         @connection_parameters, @config = connection_parameters, config
 
         # @local_tz is initialized as nil to avoid warnings when connect tries to use it
@@ -988,12 +986,11 @@ module ActiveRecord
       # Returns just a table's primary key
       def primary_key(table)
         row = exec_query(<<-end_sql, 'SCHEMA').rows.first
-          SELECT DISTINCT(attr.attname)
+          SELECT attr.attname
           FROM pg_attribute attr
-          INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
           INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
           WHERE cons.contype = 'p'
-            AND dep.refobjid = '#{quote_table_name(table)}'::regclass
+            AND cons.conrelid = '#{quote_table_name(table)}'::regclass
         end_sql
 
         row && row.first
@@ -1078,6 +1075,13 @@ module ActiveRecord
           when nil, 0..0x3fffffff; super(type)
           else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
           end
+        when 'text'
+          # PostgreSQL doesn't support limits on text columns.
+          # The hard limit is 1Gb, according to section 8.3 in the manual.
+          case limit
+          when nil, 0..0x3fffffff; super(type)
+          else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
+          end
         when 'integer'
           return 'integer' unless limit
 
@@ -1135,11 +1139,15 @@ module ActiveRecord
           @connection.server_version
         end
 
+        # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
+        FOREIGN_KEY_VIOLATION = "23503"
+        UNIQUE_VIOLATION      = "23505"
+
         def translate_exception(exception, message)
-          case exception.message
-          when /duplicate key value violates unique constraint/
+          case exception.result.error_field(PGresult::PG_DIAG_SQLSTATE)
+          when UNIQUE_VIOLATION
             RecordNotUnique.new(message, exception)
-          when /violates foreign key constraint/
+          when FOREIGN_KEY_VIOLATION
             InvalidForeignKey.new(message, exception)
           else
             super
diff --git a/lib/active_record/connection_adapters/schema_cache.rb b/lib/active_record/connection_adapters/schema_cache.rb
index 4e8932a..bc8d24a 100644
--- a/lib/active_record/connection_adapters/schema_cache.rb
+++ b/lib/active_record/connection_adapters/schema_cache.rb
@@ -1,7 +1,7 @@
 module ActiveRecord
   module ConnectionAdapters
     class SchemaCache
-      attr_reader :columns, :columns_hash, :primary_keys, :tables
+      attr_reader :primary_keys, :tables
       attr_reader :connection
 
       def initialize(conn)
@@ -30,6 +30,25 @@ module ActiveRecord
         @tables[name] = connection.table_exists?(name)
       end
 
+      # Get the columns for a table
+      def columns(table = nil)
+        if table
+          @columns[table]
+        else
+          @columns
+        end
+      end
+
+      # Get the columns for a table as a hash, key is the column name
+      # value is the column object.
+      def columns_hash(table = nil)
+        if table
+          @columns_hash[table]
+        else
+          @columns_hash
+        end
+      end
+
       # Clears out internal caches
       def clear!
         @columns.clear
diff --git a/lib/active_record/connection_adapters/sqlite_adapter.rb b/lib/active_record/connection_adapters/sqlite_adapter.rb
index e80b465..455560a 100644
--- a/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -490,7 +490,11 @@ module ActiveRecord
         end
 
         def copy_table(from, to, options = {}) #:nodoc:
-          options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
+          from_primary_key = primary_key(from)
+          options[:primary_key] = from_primary_key if from_primary_key != 'id'
+          unless options[:primary_key]
+            options[:id] = !columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == from_primary_key
+          end
           create_table(to, options) do |definition|
             @definition = definition
             columns(from).each do |column|
@@ -504,7 +508,7 @@ module ActiveRecord
                 :precision => column.precision, :scale => column.scale,
                 :null => column.null)
             end
-            @definition.primary_key(primary_key(from)) if primary_key(from)
+            @definition.primary_key(from_primary_key) if from_primary_key
             yield @definition if block_given?
           end
 
@@ -530,7 +534,7 @@ module ActiveRecord
 
             unless columns.empty?
               # index name can't be the same
-              opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
+              opts = { :name => name.gsub(/(^|_)(#{from})_/, "\\1#{to}_") }
               opts[:unique] = true if index.unique
               add_index(to, columns, opts)
             end
diff --git a/lib/active_record/explain.rb b/lib/active_record/explain.rb
index bdccf53..236834f 100644
--- a/lib/active_record/explain.rb
+++ b/lib/active_record/explain.rb
@@ -11,11 +11,12 @@ module ActiveRecord
       end
     end
 
-    # If auto explain is enabled, this method triggers EXPLAIN logging for the
-    # queries triggered by the block if it takes more than the threshold as a
-    # whole. That is, the threshold is not checked against each individual
-    # query, but against the duration of the entire block. This approach is
-    # convenient for relations.
+    # If the database adapter supports explain and auto explain is enabled,
+    # this method triggers EXPLAIN logging for the queries triggered by the
+    # block if it takes more than the threshold as a whole. That is, the
+    # threshold is not checked against each individual query, but against the
+    # duration of the entire block. This approach is convenient for relations.
+
     #
     # The available_queries_for_explain thread variable collects the queries
     # to be explained. If the value is nil, it means queries are not being
@@ -26,7 +27,7 @@ module ActiveRecord
 
       threshold = auto_explain_threshold_in_seconds
       current   = Thread.current
-      if threshold && current[:available_queries_for_explain].nil?
+      if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
         begin
           queries = current[:available_queries_for_explain] = []
           start = Time.now
diff --git a/lib/active_record/integration.rb b/lib/active_record/integration.rb
index 2c42f4c..f2ace18 100644
--- a/lib/active_record/integration.rb
+++ b/lib/active_record/integration.rb
@@ -1,5 +1,16 @@
 module ActiveRecord
   module Integration
+    extend ActiveSupport::Concern
+
+    included do
+      ##
+      # :singleton-method:
+      # Indicates the format used to generate the timestamp format in the cache key.
+      # This is +:number+, by default.
+      class_attribute :cache_timestamp_format, :instance_writer => false
+      self.cache_timestamp_format = :number
+    end
+
     # Returns a String, which Action Pack uses for constructing an URL to this
     # object. The default implementation returns this record's id as a String,
     # or nil if this record's unsaved.
@@ -39,7 +50,7 @@ module ActiveRecord
       when new_record?
         "#{self.class.model_name.cache_key}/new"
       when timestamp = self[:updated_at]
-        timestamp = timestamp.utc.to_s(:number)
+        timestamp = timestamp.utc.to_s(cache_timestamp_format)
         "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
       else
         "#{self.class.model_name.cache_key}/#{id}"
diff --git a/lib/active_record/locking/optimistic.rb b/lib/active_record/locking/optimistic.rb
index 7deac25..b288199 100644
--- a/lib/active_record/locking/optimistic.rb
+++ b/lib/active_record/locking/optimistic.rb
@@ -102,6 +102,8 @@ module ActiveRecord
         def destroy #:nodoc:
           return super unless locking_enabled?
 
+          destroy_associations
+
           if persisted?
             table = self.class.arel_table
             lock_col = self.class.locking_column
diff --git a/lib/active_record/log_subscriber.rb b/lib/active_record/log_subscriber.rb
index a25f2c7..2b6488f 100644
--- a/lib/active_record/log_subscriber.rb
+++ b/lib/active_record/log_subscriber.rb
@@ -32,7 +32,11 @@ module ActiveRecord
 
       unless (payload[:binds] || []).empty?
         binds = "  " + payload[:binds].map { |col,v|
-          [col.name, v]
+          if col
+            [col.name, v]
+          else
+            [nil, v]
+          end
         }.inspect
       end
 
diff --git a/lib/active_record/migration.rb b/lib/active_record/migration.rb
index e668659..e8ec934 100644
--- a/lib/active_record/migration.rb
+++ b/lib/active_record/migration.rb
@@ -458,7 +458,7 @@ module ActiveRecord
       say_with_time "#{method}(#{arg_list})" do
         unless reverting?
           unless arguments.empty? || method == :execute
-            arguments[0] = Migrator.proper_table_name(arguments.first)
+            arguments[0] = Migrator.proper_table_name(arguments.first) unless method == :assume_migrated_upto_version
             arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
           end
         end
@@ -627,7 +627,7 @@ module ActiveRecord
         seen = Hash.new false
 
         migrations = files.map do |file|
-          version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
+          version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
 
           raise IllegalMigrationNameError.new(file) unless version
           version = version.to_i
diff --git a/lib/active_record/nested_attributes.rb b/lib/active_record/nested_attributes.rb
index d2065d7..0509165 100644
--- a/lib/active_record/nested_attributes.rb
+++ b/lib/active_record/nested_attributes.rb
@@ -277,13 +277,14 @@ module ActiveRecord
 
             type = (reflection.collection? ? :collection : :one_to_one)
 
+            # remove_possible_method :pirate_attributes=
+            #
             # def pirate_attributes=(attributes)
             #   assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
             # end
             class_eval <<-eoruby, __FILE__, __LINE__ + 1
-              if method_defined?(:#{association_name}_attributes=)
-                remove_method(:#{association_name}_attributes=)
-              end
+              remove_possible_method(:#{association_name}_attributes=)
+
               def #{association_name}_attributes=(attributes)
                 assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
               end
diff --git a/lib/active_record/persistence.rb b/lib/active_record/persistence.rb
index fd3380a..36b6b5f 100644
--- a/lib/active_record/persistence.rb
+++ b/lib/active_record/persistence.rb
@@ -194,7 +194,7 @@ module ActiveRecord
       raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
       raise ActiveRecordError, "can not update on a new record object" unless persisted?
 
-      updated_count = self.class.update_all({ name => value }, self.class.primary_key => id)
+      updated_count = self.class.unscoped.update_all({ name => value }, self.class.primary_key => id)
 
       raw_write_attribute(name, value)
 
diff --git a/lib/active_record/query_cache.rb b/lib/active_record/query_cache.rb
index 466d148..2156889 100644
--- a/lib/active_record/query_cache.rb
+++ b/lib/active_record/query_cache.rb
@@ -6,19 +6,19 @@ module ActiveRecord
     module ClassMethods
       # Enable the query cache within the block if Active Record is configured.
       def cache(&block)
-        if ActiveRecord::Base.configurations.blank?
-          yield
-        else
+        if ActiveRecord::Base.connected?
           connection.cache(&block)
+        else
+          yield
         end
       end
 
       # Disable the query cache within the block if Active Record is configured.
       def uncached(&block)
-        if ActiveRecord::Base.configurations.blank?
-          yield
-        else
+        if ActiveRecord::Base.connected?
           connection.uncached(&block)
+        else
+          yield
         end
       end
     end
diff --git a/lib/active_record/railtie.rb b/lib/active_record/railtie.rb
index 13b7c6e..055d27d 100644
--- a/lib/active_record/railtie.rb
+++ b/lib/active_record/railtie.rb
@@ -83,6 +83,13 @@ module ActiveRecord
       end
     end
 
+    initializer "active_record.validate_explain_support" do |app|
+      if app.config.active_record[:auto_explain_threshold_in_seconds] &&
+        !ActiveRecord::Base.connection.supports_explain?
+        warn "auto_explain_threshold_in_seconds is set but will be ignored because your adapter does not support this feature. Please unset the configuration to avoid this warning."
+      end
+    end
+
     # Expose database runtime to controller for logging.
     initializer "active_record.log_runtime" do |app|
       require "active_record/railties/controller_runtime"
diff --git a/lib/active_record/railties/databases.rake b/lib/active_record/railties/databases.rake
index ae6d9c8..8f7c7f6 100644
--- a/lib/active_record/railties/databases.rake
+++ b/lib/active_record/railties/databases.rake
@@ -64,10 +64,21 @@ db_namespace = namespace :db do
     end
   end
 
+  # If neither encoding nor collation is specified, use the utf-8 defaults.
   def mysql_creation_options(config)
-    @charset   = ENV['CHARSET']   || 'utf8'
-    @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
-    {:charset => (config['encoding'] || @charset), :collation => (config['collation'] || @collation)}
+    default_charset   = ENV['CHARSET']    || 'utf8'
+    default_collation = ENV['COLLATION']  || 'utf8_unicode_ci'
+
+    Hash.new.tap do |options|
+      options[:charset]     = config['encoding']   if config.include? 'encoding'
+      options[:collation]   = config['collation']  if config.include? 'collation'
+
+      # Set default charset only when collation isn't set.
+      options[:charset]   ||= default_charset unless options[:collation]
+
+      # Set default collation only when charset is also default.
+      options[:collation] ||= default_collation if options[:charset] == default_charset
+    end
   end
 
   def create_database(config)
@@ -101,9 +112,12 @@ db_namespace = namespace :db do
           error_class = config['adapter'] =~ /mysql2/ ? Mysql2::Error : Mysql::Error
         end
         access_denied_error = 1045
+
+        create_options = mysql_creation_options(config)
+
         begin
           ActiveRecord::Base.establish_connection(config.merge('database' => nil))
-          ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
+          ActiveRecord::Base.connection.create_database(config['database'], create_options)
           ActiveRecord::Base.establish_connection(config)
         rescue error_class => sqlerr
           if sqlerr.errno == access_denied_error
@@ -119,7 +133,7 @@ db_namespace = namespace :db do
             ActiveRecord::Base.establish_connection(config)
           else
             $stderr.puts sqlerr.error
-            $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['encoding'] || @charset}, collation: #{config['collation'] || @collation}"
+            $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{create_options[:charset]}, collation: #{create_options[:collation]}"
             $stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['encoding']
           end
         end
@@ -419,11 +433,11 @@ db_namespace = namespace :db do
       when 'sqlserver'
         `smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f #{filename} -A -U`
       when "firebird"
-        set_firebird_env(abcs[Rails.env])
-        db_string = firebird_db_string(abcs[Rails.env])
+        set_firebird_env(config)
+        db_string = firebird_db_string(config)
         sh "isql -a #{db_string} > #{filename}"
       else
-        raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
+        raise "Task not supported by '#{config['adapter']}'"
       end
 
       if ActiveRecord::Base.connection.supports_migrations?
diff --git a/lib/active_record/relation.rb b/lib/active_record/relation.rb
index 4b3b30d..ae1a575 100644
--- a/lib/active_record/relation.rb
+++ b/lib/active_record/relation.rb
@@ -464,7 +464,7 @@ module ActiveRecord
         node.left.relation.name == table_name
       }
 
-      Hash[equalities.map { |where| [where.left.name, where.right] }]
+      Hash[equalities.map { |where| [where.left.name, where.right] }].with_indifferent_access
     end
 
     def scope_for_create
diff --git a/lib/active_record/relation/batches.rb b/lib/active_record/relation/batches.rb
index 2fd8988..14701f6 100644
--- a/lib/active_record/relation/batches.rb
+++ b/lib/active_record/relation/batches.rb
@@ -59,11 +59,11 @@ module ActiveRecord
         relation = apply_finder_options(finder_options)
       end
 
-      start = options.delete(:start).to_i
+      start = options.delete(:start)
       batch_size = options.delete(:batch_size) || 1000
 
       relation = relation.reorder(batch_order).limit(batch_size)
-      records = relation.where(table[primary_key].gteq(start)).all
+      records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
 
       while records.any?
         records_size = records.size
diff --git a/lib/active_record/relation/calculations.rb b/lib/active_record/relation/calculations.rb
index 802059d..1f9dbdc 100644
--- a/lib/active_record/relation/calculations.rb
+++ b/lib/active_record/relation/calculations.rb
@@ -177,8 +177,15 @@ module ActiveRecord
     #   Person.where(:confirmed => true).limit(5).pluck(:id)
     #
     def pluck(column_name)
-      column_name = column_name.to_s
-      klass.connection.select_all(select(column_name).arel).map! do |attributes|
+      if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
+        column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
+      else
+        column_name = column_name.to_s
+      end
+
+      relation = clone
+      relation.select_values = [column_name]
+      klass.connection.select_all(relation.arel).map! do |attributes|
         klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
       end
     end
@@ -188,7 +195,8 @@ module ActiveRecord
     def perform_calculation(operation, column_name, options = {})
       operation = operation.to_s.downcase
 
-      distinct = options[:distinct]
+      # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
+      distinct = options[:distinct] || self.uniq_value
 
       if operation == "count"
         column_name ||= (select_for_count || :all)
diff --git a/lib/active_record/relation/finder_methods.rb b/lib/active_record/relation/finder_methods.rb
index abc67d9..08cfe4f 100644
--- a/lib/active_record/relation/finder_methods.rb
+++ b/lib/active_record/relation/finder_methods.rb
@@ -253,9 +253,11 @@ module ActiveRecord
       orders = relation.order_values.map { |val| val.presence }.compact
       values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
 
-      relation = relation.dup
+      relation = relation.dup.select(values)
+
+      id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
+      ids_array = id_rows.map {|row| row[primary_key]}
 
-      ids_array = relation.select(values).collect {|row| row[primary_key]}
       ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
     end
 
@@ -263,7 +265,7 @@ module ActiveRecord
       conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
       result = where(conditions).send(match.finder)
 
-      if match.bang? && result.blank?
+      if match.bang? && result.nil?
         raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
       else
         yield(result) if block_given?
diff --git a/lib/active_record/relation/predicate_builder.rb b/lib/active_record/relation/predicate_builder.rb
index 236fd5c..413b81c 100644
--- a/lib/active_record/relation/predicate_builder.rb
+++ b/lib/active_record/relation/predicate_builder.rb
@@ -20,7 +20,7 @@ module ActiveRecord
             table = Arel::Table.new(table_name, engine)
           end
 
-          attribute = table[column.to_sym]
+          attribute = table[column]
 
           case value
           when ActiveRecord::Relation
@@ -51,10 +51,6 @@ module ActiveRecord
           when Class
             # FIXME: I think we need to deprecate this behavior
             attribute.eq(value.name)
-          when Integer, ActiveSupport::Duration
-            # Arel treats integers as literals, but they should be quoted when compared with strings
-            column = engine.connection.schema_cache.columns_hash[table.name][attribute.name.to_s]
-            attribute.eq(Arel::Nodes::SqlLiteral.new(engine.connection.quote(value, column)))
           else
             attribute.eq(value)
           end
diff --git a/lib/active_record/result.rb b/lib/active_record/result.rb
index 9ceab2e..b8d2cd2 100644
--- a/lib/active_record/result.rb
+++ b/lib/active_record/result.rb
@@ -26,9 +26,15 @@ module ActiveRecord
 
     private
     def hash_rows
-      @hash_rows ||= @rows.map { |row|
-        Hash[@columns.zip(row)]
-      }
+      @hash_rows ||=
+        begin
+          # We freeze the strings to prevent them getting duped when
+          # used as keys in ActiveRecord::Model's @attributes hash
+          columns = @columns.map { |c| c.dup.freeze }
+          @rows.map { |row|
+            Hash[columns.zip(row)]
+          }
+        end
     end
   end
 end
diff --git a/lib/active_record/scoping/named.rb b/lib/active_record/scoping/named.rb
index d6b0265..767b30f 100644
--- a/lib/active_record/scoping/named.rb
+++ b/lib/active_record/scoping/named.rb
@@ -161,16 +161,14 @@ module ActiveRecord
         #     end
         #
         #     def self.titles
-        #       map(&:title)
+        #       pluck(:title)
         #     end
-        #
         #   end
         #
         # We are able to call the methods like this:
         #
         #   Article.published.featured.latest_article
         #   Article.featured.titles
-
         def scope(name, scope_options = {})
           name = name.to_sym
           valid_scope_name?(name)
diff --git a/lib/active_record/validations/uniqueness.rb b/lib/active_record/validations/uniqueness.rb
index 154aace..7950885 100644
--- a/lib/active_record/validations/uniqueness.rb
+++ b/lib/active_record/validations/uniqueness.rb
@@ -26,7 +26,7 @@ module ActiveRecord
         relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
 
         Array.wrap(options[:scope]).each do |scope_item|
-          scope_value = record.send(scope_item)
+          scope_value = record.read_attribute(scope_item)
           relation = relation.and(table[scope_item].eq(scope_value))
         end
 
diff --git a/lib/active_record/version.rb b/lib/active_record/version.rb
index a340cfa..9069d99 100644
--- a/lib/active_record/version.rb
+++ b/lib/active_record/version.rb
@@ -2,7 +2,7 @@ module ActiveRecord
   module VERSION #:nodoc:
     MAJOR = 3
     MINOR = 2
-    TINY  = 12
+    TINY  = 13
     PRE   = nil
 
     STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/metadata.yml b/metadata.yml
index 71e1f36..336bf0c 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,14 +1,14 @@
 --- !ruby/object:Gem::Specification
 name: activerecord
 version: !ruby/object:Gem::Version
-  version: 3.2.12
+  version: 3.2.13
 platform: ruby
 authors:
 - David Heinemeier Hansson
 autorequire: 
 bindir: bin
 cert_chain: []
-date: 2013-02-11 00:00:00.000000000 Z
+date: 2013-03-18 00:00:00.000000000 Z
 dependencies:
 - !ruby/object:Gem::Dependency
   name: activesupport
@@ -16,54 +16,54 @@ dependencies:
     requirements:
     - - '='
       - !ruby/object:Gem::Version
-        version: 3.2.12
+        version: 3.2.13
   type: :runtime
   prerelease: false
   version_requirements: !ruby/object:Gem::Requirement
     requirements:
     - - '='
       - !ruby/object:Gem::Version
-        version: 3.2.12
+        version: 3.2.13
 - !ruby/object:Gem::Dependency
   name: activemodel
   requirement: !ruby/object:Gem::Requirement
     requirements:
     - - '='
       - !ruby/object:Gem::Version
-        version: 3.2.12
+        version: 3.2.13
   type: :runtime
   prerelease: false
   version_requirements: !ruby/object:Gem::Requirement
     requirements:
     - - '='
       - !ruby/object:Gem::Version
-        version: 3.2.12
+        version: 3.2.13
 - !ruby/object:Gem::Dependency
   name: arel
   requirement: !ruby/object:Gem::Requirement
     requirements:
-    - - "~>"
+    - - ~>
       - !ruby/object:Gem::Version
         version: 3.0.2
   type: :runtime
   prerelease: false
   version_requirements: !ruby/object:Gem::Requirement
     requirements:
-    - - "~>"
+    - - ~>
       - !ruby/object:Gem::Version
         version: 3.0.2
 - !ruby/object:Gem::Dependency
   name: tzinfo
   requirement: !ruby/object:Gem::Requirement
     requirements:
-    - - "~>"
+    - - ~>
       - !ruby/object:Gem::Version
         version: 0.3.29
   type: :runtime
   prerelease: false
   version_requirements: !ruby/object:Gem::Requirement
     requirements:
-    - - "~>"
+    - - ~>
       - !ruby/object:Gem::Version
         version: 0.3.29
 description: Databases on Rails. Build a persistent domain model by mapping database
@@ -229,23 +229,23 @@ licenses: []
 metadata: {}
 post_install_message: 
 rdoc_options:
-- "--main"
+- --main
 - README.rdoc
 require_paths:
 - lib
 required_ruby_version: !ruby/object:Gem::Requirement
   requirements:
-  - - ">="
+  - - '>='
     - !ruby/object:Gem::Version
       version: 1.8.7
 required_rubygems_version: !ruby/object:Gem::Requirement
   requirements:
-  - - ">="
+  - - '>='
     - !ruby/object:Gem::Version
       version: '0'
 requirements: []
 rubyforge_project: 
-rubygems_version: 2.0.0.rc.2
+rubygems_version: 2.0.2
 signing_key: 
 specification_version: 4
 summary: Object-relational mapper framework (part of Rails).

-- 
ruby-activerecord-3.2.git



More information about the Pkg-ruby-extras-commits mailing list