aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb15
-rw-r--r--activerecord/CHANGELOG.md5
-rw-r--r--activerecord/lib/active_record/associations.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.rb9
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb20
-rw-r--r--activerecord/lib/active_record/core.rb3
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/null_relation.rb6
-rw-r--r--activerecord/lib/active_record/timestamp.rb3
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb10
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb8
-rw-r--r--activerecord/test/models/topic.rb1
15 files changed, 71 insertions, 30 deletions
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 5705ab590c..02ac111392 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -29,13 +29,14 @@ module AbstractController
# * <tt>only</tt> - The callback should be run only for this action
# * <tt>except</tt> - The callback should be run for all actions except this action
def _normalize_callback_options(options)
- if only = options[:only]
- only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
- options[:if] = Array(options[:if]) << only
- end
- if except = options[:except]
- except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
- options[:unless] = Array(options[:unless]) << except
+ _normalize_callback_option(options, :only, :if)
+ _normalize_callback_option(options, :except, :unless)
+ end
+
+ def _normalize_callback_option(options, from, to) # :nodoc:
+ if from = options[from]
+ from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ")
+ options[to] = Array(options[to]) << from
end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 28d8b066e0..11559e3cb3 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,10 @@
## Rails 4.0.0 (unreleased) ##
+* :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.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a86e43664e..5949269f2a 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1125,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]
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 536e108f0e..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
@@ -835,9 +837,8 @@ module ActiveRecord
# Returns a <tt>Relation</tt> object for the records in this association
def scope
association = @association
- scope = @association.scope
- scope.none! if @association.owner.new_record?
- scope.extending! do
+
+ @association.scope.extending! do
define_method(:proxy_association) { association }
end
end
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/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/core.rb b/activerecord/lib/active_record/core.rb
index 98032db2ef..7b4710e6b3 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
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 29a99a5336..79d37147d0 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -883,7 +883,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/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/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/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 6355094a79..45b0a76f5a 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -754,6 +754,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")
@@ -1655,6 +1663,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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)
end
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/models/topic.rb b/activerecord/test/models/topic.rb
index 4b27c16681..ab31d85e46 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"