activerecord_where_assoc 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/ALTERNATIVES_PROBLEMS.md +15 -5
- data/CHANGELOG.md +6 -0
- data/EXAMPLES.md +33 -2
- data/README.md +62 -122
- data/lib/active_record_where_assoc.rb +10 -1
- data/lib/active_record_where_assoc/active_record_compat.rb +31 -9
- data/lib/active_record_where_assoc/core_logic.rb +221 -109
- data/lib/active_record_where_assoc/exceptions.rb +3 -0
- data/lib/active_record_where_assoc/query_methods.rb +315 -116
- data/lib/active_record_where_assoc/version.rb +1 -1
- metadata +5 -5
@@ -3,161 +3,359 @@
|
|
3
3
|
require_relative "active_record_compat"
|
4
4
|
require_relative "exceptions"
|
5
5
|
|
6
|
+
# See ActiveRecordWhereAssoc::QueryMethods
|
6
7
|
module ActiveRecordWhereAssoc
|
8
|
+
# This module adds new variations of +#where+ to your Models/relations/associations/scopes.
|
9
|
+
# These variations check if an association has records, so you can check if a +Post+ has
|
10
|
+
# any +Comments+.
|
11
|
+
#
|
12
|
+
# These variations return a new relation (just like +#where+) so you can chain them with
|
13
|
+
# other scoping methods such as +#where+, +#order+, +#limit+, more of these variations, etc.
|
14
|
+
#
|
15
|
+
# The arguments common to all methods are documented here at the top.
|
16
|
+
#
|
17
|
+
# For brevity, the examples are all directly on models, such as User, Post, Comment, but
|
18
|
+
# the methods are available and behave the same on:
|
19
|
+
# * associations: <tt>my_user.posts.where_assoc_exists(:comments)</tt>
|
20
|
+
# * relations: <tt>Posts.where(serious: true).where_assoc_exists(:comments)</tt>
|
21
|
+
# * scopes: (On the Post model) <tt>scope :with_comments, -> { where_assoc_exists(:comments) }</tt>
|
22
|
+
# * models: <tt>Post.where_assoc_exists(:comments)</tt>
|
23
|
+
#
|
24
|
+
# You may also consider viewing the gem's README. It contains known issues and some tips.
|
25
|
+
# You can view the {README on github}[https://github.com/MaxLap/activerecord_where_assoc/blob/master/README.md].
|
26
|
+
#
|
27
|
+
# If you need extra convincing to try this gem, I have a whole document with the problems of
|
28
|
+
# the other ways of doing this kind of filtering:
|
29
|
+
# {alternatives' problems}[https://github.com/MaxLap/activerecord_where_assoc/blob/master/ALTERNATIVES_PROBLEMS.md].
|
30
|
+
#
|
31
|
+
# === Association
|
32
|
+
# The associations referred here are the links between your different models. They are your
|
33
|
+
# +#belongs_to+, +#has_many+, +#has_one+, +#has_and_belongs_to_many+.
|
34
|
+
#
|
35
|
+
# This gem is about getting records from your database if their associations match (or don't
|
36
|
+
# match) a certain condition (which by default is just to exist).
|
37
|
+
#
|
38
|
+
# Every method here has an *association_name* parameter. This is the association you want to
|
39
|
+
# check if records exists.
|
40
|
+
#
|
41
|
+
# # Posts with at least one comment
|
42
|
+
# Post.where_assoc_exists(:comments)
|
43
|
+
#
|
44
|
+
# # Posts with no comments
|
45
|
+
# Post.where_assoc_not_exists(:comments)
|
46
|
+
#
|
47
|
+
# If you want, you can pass an array of associations. They will be followed in order, just
|
48
|
+
# like a has_many :through would.
|
49
|
+
#
|
50
|
+
# # Posts which have at least one comment with a reply
|
51
|
+
# # In other words: Posts which have at least one reply reachable through his comments
|
52
|
+
# Post.where_assoc_exists([:comments, :replies])
|
53
|
+
#
|
54
|
+
# === Condition
|
55
|
+
# After the +association_name+ argument, you can pass additional conditions the associated
|
56
|
+
# record must also match to be considered as existing.
|
57
|
+
#
|
58
|
+
# This +condition+ argument is passed directly to +#where+, so you can pass in the following:
|
59
|
+
#
|
60
|
+
# # Posts that have at least one comment considered as spam
|
61
|
+
# # Using a Hash
|
62
|
+
# Post.where_assoc_exists(:comments, is_spam: true)
|
63
|
+
#
|
64
|
+
# # Using a String
|
65
|
+
# Post.where_assoc_exists(:comments, "is_spam = true")
|
66
|
+
#
|
67
|
+
# # Using an Array (a string and its binds)
|
68
|
+
# Post.where_assoc_exists(:comments, ["is_spam = ?", true])
|
69
|
+
#
|
70
|
+
# If the condition is blank, it is ignored (just like +#where+ does).
|
71
|
+
#
|
72
|
+
# Note, if you specify multiple associations using an Array, the conditions will only be applied
|
73
|
+
# to the last association.
|
74
|
+
#
|
75
|
+
# # Users which have a post that has a comment marked as spam.
|
76
|
+
# # is_spam is only checked on the comment.
|
77
|
+
# User.where_assoc_exists([:posts, :comments], is_spam: true)
|
78
|
+
#
|
79
|
+
# If you want something else, you will need to use a block (see below) to nest multiple calls.
|
80
|
+
#
|
81
|
+
# # Users which have a post made in the last 5 days which has comments
|
82
|
+
# User.where_assoc_exists(:posts) {
|
83
|
+
# where("created_at > ?", 5.days.ago).where_assoc_exists(:comments)
|
84
|
+
# }
|
85
|
+
#
|
86
|
+
# === Block
|
87
|
+
# The block is used to add more complex conditions. The effect is the same as the condition
|
88
|
+
# parameter, in that these conditions must be matched for the association to be considered
|
89
|
+
# to exist, but lets you use any scoping methods, such as +#where+, +#joins+, nested
|
90
|
+
# +#where_assoc_*+, scopes on the model, etc.
|
91
|
+
#
|
92
|
+
# Note that using +#joins+ might lead to unexpected results when using #where_assoc_count,
|
93
|
+
# since if the joins adds rows, it will change the resulting count. It probably makes more
|
94
|
+
# sense to, again, use one of the +where_assoc_*+ methods.
|
95
|
+
#
|
96
|
+
# There are 2 ways of using the block for adding conditions to the association.
|
97
|
+
#
|
98
|
+
# [A block that receives one argument]
|
99
|
+
# The block receives a relation on the target association and return a relation with added
|
100
|
+
# filters or may return nil to do nothing.
|
101
|
+
#
|
102
|
+
# # These are all equivalent. Posts which have a comment marked as spam
|
103
|
+
# # Using a where for the added condition
|
104
|
+
# Post.where_assoc_exists(:comments) { |comments_scope| comments_scope.where(is_spam: true) }
|
105
|
+
#
|
106
|
+
# # Applying a scope of the relation
|
107
|
+
# Post.where_assoc_exists(:comments) { |comments_scope| comments_scope.spam_flagged }
|
108
|
+
#
|
109
|
+
# # Applying a scope of the relation, using the &:shortcut for procs
|
110
|
+
# Post.where_assoc_exists(:comments, &:spam_flagged)
|
111
|
+
#
|
112
|
+
# [A block that receives no argument]
|
113
|
+
# Instead of receiving the relation as argument, the relation is used as the "self" of
|
114
|
+
# the block. Everything else is identical to the block with one argument.
|
115
|
+
#
|
116
|
+
# # These are all equivalent. Posts which have a comment marked as spam
|
117
|
+
# # Using a where for the added condition
|
118
|
+
# Post.where_assoc_exists(:comments) { where(is_spam: true) }
|
119
|
+
#
|
120
|
+
# # Applying a scope of the relation
|
121
|
+
# Post.where_assoc_exists(:comments) { spam_flagged }
|
122
|
+
#
|
123
|
+
# The main reason to use a block with an argument instead of without one is when you need
|
124
|
+
# to call methods on the self outside of the block, such as:
|
125
|
+
#
|
126
|
+
# Post.where_assoc_exists(:comments) { |comments| comments.where(author_id: foo(:bar)) }
|
127
|
+
# Post.where_assoc_exists(:comments) { |comments| comments.where(author_id: self.foo(:bar)) }
|
128
|
+
# # In both cases, using the version without arguments would not work, since the #foo
|
129
|
+
# # would be called on the scope that was given to the block, instead of on the caller
|
130
|
+
# # of the #where_assoc_exists method.
|
131
|
+
#
|
132
|
+
# # THESE ARE WRONG!
|
133
|
+
# Post.where_assoc_exists(:comments) { where(author_id: foo(:bar)) }
|
134
|
+
# Post.where_assoc_exists(:comments) { where(author_id: self.foo(:bar)) }
|
135
|
+
# # THESE ARE WRONG!
|
136
|
+
#
|
137
|
+
# === Options
|
138
|
+
# Some options are available to tweak how queries are generated. The default values of the options
|
139
|
+
# can be changed globally:
|
140
|
+
#
|
141
|
+
# # Somewhere in your setup code, such as an initializer in Rails
|
142
|
+
# ActiveRecordWhereAssoc.default_options[:ignore_limit] = true
|
143
|
+
#
|
144
|
+
# Or you can pass them as arguments after the +condition+ argument.
|
145
|
+
#
|
146
|
+
# Post.where_assoc_exists(:comments, "is_spam = TRUE", ignore_limit: true)
|
147
|
+
# # Because this is 2 consecutive hashes, must use the +{}+
|
148
|
+
# Post.where_assoc_exists(:comments, {is_spam: true}, ignore_limit: true)
|
149
|
+
#
|
150
|
+
# Note, if you don't need a condition, you must pass nil as condition to provide options:
|
151
|
+
# Post.where_assoc_exists(:comments, nil, ignore_limit: true)
|
152
|
+
#
|
153
|
+
# [ignore_limit]
|
154
|
+
# When true, +#limit+ and +#offset+ that are set from default_scope, on associations, and from
|
155
|
+
# +#has_one+ are ignored. <br>
|
156
|
+
# Removing the limit from +#has_one+ makes them be treated like a +#has_many+.
|
157
|
+
#
|
158
|
+
# Main reasons to use ignore_limit: true
|
159
|
+
# * Needed for MySQL to be able to do anything with +#has_one+ associations because MySQL
|
160
|
+
# doesn't support sub-limit. <br>
|
161
|
+
# See {MySQL doesn't support limit}[https://github.com/MaxLap/activerecord_where_assoc#mysql-doesnt-support-sub-limit] <br>
|
162
|
+
# Note, this does mean the +#has_one+ will be treated as if it was a +#has_many+ for MySQL too.
|
163
|
+
# * You have a +#has_one+ association which you know can never have more than one record and are
|
164
|
+
# dealing with a heavy/slow query. The query used to deal with +#has_many+ is less complex, and
|
165
|
+
# may prove faster.
|
166
|
+
# * For this one special case, you want to check the other records that match your has_one
|
167
|
+
#
|
168
|
+
# [never_alias_limit]
|
169
|
+
# When true, +#where_assoc_*+ will not use +#from+ to build relations that have +#limit+ or +#offset+ set
|
170
|
+
# on default_scope or on associations or for +#has_one+. <br>
|
171
|
+
# This allows changing the from as part of the conditions (such as for a scope)
|
172
|
+
#
|
173
|
+
# Main reasons to use this: you have to use +#from+ in the block of +#where_assoc_*+ method
|
174
|
+
# (ex: because a scope needs +#from+).
|
175
|
+
#
|
176
|
+
# Why this isn't the default:
|
177
|
+
# * From very few tests, the aliasing way seems to produce better plans.
|
178
|
+
# * Using aliasing produces a shorter query.
|
179
|
+
#
|
180
|
+
# [poly_belongs_to]
|
181
|
+
# Specify what to do when a polymorphic belongs_to is encountered. Things are tricky because the query can
|
182
|
+
# end up searching in multiple Models, and just knowing which ones to look into can require an expensive query.
|
183
|
+
# It's also possible that you only want to search for those that match some specific Models, ignoring the other ones.
|
184
|
+
# [:pluck]
|
185
|
+
# Do a +#pluck+ in the column to detect to possible choices. This option can have a performance cost for big tables
|
186
|
+
# or when the query if done often, as the +#pluck+ will be executed each time
|
187
|
+
# [model or array of models]
|
188
|
+
# Specify which models to search for. This avoids the performance cost of +#pluck+ and can allow to filter some
|
189
|
+
# of the choices out that don't interest you. <br>
|
190
|
+
# Note, these are not instances, it's actual models, ex: <code>[Post, Comment]</code>
|
191
|
+
# [a hash]
|
192
|
+
# The keys must be models (same behavior as an array of models). <br>
|
193
|
+
# The values are conditions to apply only for key's model.
|
194
|
+
# The conditions are either a proc (behaves like the block, but only for that model) or the same things +#where+
|
195
|
+
# can receive. (String, Hash, Array, nil). Ex:
|
196
|
+
# List.where_assoc_exists(:items, nil, poly_belongs_to: {Car => "color = 'blue'",
|
197
|
+
# Computer => proc { brand_new.where(core: 4) } })
|
198
|
+
# [:raise]
|
199
|
+
# (default) raise an exception when a polymorphic belongs_to is encountered.
|
7
200
|
module QueryMethods
|
8
|
-
#
|
9
|
-
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Here is a quick overview of the arguments received followed by a detailed explanation
|
13
|
-
# along with more examples. You may also consider viewing the gem's README. It contains
|
14
|
-
# known issues and some tips. The readme is packaged with the gem and viewable on github:
|
15
|
-
# https://github.com/MaxLap/activerecord_where_assoc
|
16
|
-
#
|
201
|
+
# :section: Basic methods
|
202
|
+
|
203
|
+
# Returns a new relation with a condition added (a +#where+) that checks if an association
|
204
|
+
# of the model exists. Extra conditions the associated model must match can also be specified.
|
17
205
|
#
|
18
|
-
#
|
19
|
-
#
|
206
|
+
# You could say this is a way of doing a +#select+ that uses associations of your model
|
207
|
+
# on the SQL side, but faster and more concise.
|
20
208
|
#
|
21
|
-
#
|
22
|
-
# Post.where_assoc_exists(:comments)
|
209
|
+
# Examples (with an equivalent ruby +#select+)
|
23
210
|
#
|
24
|
-
#
|
25
|
-
#
|
211
|
+
# # Posts that have comments
|
212
|
+
# Post.where_assoc_exists(:comments)
|
213
|
+
# Post.all.select { |post| post.comments.exists? }
|
26
214
|
#
|
27
|
-
#
|
215
|
+
# # Posts that have comments marked as spam
|
216
|
+
# Post.where_assoc_exists(:comments, is_spam: true)
|
217
|
+
# Post.select { |post| post.comments.any? {|comment| comment.is_spam } }
|
28
218
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
219
|
+
# # Posts that have comments that have replies
|
220
|
+
# Post.where_assoc_exists([:comments, :replies])
|
221
|
+
# Post.select { |post| post.comments.any? {|comment| comment.replies.exists? } }
|
32
222
|
#
|
33
|
-
#
|
223
|
+
# [association_name]
|
224
|
+
# The association that must exist <br>
|
225
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Association
|
34
226
|
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
227
|
+
# [condition]
|
228
|
+
# Extra conditions the association must match <br>
|
229
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Condition
|
38
230
|
#
|
39
|
-
#
|
40
|
-
#
|
231
|
+
# [options]
|
232
|
+
# Options to alter the generated query <br>
|
233
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Options
|
41
234
|
#
|
42
|
-
#
|
43
|
-
#
|
235
|
+
# [&block]
|
236
|
+
# More complex conditions the associated record must match (can also use scopes of the association's model) <br>
|
237
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Block
|
44
238
|
#
|
45
|
-
|
46
|
-
|
47
|
-
|
239
|
+
def where_assoc_exists(association_name, conditions = nil, options = {}, &block)
|
240
|
+
ActiveRecordWhereAssoc::CoreLogic.do_where_assoc_exists(self, association_name, conditions, options, &block)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Returns a new relation with a condition added (a +#where+) that checks if an association
|
244
|
+
# of the model does not exist. Extra conditions the associated model that exists must not match
|
245
|
+
# can also be specified.
|
48
246
|
#
|
49
|
-
#
|
247
|
+
# This the exact opposite of what #where_assoc_exists does, so a #where_assoc_not_exists with
|
248
|
+
# the same arguments will keep every records that were rejected by the #where_assoc_exists.
|
50
249
|
#
|
51
|
-
#
|
250
|
+
# You could say this is a way of doing a +#reject+ that uses associations of your model
|
251
|
+
# on the SQL side, but faster and more concise.
|
52
252
|
#
|
53
|
-
#
|
54
|
-
# considered as "existing". The argument is passed directly to #where.
|
253
|
+
# Examples (with an equivalent ruby +#reject+)
|
55
254
|
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
255
|
+
# # Posts that have no comments
|
256
|
+
# Post.where_assoc_not_exists(:comments)
|
257
|
+
# Post.all.reject { |post| post.comments.exists? }
|
59
258
|
#
|
60
|
-
#
|
61
|
-
#
|
259
|
+
# # Posts that don't have comments marked as spam (but might have unmarked comments)
|
260
|
+
# Post.where_assoc_not_exists(:comments, is_spam: true)
|
261
|
+
# Post.reject { |post| post.comments.any? {|comment| comment.is_spam } }
|
62
262
|
#
|
63
|
-
#
|
64
|
-
#
|
263
|
+
# # Posts that don't have comments that have replies (but can have comments that have no replies)
|
264
|
+
# Post.where_assoc_exists([:comments, :replies])
|
265
|
+
# Post.reject { |post| post.comments.any? {|comment| comment.replies.exists? } }
|
65
266
|
#
|
66
|
-
#
|
267
|
+
# [association_name]
|
268
|
+
# The association that must exist <br>
|
269
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Association
|
67
270
|
#
|
68
|
-
#
|
271
|
+
# [condition]
|
272
|
+
# Extra conditions the association must not match <br>
|
273
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Condition
|
69
274
|
#
|
70
|
-
#
|
71
|
-
#
|
275
|
+
# [options]
|
276
|
+
# Options to alter the generated query <br>
|
277
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Options
|
72
278
|
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
279
|
+
# [&block]
|
280
|
+
# More complex conditions the associated record must match (can also use scopes of the association's model) <br>
|
281
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Block
|
76
282
|
#
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
283
|
+
def where_assoc_not_exists(association_name, conditions = nil, options = {}, &block)
|
284
|
+
ActiveRecordWhereAssoc::CoreLogic.do_where_assoc_not_exists(self, association_name, conditions, options, &block)
|
285
|
+
end
|
286
|
+
|
287
|
+
# :section: Complex method
|
288
|
+
|
289
|
+
# Returns a new relation with a condition added (a +#where+) that checks how many records an association
|
290
|
+
# of the model has. Extra conditions the associated model must match can also be specified.
|
81
291
|
#
|
82
|
-
#
|
292
|
+
# This method is a generalization of #where_assoc_exists and #where_assoc_not_exists. It does the same
|
293
|
+
# thing, but can be more precise over how many records should exist (and match the extra conditions)
|
294
|
+
# To clarify, here are equivalent examples:
|
83
295
|
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
# #where, #joins, # nested #where_assoc_* and scopes of the model. Note that using
|
87
|
-
# #joins might lead to unexpected results when using #where_assoc_count, since if
|
88
|
-
# the joins adds rows, it will change the resulting count.
|
296
|
+
# Post.where_assoc_exists(:comments)
|
297
|
+
# Post.where_assoc_count(1, :<=, :comments)
|
89
298
|
#
|
90
|
-
#
|
299
|
+
# Post.where_assoc_not_exists(:comments)
|
300
|
+
# Post.where_assoc_count(0, :==, :comments)
|
91
301
|
#
|
92
|
-
#
|
93
|
-
# The block receives a relation on the target association and return a relation with added
|
94
|
-
# filters or may return nil to do nothing.
|
302
|
+
# But these have no equivalent:
|
95
303
|
#
|
96
|
-
#
|
97
|
-
#
|
304
|
+
# # Posts with at least 5 comments
|
305
|
+
# Post.where_assoc_count(5, :<=, :comments)
|
98
306
|
#
|
99
|
-
#
|
100
|
-
#
|
307
|
+
# # Posts with less than 5 comments
|
308
|
+
# Post.where_assoc_count(5, :>, :comments)
|
101
309
|
#
|
102
|
-
#
|
103
|
-
#
|
310
|
+
# You could say this is a way of doing a +#select+ that +#count+ the associations of your model
|
311
|
+
# on the SQL side, but faster and more concise.
|
104
312
|
#
|
313
|
+
# Examples (with an equivalent ruby +#select+ and +#count+)
|
105
314
|
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
315
|
+
# # Posts with at least 5 comments
|
316
|
+
# Post.where_assoc_count(5, :<=, :comments)
|
317
|
+
# Post.all.select { |post| post.comments.count >= 5 }
|
109
318
|
#
|
110
|
-
#
|
111
|
-
#
|
319
|
+
# # Posts that have at least 5 comments marked as spam
|
320
|
+
# Post.where_assoc_count(5, :<=, :comments, is_spam: true)
|
321
|
+
# Post.all.select { |post| post.comments.where(is_spam: true).count >= 5 }
|
112
322
|
#
|
113
|
-
#
|
114
|
-
#
|
323
|
+
# # Posts that have at least 10 replies spread over their comments
|
324
|
+
# Post.where_assoc_count(10, :<=, [:comments, :replies])
|
325
|
+
# Post.select { |post| post.comments.sum { |comment| comment.replies.count } >= 5 }
|
115
326
|
#
|
116
|
-
#
|
117
|
-
#
|
327
|
+
# [left_operand]
|
328
|
+
# 1st argument, the left side of the comparison. <br>
|
329
|
+
# One of:
|
330
|
+
# * a number
|
331
|
+
# * a string of SQL to embed in the query
|
332
|
+
# * a range (operator must be :== or :!=), will use BETWEEN or NOT BETWEEN<br>
|
333
|
+
# supports infinite ranges and exclusive end
|
118
334
|
#
|
119
|
-
#
|
335
|
+
# # Posts with 5 to 10 comments
|
336
|
+
# Post.where_assoc_count(5..10, :==, :comments)
|
120
337
|
#
|
121
|
-
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
# Returns a new relation, which is the result of filtering the current relation
|
126
|
-
# based on if a record for the specified association of the model doesn't exist.
|
127
|
-
# Conditions the associated model must match to count as existing can also be specified.
|
128
|
-
#
|
129
|
-
# The parameters and everything is identical to #where_assoc_exists. The only
|
130
|
-
# difference is that a record is matched if no matching association record that
|
131
|
-
# fulfill the conditions are found.
|
132
|
-
def where_assoc_not_exists(association_name, given_scope = nil, options = {}, &block)
|
133
|
-
ActiveRecordWhereAssoc::CoreLogic.do_where_assoc_not_exists(self, association_name, given_scope, options, &block)
|
134
|
-
end
|
135
|
-
|
136
|
-
# Returns a new relation, which is the result of filtering the current relation
|
137
|
-
# based on how many records for the specified association of the model exists. Conditions
|
138
|
-
# the associated model must match can also be specified.
|
338
|
+
# # Posts with less than 5 or more than 10 comments
|
339
|
+
# Post.where_assoc_count(5..10, :!=, :comments)
|
139
340
|
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
# specific about how many matches there should be. To clarify, here are equivalent examples:
|
341
|
+
# [operator]
|
342
|
+
# The operator to use, one of these symbols: <code> :< :<= :== :!= :>= :> </code>
|
143
343
|
#
|
144
|
-
#
|
145
|
-
#
|
344
|
+
# [association_name]
|
345
|
+
# The association that must exist <br>
|
346
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Association
|
146
347
|
#
|
147
|
-
#
|
148
|
-
#
|
348
|
+
# [condition]
|
349
|
+
# Extra conditions the association must match to count <br>
|
350
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Condition
|
149
351
|
#
|
150
|
-
#
|
151
|
-
#
|
352
|
+
# [options]
|
353
|
+
# Options to alter the generated query <br>
|
354
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Options
|
152
355
|
#
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
# a range (operator must be :== or :!=), will use BETWEEN or NOT BETWEEN
|
157
|
-
# supports infinite ranges and exclusive end
|
158
|
-
# 2nd argument: the operator to use: :<, :<=, :==, :!=, :>=, :>
|
159
|
-
# 3rd, 4th and 5th arguments: same as #where_assoc_exists' 1st, 2nd and 3rd arguments
|
160
|
-
# block: same as #where_assoc_exists' block
|
356
|
+
# [&block]
|
357
|
+
# More complex conditions the associated record must match (can also use scopes of the association's model) <br>
|
358
|
+
# See ActiveRecordWhereAssoc::QueryMethods@Block
|
161
359
|
#
|
162
360
|
# The order of the parameters may seem confusing. But you will get used to it. To help
|
163
361
|
# remember the order of the parameters, remember that the goal is to do:
|
@@ -168,13 +366,14 @@ module ActiveRecordWhereAssoc
|
|
168
366
|
# comparing against is the total number of records of that last association.
|
169
367
|
#
|
170
368
|
# # The users that have received at least 5 comments total on all of their posts
|
171
|
-
# # So this can be one post that has 5 comments of 5 posts with 1 comments
|
369
|
+
# # So this can be from one post that has 5 comments of from 5 posts with 1 comments
|
172
370
|
# User.where_assoc_count(5, :<=, [:posts, :comments])
|
173
371
|
#
|
174
372
|
# # The users that have at least 5 posts with at least one comments
|
175
373
|
# User.where_assoc_count(5, :<=, :posts) { where_assoc_exists(:comments) }
|
176
|
-
|
177
|
-
|
374
|
+
#
|
375
|
+
def where_assoc_count(left_operand, operator, association_name, conditions = nil, options = {}, &block)
|
376
|
+
ActiveRecordWhereAssoc::CoreLogic.do_where_assoc_count(self, left_operand, operator, association_name, conditions, options, &block)
|
178
377
|
end
|
179
378
|
end
|
180
379
|
end
|