diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 4d8b19b8ab..3b47799989 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* Make `increment_counter`/`decrement_counter` accept an amount argument + + ```ruby + Post.increment_counter(:comments_count, 5, by: 3) + ``` + + *fatkodima* + * Add support for `Array#intersect?` to `ActiveRecord::Relation`. `Array#intersect?` is only available on Ruby 3.1 or later. diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 8a72ee2f61..480f08a081 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -126,6 +126,7 @@ def update_counters(id, counters) # # * +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. + # * :by - The amount by which to increment the value. Defaults to +1+. # * :touch - 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. @@ -136,10 +137,14 @@ def update_counters(id, counters) # DiscussionBoard.increment_counter(:posts_count, 5) # # # Increment the posts_count column for the record with an id of 5 + # # by a specific amount. + # DiscussionBoard.increment_counter(:posts_count, 5, by: 3) + # + # # 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, touch: touch) + def increment_counter(counter_name, id, by: 1, touch: nil) + update_counters(id, counter_name => by, touch: touch) end # Decrement a numeric field by one, via a direct SQL update. @@ -151,6 +156,7 @@ def increment_counter(counter_name, id, touch: nil) # # * +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. + # * :by - The amount by which to increment the value. Defaults to +1+. # * :touch - 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. @@ -161,10 +167,14 @@ def increment_counter(counter_name, id, touch: nil) # DiscussionBoard.decrement_counter(:posts_count, 5) # # # Decrement the posts_count column for the record with an id of 5 + # by a specific amount. + # DiscussionBoard.decrement_counter(:posts_count, 5, by: 3) + # + # # 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, touch: touch) + def decrement_counter(counter_name, id, by: 1, touch: nil) + update_counters(id, counter_name => -by, touch: touch) end def counter_cache_column?(name) # :nodoc: diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb index 52d34747df..6a16d14d8b 100644 --- a/activerecord/test/cases/counter_cache_test.rb +++ b/activerecord/test/cases/counter_cache_test.rb @@ -40,12 +40,24 @@ class ::SpecialReply < ::Reply end end + test "increment counter by specific amount" do + assert_difference "@topic.reload.replies_count", +2 do + Topic.increment_counter(:replies_count, @topic.id, by: 2) + end + end + test "decrement counter" do assert_difference "@topic.reload.replies_count", -1 do Topic.decrement_counter(:replies_count, @topic.id) end end + test "decrement counter by specific amount" do + assert_difference "@topic.reload.replies_count", -2 do + Topic.decrement_counter(:replies_count, @topic.id, by: 2) + end + end + test "reset counters" do # throw the count off by 1 Topic.increment_counter(:replies_count, @topic.id)