counter_culture 3.11.2 → 3.11.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c568fc84225757b834a008eb60dbadfe5dea148078c855fe76a2b5711b0c5b1e
4
- data.tar.gz: 3abc94f197b60fb6778eadb98ad402e6b961155c492970111b85bbf1bd171237
3
+ metadata.gz: 4978dfaab9ec73c8b3215c2d2f811093cc00cdf7daccfe877749f301a09ae11a
4
+ data.tar.gz: e2e1322882fdeab8c7239c7cf16e4019b5450d3f435c478ad1d9e2d33cf5a251
5
5
  SHA512:
6
- metadata.gz: adfd25e90ca300e1d6bc19296b343a8fbd9a272076e48591dddc513ffa4173dff10ef2d26cec6bd08bdfbc372ccf1b94e97cee7d7827205523efe0278d1bffea
7
- data.tar.gz: 733126919913da347057de9c81cbbdf7b4b45e9ea1bbbecad12586a06432801e37b1c8833078cc93ce11fdc0ad3b201c5484822dcebc708f970d2106c022d35b
6
+ metadata.gz: a60b46c8b713dbbb7f2969c9b6885e9975a1c5723eb3566a04b011380eafb077c3051386586cff7c77b5ba171888ef577aa5d1dd6be4a4d97ea8e1db1fdca91d
7
+ data.tar.gz: 51feccb08cf927e8fc6f1d41f39f91269411db70d96d3b44d57ddf35c67130790c1340cb27aff0e0f4701934b40bc17485345667d1bffcbfb145eb4a5d98bea6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 3.11.4 (November 5, 2025)
2
+
3
+ Bugfixes:
4
+ - Fix counter cache not using the correct type with multiple STI models in the association chain (#421)
5
+
6
+ ## 3.11.3 (October 22, 2025)
7
+
8
+ Bugfixes:
9
+ - Fix in-memory counter updates for conditional counters when condition changes (#420)
10
+
1
11
  ## 3.11.2 (July 10, 2025)
2
12
 
3
13
  Bugfixes:
@@ -150,7 +150,27 @@ module CounterCulture
150
150
  execute_now_or_after_commit(obj) do
151
151
  conditions = primary_key_conditions(primary_key, id_to_change)
152
152
  klass.where(conditions).update_all updates.join(', ')
153
- unless options[:was]
153
+ # Determine if we should update the in-memory counter on the associated object.
154
+ # When updating the old counter (was: true), we need to carefully consider two scenarios:
155
+ # 1) The belongs_to relation changed (e.g., moving a child from parent A to parent B):
156
+ # In this case, obj.association now points to parent B, but we're decrementing parent A's
157
+ # counter. We should NOT update the in-memory counter because it would incorrectly
158
+ # modify parent B's cached value.
159
+ # 2) A conditional counter's condition changed (e.g., condition: true → false):
160
+ # In this case, obj.association still points to the same parent, but the counter column
161
+ # name changed (e.g., from 'conditional_count' to nil). We SHOULD update the in-memory
162
+ # counter so the parent object reflects the decremented value without requiring a reload.
163
+ # We distinguish these cases by comparing foreign keys: if the current and previous foreign
164
+ # keys are identical, we're in scenario 2 and should update the in-memory counter.
165
+ should_update_counter = if options[:was]
166
+ current_fk = foreign_key_value(obj, relation, false)
167
+ previous_fk = foreign_key_value(obj, relation, true)
168
+ current_fk == previous_fk && current_fk.present?
169
+ else
170
+ true
171
+ end
172
+
173
+ if should_update_counter
154
174
  assign_to_associated_object(obj, relation, change_counter_column, operator, delta_magnitude)
155
175
  end
156
176
  end
@@ -220,11 +240,11 @@ module CounterCulture
220
240
  Array.wrap(primary_key).map { |pk| value.try(pk&.to_sym) }.compact.presence
221
241
  end
222
242
 
223
- # gets the reflect object on the given relation
243
+ # gets the reflect object on the given relation and the model that defines this reflect
224
244
  #
225
245
  # relation: a symbol or array of symbols; specifies the relation
226
246
  # that has the counter cache column
227
- def relation_reflect(relation)
247
+ def relation_reflect_and_model(relation)
228
248
  relation = relation.is_a?(Enumerable) ? relation.dup : [relation]
229
249
 
230
250
  # go from one relation to the next until we hit the last reflect object
@@ -242,7 +262,16 @@ module CounterCulture
242
262
  end
243
263
  end
244
264
 
245
- return reflect
265
+ return [reflect, klass]
266
+ end
267
+
268
+
269
+ # gets the reflect object on the given relation
270
+ #
271
+ # relation: a symbol or array of symbols; specifies the relation
272
+ # that has the counter cache column
273
+ def relation_reflect(relation)
274
+ relation_reflect_and_model(relation).first
246
275
  end
247
276
 
248
277
  # gets the class of the given relation
@@ -59,7 +59,7 @@ module CounterCulture
59
59
  class Reconciliation
60
60
  attr_reader :counter, :options, :relation_class
61
61
 
62
- delegate :model, :relation, :full_primary_key, :relation_reflect, :polymorphic?, :to => :counter
62
+ delegate :model, :relation, :full_primary_key, :relation_reflect_and_model, :relation_reflect, :polymorphic?, :to => :counter
63
63
  delegate *CounterCulture::Counter::CONFIG_OPTIONS, :to => :counter
64
64
 
65
65
  def initialize(counter, changes_holder, options, relation_class)
@@ -257,11 +257,11 @@ module CounterCulture
257
257
  # store joins in an array so that we can later apply column-specific
258
258
  # conditions
259
259
  join_clauses = reverse_relation.each_with_index.map do |cur_relation, index|
260
- reflect = relation_reflect(cur_relation)
260
+ reflect, model = relation_reflect_and_model(cur_relation)
261
261
 
262
- target_table = quote_table_name(reflect.active_record.table_name)
262
+ target_table = quote_table_name(model.table_name)
263
263
  target_table_alias = parameterize(target_table)
264
- if relation_class.table_name == reflect.active_record.table_name
264
+ if relation_class.table_name == model.table_name
265
265
  # join with alias to avoid ambiguous table name in
266
266
  # self-referential models
267
267
  target_table_alias += "_#{target_table_alias}"
@@ -299,8 +299,8 @@ module CounterCulture
299
299
 
300
300
  # adds 'type' condition to JOIN clause if the current model is a
301
301
  # child in a Single Table Inheritance
302
- if reflect.active_record.column_names.include?('type') &&
303
- !model.descends_from_active_record?
302
+ if model.column_names.include?('type') &&
303
+ !model.descends_from_active_record?
304
304
  joins_sql += " AND #{target_table_alias}.type IN ('#{model.name}')"
305
305
  end
306
306
  if polymorphic?
@@ -1,3 +1,3 @@
1
1
  module CounterCulture
2
- VERSION = '3.11.2'.freeze
2
+ VERSION = '3.11.4'.freeze
3
3
  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.11.2
4
+ version: 3.11.4
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-07-10 00:00:00.000000000 Z
11
+ date: 2025-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord