counter_culture 3.13.2 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +2 -2
- data/lib/counter_culture/configuration.rb +7 -0
- data/lib/counter_culture/counter.rb +16 -2
- data/lib/counter_culture/reconciler.rb +25 -6
- data/lib/counter_culture/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55a968373b0c0e7f763a62f22c1fcee0156a27227a605d07d85dc464d9973873
|
|
4
|
+
data.tar.gz: b2c63fa6c9c9be56a8511e39663c82ede80e8a07d49d0991c2e7a0cd7c69d2c8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba7889dd178c96cff2b98b111ff7129dbba01c5e73f419c5a68489850ddf0bca205dd03e23302994a517b82a592a2967db6c39598a5b286420dbb166bb04c977
|
|
7
|
+
data.tar.gz: e2261e735bccf84dd64549788facecd1fa7d240ef450852b69be596f57a4f93c3a9db63227e64bc9dd02380aab3c78c9c7d7decae12070187c242a3f96db14e6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 3.14.0 (June 26, 2026)
|
|
2
|
+
|
|
3
|
+
New features:
|
|
4
|
+
- Use the built-in `ActiveRecord.after_all_transactions_commit` API for `execute_after_commit` on Rails 7.2+, so the `after_commit_action` gem is no longer required on modern Rails. Older Rails versions keep using the gem as before.
|
|
5
|
+
|
|
6
|
+
Changes:
|
|
7
|
+
- On Rails 7.2+, counter_culture no longer mixes the `after_commit_action` gem's `AfterCommitAction` module into models that use `execute_after_commit`. If your own code called the `execute_after_commit` instance method on those models (relying on counter_culture to make it available), add the `after_commit_action` gem and `include AfterCommitAction` yourself.
|
|
8
|
+
|
|
9
|
+
## 3.13.3 (June 25, 2026)
|
|
10
|
+
|
|
11
|
+
Bugfixes:
|
|
12
|
+
- Fix `counter_culture_fix_counts` raising a SQL syntax error for counter caches stored in reserved-word columns (e.g. `order`) by quoting identifiers in the reconciler (#434)
|
|
13
|
+
|
|
1
14
|
## 3.13.2 (June 25, 2026)
|
|
2
15
|
|
|
3
16
|
Bugfixes:
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# counter_culture [](https://circleci.com/gh/magnusvk/counter_culture/tree/main)
|
|
2
2
|
|
|
3
3
|
Turbo-charged counter caches for your Rails app. Huge improvements over the Rails standard counter caches:
|
|
4
4
|
|
|
@@ -272,7 +272,7 @@ Another option is to simply defer the update of counter caches to outside of the
|
|
|
272
272
|
```ruby
|
|
273
273
|
counter_culture :category, execute_after_commit: true
|
|
274
274
|
```
|
|
275
|
-
[NOTE]
|
|
275
|
+
[NOTE] On Rails 7.2 and newer this uses the built-in `ActiveRecord.after_all_transactions_commit` API and requires no extra dependencies. On older Rails versions you need to manually specify the `after_commit_action` gem as a dependency in your Gemfile to use this feature:
|
|
276
276
|
```ruby
|
|
277
277
|
...
|
|
278
278
|
gem "after_commit_action"
|
|
@@ -15,6 +15,13 @@ module CounterCulture
|
|
|
15
15
|
Gem::Requirement.new('>= 7.2.0').satisfied_by?(ActiveRecord.version)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# Rails 7.2+ ships `ActiveRecord.after_all_transactions_commit`, which lets
|
|
19
|
+
# `execute_after_commit` defer counter updates natively instead of relying on
|
|
20
|
+
# the `after_commit_action` gem.
|
|
21
|
+
def self.supports_native_after_commit?
|
|
22
|
+
Gem::Requirement.new('>= 7.2.0').satisfied_by?(ActiveRecord.version)
|
|
23
|
+
end
|
|
24
|
+
|
|
18
25
|
class Configuration
|
|
19
26
|
attr_reader :use_read_replica
|
|
20
27
|
|
|
@@ -21,7 +21,10 @@ module CounterCulture
|
|
|
21
21
|
@execute_after_commit = options.fetch(:execute_after_commit, false)
|
|
22
22
|
@include_soft_deleted = options.fetch(:include_soft_deleted, false)
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# Rails 7.2+ ships `ActiveRecord.after_all_transactions_commit`, which
|
|
25
|
+
# does everything we need the `after_commit_action` gem for. Only fall
|
|
26
|
+
# back to the gem on older Rails versions.
|
|
27
|
+
if @execute_after_commit && !CounterCulture.supports_native_after_commit?
|
|
25
28
|
begin
|
|
26
29
|
require 'after_commit_action'
|
|
27
30
|
rescue LoadError
|
|
@@ -390,7 +393,18 @@ module CounterCulture
|
|
|
390
393
|
execute_after_commit = @execute_after_commit.is_a?(Proc) ? @execute_after_commit.call : @execute_after_commit
|
|
391
394
|
|
|
392
395
|
if execute_after_commit
|
|
393
|
-
|
|
396
|
+
if CounterCulture.supports_native_after_commit?
|
|
397
|
+
# NOTE: `after_all_transactions_commit` waits for *every* open
|
|
398
|
+
# transaction across *all* database connections and skips the block if
|
|
399
|
+
# any of them roll back, whereas `after_commit_action` tied the block to
|
|
400
|
+
# this record's own connection only. The two are equivalent for a
|
|
401
|
+
# single database; they differ only when a record is saved inside
|
|
402
|
+
# transactions that span multiple databases (which Rails itself
|
|
403
|
+
# discourages).
|
|
404
|
+
ActiveRecord.after_all_transactions_commit(&block)
|
|
405
|
+
else
|
|
406
|
+
obj.execute_after_commit(&block)
|
|
407
|
+
end
|
|
394
408
|
else
|
|
395
409
|
block.call
|
|
396
410
|
end
|
|
@@ -108,13 +108,15 @@ module CounterCulture
|
|
|
108
108
|
next unless column_name
|
|
109
109
|
|
|
110
110
|
relation_class_table_name = quote_table_name(relation_class.table_name)
|
|
111
|
+
# quote the cache column so reserved-word/quoting-sensitive names work
|
|
112
|
+
quoted_column_name = quote_column_name(column_name)
|
|
111
113
|
|
|
112
114
|
# select join column and count (from above) as well as cache column ('column_name') for later comparison
|
|
113
115
|
counts_query = scope.select(
|
|
114
116
|
"#{primary_key_select}, " \
|
|
115
117
|
"#{association_primary_key_select}, " \
|
|
116
118
|
"#{count_select} AS count, " \
|
|
117
|
-
"MAX(#{relation_class_table_name}.#{
|
|
119
|
+
"MAX(#{relation_class_table_name}.#{quoted_column_name}) AS #{quoted_column_name}"
|
|
118
120
|
)
|
|
119
121
|
|
|
120
122
|
# we need to join together tables until we get back to the table this class itself lives in
|
|
@@ -155,9 +157,20 @@ module CounterCulture
|
|
|
155
157
|
|
|
156
158
|
track_change(record, column_name, count)
|
|
157
159
|
|
|
158
|
-
updates
|
|
159
|
-
#
|
|
160
|
-
updates
|
|
160
|
+
# Build a Hash of column => value updates and let Rails (Arel) qualify
|
|
161
|
+
# and escape the column names and cast/quote the values, consistent with
|
|
162
|
+
# the Arel-based updates used in Counter (see #425). This updates the
|
|
163
|
+
# actual counter.
|
|
164
|
+
updates = { column_name => count }
|
|
165
|
+
|
|
166
|
+
# Set lock_version = lock_version (no-op) to skip Rails auto-increment,
|
|
167
|
+
# just as Counter does (see #429); a Hash update_all would otherwise
|
|
168
|
+
# bump the optimistic locking column.
|
|
169
|
+
if relation_class.locking_enabled?
|
|
170
|
+
lock_column = relation_class.locking_column
|
|
171
|
+
updates[lock_column] = relation_class.arel_table[lock_column]
|
|
172
|
+
end
|
|
173
|
+
|
|
161
174
|
# and here we update the timestamp, if so desired
|
|
162
175
|
if options[:touch]
|
|
163
176
|
current_time = record.send(:current_time_from_proper_timezone)
|
|
@@ -168,13 +181,13 @@ module CounterCulture
|
|
|
168
181
|
timestamp_columns << options[:touch]
|
|
169
182
|
end
|
|
170
183
|
timestamp_columns.each do |timestamp_column|
|
|
171
|
-
updates
|
|
184
|
+
updates[timestamp_column] = current_time
|
|
172
185
|
end
|
|
173
186
|
end
|
|
174
187
|
|
|
175
188
|
with_writing_db_connection do
|
|
176
189
|
conditions = Array.wrap(relation_class.primary_key).map { |key| [key, record.send(key)] }.to_h
|
|
177
|
-
relation_class.where(conditions).distinct(false).update_all(updates
|
|
190
|
+
relation_class.where(conditions).distinct(false).update_all(updates)
|
|
178
191
|
end
|
|
179
192
|
end
|
|
180
193
|
end
|
|
@@ -351,6 +364,12 @@ module CounterCulture
|
|
|
351
364
|
end
|
|
352
365
|
end
|
|
353
366
|
|
|
367
|
+
def quote_column_name(column_name)
|
|
368
|
+
@connection_handler.call do |connection|
|
|
369
|
+
connection.quote_column_name(column_name)
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
354
373
|
def parameterize(string)
|
|
355
374
|
if ACTIVE_RECORD_VERSION < Gem::Version.new("5.0")
|
|
356
375
|
string.parameterize('_')
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: counter_culture
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Magnus von Koeller
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|