aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/aggregations.rb36
-rw-r--r--activerecord/lib/active_record/associations.rb112
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb12
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb4
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb10
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb12
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb10
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb3
-rw-r--r--activerecord/lib/active_record/autosave_association.rb22
-rw-r--r--activerecord/lib/active_record/base.rb40
-rw-r--r--activerecord/lib/active_record/callbacks.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb30
-rw-r--r--activerecord/lib/active_record/connection_handling.rb16
-rw-r--r--activerecord/lib/active_record/core.rb5
-rw-r--r--activerecord/lib/active_record/counter_cache.rb4
-rw-r--r--activerecord/lib/active_record/errors.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb12
-rw-r--r--activerecord/lib/active_record/locking/pessimistic.rb6
-rw-r--r--activerecord/lib/active_record/migration.rb6
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb2
-rw-r--r--activerecord/lib/active_record/model_schema.rb2
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb70
-rw-r--r--activerecord/lib/active_record/null_relation.rb6
-rw-r--r--activerecord/lib/active_record/persistence.rb39
-rw-r--r--activerecord/lib/active_record/railtie.rb33
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb29
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb24
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb8
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb28
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb4
-rw-r--r--activerecord/lib/active_record/sanitization.rb24
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb14
-rw-r--r--activerecord/lib/active_record/timestamp.rb3
-rw-r--r--activerecord/lib/active_record/transactions.rb28
45 files changed, 424 insertions, 301 deletions
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.