counter_culture 3.10.1 → 3.11.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 +11 -1
- data/lib/counter_culture/configuration.rb +4 -0
- data/lib/counter_culture/counter.rb +23 -10
- data/lib/counter_culture/reconciler.rb +34 -8
- data/lib/counter_culture/version.rb +1 -1
- data/lib/counter_culture.rb +8 -3
- 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: dbbb4f3f637d653c94426d45846cb656d5f199db3706f9d0b2187ca6a67e2be0
|
4
|
+
data.tar.gz: 55d928ec95802a130b82b9c25b2a970eca01ee9c07d9b7346731b9e6fb17aaa0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89d37375626c427c86481ea57f0de65e59939982436d13968e223956473d94ab29a5b270715c577348bc8b194f46d6beaae80d8a6fc5554f04e9bf221934bad2
|
7
|
+
data.tar.gz: f6a7886d1d5e72b8e0e414e1b83e47b24ff87d2214597565a526f8797c605ee662d2cd3c3fdc3f540368c424559a2eedad2c634e8713725ed2f7921ebab3b701
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
+
## 3.11.0 (June 26, 2025)
|
2
|
+
|
3
|
+
New features:
|
4
|
+
- Support for composite primary keys in Rails v7.2+ (#413)
|
5
|
+
|
6
|
+
## 3.10.2 (June 24, 2025)
|
7
|
+
|
8
|
+
Bugfixes:
|
9
|
+
- Fix incorrect in-memory counter updates on `belongs_to` association (#415)
|
10
|
+
|
1
11
|
## 3.10.1 (April 30, 2025)
|
2
12
|
|
3
|
-
|
13
|
+
Bugfixes:
|
4
14
|
- Fix issue when using `delegate` instead of `has_many :through` (#411)
|
5
15
|
|
6
16
|
## 3.10.0 (April 15, 2025)
|
@@ -113,7 +113,9 @@ module CounterCulture
|
|
113
113
|
end
|
114
114
|
|
115
115
|
if @with_papertrail
|
116
|
-
|
116
|
+
conditions = primary_key_conditions(primary_key, id_to_change)
|
117
|
+
instance = klass.where(conditions).first
|
118
|
+
|
117
119
|
if instance
|
118
120
|
if instance.paper_trail.respond_to?(:save_with_version)
|
119
121
|
# touch_with_version is deprecated starting in PaperTrail 9.0.0
|
@@ -137,8 +139,11 @@ module CounterCulture
|
|
137
139
|
|
138
140
|
unless Thread.current[:aggregate_counter_updates]
|
139
141
|
execute_now_or_after_commit(obj) do
|
140
|
-
|
141
|
-
|
142
|
+
conditions = primary_key_conditions(primary_key, id_to_change)
|
143
|
+
klass.where(conditions).update_all updates.join(', ')
|
144
|
+
unless options[:was]
|
145
|
+
assign_to_associated_object(obj, relation, change_counter_column, operator, delta_magnitude)
|
146
|
+
end
|
142
147
|
end
|
143
148
|
end
|
144
149
|
end
|
@@ -172,7 +177,7 @@ module CounterCulture
|
|
172
177
|
|
173
178
|
# the string to pass to order() in order to sort by primary key
|
174
179
|
def full_primary_key(klass)
|
175
|
-
"#{klass.quoted_table_name}.#{
|
180
|
+
Array.wrap(klass.quoted_primary_key).map { |pk| "#{klass.quoted_table_name}.#{pk}" }.join(', ')
|
176
181
|
end
|
177
182
|
|
178
183
|
# gets the value of the foreign key on the given relation
|
@@ -186,23 +191,24 @@ module CounterCulture
|
|
186
191
|
original_relation = relation
|
187
192
|
relation = relation.is_a?(Enumerable) ? relation.dup : [relation]
|
188
193
|
|
189
|
-
if was
|
194
|
+
value = if was
|
190
195
|
first = relation.shift
|
191
196
|
foreign_key_value = attribute_was(obj, relation_foreign_key(first))
|
192
197
|
klass = relation_klass(first, source: obj, was: was)
|
193
198
|
if foreign_key_value
|
194
|
-
|
195
|
-
|
196
|
-
|
199
|
+
primary_key = relation_primary_key(first, source: obj, was: was)
|
200
|
+
conditions = primary_key_conditions(primary_key, foreign_key_value)
|
201
|
+
klass.where(conditions).first
|
197
202
|
end
|
198
203
|
else
|
199
|
-
|
204
|
+
obj
|
200
205
|
end
|
201
206
|
while !value.nil? && relation.size > 0
|
202
207
|
value = value.send(relation.shift)
|
203
208
|
end
|
204
209
|
|
205
|
-
|
210
|
+
primary_key = relation_primary_key(original_relation, source: obj, was: was)
|
211
|
+
Array.wrap(primary_key).map { |pk| value.try(pk&.to_sym) }.compact.presence
|
206
212
|
end
|
207
213
|
|
208
214
|
# gets the reflect object on the given relation
|
@@ -308,6 +314,7 @@ module CounterCulture
|
|
308
314
|
return reflect.options[:primary_key] if reflect.options.key?(:primary_key)
|
309
315
|
return relation_klass(relation, source: source, was: was).try(:primary_key)
|
310
316
|
end
|
317
|
+
|
311
318
|
reflect.association_primary_key(klass)
|
312
319
|
end
|
313
320
|
|
@@ -439,6 +446,12 @@ module CounterCulture
|
|
439
446
|
end
|
440
447
|
end
|
441
448
|
|
449
|
+
def primary_key_conditions(primary_key, fk_value)
|
450
|
+
Array.wrap(primary_key)
|
451
|
+
.zip(Array.wrap(fk_value))
|
452
|
+
.to_h
|
453
|
+
end
|
454
|
+
|
442
455
|
def counter_update_snippet(update, klass, id_to_change, operator, delta_magnitude)
|
443
456
|
if Thread.current[:aggregate_counter_updates]
|
444
457
|
remember_counter_update(
|
@@ -111,8 +111,8 @@ module CounterCulture
|
|
111
111
|
|
112
112
|
# select join column and count (from above) as well as cache column ('column_name') for later comparison
|
113
113
|
counts_query = scope.select(
|
114
|
-
"#{
|
115
|
-
"#{
|
114
|
+
"#{primary_key_select}, " \
|
115
|
+
"#{association_primary_key_select}, " \
|
116
116
|
"#{count_select} AS count, " \
|
117
117
|
"MAX(#{relation_class_table_name}.#{column_name}) AS #{column_name}"
|
118
118
|
)
|
@@ -173,7 +173,8 @@ module CounterCulture
|
|
173
173
|
end
|
174
174
|
|
175
175
|
with_writing_db_connection do
|
176
|
-
|
176
|
+
conditions = Array.wrap(relation_class.primary_key).map { |key| [key, record.send(key)] }.to_h
|
177
|
+
relation_class.where(conditions).update_all(updates.join(', '))
|
177
178
|
end
|
178
179
|
end
|
179
180
|
end
|
@@ -199,11 +200,12 @@ module CounterCulture
|
|
199
200
|
def track_change(record, column_name, count)
|
200
201
|
@changes_holder << {
|
201
202
|
:entity => relation_class.name,
|
202
|
-
relation_class.primary_key.to_sym => record.send(relation_class.primary_key),
|
203
203
|
:what => column_name,
|
204
204
|
:wrong => record.send(column_name),
|
205
205
|
:right => count
|
206
|
-
}
|
206
|
+
}.tap do |h|
|
207
|
+
Array.wrap(relation_class.primary_key).each { |pk| h[pk.to_sym] = record.send(pk) }
|
208
|
+
end
|
207
209
|
end
|
208
210
|
|
209
211
|
def count_select
|
@@ -217,10 +219,23 @@ module CounterCulture
|
|
217
219
|
@count_select = "SUM(COALESCE(#{self_table_name}.#{delta_column}, 0))"
|
218
220
|
end
|
219
221
|
else
|
220
|
-
|
222
|
+
primary_key = Array.wrap(model.primary_key).first
|
223
|
+
count_column = "#{self_table_name}.#{primary_key}"
|
224
|
+
@count_select = "COUNT(#{count_column})*#{delta_magnitude}"
|
221
225
|
end
|
222
226
|
end
|
223
227
|
|
228
|
+
def primary_key_select
|
229
|
+
relation_class_table_name = quote_table_name(relation_class.table_name)
|
230
|
+
Array.wrap(relation_class.primary_key).map { |pk| "#{relation_class_table_name}.#{pk}" }.join(', ')
|
231
|
+
end
|
232
|
+
|
233
|
+
def association_primary_key_select
|
234
|
+
relation_class_table_name = quote_table_name(relation_class.table_name)
|
235
|
+
association_primary_key = relation_reflect(relation).association_primary_key(relation_class)
|
236
|
+
Array.wrap(association_primary_key).map { |apk| "#{relation_class_table_name}.#{apk}" }.join(', ')
|
237
|
+
end
|
238
|
+
|
224
239
|
def self_table_name
|
225
240
|
return @self_table_name if @self_table_name
|
226
241
|
|
@@ -271,8 +286,17 @@ module CounterCulture
|
|
271
286
|
[target_table_key, source_table_key]
|
272
287
|
end
|
273
288
|
|
289
|
+
source_table_key = Array.wrap(source_table_key)
|
290
|
+
target_table_key = Array.wrap(target_table_key)
|
291
|
+
|
292
|
+
join_conditions =
|
293
|
+
source_table_key
|
294
|
+
.zip(target_table_key).map do |source_key, target_key|
|
295
|
+
"#{source_table}.#{source_key} = #{target_table_alias}.#{target_key}"
|
296
|
+
end.join(' AND ')
|
274
297
|
joins_sql = "LEFT JOIN #{target_table} AS #{target_table_alias} "\
|
275
|
-
"ON #{
|
298
|
+
"ON #{join_conditions}"
|
299
|
+
|
276
300
|
# adds 'type' condition to JOIN clause if the current model is a
|
277
301
|
# child in a Single Table Inheritance
|
278
302
|
if reflect.active_record.column_names.include?('type') &&
|
@@ -289,7 +313,9 @@ module CounterCulture
|
|
289
313
|
# conditions must be applied to the join on which we are counting
|
290
314
|
if where
|
291
315
|
if where.respond_to?(:to_sql)
|
292
|
-
|
316
|
+
model_primary_key = Array.wrap(model.primary_key)
|
317
|
+
where_select = model_primary_key.map { |pk| "#{model.table_name}.#{pk}" }.join(', ')
|
318
|
+
joins_sql += " AND (#{target_table_alias}.#{model_primary_key.first}) IN (#{where.select(where_select).to_sql})"
|
293
319
|
else
|
294
320
|
joins_sql += " AND (#{model.send(:sanitize_sql_for_conditions, where)})"
|
295
321
|
end
|
data/lib/counter_culture.rb
CHANGED
@@ -35,9 +35,14 @@ module CounterCulture
|
|
35
35
|
end.compact
|
36
36
|
|
37
37
|
if update_snippets.any?
|
38
|
-
klass
|
39
|
-
|
40
|
-
|
38
|
+
primary_key = Thread.current[:primary_key_map][klass]
|
39
|
+
|
40
|
+
conditions =
|
41
|
+
Array.wrap(primary_key)
|
42
|
+
.zip(Array.wrap(rec_id))
|
43
|
+
.to_h
|
44
|
+
|
45
|
+
klass.where(conditions).update_all(update_snippets.join(', '))
|
41
46
|
end
|
42
47
|
end
|
43
48
|
end
|
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.11.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: 2025-
|
11
|
+
date: 2025-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|