aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorJarred Trost <jtrost@gmail.com>2016-12-30 16:01:25 -0600
committerKasper Timm Hansen <kaspth@gmail.com>2017-01-01 18:33:50 +0100
commitbf77e641ce807857fcf804250c4f8ce817ae66be (patch)
tree9c7b1c70a26db3e727a06e38ab0bc6599c8c5aa4 /activerecord/lib
parente42cbb7d31db1774d16a158586c5577c7ad1427c (diff)
downloadrails-bf77e641ce807857fcf804250c4f8ce817ae66be.tar.gz
rails-bf77e641ce807857fcf804250c4f8ce817ae66be.tar.bz2
rails-bf77e641ce807857fcf804250c4f8ce817ae66be.zip
Added option to ActiveRecord::CounterCache methods.
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/counter_cache.rb76
1 files changed, 68 insertions, 8 deletions
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index e2da512813..af29b4c5bb 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -12,13 +12,21 @@ module ActiveRecord
#
# * +id+ - The id of the object you wish to reset a counter on.
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
# # For Post with id #1 records reset the comments_count
# Post.reset_counters(1, :comments)
- def reset_counters(id, *counters)
+ #
+ # # For Post with id #1 records reset the comments_count
+ # # and updates the +updated_at+ and/or +updated_on+ attributes.
+ # Post.reset_counters(1, :comments, touch: true)
+ def reset_counters(id, *counters, touch: nil)
object = find(id)
+
counters.each do |counter_association|
has_many_association = _reflect_on_association(counter_association)
unless has_many_association
@@ -37,9 +45,14 @@ module ActiveRecord
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
counter_name = reflection.counter_cache_column
- unscoped.where(primary_key => object.id).update_all(
- counter_name => object.send(counter_association).count(:all)
- )
+ updates = { counter_name.to_sym => object.send(counter_association).count(:all) }
+
+ touch_time = object.send(:current_time_from_proper_timezone)
+ resolve_timestamp_columns(object, touch).each do |column|
+ updates[column] = touch_time
+ end
+
+ unscoped.where(primary_key => object.id).update_all(updates)
end
return true
end
@@ -55,6 +68,9 @@ module ActiveRecord
# * +id+ - The id of the object you wish to update a counter on or an array of ids.
# * +counters+ - A Hash containing the names of the fields
# to update as keys and the amount to update the field by as values.
+ # * <tt>:touch</tt> option - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
@@ -73,13 +89,34 @@ module ActiveRecord
# # UPDATE posts
# # SET comment_count = COALESCE(comment_count, 0) + 1
# # WHERE id IN (10, 15)
+ #
+ # # For the Posts with id of 10 and 15, increment the comment_count by 1
+ # # and update the updated_at value for each counter.
+ # Post.update_counters [10, 15], comment_count: 1, touch: true
+ # # Executes the following SQL:
+ # # UPDATE posts
+ # # SET comment_count = COALESCE(comment_count, 0) + 1,
+ # # `updated_at` = '2016-10-13T09:59:23-05:00'
+ # # WHERE id IN (10, 15)
def update_counters(id, counters)
+ touch = counters.delete(:touch)
+
updates = counters.map do |counter_name, value|
operator = value < 0 ? "-" : "+"
quoted_column = connection.quote_column_name(counter_name)
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
end
+ if touch
+ object = find(id)
+ touch_time = object.send(:current_time_from_proper_timezone)
+ timestamps = resolve_timestamp_columns(object, touch)
+
+ timestamps.map do |column|
+ updates << "#{connection.quote_column_name(column.to_s)} = #{connection.quote(touch_time)}"
+ end
+ end
+
unscoped.where(primary_key => id).update_all updates.join(", ")
end
@@ -94,13 +131,20 @@ module ActiveRecord
#
# * +counter_name+ - The name of the field that should be incremented.
# * +id+ - The id of the object that should be incremented or an array of ids.
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
# # Increment the posts_count column for the record with an id of 5
# DiscussionBoard.increment_counter(:posts_count, 5)
- def increment_counter(counter_name, id)
- update_counters(id, counter_name => 1)
+ #
+ # # Increment the posts_count column for the record with an id of 5
+ # # and update the updated_at value.
+ # DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
+ def increment_counter(counter_name, id, touch: nil)
+ update_counters(id, { counter_name => 1 }.merge(touch: touch))
end
# Decrement a numeric field by one, via a direct SQL update.
@@ -112,14 +156,30 @@ module ActiveRecord
#
# * +counter_name+ - The name of the field that should be decremented.
# * +id+ - The id of the object that should be decremented or an array of ids.
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
+ # touch that column or an array of symbols to touch just those ones.
#
# ==== Examples
#
# # Decrement the posts_count column for the record with an id of 5
# DiscussionBoard.decrement_counter(:posts_count, 5)
- def decrement_counter(counter_name, id)
- update_counters(id, counter_name => -1)
+ #
+ # # Decrement the posts_count column for the record with an id of 5
+ # # and update the updated_at value.
+ # DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
+ def decrement_counter(counter_name, id, touch: nil)
+ update_counters(id, { counter_name => -1 }.merge(touch: touch))
end
+
+ private
+ def resolve_timestamp_columns(object, touch)
+ if touch == true
+ object.send(:timestamp_attributes_for_update_in_model)
+ else
+ Array(touch)
+ end
+ end
end
private