diff options
Diffstat (limited to 'activerecord')
72 files changed, 728 insertions, 390 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 28d8b066e0..f166d73801 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,53 @@  ## Rails 4.0.0 (unreleased) ## +*   Fix postgresql adapter to handle BC timestamps correctly + +        HistoryEvent.create!(:name => "something", :occured_at => Date.new(0) - 5.years) + +    *Bogdan Gusiev* + +*   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* + +*   Don't change STI type when calling `ActiveRecord::Base#becomes`. +    Add `ActiveRecord::Base#becomes!` with the previous behavior. + +    See #3023 for more information. + +    *Thomas Hollstegge* + +*   `rename_index` can be used inside a `change_table` block. + +        change_table :accounts do |t| +          t.rename_index :user_id, :account_id +        end + +    *Jarek Radosz* + +*   `#pluck` can be used on a relation with `select` clause. Fix #7551 + +    Example: + +        Topic.select([:approved, :id]).order(:id).pluck(:id) + +    *Yves Senn* + +*   Do not create useless database transaction when building `has_one` association. + +    Example: + +        User.has_one :profile +        User.new.build_profile + +    *Bogdan Gusiev* + +*   `:counter_cache` option for `has_many` associations to support custom named counter caches. +    Fix #7993 + +    *Yves Senn* +  *   Deprecate the possibility to pass a string as third argument of `add_index`.      Pass `unique: true` instead. @@ -19,7 +67,7 @@      *Nikita Afanasenko* -*   Use query cache/uncache when using DATABASE_URL. +*   Use query cache/uncache when using `DATABASE_URL`.      Fix #6951.      *kennyj* diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 53791d96ef..31ddb01123 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -1,4 +1,4 @@ -version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip +version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip  Gem::Specification.new do |s|    s.platform    = Gem::Platform::RUBY @@ -8,21 +8,22 @@ Gem::Specification.new do |s|    s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.'    s.required_ruby_version = '>= 1.9.3' -  s.license     = 'MIT' -  s.author            = 'David Heinemeier Hansson' -  s.email             = 'david@loudthinking.com' -  s.homepage          = 'http://www.rubyonrails.org' +  s.license = 'MIT' + +  s.author   = 'David Heinemeier Hansson' +  s.email    = 'david@loudthinking.com' +  s.homepage = 'http://www.rubyonrails.org'    s.files        = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*']    s.require_path = 'lib' -  s.extra_rdoc_files = %w( README.rdoc ) +  s.extra_rdoc_files = %w(README.rdoc)    s.rdoc_options.concat ['--main',  'README.rdoc'] -  s.add_dependency('activesupport', version) -  s.add_dependency('activemodel',   version) -  s.add_dependency('arel',          '~> 3.0.2') +  s.add_dependency 'activesupport', version +  s.add_dependency 'activemodel',   version -  s.add_dependency('activerecord-deprecated_finders', '0.0.1') +  s.add_dependency 'arel',                            '~> 3.0.2' +  s.add_dependency 'activerecord-deprecated_finders', '0.0.1'  end diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 3db8e0716b..8101f7a45e 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -16,8 +16,8 @@ module ActiveRecord      # the database).      #      #   class Customer < ActiveRecord::Base -    #     composed_of :balance, :class_name => "Money", :mapping => %w(balance amount) -    #     composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ] +    #     composed_of :balance, class_name: "Money", mapping: %w(balance amount) +    #     composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]      #   end      #      # The customer class now has the following methods to manipulate the value objects: @@ -138,15 +138,15 @@ module ActiveRecord      #      #   class NetworkResource < ActiveRecord::Base      #     composed_of :cidr, -    #                 :class_name => 'NetAddr::CIDR', -    #                 :mapping => [ %w(network_address network), %w(cidr_range bits) ], -    #                 :allow_nil => true, -    #                 :constructor => Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") }, -    #                 :converter => Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) } +    #                 class_name: 'NetAddr::CIDR', +    #                 mapping: [ %w(network_address network), %w(cidr_range bits) ], +    #                 allow_nil: true, +    #                 constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") }, +    #                 converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }      #   end      #      #   # This calls the :constructor -    #   network_resource = NetworkResource.new(:network_address => '192.168.0.1', :cidr_range => 24) +    #   network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)      #      #   # These assignments will both use the :converter      #   network_resource.cidr = [ '192.168.2.1', 8 ] @@ -165,7 +165,7 @@ module ActiveRecord      # by specifying an instance of the value object in the conditions hash. The following example      # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":      # -    #   Customer.where(:balance => Money.new(20, "USD")).all +    #   Customer.where(balance: Money.new(20, "USD")).all      #      module ClassMethods        # Adds reader and writer methods for manipulating a value object: @@ -197,17 +197,17 @@ module ActiveRecord        #   can return nil to skip the assignment.        #        # Option examples: -      #   composed_of :temperature, :mapping => %w(reading celsius) -      #   composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), -      #                         :converter => Proc.new { |balance| balance.to_money } -      #   composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ] +      #   composed_of :temperature, mapping: %w(reading celsius) +      #   composed_of :balance, class_name: "Money", mapping: %w(balance amount), +      #                         converter: Proc.new { |balance| balance.to_money } +      #   composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]        #   composed_of :gps_location -      #   composed_of :gps_location, :allow_nil => true +      #   composed_of :gps_location, allow_nil: true        #   composed_of :ip_address, -      #               :class_name => 'IPAddr', -      #               :mapping => %w(ip to_i), -      #               :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) }, -      #               :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) } +      #               class_name: 'IPAddr', +      #               mapping: %w(ip to_i), +      #               constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) }, +      #               converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }        #        def composed_of(part_id, options = {})          options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 69b95f814c..651b17920c 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1,6 +1,9 @@  require 'active_support/core_ext/enumerable'  require 'active_support/core_ext/string/conversions'  require 'active_support/core_ext/module/remove_method' +require 'active_support/dependencies/autoload' +require 'active_support/concern' +require 'active_record/errors'  module ActiveRecord    class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: @@ -305,11 +308,11 @@ module ActiveRecord      #   end      #   class Programmer < ActiveRecord::Base      #     has_many :assignments -    #     has_many :projects, :through => :assignments +    #     has_many :projects, through: :assignments      #   end      #   class Project < ActiveRecord::Base      #     has_many :assignments -    #     has_many :programmers, :through => :assignments +    #     has_many :programmers, through: :assignments      #   end      #      # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table @@ -426,7 +429,7 @@ module ActiveRecord      # object from an association collection.      #      #   class Project -    #     has_and_belongs_to_many :developers, :after_add => :evaluate_velocity +    #     has_and_belongs_to_many :developers, after_add: :evaluate_velocity      #      #     def evaluate_velocity(developer)      #       ... @@ -437,7 +440,7 @@ module ActiveRecord      #      #   class Project      #     has_and_belongs_to_many :developers, -    #                             :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}] +    #                             after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]      #   end      #      # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+. @@ -507,7 +510,7 @@ module ActiveRecord      #      #   class Author < ActiveRecord::Base      #     has_many :authorships -    #     has_many :books, :through => :authorships +    #     has_many :books, through: :authorships      #   end      #      #   class Authorship < ActiveRecord::Base @@ -523,7 +526,7 @@ module ActiveRecord      #      #   class Firm < ActiveRecord::Base      #     has_many   :clients -    #     has_many   :invoices, :through => :clients +    #     has_many   :invoices, through: :clients      #   end      #      #   class Client < ActiveRecord::Base @@ -543,7 +546,7 @@ module ActiveRecord      #      #   class Group < ActiveRecord::Base      #     has_many   :users -    #     has_many   :avatars, :through => :users +    #     has_many   :avatars, through: :users      #   end      #      #   class User < ActiveRecord::Base @@ -571,7 +574,7 @@ module ActiveRecord      # works correctly (where <tt>tags</tt> is a +has_many+ <tt>:through</tt> association):      #      #   @post = Post.first -    #   @tag = @post.tags.build :name => "ruby" +    #   @tag = @post.tags.build name: "ruby"      #   @tag.save      #      # The last line ought to save the through record (a <tt>Taggable</tt>). This will only work if the @@ -579,7 +582,7 @@ module ActiveRecord      #      #   class Taggable < ActiveRecord::Base      #     belongs_to :post -    #     belongs_to :tag, :inverse_of => :taggings +    #     belongs_to :tag, inverse_of: :taggings      #   end      #      # == Nested Associations @@ -589,8 +592,8 @@ module ActiveRecord      #      #   class Author < ActiveRecord::Base      #     has_many :posts -    #     has_many :comments, :through => :posts -    #     has_many :commenters, :through => :comments +    #     has_many :comments, through: :posts +    #     has_many :commenters, through: :comments      #   end      #      #   class Post < ActiveRecord::Base @@ -608,12 +611,12 @@ module ActiveRecord      #      #   class Author < ActiveRecord::Base      #     has_many :posts -    #     has_many :commenters, :through => :posts +    #     has_many :commenters, through: :posts      #   end      #      #   class Post < ActiveRecord::Base      #     has_many :comments -    #     has_many :commenters, :through => :comments +    #     has_many :commenters, through: :comments      #   end      #      #   class Comment < ActiveRecord::Base @@ -632,11 +635,11 @@ module ActiveRecord      # must adhere to.      #      #   class Asset < ActiveRecord::Base -    #     belongs_to :attachable, :polymorphic => true +    #     belongs_to :attachable, polymorphic: true      #   end      #      #   class Post < ActiveRecord::Base -    #     has_many :assets, :as => :attachable         # The :as option specifies the polymorphic interface to use. +    #     has_many :assets, as: :attachable         # The :as option specifies the polymorphic interface to use.      #   end      #      #   @asset.attachable = @post @@ -653,7 +656,7 @@ module ActiveRecord      # column in the posts table.      #      #   class Asset < ActiveRecord::Base -    #     belongs_to :attachable, :polymorphic => true +    #     belongs_to :attachable, polymorphic: true      #      #     def attachable_type=(sType)      #        super(sType.to_s.classify.constantize.base_class.to_s) @@ -661,8 +664,8 @@ module ActiveRecord      #   end      #      #   class Post < ActiveRecord::Base -    #     # because we store "Post" in attachable_type now :dependent => :destroy will work -    #     has_many :assets, :as => :attachable, :dependent => :destroy +    #     # because we store "Post" in attachable_type now dependent: :destroy will work +    #     has_many :assets, as: :attachable, dependent: :destroy      #   end      #      #   class GuestPost < Post @@ -724,7 +727,7 @@ module ActiveRecord      #      # To include a deep hierarchy of associations, use a hash:      # -    #   Post.includes(:author, {:comments => {:author => :gravatar}}).each do |post| +    #   Post.includes(:author, {comments: {author: :gravatar}}).each do |post|      #      # That'll grab not only all the comments but all their authors and gravatar pictures.      # You can mix and match symbols, arrays and hashes in any combination to describe the @@ -749,13 +752,13 @@ module ActiveRecord      # In the above example posts with no approved comments are not returned at all, because      # the conditions apply to the SQL statement as a whole and not just to the association.      # You must disambiguate column references for this fallback to happen, for example -    # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not. +    # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.      #      # If you do want eager load only some members of an association it is usually more natural      # to include an association which has conditions defined on it:      #      #   class Post < ActiveRecord::Base -    #     has_many :approved_comments, -> { where approved: true }, :class_name => 'Comment' +    #     has_many :approved_comments, -> { where approved: true }, class_name: 'Comment'      #   end      #      #   Post.includes(:approved_comments) @@ -767,7 +770,7 @@ module ActiveRecord      # returning all the associated objects:      #      #   class Picture < ActiveRecord::Base -    #     has_many :most_recent_comments, -> { order('id DESC').limit(10) }, :class_name => 'Comment' +    #     has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment'      #   end      #      #   Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments. @@ -775,7 +778,7 @@ module ActiveRecord      # Eager loading is supported with polymorphic associations.      #      #   class Address < ActiveRecord::Base -    #     belongs_to :addressable, :polymorphic => true +    #     belongs_to :addressable, polymorphic: true      #   end      #      # A call that tries to eager load the addressable model @@ -809,10 +812,10 @@ module ActiveRecord      #      #   TreeMixin.joins(:children)      #   # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ... -    #   TreeMixin.joins(:children => :parent) +    #   TreeMixin.joins(children: :parent)      #   # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...      #                               INNER JOIN parents_mixins ... -    #   TreeMixin.joins(:children => {:parent => :children}) +    #   TreeMixin.joins(children: {parent: :children})      #   # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...      #                               INNER JOIN parents_mixins ...      #                               INNER JOIN mixins childrens_mixins_2 @@ -821,10 +824,10 @@ module ActiveRecord      #      #   Post.joins(:categories)      #   # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ... -    #   Post.joins(:categories => :posts) +    #   Post.joins(categories: :posts)      #   # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...      #                              INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories -    #   Post.joins(:categories => {:posts => :categories}) +    #   Post.joins(categories: {posts: :categories})      #   # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...      #                              INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories      #                              INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2 @@ -868,7 +871,7 @@ module ActiveRecord      #      #     module Billing      #       class Account < ActiveRecord::Base -    #         belongs_to :firm, :class_name => "MyApplication::Business::Firm" +    #         belongs_to :firm, class_name: "MyApplication::Business::Firm"      #       end      #     end      #   end @@ -910,16 +913,16 @@ module ActiveRecord      # example, if we changed our model definitions to:      #      #    class Dungeon < ActiveRecord::Base -    #      has_many :traps, :inverse_of => :dungeon -    #      has_one :evil_wizard, :inverse_of => :dungeon +    #      has_many :traps, inverse_of: :dungeon +    #      has_one :evil_wizard, inverse_of: :dungeon      #    end      #      #    class Trap < ActiveRecord::Base -    #      belongs_to :dungeon, :inverse_of => :traps +    #      belongs_to :dungeon, inverse_of: :traps      #    end      #      #    class EvilWizard < ActiveRecord::Base -    #      belongs_to :dungeon, :inverse_of => :evil_wizard +    #      belongs_to :dungeon, inverse_of: :evil_wizard      #    end      #      # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same @@ -942,7 +945,7 @@ module ActiveRecord      # For example:      #      #     class Author -    #       has_many :posts, :dependent => :destroy +    #       has_many :posts, dependent: :destroy      #     end      #     Author.find(1).destroy # => Will destroy all of the author's posts, too      # @@ -1026,12 +1029,12 @@ module ActiveRecord        #   parent object.        # [collection.delete(object, ...)]        #   Removes one or more objects from the collection by setting their foreign keys to +NULL+. -      #   Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>, -      #   and deleted if they're associated with <tt>:dependent => :delete_all</tt>. +      #   Objects will be in addition destroyed if they're associated with <tt>dependent: :destroy</tt>, +      #   and deleted if they're associated with <tt>dependent: :delete_all</tt>.        #        #   If the <tt>:through</tt> option is used, then the join records are deleted (rather than -      #   nullified) by default, but you can specify <tt>:dependent => :destroy</tt> or -      #   <tt>:dependent => :nullify</tt> to override this. +      #   nullified) by default, but you can specify <tt>dependent: :destroy</tt> or +      #   <tt>dependent: :nullify</tt> to override this.        # [collection.destroy(object, ...)]        #   Removes one or more objects from the collection by running <tt>destroy</tt> on        #   each record, regardless of any dependent option, ensuring callbacks are run. @@ -1049,8 +1052,8 @@ module ActiveRecord        #   method loads the models and calls <tt>collection=</tt>. See above.        # [collection.clear]        #   Removes every object from the collection. This destroys the associated objects if they -      #   are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the -      #   database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+. +      #   are associated with <tt>dependent: :destroy</tt>, deletes them directly from the +      #   database if <tt>dependent: :delete_all</tt>, otherwise sets their foreign keys to +NULL+.        #   If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.        #   Join models are directly deleted.        # [collection.empty?] @@ -1078,7 +1081,7 @@ module ActiveRecord        # === Example        #        # Example: A Firm class declares <tt>has_many :clients</tt>, which will add: -      # * <tt>Firm#clients</tt> (similar to <tt>Clients.all :conditions => ["firm_id = ?", id]</tt>) +      # * <tt>Firm#clients</tt> (similar to <tt>Clients.all conditions: ["firm_id = ?", id]</tt>)        # * <tt>Firm#clients<<</tt>        # * <tt>Firm#clients.delete</tt>        # * <tt>Firm#clients.destroy</tt> @@ -1088,8 +1091,8 @@ module ActiveRecord        # * <tt>Firm#clients.clear</tt>        # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)        # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>) -      # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>) -      # * <tt>Firm#clients.exists?(:name => 'ACME')</tt> (similar to <tt>Client.exists?(:name => 'ACME', :firm_id => firm.id)</tt>) +      # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, conditions: "firm_id = #{id}")</tt>) +      # * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)        # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)        # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)        # The declaration can also include an options hash to specialize the behavior of the association. @@ -1122,6 +1125,9 @@ module ActiveRecord        #   If using with the <tt>:through</tt> option, the association on the join model must be        #   a +belongs_to+, and the records which get deleted are the join records, rather than        #   the associated records. +      # [:counter_cache] +      #   This option can be used to configure a custom named <tt>:counter_cache.</tt> You only need this option, +      #   when you customized the name of your <tt>:counter_cache</tt> on the <tt>belongs_to</tt> association.        # [:as]        #   Specifies a polymorphic interface (See <tt>belongs_to</tt>).        # [:through] @@ -1143,7 +1149,7 @@ module ActiveRecord        # [:source]        #   Specifies the source association name used by <tt>has_many :through</tt> queries.        #   Only use it if the name cannot be inferred from the association. -      #   <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or +      #   <tt>has_many :subscribers, through: :subscriptions</tt> will look for either <tt>:subscribers</tt> or        #   <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.        # [:source_type]        #   Specifies type of the source association used by <tt>has_many :through</tt> queries where the source @@ -1207,7 +1213,7 @@ module ActiveRecord        # === Example        #        # An Account class declares <tt>has_one :beneficiary</tt>, which will add: -      # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(:conditions => "account_id = #{id}")</tt>) +      # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(conditions: "account_id = #{id}")</tt>)        # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)        # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)        # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>) @@ -1247,7 +1253,7 @@ module ActiveRecord        # [:source]        #   Specifies the source association name used by <tt>has_one :through</tt> queries.        #   Only use it if the name cannot be inferred from the association. -      #   <tt>has_one :favorite, :through => :favorites</tt> will look for a +      #   <tt>has_one :favorite, through: :favorites</tt> will look for a        #   <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.        # [:source_type]        #   Specifies type of the source association used by <tt>has_one :through</tt> queries where the source @@ -1267,11 +1273,11 @@ module ActiveRecord        #   See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.        #        # Option examples: -      #   has_one :credit_card, :dependent => :destroy  # destroys the associated credit card -      #   has_one :credit_card, :dependent => :nullify  # updates the associated records foreign +      #   has_one :credit_card, dependent: :destroy  # destroys the associated credit card +      #   has_one :credit_card, dependent: :nullify  # updates the associated records foreign        #                                                 # key value to NULL rather than destroying it -      #   has_one :last_comment, -> { order 'posted_on' }, :class_name => "Comment" -      #   has_one :project_manager, -> { where role: 'project_manager' }, :class_name => "Person" +      #   has_one :last_comment, -> { order 'posted_on' }, class_name: "Comment" +      #   has_one :project_manager, -> { where role: 'project_manager' }, class_name: "Person"        #   has_one :attachment, as: :attachable        #   has_one :boss, readonly: :true        #   has_one :club, through: :membership @@ -1326,12 +1332,12 @@ module ActiveRecord        #   Specify the foreign key used for the association. By default this is guessed to be the name        #   of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>        #   association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly, -      #   <tt>belongs_to :favorite_person, :class_name => "Person"</tt> will use a foreign key +      #   <tt>belongs_to :favorite_person, class_name: "Person"</tt> will use a foreign key        #   of "favorite_person_id".        # [:foreign_type]        #   Specify the column used to store the associated object's type, if this is a polymorphic        #   association. By default this is guessed to be the name of the association with a "_type" -      #   suffix. So a class that defines a <tt>belongs_to :taggable, :polymorphic => true</tt> +      #   suffix. So a class that defines a <tt>belongs_to :taggable, polymorphic: true</tt>        #   association will use "taggable_type" as the default <tt>:foreign_type</tt>.        # [:primary_key]        #   Specify the method that returns the primary key of associated object used for the association. @@ -1351,7 +1357,7 @@ module ActiveRecord        #   <tt>#{table_name}_count</tt> is created on the associate class (such that Post.comments_count will        #   return the count cached, see note below). You can also specify a custom counter        #   cache column by providing a column name instead of a +true+/+false+ value to this -      #   option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.) +      #   option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)        #   Note: Specifying a counter cache will add it to that model's list of readonly attributes        #   using +attr_readonly+.        # [:polymorphic] @@ -1409,7 +1415,7 @@ module ActiveRecord        #        #   class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration        #     def change -      #       create_table :developers_projects, :id => false do |t| +      #       create_table :developers_projects, id: false do |t|        #         t.integer :developer_id        #         t.integer :project_id        #       end diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index 1b382f7285..fcdfc1e150 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -1,5 +1,8 @@ +require 'active_record/associations' +  module ActiveRecord::Associations::Builder    class CollectionAssociation < Association #:nodoc: +      CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]      def valid_options diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb index ab8225460a..0d1bdd21ee 100644 --- a/activerecord/lib/active_record/associations/builder/has_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_many.rb @@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder      end      def valid_options -      super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of] +      super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache]      end      def valid_dependent_options diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 54215cf88d..862ff201de 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -174,8 +174,6 @@ module ActiveRecord        # association, it will be used for the query. Otherwise, construct options and pass them with        # scope to the target class's +count+.        def count(column_name = nil, count_options = {}) -        return 0 if owner.new_record? -          column_name, count_options = nil, column_name if column_name.is_a?(Hash)          if options[:counter_sql] || options[:finder_sql] @@ -366,6 +364,16 @@ module ActiveRecord          record        end +      def scope(opts = {}) +        scope = super() +        scope.none! if opts.fetch(:nullify, true) && null_scope? +        scope +      end + +      def null_scope? +        owner.new_record? && !foreign_key_present? +      end +        private          def custom_counter_sql diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index e73f940334..e444b0ed83 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -28,10 +28,12 @@ module ActiveRecord      # is computed directly through SQL and does not trigger by itself the      # instantiation of the actual post records.      class CollectionProxy < Relation +      delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope) +        def initialize(association) #:nodoc:          @association = association          super association.klass, association.klass.arel_table -        merge! association.scope +        merge! association.scope(nullify: false)        end        def target diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 74864d271f..f59565ae77 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -76,7 +76,7 @@ module ActiveRecord          end          def cached_counter_attribute_name(reflection = reflection) -          "#{reflection.name}_count" +          options[:counter_cache] || "#{reflection.name}_count"          end          def update_counter(difference, reflection = reflection) diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 06bead41de..ee816d2392 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -28,7 +28,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 @@ -90,6 +90,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/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 437fd00948..e0bfdb8f3e 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -369,14 +369,10 @@ module ActiveRecord      end      def typecasted_attribute_value(name) -      if self.class.serialized_attributes.include?(name) -        @attributes[name].serialized_value -      else -        # FIXME: we need @attributes to be used consistently. -        # If the values stored in @attributes were already typecasted, this code -        # could be simplified -        read_attribute(name) -      end +      # FIXME: we need @attributes to be used consistently. +      # If the values stored in @attributes were already typecasted, this code +      # could be simplified +      read_attribute(name)      end    end  end diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 5b9ed81424..47d4a938af 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -5,7 +5,7 @@ module ActiveRecord        included do          # Returns a hash of all the attributes that have been specified for -        # serialization as keys and their class restriction as values. +        # serialization as keys and their class restriction as values.          class_attribute :serialized_attributes, instance_accessor: false          self.serialized_attributes = {}        end @@ -129,6 +129,14 @@ module ActiveRecord              end            end          end + +        def typecasted_attribute_value(name) +          if self.class.serialized_attributes.include?(name) +            @attributes[name].serialized_value +          else +            super +          end +        end        end      end    end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 806dc5b1d2..427c61079a 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -64,8 +64,7 @@ module ActiveRecord        private        def round_usec(value) -        return unless value -        value.change(:usec => 0) +        value.change(usec: 0) if value        end      end    end diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index a30f888a7a..907fe70522 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -16,7 +16,7 @@ module ActiveRecord    # Note that it also means that associations marked for destruction won't    # be destroyed directly. They will however still be marked for destruction.    # -  # Note that <tt>:autosave => false</tt> is not same as not declaring <tt>:autosave</tt>. +  # Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.    # When the <tt>:autosave</tt> option is not present new associations are saved.    #    # == Validation @@ -37,7 +37,7 @@ module ActiveRecord    # === One-to-one Example    #    #   class Post -  #     has_one :author, :autosave => true +  #     has_one :author, autosave: true    #   end    #    # Saving changes to the parent and its associated model can now be performed @@ -81,27 +81,27 @@ module ActiveRecord    #     has_many :comments # :autosave option is not declared    #   end    # -  #   post = Post.new(:title => 'ruby rocks') -  #   post.comments.build(:body => 'hello world') +  #   post = Post.new(title: 'ruby rocks') +  #   post.comments.build(body: 'hello world')    #   post.save # => saves both post and comment    # -  #   post = Post.create(:title => 'ruby rocks') -  #   post.comments.build(:body => 'hello world') +  #   post = Post.create(title: 'ruby rocks') +  #   post.comments.build(body: 'hello world')    #   post.save # => saves both post and comment    # -  #   post = Post.create(:title => 'ruby rocks') -  #   post.comments.create(:body => 'hello world') +  #   post = Post.create(title: 'ruby rocks') +  #   post.comments.create(body: 'hello world')    #   post.save # => saves both post and comment    #    # When <tt>:autosave</tt> is true all children are saved, no matter whether they    # are new records or not:    #    #   class Post -  #     has_many :comments, :autosave => true +  #     has_many :comments, autosave: true    #   end    # -  #   post = Post.create(:title => 'ruby rocks') -  #   post.comments.create(:body => 'hello world') +  #   post = Post.create(title: 'ruby rocks') +  #   post.comments.create(body: 'hello world')    #   post.comments[0].body = 'hi everyone'    #   post.save # => saves both post and comment, with 'hi everyone' as body    # diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index eabbd80f66..5eacb8f143 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -8,7 +8,6 @@ require 'active_support/core_ext/class/attribute_accessors'  require 'active_support/core_ext/class/delegating_attributes'  require 'active_support/core_ext/array/extract_options'  require 'active_support/core_ext/hash/deep_merge' -require 'active_support/core_ext/hash/indifferent_access'  require 'active_support/core_ext/hash/slice'  require 'active_support/core_ext/string/behavior'  require 'active_support/core_ext/kernel/singleton_class' @@ -36,7 +35,7 @@ module ActiveRecord #:nodoc:    # method is especially useful when you're receiving the data from somewhere else, like an    # HTTP request. It works like this:    # -  #   user = User.new(:name => "David", :occupation => "Code Artist") +  #   user = User.new(name: "David", occupation: "Code Artist")    #   user.name # => "David"    #    # You can also use block initialization: @@ -69,7 +68,7 @@ module ActiveRecord #:nodoc:    #     end    #    #     def self.authenticate_safely_simply(user_name, password) -  #       where(:user_name => user_name, :password => password).first +  #       where(user_name: user_name, password: password).first    #     end    #   end    # @@ -87,27 +86,27 @@ module ActiveRecord #:nodoc:    #    #   Company.where(    #     "id = :id AND name = :name AND division = :division AND created_at > :accounting_date", -  #     { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' } +  #     { id: 3, name: "37signals", division: "First", accounting_date: '2005-01-01' }    #   ).first    #    # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND    # operator. For instance:    # -  #   Student.where(:first_name => "Harvey", :status => 1) +  #   Student.where(first_name: "Harvey", status: 1)    #   Student.where(params[:student])    #    # A range may be used in the hash to use the SQL BETWEEN operator:    # -  #   Student.where(:grade => 9..12) +  #   Student.where(grade: 9..12)    #    # An array may be used in the hash to use the SQL IN operator:    # -  #   Student.where(:grade => [9,11,12]) +  #   Student.where(grade: [9,11,12])    #    # When joining tables, nested hashes or keys written in the form 'table_name.column_name'    # can be used to qualify the table name of a particular condition. For instance:    # -  #   Student.joins(:schools).where(:schools => { :category => 'public' }) +  #   Student.joins(:schools).where(schools: { category: 'public' })    #   Student.joins(:schools).where('schools.category' => 'public' )    #    # == Overwriting default accessors @@ -141,10 +140,10 @@ module ActiveRecord #:nodoc:    # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call    # to determine whether the user has a name:    # -  #   user = User.new(:name => "David") +  #   user = User.new(name: "David")    #   user.name? # => true    # -  #   anonymous = User.new(:name => "") +  #   anonymous = User.new(name: "")    #   anonymous.name? # => false    #    # == Accessing attributes before they have been typecasted @@ -165,8 +164,8 @@ module ActiveRecord #:nodoc:    # to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt> and thus produces finders    # like <tt>Person.find_by_user_name</tt>, <tt>Person.find_all_by_last_name</tt>, and    # <tt>Payment.find_by_transaction_id</tt>. Instead of writing -  # <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>. -  # And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do +  # <tt>Person.where(user_name: user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>. +  # And instead of writing <tt>Person.where(last_name: last_name).all</tt>, you just do    # <tt>Person.find_all_by_last_name(last_name)</tt>.    #    # It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an @@ -175,7 +174,7 @@ module ActiveRecord #:nodoc:    #    # It's also possible to use multiple attributes in the same find by separating them with "_and_".    # -  #  Person.where(:user_name => user_name, :password => password).first +  #  Person.where(user_name: user_name, password: password).first    #  Person.find_by_user_name_and_password(user_name, password) # with dynamic finder    #    # It's even possible to call these dynamic finder methods on relations and named scopes. @@ -189,13 +188,13 @@ module ActiveRecord #:nodoc:    # unless they are given in a block.    #    #   # No 'Summer' tag exists -  #   Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") +  #   Tag.find_or_create_by_name("Summer") # equal to Tag.create(name: "Summer")    #    #   # Now the 'Summer' tag does exist    #   Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")    #    #   # Now 'Bob' exist and is an 'admin' -  #   User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true } +  #   User.find_or_create_by_name('Bob', age: 40) { |u| u.admin = true }    #    # Adding an exclamation point (!) on to the end of <tt>find_or_create_by_</tt> will    # raise an <tt>ActiveRecord::RecordInvalid</tt> error if the new record is invalid. @@ -210,7 +209,7 @@ module ActiveRecord #:nodoc:    # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of    # a list of parameters.    # -  #   Tag.find_or_create_by_name(:name => "rails", :creator => current_user) +  #   Tag.find_or_create_by_name(name: "rails", creator: current_user)    #    # That will either find an existing tag named "rails", or create a new one while setting the    # user that created it. @@ -232,7 +231,7 @@ module ActiveRecord #:nodoc:    #     serialize :preferences    #   end    # -  #   user = User.create(:preferences => { "background" => "black", "display" => large }) +  #   user = User.create(preferences: { "background" => "black", "display" => large })    #   User.find(user.id).preferences # => { "background" => "black", "display" => large }    #    # You can also specify a class option as the second parameter that'll raise an exception @@ -242,7 +241,7 @@ module ActiveRecord #:nodoc:    #     serialize :preferences, Hash    #   end    # -  #   user = User.create(:preferences => %w( one two three )) +  #   user = User.create(preferences: %w( one two three ))    #   User.find(user.id).preferences    # raises SerializationTypeMismatch    #    # When you specify a class option, the default value for that attribute will be a new @@ -267,9 +266,9 @@ module ActiveRecord #:nodoc:    #   class Client < Company; end    #   class PriorityClient < Client; end    # -  # When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in +  # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in    # the companies table with type = "Firm". You can then fetch this row again using -  # <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object. +  # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.    #    # If you don't have a type column defined in your table, single-table inheritance won't    # be triggered. In that case, it'll work just like normal subclasses with no special magic @@ -333,7 +332,6 @@ module ActiveRecord #:nodoc:      extend Translation      extend DynamicMatchers      extend Explain -    extend ConnectionHandling      include Persistence      include ReadonlyAttributes diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 725bfffef2..1c9c627090 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -35,7 +35,7 @@ module ActiveRecord    #   class CreditCard < ActiveRecord::Base    #     # Strip everything but digits, so the user can specify "555 234 34" or    #     # "5552-3434" and both will mean "55523434" -  #     before_validation(:on => :create) do +  #     before_validation(on: :create) do    #       self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")    #     end    #   end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 1da95f451f..db0db272a6 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -1,7 +1,7 @@  require 'thread'  require 'monitor'  require 'set' -require 'active_support/core_ext/module/deprecation' +require 'active_support/deprecation'  module ActiveRecord    # Raised when a connection could not be obtained within the connection @@ -494,10 +494,18 @@ module ActiveRecord          @class_to_pool = Hash.new { |h,k| h[k] = {} }        end -      def connection_pools +      def connection_pool_list          owner_to_pool.values.compact        end +      def connection_pools +        ActiveSupport::Deprecation.warn( +          "In the next release, this will return the same as #connection_pool_list. " \ +          "(An array of pools, rather than a hash mapping specs to pools.)" +        ) +        Hash[connection_pool_list.map { |pool| [pool.spec, pool] }] +      end +        def establish_connection(owner, spec)          @class_to_pool.clear          owner_to_pool[owner] = ConnectionAdapters::ConnectionPool.new(spec) @@ -506,23 +514,23 @@ module ActiveRecord        # Returns true if there are any active connections among the connection        # pools that the ConnectionHandler is managing.        def active_connections? -        connection_pools.any?(&:active_connection?) +        connection_pool_list.any?(&:active_connection?)        end        # Returns any connections in use by the current thread back to the pool,        # and also returns connections to the pool cached by threads that are no        # longer alive.        def clear_active_connections! -        connection_pools.each(&:release_connection) +        connection_pool_list.each(&:release_connection)        end        # Clears the cache which maps classes.        def clear_reloadable_connections! -        connection_pools.each(&:clear_reloadable_connections!) +        connection_pool_list.each(&:clear_reloadable_connections!)        end        def clear_all_connections! -        connection_pools.each(&:disconnect!) +        connection_pool_list.each(&:disconnect!)        end        # Locate the connection of the nearest super class. This can be an diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 38960ab873..7ec6abbc45 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -324,6 +324,7 @@ module ActiveRecord      #   change_table :table do |t|      #     t.column      #     t.index +    #     t.rename_index      #     t.timestamps      #     t.change      #     t.change_default @@ -386,6 +387,13 @@ module ActiveRecord          @base.index_exists?(@table_name, column_name, options)        end +      # Renames the given index on the table. +      # +      #  t.rename_index(:user_id, :account_id) +      def rename_index(index_name, new_index_name) +        @base.rename_index(@table_name, index_name, new_index_name) +      end +        # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps        #        #  t.timestamps diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 73aaffc146..f1e42dfbbe 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -540,7 +540,7 @@ module ActiveRecord                  column_type_sql << "(#{precision})"                end              elsif scale -              raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" +              raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"              end            elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit]) @@ -629,11 +629,13 @@ module ActiveRecord                index_options = options[:where] ? " WHERE #{options[:where]}" : ""              end            else -            message = "Passing a string as third argument of `add_index` is deprecated and will" + -              " be removed in Rails 4.1." + -              " Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead" +            if options +              message = "Passing a string as third argument of `add_index` is deprecated and will" + +                " be removed in Rails 4.1." + +                " Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead" -            ActiveSupport::Deprecation.warn message +              ActiveSupport::Deprecation.warn message +            end              index_type = options            end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 76667616a1..e9677415cc 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/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.9'  require 'mysql'  class Mysql diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb index 62d091357d..c04a799b8d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb @@ -8,6 +8,8 @@ module ActiveRecord            case string            when 'infinity'; 1.0 / 0.0            when '-infinity'; -1.0 / 0.0 +          when / BC$/ +            super("-" + string.sub(/ BC$/, ""))            else              super            end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index 9d3fa18e3a..62a4d76928 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -129,11 +129,15 @@ module ActiveRecord          # Quote date/time values for use in SQL input. Includes microseconds          # if the value is a Time responding to usec.          def quoted_date(value) #:nodoc: +          result = super            if value.acts_like?(:time) && value.respond_to?(:usec) -            "#{super}.#{sprintf("%06d", value.usec)}" -          else -            super +            result = "#{result}.#{sprintf("%06d", value.usec)}" +          end + +          if value.year < 0 +            result = result.sub(/^-/, "") + " BC"            end +          result          end        end      end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 82a0b662f4..7c561b6f82 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -396,6 +396,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 @@ -422,20 +429,17 @@ module ActiveRecord          # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and          # requires that the ORDER BY include the distinct column.          # -        #   distinct("posts.id", "posts.created_at desc") +        #   distinct("posts.id", ["posts.created_at desc"]) +        #   # => "DISTINCT posts.id, posts.created_at AS alias_0"          def distinct(columns, orders) #:nodoc: -          return "DISTINCT #{columns}" if orders.empty? - -          # Construct a clean list of column names from the ORDER BY clause, removing -          # any ASC/DESC modifiers -          order_columns = orders.collect do |s| -            s = s.to_sql unless s.is_a?(String) -            s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') -          end -          order_columns.delete_if { |c| c.blank? } -          order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" } - -          "DISTINCT #{columns}, #{order_columns * ', '}" +          order_columns = orders.map{ |s| +              # Convert Arel node to string +              s = s.to_sql unless s.is_a?(String) +              # Remove any ASC/DESC modifiers +              s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') +            }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } + +          [super].concat(order_columns).join(', ')          end        end      end diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 8ce7a7a463..d6d998c7be 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -5,18 +5,18 @@ module ActiveRecord      # example for regular databases (MySQL, Postgresql, etc):      #      #   ActiveRecord::Base.establish_connection( -    #     :adapter  => "mysql", -    #     :host     => "localhost", -    #     :username => "myuser", -    #     :password => "mypass", -    #     :database => "somedatabase" +    #     adapter:  "mysql", +    #     host:     "localhost", +    #     username: "myuser", +    #     password: "mypass", +    #     database: "somedatabase"      #   )      #      # Example for SQLite database:      #      #   ActiveRecord::Base.establish_connection( -    #     :adapter => "sqlite", -    #     :database  => "path/to/dbfile" +    #     adapter: "sqlite", +    #     database:  "path/to/dbfile"      #   )      #      # Also accepts keys as strings (for parsing from YAML for example): @@ -64,7 +64,7 @@ module ActiveRecord      # Returns the configuration of the associated connection as a hash:      #      #  ActiveRecord::Base.connection_config -    #  # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"} +    #  # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}      #      # Please use only for reading.      def connection_config diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 98032db2ef..957027c1ee 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -3,9 +3,6 @@ require 'active_support/core_ext/object/duplicable'  require 'thread'  module ActiveRecord -  ActiveSupport.on_load(:active_record_config) do -  end -    module Core      extend ActiveSupport::Concern @@ -157,7 +154,7 @@ module ActiveRecord      #      # ==== Example:      #   # Instantiates a single new object -    #   User.new(:first_name => 'Jamie') +    #   User.new(first_name: 'Jamie')      def initialize(attributes = nil)        defaults = self.class.column_defaults.dup        defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? } diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 57838ff984..c53b7b3e78 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -56,7 +56,7 @@ module ActiveRecord        #        #   # For the Post with id of 5, decrement the comment_count by 1, and        #   # increment the action_count by 1 -      #   Post.update_counters 5, :comment_count => -1, :action_count => 1 +      #   Post.update_counters 5, comment_count: -1, action_count: 1        #   # Executes the following SQL:        #   # UPDATE posts        #   #    SET comment_count = COALESCE(comment_count, 0) - 1, @@ -64,7 +64,7 @@ module ActiveRecord        #   #  WHERE id = 5        #        #   # For the Posts with id of 10 and 15, increment the comment_count by 1 -      #   Post.update_counters [10, 15], :comment_count => 1 +      #   Post.update_counters [10, 15], comment_count: 1        #   # Executes the following SQL:        #   # UPDATE posts        #   #    SET comment_count = COALESCE(comment_count, 0) + 1 diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 04c0fbfe70..c615d59725 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -22,7 +22,7 @@ module ActiveRecord    #   end    #    #   # Comments are not patches, this assignment raises AssociationTypeMismatch. -  #   @ticket.patches << Comment.new(:content => "Please attach tests to your patch.") +  #   @ticket.patches << Comment.new(content: "Please attach tests to your patch.")    class AssociationTypeMismatch < ActiveRecordError    end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 29a99a5336..7922bbcfa0 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -250,7 +250,7 @@ module ActiveRecord    #    #   ### in fruit.rb    # -  #   belongs_to :eater, :polymorphic => true +  #   belongs_to :eater, polymorphic: true    #    #   ### in fruits.yml    # @@ -381,6 +381,12 @@ module ActiveRecord      @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} } +    def self.find_table_name(fixture_set_name) # :nodoc: +      ActiveSupport::Deprecation.warn( +        "ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases.  Use ActiveRecord::Fixtures.default_fixture_model_name instead.") +      default_fixture_model_name(fixture_set_name) +    end +      def self.default_fixture_model_name(fixture_set_name) # :nodoc:        ActiveRecord::Base.pluralize_table_names ?          fixture_set_name.singularize.camelize : @@ -730,7 +736,7 @@ module ActiveRecord        #        # Examples:        # -      #   set_fixture_class :some_fixture        => SomeModel, +      #   set_fixture_class some_fixture:        SomeModel,        #                     'namespaced/fixture' => Another::Model        #        # The keys must be the fixture names, that coincide with the short paths to the fixture files. @@ -883,7 +889,7 @@ module ActiveRecord      end      def enlist_fixture_connections -      ActiveRecord::Base.connection_handler.connection_pools.map(&:connection) +      ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)      end      private diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb index 58af92f0b1..b4bb95a392 100644 --- a/activerecord/lib/active_record/locking/pessimistic.rb +++ b/activerecord/lib/active_record/locking/pessimistic.rb @@ -3,12 +3,12 @@ module ActiveRecord      # Locking::Pessimistic provides support for row-level locking using      # SELECT ... FOR UPDATE and other lock types.      # -    # Pass <tt>:lock => true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive +    # Pass <tt>lock: true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive      # lock on the selected rows:      #   # select * from accounts where id=1 for update -    #   Account.find(1, :lock => true) +    #   Account.find(1, lock: true)      # -    # Pass <tt>:lock => 'some locking clause'</tt> to give a database-specific locking clause +    # Pass <tt>lock: 'some locking clause'</tt> to give a database-specific locking clause      # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:      #      #   Account.transaction do diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index c3b9a0f9b7..22347fcaef 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -642,7 +642,11 @@ module ActiveRecord        def proper_table_name(name)          # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string -        name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}" +        if name.respond_to? :table_name +          name.table_name +        else +          "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}" +        end        end        def migrations_paths diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb index e880ae97bb..ebf64cbcdc 100644 --- a/activerecord/lib/active_record/migration/join_table.rb +++ b/activerecord/lib/active_record/migration/join_table.rb @@ -8,7 +8,7 @@ module ActiveRecord        end        def join_table_name(table_1, table_2) -        [table_1, table_2].sort.join("_").to_sym +        [table_1.to_s, table_2.to_s].sort.join("_").to_sym        end      end    end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 1b95b72c8a..628ab0f566 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -285,7 +285,7 @@ module ActiveRecord        #        #      JobLevel.reset_column_information        #      %w{assistant executive manager director}.each do |type| -      #        JobLevel.create(:name => type) +      #        JobLevel.create(name: type)        #      end        #    end        # diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index aba56c2861..4c9bd76d7c 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -50,14 +50,14 @@ module ActiveRecord      # Enabling nested attributes on a one-to-one association allows you to      # create the member and avatar in one go:      # -    #   params = { :member => { :name => 'Jack', :avatar_attributes => { :icon => 'smiling' } } } +    #   params = { member: { name: 'Jack', avatar_attributes: { icon: 'smiling' } } }      #   member = Member.create(params[:member])      #   member.avatar.id # => 2      #   member.avatar.icon # => 'smiling'      #      # It also allows you to update the avatar through the member:      # -    #   params = { :member => { :avatar_attributes => { :id => '2', :icon => 'sad' } } } +    #   params = { member: { avatar_attributes: { id: '2', icon: 'sad' } } }      #   member.update_attributes params[:member]      #   member.avatar.icon # => 'sad'      # @@ -68,13 +68,13 @@ module ActiveRecord      #      #   class Member < ActiveRecord::Base      #     has_one :avatar -    #     accepts_nested_attributes_for :avatar, :allow_destroy => true +    #     accepts_nested_attributes_for :avatar, allow_destroy: true      #   end      #      # Now, when you add the <tt>_destroy</tt> key to the attributes hash, with a      # value that evaluates to +true+, you will destroy the associated model:      # -    #   member.avatar_attributes = { :id => '2', :_destroy => '1' } +    #   member.avatar_attributes = { id: '2', _destroy: '1' }      #   member.avatar.marked_for_destruction? # => true      #   member.save      #   member.reload.avatar # => nil @@ -97,11 +97,11 @@ module ActiveRecord      # be instantiated, unless the hash also contains a <tt>_destroy</tt> key      # that evaluates to +true+.      # -    #   params = { :member => { -    #     :name => 'joe', :posts_attributes => [ -    #       { :title => 'Kari, the awesome Ruby documentation browser!' }, -    #       { :title => 'The egalitarian assumption of the modern citizen' }, -    #       { :title => '', :_destroy => '1' } # this will be ignored +    #   params = { member: { +    #     name: 'joe', posts_attributes: [ +    #       { title: 'Kari, the awesome Ruby documentation browser!' }, +    #       { title: 'The egalitarian assumption of the modern citizen' }, +    #       { title: '', _destroy: '1' } # this will be ignored      #     ]      #   }}      # @@ -116,14 +116,14 @@ module ActiveRecord      #      #    class Member < ActiveRecord::Base      #      has_many :posts -    #      accepts_nested_attributes_for :posts, :reject_if => proc { |attributes| attributes['title'].blank? } +    #      accepts_nested_attributes_for :posts, reject_if: proc { |attributes| attributes['title'].blank? }      #    end      # -    #   params = { :member => { -    #     :name => 'joe', :posts_attributes => [ -    #       { :title => 'Kari, the awesome Ruby documentation browser!' }, -    #       { :title => 'The egalitarian assumption of the modern citizen' }, -    #       { :title => '' } # this will be ignored because of the :reject_if proc +    #   params = { member: { +    #     name: 'joe', posts_attributes: [ +    #       { title: 'Kari, the awesome Ruby documentation browser!' }, +    #       { title: 'The egalitarian assumption of the modern citizen' }, +    #       { title: '' } # this will be ignored because of the :reject_if proc      #     ]      #   }}      # @@ -136,12 +136,12 @@ module ActiveRecord      #      #    class Member < ActiveRecord::Base      #      has_many :posts -    #      accepts_nested_attributes_for :posts, :reject_if => :new_record? +    #      accepts_nested_attributes_for :posts, reject_if: :new_record?      #    end      #      #    class Member < ActiveRecord::Base      #      has_many :posts -    #      accepts_nested_attributes_for :posts, :reject_if => :reject_posts +    #      accepts_nested_attributes_for :posts, reject_if: :reject_posts      #      #      def reject_posts(attributed)      #        attributed['title'].blank? @@ -152,10 +152,10 @@ module ActiveRecord      # associated record, the matching record will be modified:      #      #   member.attributes = { -    #     :name => 'Joe', -    #     :posts_attributes => [ -    #       { :id => 1, :title => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!' }, -    #       { :id => 2, :title => '[UPDATED] other post' } +    #     name: 'Joe', +    #     posts_attributes: [ +    #       { id: 1, title: '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!' }, +    #       { id: 2, title: '[UPDATED] other post' }      #     ]      #   }      # @@ -170,11 +170,11 @@ module ActiveRecord      #      #   class Member < ActiveRecord::Base      #     has_many :posts -    #     accepts_nested_attributes_for :posts, :allow_destroy => true +    #     accepts_nested_attributes_for :posts, allow_destroy: true      #   end      # -    #   params = { :member => { -    #     :posts_attributes => [{ :id => '2', :_destroy => '1' }] +    #   params = { member: { +    #     posts_attributes: [{ id: '2', _destroy: '1' }]      #   }}      #      #   member.attributes = params[:member] @@ -197,12 +197,12 @@ module ActiveRecord      # <tt>inverse_of</tt> as this example illustrates:      #      #   class Member < ActiveRecord::Base -    #     has_many :posts, :inverse_of => :member +    #     has_many :posts, inverse_of: :member      #     accepts_nested_attributes_for :posts      #   end      #      #   class Post < ActiveRecord::Base -    #     belongs_to :member, :inverse_of => :posts +    #     belongs_to :member, inverse_of: :posts      #     validates_presence_of :member      #   end      module ClassMethods @@ -248,11 +248,11 @@ module ActiveRecord        #        # Examples:        #   # creates avatar_attributes= -      #   accepts_nested_attributes_for :avatar, :reject_if => proc { |attributes| attributes['name'].blank? } +      #   accepts_nested_attributes_for :avatar, reject_if: proc { |attributes| attributes['name'].blank? }        #   # creates avatar_attributes= -      #   accepts_nested_attributes_for :avatar, :reject_if => :all_blank +      #   accepts_nested_attributes_for :avatar, reject_if: :all_blank        #   # creates avatar_attributes= and posts_attributes= -      #   accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true +      #   accepts_nested_attributes_for :avatar, :posts, allow_destroy: true        def accepts_nested_attributes_for(*attr_names)          options = { :allow_destroy => false, :update_only => false }          options.update(attr_names.extract_options!) @@ -348,9 +348,9 @@ module ActiveRecord      # For example:      #      #   assign_nested_attributes_for_collection_association(:people, { -    #     '1' => { :id => '1', :name => 'Peter' }, -    #     '2' => { :name => 'John' }, -    #     '3' => { :id => '2', :_destroy => true } +    #     '1' => { id: '1', name: 'Peter' }, +    #     '2' => { name: 'John' }, +    #     '3' => { id: '2', _destroy: true }      #   })      #      # Will update the name of the Person with ID 1, build a new associated @@ -360,9 +360,9 @@ module ActiveRecord      # Also accepts an Array of attribute hashes:      #      #   assign_nested_attributes_for_collection_association(:people, [ -    #     { :id => '1', :name => 'Peter' }, -    #     { :name => 'John' }, -    #     { :id => '2', :_destroy => true } +    #     { id: '1', name: 'Peter' }, +    #     { name: 'John' }, +    #     { id: '2', _destroy: true }      #   ])      def assign_nested_attributes_for_collection_association(association_name, attributes_collection)        options = self.nested_attributes_options[association_name] diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb index 4c1c91e3df..711fc8b883 100644 --- a/activerecord/lib/active_record/null_relation.rb +++ b/activerecord/lib/active_record/null_relation.rb @@ -46,7 +46,11 @@ module ActiveRecord        {}      end -    def count +    def count(*) +      0 +    end + +    def sum(*)        0      end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 8e749772a1..eed49e17b1 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -72,11 +72,9 @@ module ActiveRecord      # +save+ returns +false+. See ActiveRecord::Callbacks for further      # details.      def save(*) -      begin -        create_or_update -      rescue ActiveRecord::RecordInvalid -        false -      end +      create_or_update +    rescue ActiveRecord::RecordInvalid +      false      end      # Saves the model. @@ -155,7 +153,18 @@ module ActiveRecord        became.instance_variable_set("@new_record", new_record?)        became.instance_variable_set("@destroyed", destroyed?)        became.instance_variable_set("@errors", errors) -      became.public_send("#{klass.inheritance_column}=", klass.name) unless self.class.descends_from_active_record? +      became +    end + +    # Wrapper around +becomes+ that also changes the instance's sti column value. +    # This is especially useful if you want to persist the changed class in your +    # database. +    # +    # Note: The old instance's sti column value will be changed too, as both objects +    # share the same set of attributes. +    def becomes!(klass) +      became = becomes(klass) +      became.public_send("#{klass.inheritance_column}=", klass.sti_name) unless self.class.descends_from_active_record?        became      end @@ -171,7 +180,7 @@ module ActiveRecord        name = name.to_s        verify_readonly_attribute(name)        send("#{name}=", value) -      save(:validate => false) +      save(validate: false)      end      # Updates the attributes of the model from the passed-in hash and saves the @@ -226,8 +235,8 @@ module ActiveRecord        updated_count = self.class.where(self.class.primary_key => id).update_all(attributes) -      attributes.each do |k,v| -        raw_write_attribute(k,v) +      attributes.each do |k, v| +        raw_write_attribute(k, v)        end        updated_count == 1 @@ -379,10 +388,14 @@ module ActiveRecord      # Returns the number of affected rows.      def update(attribute_names = @attributes.keys)        attributes_with_values = arel_attributes_with_values_for_update(attribute_names) -      return 0 if attributes_with_values.empty? -      klass = self.class -      stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values) -      klass.connection.update stmt + +      if attributes_with_values.empty? +        0 +      else +        klass = self.class +        stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values) +        klass.connection.update stmt +      end      end      # Creates a record with values matching those of the instance attributes diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 77e41ea927..5464ca6066 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -92,6 +92,33 @@ module ActiveRecord      initializer "active_record.set_configs" do |app|        ActiveSupport.on_load(:active_record) do +        begin +          old_behavior, ActiveSupport::Deprecation.behavior = ActiveSupport::Deprecation.behavior, :stderr +          whitelist_attributes = app.config.active_record.delete(:whitelist_attributes) + +          if respond_to?(:mass_assignment_sanitizer=) +            mass_assignment_sanitizer = nil +          else +            mass_assignment_sanitizer = app.config.active_record.delete(:mass_assignment_sanitizer) +          end + +          unless whitelist_attributes.nil? && mass_assignment_sanitizer.nil? +            ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, [] +              Model based mass assignment security has been extracted +              out of Rails into a gem. Please use the new recommended protection model for +              params or add `protected_attributes` to your Gemfile to use the old one. + +              To disable this message remove the `whitelist_attributes` option from your +              `config/application.rb` file and any `mass_assignment_sanitizer` options +              from your `config/environments/*.rb` files. + +              See http://edgeguides.rubyonrails.org/security.html#mass-assignment for more information +            EOF +          end +        ensure +          ActiveSupport::Deprecation.behavior = old_behavior +        end +          app.config.active_record.each do |k,v|            send "#{k}=", v          end @@ -122,8 +149,10 @@ module ActiveRecord        ActiveSupport.on_load(:active_record) do          ActionDispatch::Reloader.send(hook) do -          ActiveRecord::Base.clear_reloadable_connections! -          ActiveRecord::Base.clear_cache! +          if ActiveRecord::Base.connected? +            ActiveRecord::Base.clear_reloadable_connections! +            ActiveRecord::Base.clear_cache! +          end          end        end      end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index cb9cc5a9df..3ee55c580e 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -486,7 +486,7 @@ module ActiveRecord      # Returns a hash of where conditions      #      #   Users.where(name: 'Oscar').where_values_hash -    #   # => {:name=>"oscar"} +    #   # => {name: "oscar"}      def where_values_hash        equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|          node.left.relation.name == table_name diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index a7d2f4bd24..741f94f777 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/object/try' -  module ActiveRecord    module Calculations      # Count the records. @@ -145,7 +143,7 @@ module ActiveRecord      #   # SELECT DISTINCT role FROM people      #   # => ['admin', 'member', 'guest']      # -    #   Person.where(:age => 21).limit(5).pluck(:id) +    #   Person.where(age: 21).limit(5).pluck(:id)      #   # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5      #   # => [2, 3]      # @@ -165,7 +163,9 @@ module ActiveRecord        if has_include?(column_names.first)          construct_relation_for_association_calculations.pluck(*column_names)        else -        result  = klass.connection.select_all(select(column_names).arel, nil, bind_values) +        relation = spawn +        relation.select_values = column_names +        result = klass.connection.select_all(relation.arel, nil, bind_values)          columns = result.columns.map do |key|            klass.column_types.fetch(key) {              result.column_types.fetch(key) { @@ -271,17 +271,19 @@ module ActiveRecord          group_fields = group_attrs        end -      group_aliases = group_fields.map { |field| column_alias_for(field) } +      group_aliases = group_fields.map { |field| +        column_alias_for(field) +      }        group_columns = group_aliases.zip(group_fields).map { |aliaz,field|          [aliaz, column_for(field)]        } -      group = @klass.connection.adapter_name == 'FrontBase' ? group_aliases : group_fields +      group = group_fields        if operation == 'count' && column_name == :all          aggregate_alias = 'count_all'        else -        aggregate_alias = column_alias_for(operation, column_name) +        aggregate_alias = column_alias_for([operation, column_name].join(' '))        end        select_values = [ @@ -300,7 +302,8 @@ module ActiveRecord          end        } -      relation = except(:group).group(group) +      relation = except(:group) +      relation.group_values  = group        relation.select_values = select_values        calculated_data = @klass.connection.select_all(relation, nil, bind_values) @@ -329,10 +332,12 @@ module ActiveRecord      #   column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"      #   column_alias_for("count(*)")                 # => "count_all"      #   column_alias_for("count", "id")              # => "count_id" -    def column_alias_for(*keys) -      keys.map! {|k| k.respond_to?(:to_sql) ? k.to_sql : k} -      table_name = keys.join(' ') -      table_name.downcase! +    def column_alias_for(keys) +      if keys.respond_to? :name +        keys = "#{keys.relation.name}.#{keys.name}" +      end + +      table_name = keys.to_s.downcase        table_name.gsub!(/\*/, 'all')        table_name.gsub!(/\W+/, ' ')        table_name.strip! diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index ab8b36c8ab..dbfa92bbbd 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -1,3 +1,4 @@ +require 'thread'  module ActiveRecord    module Delegation # :nodoc: @@ -6,6 +7,8 @@ module ActiveRecord      delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,               :connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass +    @@delegation_mutex = Mutex.new +      def self.delegate_to_scoped_klass(method)        if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/          module_eval <<-RUBY, __FILE__, __LINE__ + 1 @@ -32,13 +35,28 @@ module ActiveRecord      def method_missing(method, *args, &block)        if @klass.respond_to?(method) -        ::ActiveRecord::Delegation.delegate_to_scoped_klass(method) +        @@delegation_mutex.synchronize do +          unless ::ActiveRecord::Delegation.method_defined?(method) +            ::ActiveRecord::Delegation.delegate_to_scoped_klass(method) +          end +        end +          scoping { @klass.send(method, *args, &block) }        elsif Array.method_defined?(method) -        ::ActiveRecord::Delegation.delegate method, :to => :to_a +        @@delegation_mutex.synchronize do +          unless ::ActiveRecord::Delegation.method_defined?(method) +            ::ActiveRecord::Delegation.delegate method, :to => :to_a +          end +        end +          to_a.send(method, *args, &block)        elsif arel.respond_to?(method) -        ::ActiveRecord::Delegation.delegate method, :to => :arel +        @@delegation_mutex.synchronize do +          unless ::ActiveRecord::Delegation.method_defined?(method) +            ::ActiveRecord::Delegation.delegate method, :to => :arel +          end +        end +          arel.send(method, *args, &block)        else          super diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 99c2f45bc8..eafe4a54c4 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/hash/indifferent_access' -  module ActiveRecord    module FinderMethods      # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). @@ -78,7 +76,7 @@ module ActiveRecord      #      #   Person.first # returns the first object fetched by SELECT * FROM people      #   Person.where(["user_name = ?", user_name]).first -    #   Person.where(["user_name = :u", { :u => user_name }]).first +    #   Person.where(["user_name = :u", { u: user_name }]).first      #   Person.order("created_on DESC").offset(5).first      #   Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3      def first(limit = nil) @@ -225,7 +223,7 @@ module ActiveRecord      def construct_limited_ids_condition(relation)        orders = relation.order_values.map { |val| val.presence }.compact -      values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders) +      values = @klass.connection.distinct("#{quoted_table_name}.#{primary_key}", orders)        relation = relation.dup @@ -234,8 +232,6 @@ module ActiveRecord      end      def find_with_ids(*ids) -      return to_a.find { |*block_args| yield(*block_args) } if block_given? -        expects_array = ids.first.kind_of?(Array)        return ids.first if expects_array && ids.first.empty? diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index bd7aeb0c4e..83074e72c1 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -36,10 +36,10 @@ module ActiveRecord        queries = []        # Find the foreign key when using queries such as: -      # Post.where(:author => author) +      # Post.where(author: author)        #        # For polymorphic relationships, find the foreign key and type: -      # PriceEstimate.where(:estimate_of => treasure) +      # PriceEstimate.where(estimate_of: treasure)        if klass && value.class < Base && reflection = klass.reflect_on_association(column.to_sym)          if reflection.polymorphic?            queries << build(table[reflection.foreign_type], value.class.base_class) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4fdc296c7e..b3712b4ad6 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -218,7 +218,6 @@ module ActiveRecord      # Like #order, but modifies relation in place.      def order!(*args)        args.flatten! -        validate_order_args args        references = args.reject { |arg| Arel::Node === arg } @@ -245,7 +244,6 @@ module ActiveRecord      # Like #reorder, but modifies relation in place.      def reorder!(*args)        args.flatten! -        validate_order_args args        self.reordering_value = true @@ -357,17 +355,17 @@ module ActiveRecord      #    author = Author.find(1)      #      #    # The following queries will be equivalent: -    #    Post.where(:author => author) -    #    Post.where(:author_id => author) +    #    Post.where(author: author) +    #    Post.where(author_id: author)      #      # This also works with polymorphic belongs_to relationships:      # -    #    treasure = Treasure.create(:name => 'gold coins') -    #    treasure.price_estimates << PriceEstimate.create(:price => 125) +    #    treasure = Treasure.create(name: 'gold coins') +    #    treasure.price_estimates << PriceEstimate.create(price: 125)      #      #    # The following queries will be equivalent: -    #    PriceEstimate.where(:estimate_of => treasure) -    #    PriceEstimate.where(:estimate_of_type => 'Treasure', :estimate_of_id => treasure) +    #    PriceEstimate.where(estimate_of: treasure) +    #    PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)      #      # === Joins      # @@ -379,7 +377,7 @@ module ActiveRecord      # For hash conditions, you can either use the table name in the key, or use a sub-hash.      #      #    User.joins(:posts).where({ "posts.published" => true }) -    #    User.joins(:posts).where({ :posts => { :published => true } }) +    #    User.joins(:posts).where({ posts: { published: true } })      #      # === empty condition      # @@ -478,13 +476,13 @@ module ActiveRecord      #      # For example:      # -    #   @posts = current_user.visible_posts.where(:name => params[:name]) +    #   @posts = current_user.visible_posts.where(name: params[:name])      #   # => the visible_posts method is expected to return a chainable Relation      #      #   def visible_posts      #     case role      #     when 'Country Manager' -    #       Post.where(:country => country) +    #       Post.where(country: country)      #     when 'Reviewer'      #       Post.published      #     when 'Bad User' @@ -796,7 +794,7 @@ module ActiveRecord      def reverse_sql_order(order_query)        order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty? -      order_query.map do |o| +      order_query.flat_map do |o|          case o          when Arel::Nodes::Ordering            o.reverse @@ -814,7 +812,7 @@ module ActiveRecord          else            o          end -      end.flatten +      end      end      def array_of_strings?(o) @@ -825,7 +823,7 @@ module ActiveRecord        orders = order_values        orders = reverse_sql_order(orders) if reverse_order_value -      orders = orders.uniq.reject(&:blank?).map do |order| +      orders = orders.uniq.reject(&:blank?).flat_map do |order|          case order          when Symbol            table[order].asc @@ -834,7 +832,7 @@ module ActiveRecord          else            order          end -      end.flatten +      end        arel.order(*orders) unless orders.empty?      end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 5394c1b28b..62dda542ab 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -15,11 +15,11 @@ module ActiveRecord      #      # ==== Examples      # -    #   Post.where(:published => true).joins(:comments).merge( Comment.where(:spam => false) ) +    #   Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )      #   # Performs a single join query with both where conditions.      #      #   recent_posts = Post.order('created_at DESC').first(5) -    #   Post.where(:published => true).merge(recent_posts) +    #   Post.where(published: true).merge(recent_posts)      #   # Returns the intersection of all published posts with the 5 most recently created posts.      #   # (This is just an example. You'd probably want to do this with a single query!)      # diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index f3e47a958e..2dad1dc177 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -17,7 +17,7 @@ module ActiveRecord        # Accepts an array, hash, or string of SQL conditions and sanitizes        # them into a valid SQL fragment for a WHERE clause.        #   ["name='%s' and group_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' and group_id='4'" -      #   { :name => "foo'bar", :group_id => 4 }  returns "name='foo''bar' and group_id='4'" +      #   { name: "foo'bar", group_id: 4 }  returns "name='foo''bar' and group_id='4'"        #   "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"        def sanitize_sql_for_conditions(condition, table_name = self.table_name)          return nil if condition.blank? @@ -32,7 +32,7 @@ module ActiveRecord        # Accepts an array, hash, or string of SQL conditions and sanitizes        # them into a valid SQL fragment for a SET clause. -      #   { :name => nil, :group_id => 4 }  returns "name = NULL , group_id='4'" +      #   { name: nil, group_id: 4 }  returns "name = NULL , group_id='4'"        def sanitize_sql_for_assignment(assignments)          case assignments          when Array; sanitize_sql_array(assignments) @@ -46,12 +46,12 @@ module ActiveRecord        # aggregate attribute values.        # Given:        #     class Person < ActiveRecord::Base -      #       composed_of :address, :class_name => "Address", -      #         :mapping => [%w(address_street street), %w(address_city city)] +      #       composed_of :address, class_name: "Address", +      #         mapping: [%w(address_street street), %w(address_city city)]        #     end        # Then: -      #     { :address => Address.new("813 abc st.", "chicago") } -      #       # => { :address_street => "813 abc st.", :address_city => "chicago" } +      #     { address: Address.new("813 abc st.", "chicago") } +      #       # => { address_street: "813 abc st.", address_city: "chicago" }        def expand_hash_conditions_for_aggregates(attrs)          expanded_attrs = {}          attrs.each do |attr, value| @@ -72,18 +72,18 @@ module ActiveRecord        end        # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause. -      #   { :name => "foo'bar", :group_id => 4 } +      #   { name: "foo'bar", group_id: 4 }        #     # => "name='foo''bar' and group_id= 4" -      #   { :status => nil, :group_id => [1,2,3] } +      #   { status: nil, group_id: [1,2,3] }        #     # => "status IS NULL and group_id IN (1,2,3)" -      #   { :age => 13..18 } +      #   { age: 13..18 }        #     # => "age BETWEEN 13 AND 18"        #   { 'other_records.id' => 7 }        #     # => "`other_records`.`id` = 7" -      #   { :other_records => { :id => 7 } } +      #   { other_records: { id: 7 } }        #     # => "`other_records`.`id` = 7"        # And for value objects on a composed_of relationship: -      #   { :address => Address.new("123 abc st.", "chicago") } +      #   { address: Address.new("123 abc st.", "chicago") }        #     # => "address_street='123 abc st.' and address_city='chicago'"        def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)          attrs = expand_hash_conditions_for_aggregates(attrs) @@ -96,7 +96,7 @@ module ActiveRecord        alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions        # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause. -      #   { :status => nil, :group_id => 1 } +      #   { status: nil, group_id: 1 }        #     # => "status = NULL , group_id = 1"        def sanitize_sql_hash_for_assignment(attrs)          attrs.map do |attr, value| diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 834d01a1e8..1a766093d0 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -36,7 +36,7 @@ module ActiveRecord #:nodoc:      #      # For instance:      # -    #   topic.to_xml(:skip_instruct => true, :except => [ :id, :bonus_time, :written_on, :replies_count ]) +    #   topic.to_xml(skip_instruct: true, except: [ :id, :bonus_time, :written_on, :replies_count ])      #      #   <topic>      #     <title>The First Topic</title> @@ -50,7 +50,7 @@ module ActiveRecord #:nodoc:      #      # To include first level associations use <tt>:include</tt>:      # -    #   firm.to_xml :include => [ :account, :clients ] +    #   firm.to_xml include: [ :account, :clients ]      #      #   <?xml version="1.0" encoding="UTF-8"?>      #   <firm> @@ -81,7 +81,7 @@ module ActiveRecord #:nodoc:      # associated with models.      #      #   proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) } -    #   firm.to_xml :procs => [ proc ] +    #   firm.to_xml procs: [ proc ]      #      #   <firm>      #     # ... normal attributes as shown above ... @@ -90,7 +90,7 @@ module ActiveRecord #:nodoc:      #      # To include deeper levels of associations pass a hash like this:      # -    #   firm.to_xml :include => {:account => {}, :clients => {:include => :address}} +    #   firm.to_xml include: {account: {}, clients: {include: :address}}      #   <?xml version="1.0" encoding="UTF-8"?>      #   <firm>      #     <id type="integer">1</id> @@ -120,7 +120,7 @@ module ActiveRecord #:nodoc:      #      # To include any methods on the model being called use <tt>:methods</tt>:      # -    #   firm.to_xml :methods => [ :calculated_earnings, :real_earnings ] +    #   firm.to_xml methods: [ :calculated_earnings, :real_earnings ]      #      #   <firm>      #     # ... normal attributes as shown above ... @@ -132,7 +132,7 @@ module ActiveRecord #:nodoc:      # modified version of the options hash that was given to +to_xml+:      #      #   proc = Proc.new { |options| options[:builder].tag!('abc', 'def') } -    #   firm.to_xml :procs => [ proc ] +    #   firm.to_xml procs: [ proc ]      #      #   <firm>      #     # ... normal attributes as shown above ... @@ -164,7 +164,7 @@ module ActiveRecord #:nodoc:      #     def to_xml(options = {})      #       require 'builder'      #       options[:indent] ||= 2 -    #       xml = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) +    #       xml = options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])      #       xml.instruct! unless options[:skip_instruct]      #       xml.level_one do      #         xml.tag!(:second_level, 'content') diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 920d6848c1..cf17b1d8a4 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -1,8 +1,5 @@  module ActiveRecord -  ActiveSupport.on_load(:active_record_config) do -  end -    # = Active Record Timestamp    #    # Active Record automatically timestamps create and update operations if the diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index f91abfbd19..ce6998530f 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -108,10 +108,10 @@ module ActiveRecord      #      #   # Suppose that we have a Number model with a unique column called 'i'.      #   Number.transaction do -    #     Number.create(:i => 0) +    #     Number.create(i: 0)      #     begin      #       # This will raise a unique constraint error... -    #       Number.create(:i => 0) +    #       Number.create(i: 0)      #     rescue ActiveRecord::StatementInvalid      #       # ...which we ignore.      #     end @@ -119,7 +119,7 @@ module ActiveRecord      #     # On PostgreSQL, the transaction is now unusable. The following      #     # statement will cause a PostgreSQL error, even though the unique      #     # constraint is no longer violated: -    #     Number.create(:i => 1) +    #     Number.create(i: 1)      #     # => "PGError: ERROR:  current transaction is aborted, commands      #     #     ignored until end of transaction block"      #   end @@ -134,9 +134,9 @@ module ActiveRecord      # transaction. For example, the following behavior may be surprising:      #      #   User.transaction do -    #     User.create(:username => 'Kotori') +    #     User.create(username: 'Kotori')      #     User.transaction do -    #       User.create(:username => 'Nemu') +    #       User.create(username: 'Nemu')      #       raise ActiveRecord::Rollback      #     end      #   end @@ -147,14 +147,14 @@ module ActiveRecord      # real transaction is committed.      #      # In order to get a ROLLBACK for the nested transaction you may ask for a real -    # sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong, +    # sub-transaction by passing <tt>requires_new: true</tt>. If anything goes wrong,      # the database rolls back to the beginning of the sub-transaction without rolling      # back the parent transaction. If we add it to the previous example:      #      #   User.transaction do -    #     User.create(:username => 'Kotori') -    #     User.transaction(:requires_new => true) do -    #       User.create(:username => 'Nemu') +    #     User.create(username: 'Kotori') +    #     User.transaction(requires_new: true) do +    #       User.create(username: 'Nemu')      #       raise ActiveRecord::Rollback      #     end      #   end @@ -194,7 +194,7 @@ module ActiveRecord      # automatically released. The following example demonstrates the problem:      #      #   Model.connection.transaction do                           # BEGIN -    #     Model.connection.transaction(:requires_new => true) do  # CREATE SAVEPOINT active_record_1 +    #     Model.connection.transaction(requires_new: true) do  # CREATE SAVEPOINT active_record_1      #       Model.connection.create_table(...)                    # active_record_1 now automatically released      #     end                                                     # RELEASE savepoint active_record_1      #                                                             # ^^^^ BOOM! database error! @@ -213,13 +213,13 @@ module ActiveRecord        # You can specify that the callback should only be fired by a certain action with        # the +:on+ option:        # -      #   after_commit :do_foo, :on => :create -      #   after_commit :do_bar, :on => :update -      #   after_commit :do_baz, :on => :destroy +      #   after_commit :do_foo, on: :create +      #   after_commit :do_bar, on: :update +      #   after_commit :do_baz, on: :destroy        #        # Also, to have the callback fired on create and update, but not on destroy:        # -      #   after_commit :do_zoo, :if => :persisted? +      #   after_commit :do_zoo, if: :persisted?        #        # Note that transactional fixtures do not play well with this feature. Please        # use the +test_after_commit+ gem to have these hooks fired in tests. diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index f1362dd15f..872204c644 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -216,6 +216,35 @@ module ActiveRecord          assert_equal "(number > 100)", index.where        end +      def test_distinct_zero_orders +        assert_equal "DISTINCT posts.id", +          @connection.distinct("posts.id", []) +      end + +      def test_distinct_one_order +        assert_equal "DISTINCT posts.id, posts.created_at AS alias_0", +          @connection.distinct("posts.id", ["posts.created_at desc"]) +      end + +      def test_distinct_few_orders +        assert_equal "DISTINCT posts.id, posts.created_at AS alias_0, posts.position AS alias_1", +          @connection.distinct("posts.id", ["posts.created_at desc", "posts.position asc"]) +      end + +      def test_distinct_blank_not_nil_orders +        assert_equal "DISTINCT posts.id, posts.created_at AS alias_0", +          @connection.distinct("posts.id", ["posts.created_at desc", "", "   "]) +      end + +      def test_distinct_with_arel_order +        order = Object.new +        def order.to_sql +          "posts.created_at desc" +        end +        assert_equal "DISTINCT posts.id, posts.created_at AS alias_0", +          @connection.distinct("posts.id", [order]) +      end +        def test_distinct_with_nulls          assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls first"])          assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls last"]) diff --git a/activerecord/test/cases/adapters/postgresql/sql_types_test.rb b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb new file mode 100644 index 0000000000..d7d40f6385 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb @@ -0,0 +1,18 @@ +require "cases/helper" + +class SqlTypesTest < ActiveRecord::TestCase +  def test_binary_types +    assert_equal 'bytea', type_to_sql(:binary, 100_000) +    assert_raise ActiveRecord::ActiveRecordError do +      type_to_sql :binary, 4294967295 +    end +    assert_equal 'text', type_to_sql(:text, 100_000) +    assert_raise ActiveRecord::ActiveRecordError do +      type_to_sql :text, 4294967295 +    end +  end + +  def type_to_sql(*args) +    ActiveRecord::Base.connection.type_to_sql(*args) +  end +end diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb index 26507ad654..630bdeec67 100644 --- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb +++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb @@ -75,6 +75,15 @@ class TimestampTest < ActiveRecord::TestCase      assert_equal '4', pg_datetime_precision('foos', 'updated_at')    end +  def test_bc_timestamp +    unless current_adapter?(:PostgreSQLAdapter) +      return skip("only tested on postgresql") +    end +    date = Date.new(0) - 1.second +    Developer.create!(:name => "aaron", :updated_at => date) +    assert_equal date, Developer.find_by_name("aaron").updated_at +  end +    private      def pg_datetime_precision(table_name, column_name) diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 42f5b69d4e..1b1b479f1a 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -799,12 +799,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase      assert_equal 1, developer.projects.count    end -  def test_counting_should_not_fire_sql_if_parent_is_unsaved -    assert_no_queries do -      assert_equal 0, Developer.new.projects.count -    end -  end -    unless current_adapter?(:PostgreSQLAdapter)      def test_count_with_finder_sql        assert_equal 3, projects(:active_record).developers_with_finder_sql.count @@ -862,4 +856,15 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase      def klass.name; 'Foo'; end      assert_deprecated { klass.has_and_belongs_to_many :posts, :delete_sql => 'lol' }    end + +  test "has and belongs to many associations on new records use null relations" do +    projects = Developer.new.projects +    assert_no_queries do +      assert_equal [], projects +      assert_equal [], projects.where(title: 'omg') +      assert_equal [], projects.pluck(:title) +      assert_equal 0, projects.count +    end +  end +  end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 50c23c863f..01afa087be 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -262,12 +262,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase      assert_equal firm.limited_clients.length, firm.limited_clients.count    end -  def test_counting_should_not_fire_sql_if_parent_is_unsaved -    assert_no_queries do -      assert_equal 0, Person.new.readers.count -    end -  end -    def test_finding      assert_equal 2, Firm.all.merge!(:order => "id").first.clients.length    end @@ -754,6 +748,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase      end    end +  def test_custom_named_counter_cache +    topic = topics(:first) + +    assert_difference "topic.reload.replies_count", -1 do +      topic.approved_replies.clear +    end +  end +    def test_deleting_a_collection      force_signal37_to_load_all_clients_of_firm      companies(:first_firm).clients_of_firm.create("name" => "Another Client") @@ -1148,6 +1150,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase      assert companies(:first_firm).clients.include?(Client.find(2))    end +  def test_included_in_collection_for_new_records +    client = Client.create(:name => 'Persisted') +    assert_nil client.client_of +    assert !Firm.new.clients_of_firm.include?(client), +           'includes a client that does not belong to any firm' +  end +    def test_adding_array_and_collection      assert_nothing_raised { Firm.first.clients + Firm.all.last.clients }    end @@ -1648,4 +1657,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase      klass = Class.new(ActiveRecord::Base)      assert_deprecated { klass.has_many :foo, :counter_sql => 'lol' }    end + +  test "has many associations on new records use null relations" do +    post = Post.new + +    assert_no_queries do +      assert_equal [], post.comments +      assert_equal [], post.comments.where(body: 'omg') +      assert_equal [], post.comments.pluck(:body) +      assert_equal 0,  post.comments.sum(:id) +      assert_equal 0,  post.comments.count +    end +  end  end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index b2a5d9d6f7..8e52ce1d91 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -766,12 +766,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase      assert_equal 1, authors(:mary).categories.general.count    end -  def test_counting_should_not_fire_sql_if_parent_is_unsaved -    assert_no_queries do -      assert_equal 0, Person.new.posts.count -    end -  end -    def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes      post = posts(:eager_other) @@ -876,4 +870,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase      post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"      assert_equal [tags(:general)], post.reload.tags    end + +  test "has many through associations on new records use null relations" do +    person = Person.new + +    assert_no_queries do +      assert_equal [], person.posts +      assert_equal [], person.posts.where(body: 'omg') +      assert_equal [], person.posts.pluck(:body) +      assert_equal 0,  person.posts.sum(:tags_count) +      assert_equal 0,  person.posts.count +    end +  end +  end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 2d3cb654df..ea1cfa0805 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -206,6 +206,12 @@ class HasOneAssociationsTest < ActiveRecord::TestCase      assert_equal account, firm.account    end +  def test_build_association_dont_create_transaction +    assert_no_queries { +      Firm.new.build_account +    } +  end +    def test_build_and_create_should_not_happen_within_scope      pirate = pirates(:blackbeard)      scoped_count = pirate.association(:foo_bulb).scope.where_values.count diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 8b82b79219..c503c21e27 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -12,6 +12,8 @@ require 'models/contact'  require 'models/keyboard'  class AttributeMethodsTest < ActiveRecord::TestCase +  include InTimeZone +    fixtures :topics, :developers, :companies, :computers    def setup @@ -311,26 +313,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase    def test_read_write_boolean_attribute      topic = Topic.new -    # puts "" -    # puts "New Topic" -    # puts topic.inspect      topic.approved = "false" -    # puts "Expecting false" -    # puts topic.inspect      assert !topic.approved?, "approved should be false" +      topic.approved = "false" -    # puts "Expecting false" -    # puts topic.inspect      assert !topic.approved?, "approved should be false" +      topic.approved = "true" -    # puts "Expecting true" -    # puts topic.inspect      assert topic.approved?, "approved should be true" +      topic.approved = "true" -    # puts "Expecting true" -    # puts topic.inspect      assert topic.approved?, "approved should be true" -    # puts ""    end    def test_overridden_write_attribute @@ -793,27 +786,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase    private    def cached_columns -    Topic.columns.find_all { |column| -      !Topic.serialized_attributes.include? column.name -    }.map(&:name) +    Topic.columns.map(&:name) - Topic.serialized_attributes.keys    end    def time_related_columns_on_topic      Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) }    end -  def in_time_zone(zone) -    old_zone  = Time.zone -    old_tz    = ActiveRecord::Base.time_zone_aware_attributes - -    Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil -    ActiveRecord::Base.time_zone_aware_attributes = !zone.nil? -    yield -  ensure -    Time.zone = old_zone -    ActiveRecord::Base.time_zone_aware_attributes = old_tz -  end -    def privatize(method_signature)      @target.class_eval(<<-private_method, __FILE__, __LINE__ + 1)        private diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 203e44857a..8644f2f496 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -616,6 +616,12 @@ class BasicsTest < ActiveRecord::TestCase      assert_equal 'value2', weird.read_attribute('a$b')    end +  def test_group_weirds_by_from +    Weird.create('a$b' => 'value', :from => 'aaron') +    count = Weird.group(Weird.arel_table[:from]).count +    assert_equal 1, count['aaron'] +  end +    def test_attributes_on_dummy_time      # Oracle, and Sybase do not have a TIME datatype.      return true if current_adapter?(:OracleAdapter, :SybaseAdapter) @@ -1442,6 +1448,13 @@ class BasicsTest < ActiveRecord::TestCase      assert_match(/\/#{dev.id}$/, dev.cache_key)    end +  def test_cache_key_format_is_precise_enough +    dev = Developer.first +    key = dev.cache_key +    dev.touch +    assert_not_equal key, dev.cache_key +  end +    def test_uniq_delegates_to_scoped      scope = stub      Bird.stubs(:all).returns(mock(:uniq => scope)) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index abbf2a765e..65d28ea028 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -580,4 +580,10 @@ class CalculationsTest < ActiveRecord::TestCase      assert_equal ["Over There"], Possession.pluck(:where)    end + +  def test_pluck_replaces_select_clause +    taks_relation = Topic.select(:approved, :id).order(:id) +    assert_equal [1,2,3,4], taks_relation.pluck(:id) +    assert_equal [false, true, true, true], taks_relation.pluck(:approved) +  end  end diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 631bf1aaac..2ddabe058f 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -8,7 +8,7 @@ module ActiveRecord          @subklass = Class.new(@klass)          @handler = ConnectionHandler.new -        @handler.establish_connection @klass, Base.connection_pool.spec +        @pool    = @handler.establish_connection(@klass, Base.connection_pool.spec)        end        def test_retrieve_connection @@ -44,6 +44,12 @@ module ActiveRecord          assert_same @handler.retrieve_connection_pool(@klass),            @handler.retrieve_connection_pool(@subklass)        end + +      def test_connection_pools +        assert_deprecated do +          assert_equal({ Base.connection_pool.spec => @pool }, @handler.connection_pools) +        end +      end      end    end  end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 40f1dbccde..d4fc5f204b 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -27,6 +27,8 @@ class NumericData < ActiveRecord::Base  end  class DirtyTest < ActiveRecord::TestCase +  include InTimeZone +    # Dummy to force column loads so query counts are clean.    def setup      Person.create :first_name => 'foo' @@ -121,7 +123,6 @@ class DirtyTest < ActiveRecord::TestCase    end    def test_time_attributes_changes_without_time_zone -      target = Class.new(ActiveRecord::Base)      target.table_name = 'pirates' @@ -604,16 +605,4 @@ class DirtyTest < ActiveRecord::TestCase        assert_equal %w(parrot_id), pirate.changed        assert_nil pirate.parrot_id_was      end - -    def in_time_zone(zone) -      old_zone  = Time.zone -      old_tz    = ActiveRecord::Base.time_zone_aware_attributes - -      Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil -      ActiveRecord::Base.time_zone_aware_attributes = !zone.nil? -      yield -    ensure -      Time.zone = old_zone -      ActiveRecord::Base.time_zone_aware_attributes = old_tz -    end  end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index d44ac21b05..7db7953313 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -610,6 +610,11 @@ class FinderTest < ActiveRecord::TestCase      assert_nil Topic.find_by_heading("The First Topic!")    end +  def test_find_by_one_attribute_bang_with_blank_defined +    blank_topic = BlankTopic.create(title: "The Blank One") +    assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One") +  end +    def test_find_by_one_attribute_with_conditions      assert_equal accounts(:rails_core_account), Account.where('firm_id = ?', 6).find_by_credit_limit(50)    end diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index cff6689c15..1bff005510 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -135,3 +135,18 @@ module LogIntercepter    end  end +module InTimeZone +  private + +  def in_time_zone(zone) +    old_zone  = Time.zone +    old_tz    = ActiveRecord::Base.time_zone_aware_attributes + +    Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil +    ActiveRecord::Base.time_zone_aware_attributes = !zone.nil? +    yield +  ensure +    Time.zone = old_zone +    ActiveRecord::Base.time_zone_aware_attributes = old_tz +  end +end diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb index 4614be9650..8fb03cdee0 100644 --- a/activerecord/test/cases/migration/change_table_test.rb +++ b/activerecord/test/cases/migration/change_table_test.rb @@ -164,6 +164,13 @@ module ActiveRecord          end        end +      def test_rename_index_renames_index +        with_change_table do |t| +          @connection.expect :rename_index, nil, [:delete_me, :bar, :baz] +          t.rename_index :bar, :baz +        end +      end +        def test_change_changes_column          with_change_table do |t|            @connection.expect :change_column, nil, [:delete_me, :bar, :string, {}] diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb index 768ebc5861..e28feedcf9 100644 --- a/activerecord/test/cases/migration/helper.rb +++ b/activerecord/test/cases/migration/helper.rb @@ -2,12 +2,10 @@ require "cases/helper"  module ActiveRecord    class Migration -    class << self -      attr_accessor :message_count -    end +    class << self; attr_accessor :message_count; end +    self.message_count = 0      def puts(text="") -      ActiveRecord::Migration.message_count ||= 0        ActiveRecord::Migration.message_count += 1      end diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb index 144302bd4a..d8a6565d54 100644 --- a/activerecord/test/cases/migration/references_statements_test.rb +++ b/activerecord/test/cases/migration/references_statements_test.rb @@ -64,7 +64,7 @@ module ActiveRecord          remove_reference table_name, :supplier          refute index_exists?(table_name, :supplier_id)        end -       +        def test_does_not_delete_reference_type_column          with_polymorphic_column do            remove_reference table_name, :supplier @@ -73,7 +73,7 @@ module ActiveRecord            assert column_exists?(table_name, :supplier_type, :string)          end        end -       +        def test_deletes_reference_type_column          with_polymorphic_column do            remove_reference table_name, :supplier, polymorphic: true diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index c155f29973..b77da39e29 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -755,4 +755,11 @@ class CopyMigrationsTest < ActiveRecord::TestCase    ensure      clear    end + +  def test_create_join_table_with_symbol_and_string +    connection.create_join_table :artists, 'musics' + +    assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort +  end +  end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index fe9eddbdec..3f08f9ea4d 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -185,6 +185,17 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase      assert_equal "James", mean_pirate.parrot.name      assert_equal "blue", mean_pirate.parrot.color    end + +  def test_accepts_nested_attributes_for_can_be_overridden_in_subclasses +    Pirate.accepts_nested_attributes_for(:parrot) + +    mean_pirate_class = Class.new(Pirate) do +      accepts_nested_attributes_for :parrot +    end +    mean_pirate = mean_pirate_class.new +    mean_pirate.parrot_attributes = { :name => "James" } +    assert_equal "James", mean_pirate.parrot.name +  end  end  class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase @@ -464,17 +475,15 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase    end    def test_should_unset_association_when_an_existing_record_is_destroyed -    @ship.reload      original_pirate_id = @ship.pirate.id -    @ship.attributes = {:pirate_attributes => {:id => @ship.pirate.id, :_destroy => true}} -    @ship.save! +    @ship.update_attributes! pirate_attributes: { id: @ship.pirate.id, _destroy: true } -    assert_empty Pirate.where(["id = ?", original_pirate_id]) +    assert_empty Pirate.where(id: original_pirate_id)      assert_nil @ship.pirate_id      assert_nil @ship.pirate      @ship.reload -    assert_empty Pirate.where(["id = ?", original_pirate_id]) +    assert_empty Pirate.where(id: original_pirate_id)      assert_nil @ship.pirate_id      assert_nil @ship.pirate    end @@ -491,7 +500,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase      @ship.update_attributes(:pirate_attributes => { :id => @ship.pirate.id, :_destroy => '1' })      assert_nothing_raised(ActiveRecord::RecordNotFound) { @ship.pirate.reload } - +  ensure      Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }    end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 4b938da5c4..b2609f6395 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -280,12 +280,23 @@ class PersistencesTest < ActiveRecord::TestCase    def test_update_sti_type      assert_instance_of Reply, topics(:second) -    topic = topics(:second).becomes(Topic) +    topic = topics(:second).becomes!(Topic)      assert_instance_of Topic, topic      topic.save!      assert_instance_of Topic, Topic.find(topic.id)    end +  def test_preserve_original_sti_type +    reply = topics(:second) +    assert_equal "Reply", reply.type + +    topic = reply.becomes(Topic) +    assert_equal "Reply", reply.type + +    assert_instance_of Topic, topic +    assert_equal "Reply", topic.type +  end +    def test_delete      topic = Topic.find(1)      assert_equal topic, topic.delete, 'topic.delete did not return self' diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index bc6cac0c6c..c34aeaf925 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -157,19 +157,19 @@ class RelationTest < ActiveRecord::TestCase      assert_equal 4, topics.to_a.size      assert_equal topics(:first).title, topics.first.title    end -   +    def test_finding_with_assoc_order      topics = Topic.order(:id => :desc)      assert_equal 4, topics.to_a.size      assert_equal topics(:fourth).title, topics.first.title    end -   +    def test_finding_with_reverted_assoc_order      topics = Topic.order(:id => :asc).reverse_order      assert_equal 4, topics.to_a.size      assert_equal topics(:fourth).title, topics.first.title    end -   +    def test_raising_exception_on_invalid_hash_params      assert_raise(ArgumentError) { Topic.order(:name, "id DESC", :id => :DeSc) }    end @@ -1438,4 +1438,18 @@ class RelationTest < ActiveRecord::TestCase      end      assert_no_queries { relation.to_a }    end + +  test 'group with select and includes' do +    authors_count = Post.select('author_id, COUNT(author_id) AS num_posts'). +      group('author_id').order('author_id').includes(:author).to_a + +    assert_no_queries do +      result = authors_count.map do |post| +        [post.num_posts, post.author.try(:name)] +      end + +      expected = [[1, nil], [5, "David"], [3, "Mary"], [2, "Bob"]] +      assert_equal expected, result +    end +  end  end diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb index 561b431766..1636c118a2 100644 --- a/activerecord/test/models/reference.rb +++ b/activerecord/test/models/reference.rb @@ -4,9 +4,8 @@ class Reference < ActiveRecord::Base    has_many :agents_posts_authors, :through => :person -  class << self -    attr_accessor :make_comments -  end +  class << self; attr_accessor :make_comments; end +  self.make_comments = false    before_destroy :make_comments diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 4b27c16681..f7f4cebc5a 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -33,6 +33,7 @@ class Topic < ActiveRecord::Base    end    has_many :replies, :dependent => :destroy, :foreign_key => "parent_id" +  has_many :approved_replies, -> { approved }, class_name: 'Reply', foreign_key: "parent_id", counter_cache: 'replies_count'    has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title"    has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id" @@ -106,6 +107,12 @@ class ImportantTopic < Topic    serialize :important, Hash  end +class BlankTopic < Topic +  def blank? +    true +  end +end +  module Web    class Topic < ActiveRecord::Base      has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply' diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index d0e7338f15..0cfde83778 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -192,5 +192,10 @@ end  _SQL  rescue #This version of PostgreSQL either has no XML support or is was not compiled with XML support: skipping table    end + +  create_table :limitless_fields, force: true do |t| +    t.binary :binary, limit: 100_000 +    t.text :text, limit: 100_000 +  end  end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index eec06754a5..35778d008a 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -770,6 +770,7 @@ ActiveRecord::Schema.define do    end    create_table :weirds, :force => true do |t|      t.string 'a$b' +    t.string 'from'    end    except 'SQLite' do  | 
