activerecord_where_assoc 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ef06d95cd1fa0b3de7d06e745fc1a527bdc86415adb2e6b36b86b1f1477e804
4
- data.tar.gz: 36a723c284fbb1e0b44ea9af5368995d504a53026817f545a14235212d4ae7bf
3
+ metadata.gz: c25ebdab306cdfa08879b9237211c464b023ab1445e2607ae18855b8546730bf
4
+ data.tar.gz: 76d893474ad646ab75258ab6856050193cd7bd17ccacef8a28fc7dc9e7c2de04
5
5
  SHA512:
6
- metadata.gz: 8a26660a18b817b6a5cd106e71413e064f899e6fb9c73e9f502a14fc5aeb2896412368bc932028300611c94adac234ebbff85d0e2fd3ed8b900c01fa6c5e2670
7
- data.tar.gz: febe936611a53bc9f2f75aeb639fe8d8b905304b9b910e71f5cc9020ab1c0fae6803cd303f0cd7b830e29aa092778df21ce0303150f876930a02be2ed831821e
6
+ metadata.gz: 110b91b46d60a9dbe82d31784e743e5f4d3cd4d67e3389593c97cd7461d17977e5ba941b265a7d30e04a8455d8c090f86c4ed54be87282d114ac6fa6fe712934
7
+ data.tar.gz: 14b76e774b2731063a10eaeb4654497c53521d44935172858697e7e9337e9e2032ba742c81e41bb48f925c305c53fd414d5e30817740766570ef60ee9a4853f0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.3.0 - 2025-03-04
4
+
5
+ * The arguments of `#where_assoc_count` can now be swapped when comparing to a number or a range. <br>
6
+ So `where_assoc_count(:posts, :>, 5)` is now valid for having more than 5 posts.
7
+
8
+ # 1.2.1 - 2024-12-05
9
+
10
+ * Optimize `has_one` handling for `#where_assoc_exists` with a `has_one` as last association + without any conditions or offset.
11
+ * Optimize `has_one` handling when the foreign_key has a unique index and there is no offset
12
+
3
13
  # 1.2.0 - 2024-08-31
4
14
 
5
15
  * Add support for composite primary keys in Rails 7.2
data/README.md CHANGED
@@ -13,7 +13,7 @@ my_post.comments.where_assoc_not_exists(:author, is_admin: true).where(...)
13
13
  Post.where_assoc_exists([:comments, :author], &:admins).where(...)
14
14
 
15
15
  # Find my_user's posts that have at least 5 non-spam comments (not_spam is a scope on comments)
16
- my_user.posts.where_assoc_count(5, :>=, :comments) { |comments| comments.not_spam }.where(...)
16
+ my_user.posts.where_assoc_count(:comments, :>=, 5) { |comments| comments.not_spam }.where(...)
17
17
  ```
18
18
 
19
19
  These allow for powerful, chainable, clear and easy to reuse queries. (Great for scopes)
@@ -56,7 +56,7 @@ Or install it yourself with:
56
56
 
57
57
  ## Development state
58
58
 
59
- This gem is feature complete and production ready.
59
+ This gem is feature complete and production ready.<br>
60
60
  Other than rare tweaks as new versions of Rails and Ruby are released, there shouldn't be much activity on this repository.
61
61
 
62
62
  ## Documentation
@@ -77,45 +77,36 @@ Otherwise, here is a short explanation of the main methods provided by this gem:
77
77
  ```ruby
78
78
  where_assoc_exists(association_name, conditions, options, &block)
79
79
  where_assoc_not_exists(association_name, conditions, options, &block)
80
- where_assoc_count(left_operand, operator, association_name, conditions, options, &block)
80
+ where_assoc_count(left_assoc_or_value, operator, right_assoc_or_value, conditions, options, &block)
81
81
  ```
82
82
 
83
- * These methods add a condition (a `#where`) that checks if the association exists (or not)
84
- * You can specify condition on the association, so you could check only for comments that are made by an admin.
85
- * Each method returns a new relation, meaning you can chain `#where`, `#order`, `limit`, etc.
83
+ * These methods add a condition (a `#where`) that checks if the association exists (or not)
84
+ * You can specify condition on the association, so you could check only for comments that are made by an admin.
85
+ * Each method returns a new relation, meaning you can chain `#where`, `#order`, `limit`, etc.
86
86
  * common arguments:
87
87
  * association_name: the association we are doing the condition on.
88
88
  * conditions: (optional) the condition to apply on the association. It can be anything that `#where` can receive, so: Hash, String and Array (string with binds).
89
89
  * options: [available options](https://maxlap.github.io/activerecord_where_assoc/ActiveRecordWhereAssoc/RelationReturningMethods#module-ActiveRecordWhereAssoc::RelationReturningMethods-label-Options) to alter some behaviors. (rarely necessary)
90
- * block: adds more complex conditions by receiving a relation on the association. Can use `#where`, `#where_assoc_*`, scopes, and other scoping methods.
91
- Must return a relation.
90
+ * block: adds more complex conditions by receiving a relation on the association. Can use `#where`, `#where_assoc_*`, scopes, and other scoping methods.<br>
91
+ Must return a relation.<br>
92
92
  The block either:
93
93
  * receives no argument, in which case `self` is set to the relation, so you can do `{ where(id: 123) }`
94
94
  * receives arguments, in which case the block is called with the relation as first parameter.
95
95
 
96
- The block should return the new relation to use or `nil` to do as if there were no blocks.
96
+ The block should return the new relation to use or `nil` to do as if there were no blocks.<br>
97
97
  It's common to use `where_assoc_*(..., &:scope_name)` to use a single scope.
98
98
  * `#where_assoc_count` is a generalization of `#where_assoc_exists` and `#where_assoc_not_exists`. It behaves the same way, but is more powerful, as it allows you to specify how many matches there should be.
99
99
  ```ruby
100
100
  # These are equivalent:
101
101
  Post.where_assoc_exists(:comments, is_spam: true)
102
- Post.where_assoc_count(1, :<=, :comments, is_spam: true)
102
+ Post.where_assoc_count(:comments, :>=, 1, is_spam: true)
103
103
 
104
104
  Post.where_assoc_not_exists(:comments, is_spam: true)
105
- Post.where_assoc_count(0, :==, :comments, is_spam: true)
105
+ Post.where_assoc_count(:comments, :==, 0, is_spam: true)
106
106
 
107
107
  # This has no equivalent (Posts with at least 5 spam comments)
108
- Post.where_assoc_count(5, :<=, :comments, is_spam: true)
108
+ Post.where_assoc_count(:comments, :>=, 5, is_spam: true)
109
109
  ```
110
- * `where_assoc_count`'s additional arguments
111
- The order of the parameters of `#where_assoc_count` may seem confusing, but you will get used to it. It helps to remember: the goal is to do: `5 < (SELECT COUNT(*) FROM ...)`, the number is first, then operator, then the association and its conditions.
112
- * left_operand:
113
- * a number
114
- * a string of SQL to embed in the query
115
- * a range (operator must be `:==` or `:!=`)
116
- will use SQL's `BETWEEN` or `NOT BETWEEN`
117
- supports infinite ranges and exclusive end
118
- * operator: one of `:<`, `:<=`, `:==`, `:!=`, `:>=`, `:>`
119
110
 
120
111
  ## Intuition
121
112
 
@@ -127,8 +118,8 @@ Here is the basic intuition for the methods:
127
118
 
128
119
  `#where_assoc_count` the more specific version of `#where_assoc_exists`. Filters the models, returning those *where* a record for the *association* matching a condition (by default any record in the association) do *not exists*
129
120
 
130
- The condition that you may need on the record can be quite complicated. For this reason, you can pass a block to these methods.
131
- The block will receive a relation on records of the association. Your job is then to call `where` and scopes to specify what you want to exist (or to not exist if using `#where_assoc_not_exists`).
121
+ The condition that you may need on the record can be quite complicated. For this reason, you can pass a block to these methods.
122
+ The block will receive a relation on records of the association. Your job is then to call `where` and scopes to specify what you want to exist (or to not exist if using `#where_assoc_not_exists`).
132
123
 
133
124
  So if you have `User.where_assoc_exists(:comments) {|rel| rel.where("content ilike '%github.com%'") }`, `rel` is a relation is on `Comment`, and you are specifying what you want to exist. So now we are looking for users that made a comment containing 'github.com'.
134
125
 
@@ -142,7 +133,7 @@ Sometimes, there isn't a single association that goes deep enough. In that situa
142
133
  # Find users that have a post that has a comment that was made by an admin.
143
134
  # Using &:admins to use the admins scope (or any other class method of comments)
144
135
  User.where_assoc_exists(:posts) { |posts|
145
- posts.where_assoc_exists(:comments) { |comments|
136
+ posts.where_assoc_exists(:comments) { |comments|
146
137
  comments.where_assoc_exists(:author, &:admins)
147
138
  }
148
139
  }
@@ -223,7 +214,7 @@ On MySQL databases, it is not possible to use `has_one` associations and associa
223
214
 
224
215
  I do not know of a way to do a SQL query that can deal with all the specifics of `has_one` for MySQL. If you have one, then please suggest it in an issue/pull request.
225
216
 
226
- In order to work around this, you must use the [ignore_limit](https://maxlap.github.io/activerecord_where_assoc/ActiveRecordWhereAssoc/RelationReturningMethods.html#module-ActiveRecordWhereAssoc::RelationReturningMethods-label-3Aignore_limit+option) option. The behavior is less correct, but better than being unable to use the gem.
217
+ In order to work around this, you must use the [ignore_limit](https://maxlap.github.io/activerecord_where_assoc/ActiveRecordWhereAssoc/RelationReturningMethods.html#module-ActiveRecordWhereAssoc::RelationReturningMethods-label-3Aignore_limit+option) option. The behavior is less correct, but better than being unable to use the gem.
227
218
 
228
219
  ### has_* :through vs limit/offset
229
220
  For `has_many` and `has_one` with the `:through` option, `#limit` and `#offset` are ignored. Note that `#limit` and `#offset` of the `:source` and of the `:through` side are applied correctly.
@@ -101,5 +101,28 @@ module ActiveRecordWhereAssoc
101
101
  reflection.is_a?(ActiveRecord::NullRelation)
102
102
  end
103
103
  end
104
+
105
+ if ActiveRecord.gem_version >= Gem::Version.new("6.0")
106
+ def self.indexes(model)
107
+ model.connection.schema_cache.indexes(model.table_name)
108
+ end
109
+ else
110
+ def self.indexes(model)
111
+ model.connection.indexes(model.table_name)
112
+ end
113
+ end
114
+
115
+ @unique_indexes_cache = {}
116
+ def self.has_unique_index?(model, column_names)
117
+ column_names = Array(column_names).map(&:to_s)
118
+ @unique_indexes_cache.fetch([model, column_names]) do |k|
119
+ unique_indexes = indexes(model).select(&:unique)
120
+ columns_names_set = Set.new(column_names)
121
+
122
+ # We check for an index whose columns are a subset of the columns we specify
123
+ # This way, a composite column_names will find uniqueness if just a single of the column is unique
124
+ @unique_indexes_cache[k] = unique_indexes.any? { |ui| Set.new(ui.columns) <= columns_names_set }
125
+ end
126
+ end
104
127
  end
105
128
  end
@@ -7,6 +7,17 @@ module ActiveRecordWhereAssoc
7
7
  module CoreLogic
8
8
  # Arel table used for aliasing when handling recursive associations (such as parent/children)
9
9
  ALIAS_TABLE = Arel::Table.new("_ar_where_assoc_alias_")
10
+ OPERATOR_SWAP_OPERANDS = {
11
+ "<" => ">",
12
+ "<=" => ">=",
13
+ ">" => "<",
14
+ ">=" => "<=",
15
+ # Below stays the same
16
+ "=" => "=",
17
+ "==" => "==",
18
+ "!=" => "!=",
19
+ "<>" => "<>",
20
+ }
10
21
 
11
22
  # Returns the SQL for checking if any of the received relation exists.
12
23
  # Uses a OR if there are multiple relations.
@@ -92,7 +103,28 @@ module ActiveRecordWhereAssoc
92
103
  # Returns the SQL condition to check if the specified association of the record_class has the desired number of records.
93
104
  #
94
105
  # See RelationReturningMethods#where_assoc_count or SqlReturningMethods#compare_assoc_count_sql for usage details.
95
- def self.compare_assoc_count_sql(record_class, left_operand, operator, association_names, given_conditions, options, &block)
106
+ #
107
+ # The codebase originally only allowed where_assoc_count(5, :<, :comments).
108
+ # This has since been considered confusing and error prone, with `where_assoc_count(:comments, :>, 5)` being preferable.
109
+ # So we want to allow the better way to work.
110
+ # However:
111
+ # * We originally already allowed any valid SQL to be given as left valid (even a symbol that names a column).
112
+ # * We don't want this to be a breaking change
113
+ #
114
+ # Because of this, it is not simple to correctly auto-detect which situation we are in.
115
+ # So only the simple and most common case of using a number or a range on the right side (3rd argument) is supported.
116
+ #
117
+ # And due to this history, the generated SQL will still have the counting SQL on the right side.
118
+ def self.compare_assoc_count_sql(record_class, left_assoc_or_value, operator, right_assoc_or_value, given_conditions, options, &block)
119
+ if right_assoc_or_value.is_a?(Numeric) || right_assoc_or_value.is_a?(Range)
120
+ association_names = left_assoc_or_value
121
+ left_operand = right_assoc_or_value
122
+ operator = OPERATOR_SWAP_OPERANDS.fetch(operator.to_s)
123
+ else
124
+ association_names = right_assoc_or_value
125
+ left_operand = left_assoc_or_value
126
+ end
127
+
96
128
  right_sql = only_assoc_count_sql(record_class, association_names, given_conditions, options, &block)
97
129
 
98
130
  sql_for_count_operator(left_operand, operator, right_sql)
@@ -155,9 +187,17 @@ module ActiveRecordWhereAssoc
155
187
 
156
188
  init_scopes = initial_scopes_from_reflection(record_class, reflection_chain[i..-1], constraints_chain[i], options)
157
189
  current_scopes = init_scopes.map do |alias_scope, current_scope, klass_scope|
158
- current_scope = process_association_step_limits(current_scope, reflection, record_class, options)
159
-
160
190
  if i.zero?
191
+ if given_conditions || klass_scope || last_assoc_block || current_scope.offset_value || nest_assocs_block == NestWithSumBlock
192
+ # In the deepest layer, the limit & offset complexities only matter when:
193
+ # * There is a condition to apply
194
+ # * There is an offset (which is a form of filtering)
195
+ # * We are counting the total matches
196
+ # Since last_assoc_block is always set except for the deepest association, and is only unset for the deepest layer if
197
+ # there is no condition given, using it as part of the condition does a lot of work here.
198
+ current_scope = process_association_step_limits(current_scope, reflection, record_class, options)
199
+ end
200
+
161
201
  current_scope = current_scope.where(given_conditions) if given_conditions
162
202
  if klass_scope
163
203
  if klass_scope.respond_to?(:call)
@@ -167,6 +207,8 @@ module ActiveRecordWhereAssoc
167
207
  end
168
208
  end
169
209
  current_scope = apply_proc_scope(current_scope, last_assoc_block) if last_assoc_block
210
+ else
211
+ current_scope = process_association_step_limits(current_scope, reflection, record_class, options)
170
212
  end
171
213
 
172
214
  # Those make no sense since at this point, we are only limiting the value that would match using conditions
@@ -338,10 +380,18 @@ module ActiveRecordWhereAssoc
338
380
 
339
381
  current_scope = current_scope.limit(1) if reflection.macro == :has_one
340
382
 
341
- # Order is useless without either limit or offset
342
- current_scope = current_scope.unscope(:order) if !current_scope.limit_value && !current_scope.offset_value
383
+ if !current_scope.offset_value
384
+ if current_scope.limit_value
385
+ join_keys = ActiveRecordCompat.join_keys(reflection, nil)
386
+ # #join_keys is inverted... the foreign key is on the "source" table, and the key is on the "target" table...
387
+ # Everything is so complicated in ActiveRecord.
388
+ current_scope = current_scope.unscope(:limit) if ActiveRecordCompat.has_unique_index?(current_scope.model, join_keys.key)
389
+ end
390
+
391
+ # Order is useless without either limit or offset
392
+ return current_scope.unscope(:order) if !current_scope.limit_value
393
+ end
343
394
 
344
- return current_scope unless current_scope.limit_value || current_scope.offset_value
345
395
  if %w(mysql mysql2).include?(relation_klass.connection.adapter_name.downcase)
346
396
  msg = String.new
347
397
  msg << "Associations and default_scopes with a limit or offset are not supported for MySQL (this includes has_many). "
@@ -313,18 +313,18 @@ module ActiveRecordWhereAssoc
313
313
  # To clarify, here are equivalent examples:
314
314
  #
315
315
  # Post.where_assoc_exists(:comments)
316
- # Post.where_assoc_count(1, :<=, :comments)
316
+ # Post.where_assoc_count(:comments, :>=, 1)
317
317
  #
318
318
  # Post.where_assoc_not_exists(:comments)
319
- # Post.where_assoc_count(0, :==, :comments)
319
+ # Post.where_assoc_count(:comments, :==, 0)
320
320
  #
321
- # But these have no equivalent:
321
+ # But these have no equivalent with the *_exists methods:
322
322
  #
323
323
  # # Posts with at least 5 comments
324
- # Post.where_assoc_count(5, :<=, :comments)
324
+ # Post.where_assoc_count(:comments, :>=, 5)
325
325
  #
326
326
  # # Posts with less than 5 comments
327
- # Post.where_assoc_count(5, :>, :comments)
327
+ # Post.where_assoc_count(:comments, :<, 5)
328
328
  #
329
329
  # You could say this is a way of doing a +#select+ that +#count+ the associations of your model
330
330
  # on the SQL side, but faster and more concise.
@@ -332,20 +332,30 @@ module ActiveRecordWhereAssoc
332
332
  # Examples (with an equivalent ruby +#select+ and +#count+)
333
333
  #
334
334
  # # Posts with at least 5 comments
335
- # Post.where_assoc_count(5, :<=, :comments)
335
+ # Post.where_assoc_count(:comments, :>=, 5)
336
336
  # Post.all.select { |post| post.comments.count >= 5 }
337
337
  #
338
338
  # # Posts that have at least 5 comments marked as spam
339
- # Post.where_assoc_count(5, :<=, :comments, is_spam: true)
339
+ # Post.where_assoc_count(:comments, :>=, 5, is_spam: true)
340
340
  # Post.all.select { |post| post.comments.where(is_spam: true).count >= 5 }
341
341
  #
342
342
  # # Posts that have at least 10 replies spread over their comments
343
- # Post.where_assoc_count(10, :<=, [:comments, :replies])
344
- # Post.select { |post| post.comments.sum { |comment| comment.replies.count } >= 5 }
343
+ # Post.where_assoc_count([:comments, :replies], :>=, 10)
344
+ # Post.select { |post| post.comments.sum { |comment| comment.replies.count } >= 10 }
345
345
  #
346
- # [left_operand]
346
+ # Note: Originally, the order of the arguments used to have to be: `where_assoc_count(5, :<, :comments)`.
347
+ # This was confusing and error prone, with `where_assoc_count(:comments, :>, 5)` appearing preferable.
348
+ # However, due to the flexibility of what could already be received as first argument, it was not
349
+ # simple to detect all cases of the arguments being swapped. We didn't want this to be a breaking change.
350
+ #
351
+ # So a special case was made for the main case of using a number or a range. This means the only time where
352
+ # the association_name(s) can be the first argument is when using a number.<br>
353
+ # Otherwise, the association_names must be the third arguments, such as: where_assoc_count("Some SQL", :<, :comments)
354
+ #
355
+ # [left_assoc_or_value]
347
356
  # 1st argument, the left side of the comparison. <br>
348
357
  # One of:
358
+ # * If using a number or a range as third argument, this can be the association_name(s). (See third parameter)
349
359
  # * a number
350
360
  # * a string of SQL to embed in the query
351
361
  # * a range (operator must be :== or :!=), will use BETWEEN or NOT BETWEEN<br>
@@ -353,20 +363,27 @@ module ActiveRecordWhereAssoc
353
363
  #
354
364
  # # Posts with 5 to 10 comments
355
365
  # Post.where_assoc_count(5..10, :==, :comments)
366
+ # Post.where_assoc_count(:comments, :==, 5..10) # Equivalent
356
367
  #
357
368
  # # Posts with less than 5 or more than 10 comments
358
369
  # Post.where_assoc_count(5..10, :!=, :comments)
370
+ # Post.where_assoc_count(:comments, :!=, 5..10) # Equivalent
359
371
  #
360
372
  # [operator]
361
373
  # The operator to use, one of these symbols: <code> :< :<= :== :!= :>= :> </code>
362
374
  #
363
- # [association_name]
375
+ # [right_assoc_or_value]
376
+ # If the first argument was the association_name(s), then this must be a number or a range.
377
+ #
378
+ # Otherwise, this must be the association_names(s).
379
+ #
364
380
  # The association that must have a certain number of occurrences <br>
365
- # Note that if you use an array of association names, the number of the last association
381
+ # Note that if you use an array of association names, the total number of the last association
366
382
  # is what is counted.
367
383
  #
368
384
  # # Users which have received at least 5 comments total (can be spread on all of their posts)
369
385
  # User.where_assoc_count(5, :<=, [:posts, :comments])
386
+ # User.where_assoc_count([:posts, :comments], :>=, 5)
370
387
  #
371
388
  # See RelationReturningMethods@Association
372
389
  #
@@ -382,26 +399,28 @@ module ActiveRecordWhereAssoc
382
399
  # More complex conditions the associated record must match (can also use scopes of the association's model) <br>
383
400
  # See RelationReturningMethods@Block
384
401
  #
385
- # The order of the parameters may seem confusing. But you will get used to it. It helps
386
- # to remember that the goal is to do:
402
+ # The order of the parameters may seem confusing. That's an mistake that cannot be fixed without a breaking change.
403
+ # That's why there is now a special case for using a number or range as third argument. Note that the generated SQL will always
404
+ # place the counting SQL on the right side. (Reversing the operator when needed), like so:
387
405
  # 5 < (SELECT COUNT(*) FROM ...)
388
- # So the parameters are in the same order as in that query: number, operator, association.
389
406
  #
390
407
  # To be clear, when you use multiple associations in an array, the count you will be
391
408
  # comparing against is the total number of records of that last association.
392
409
  #
393
410
  # # The users that have received at least 5 comments total on all of their posts
394
411
  # # So this can be from one post that has 5 comments of from 5 posts with 1 comments
412
+ # User.where_assoc_count([:posts, :comments], :>=, 5)
395
413
  # User.where_assoc_count(5, :<=, [:posts, :comments])
396
414
  #
397
415
  # # The users that have at least 5 posts with at least one comments
398
416
  # User.where_assoc_count(5, :<=, :posts) { where_assoc_exists(:comments) }
417
+ # User.where_assoc_count(:posts, :>=, 5) { where_assoc_exists(:comments) }
399
418
  #
400
419
  # You can get the SQL string of the condition using SqlReturningMethods#compare_assoc_count_sql.
401
420
  # You can get the SQL string for only the counting using SqlReturningMethods#only_assoc_count_sql.
402
- def where_assoc_count(left_operand, operator, association_name, conditions = nil, options = {}, &block)
403
- sql = ActiveRecordWhereAssoc::CoreLogic.compare_assoc_count_sql(self.klass, left_operand, operator,
404
- association_name, conditions, options, &block)
421
+ def where_assoc_count(left_assoc_or_value, operator, right_assoc_or_value, conditions = nil, options = {}, &block)
422
+ sql = ActiveRecordWhereAssoc::CoreLogic.compare_assoc_count_sql(self.klass, left_assoc_or_value, operator,
423
+ right_assoc_or_value, conditions, options, &block)
405
424
  where(sql)
406
425
  end
407
426
  end
@@ -45,13 +45,18 @@ module ActiveRecordWhereAssoc
45
45
  #
46
46
  # For example:
47
47
  # # Users with at least 10 posts or at least 10 comment
48
+ # User.where("#{User.compare_assoc_count_sql(:posts, :>=, 10)} OR #{User.compare_assoc_count_sql(:comments, :>=, 10)}")
49
+ # my_users.where("#{User.compare_assoc_count_sql(:posts, :>=, 10)} OR #{User.compare_assoc_count_sql(:comments, :>=, 10)}")
50
+ #
51
+ # # The older way of doing the same thing (the order of parameter used to be reversed,
52
+ # # both order work when using a number or a range as one of the operand)
48
53
  # User.where("#{User.compare_assoc_count_sql(10, :<=, :posts)} OR #{User.compare_assoc_count_sql(10, :<=, :comments)}")
49
54
  # my_users.where("#{User.compare_assoc_count_sql(10, :<=, :posts)} OR #{User.compare_assoc_count_sql(10, :<=, :comments)}")
50
55
  #
51
56
  # The parameters are the same as RelationReturningMethods#where_assoc_count, including the
52
57
  # possibility of specifying a list of association_name.
53
- def compare_assoc_count_sql(left_operand, operator, association_name, conditions = nil, options = {}, &block)
54
- ActiveRecordWhereAssoc::CoreLogic.compare_assoc_count_sql(self, left_operand, operator, association_name, conditions, options, &block)
58
+ def compare_assoc_count_sql(left_assoc_or_value, operator, right_assoc_or_value, conditions = nil, options = {}, &block)
59
+ ActiveRecordWhereAssoc::CoreLogic.compare_assoc_count_sql(self, left_assoc_or_value, operator, right_assoc_or_value, conditions, options, &block)
55
60
  end
56
61
 
57
62
  # This method returns a string containing the SQL to count an association used by RelationReturningMethods#where_assoc_count.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordWhereAssoc
4
- VERSION = "1.2.0".freeze
4
+ VERSION = "1.3.0".freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord_where_assoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Handfield Lapointe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-31 00:00:00.000000000 Z
11
+ date: 2025-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -31,9 +31,6 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.15'
34
- - - "<"
35
- - !ruby/object:Gem::Version
36
- version: '2.6'
37
34
  type: :development
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -41,9 +38,6 @@ dependencies:
41
38
  - - ">="
42
39
  - !ruby/object:Gem::Version
43
40
  version: '1.15'
44
- - - "<"
45
- - !ruby/object:Gem::Version
46
- version: '2.6'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: minitest
49
43
  requirement: !ruby/object:Gem::Requirement