aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/reflection.rb
diff options
context:
space:
mode:
authorBogdan Gusiev <agresso@gmail.com>2015-09-03 16:38:59 +0300
committerBogdan Gusiev <agresso@gmail.com>2015-09-03 16:38:59 +0300
commit712fc8a570bc0709c3be5645e9808b0ad8fcfe45 (patch)
treea76e90ac21ef66f20e1de30b056e24b0b633fd46 /activerecord/lib/active_record/reflection.rb
parent0294c61359340892f0c38f57b203ba58edbc55e5 (diff)
downloadrails-712fc8a570bc0709c3be5645e9808b0ad8fcfe45.tar.gz
rails-712fc8a570bc0709c3be5645e9808b0ad8fcfe45.tar.bz2
rails-712fc8a570bc0709c3be5645e9808b0ad8fcfe45.zip
HasManyAssociation: moved half of counter cache code to reflection
Current implementation has a lot of utility methods that accept reflection call a lot of methods on it and exit. E.g. has_counter_cache?(reflection) It causes confusion and inability to cache result of the method even through it always returns the same result for the same reflection object. It can be done easier without access to the association context by moving code into reflection itself. e.g. reflection.has_counter_cache? Reflection is less complex object than association so moving code there automatically makes it simplier to understand.
Diffstat (limited to 'activerecord/lib/active_record/reflection.rb')
-rw-r--r--activerecord/lib/active_record/reflection.rb59
1 files changed, 50 insertions, 9 deletions
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 5360db6a19..0394f89623 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -94,7 +94,8 @@ module ActiveRecord
# @api public
def reflect_on_all_associations(macro = nil)
association_reflections = reflections.values
- macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
+ association_reflections.select! { |reflection| reflection.macro == macro } if macro
+ association_reflections
end
# Returns the AssociationReflection object for the +association+ (use the symbol).
@@ -159,6 +160,54 @@ module ActiveRecord
scope_chain.flatten
end
+ def counter_cache_column
+ if belongs_to?
+ if options[:counter_cache] == true
+ "#{active_record.name.demodulize.underscore.pluralize}_count"
+ elsif options[:counter_cache]
+ options[:counter_cache].to_s
+ end
+ else
+ options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
+ end
+ end
+
+ # This shit is nasty. We need to avoid the following situation:
+ #
+ # * An associated record is deleted via record.destroy
+ # * Hence the callbacks run, and they find a belongs_to on the record with a
+ # :counter_cache options which points back at our owner. So they update the
+ # counter cache.
+ # * In which case, we must make sure to *not* update the counter cache, or else
+ # it will be decremented twice.
+ #
+ # Hence this method.
+ def inverse_which_updates_counter_cache
+ return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
+ @inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
+ inverse.counter_cache_column == counter_cache_column
+ end
+ end
+ alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
+
+ def inverse_updates_counter_in_memory?
+ inverse_of && inverse_which_updates_counter_cache == inverse_of
+ end
+
+ # Returns whether a counter cache should be used for this association.
+ #
+ # The counter_cache option must be given on either the owner or inverse
+ # association, and the column must be present on the owner.
+ def has_cached_counter?
+ options[:counter_cache] ||
+ inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
+ !!active_record.columns_hash[counter_cache_column]
+ end
+
+ def counter_must_be_updated_by_has_many?
+ !inverse_updates_counter_in_memory? && has_cached_counter?
+ end
+
def alias_candidate(name)
"#{plural_name}_#{name}"
end
@@ -321,14 +370,6 @@ module ActiveRecord
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
end
- def counter_cache_column
- if options[:counter_cache] == true
- "#{active_record.name.demodulize.underscore.pluralize}_count"
- elsif options[:counter_cache]
- options[:counter_cache].to_s
- end
- end
-
def check_validity!
check_validity_of_inverse!
end