activerecord_where_assoc 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/EXAMPLES.md +4 -3
- data/README.md +16 -4
- data/lib/active_record_where_assoc/core_logic.rb +26 -12
- data/lib/active_record_where_assoc/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd0fae20ed7a2ae8fac19fda0df7f70b9b6a842a6519684c66c571dbf2f57732
|
4
|
+
data.tar.gz: f570a564494a52517dde7aad62c79b571ab3d1e49d651f32ac2bb86b301fa4f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 240832561dd8eb5cc93ff3ebc239a0aedf8f9f79520a6404ffe6ac8f9d1b2cacd5f0057d85066ed889b62f16b5e85125acb8b943c91fa72ab0450a366d7d7569
|
7
|
+
data.tar.gz: 060f842df0bd69b72ac0e6f0a64ef222b347047d693046abe83a9bfdd6797109e202bcccc32f75a3e7bc4349f1b5a50c94982102571a699b113e65331ebca459
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 1.1.3 - 2022-08-16
|
4
|
+
|
5
|
+
* Add support for associations defined on abstract models
|
6
|
+
|
7
|
+
# 1.1.2 - 2020-12-24
|
8
|
+
|
9
|
+
* Add compatiblity for Rails 6.1
|
10
|
+
|
3
11
|
# 1.1.1 - 2020-04-13
|
4
12
|
|
5
13
|
* Fix handling for ActiveRecord's NullRelation (MyModel.none) in block and association's conditions.
|
data/EXAMPLES.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
SELECT "users".* FROM "users"
|
1
2
|
Here are some example usages of the gem, along with the generated SQL.
|
2
3
|
|
3
4
|
Each of those methods can be chained with scoping methods, so they can be used on `Post`, `my_user.posts`, `Post.where('hello')` or inside a scope. Note that for the `*_sql` variants, those should preferably be used on classes only, because otherwise, it could be confusing for a reader.
|
@@ -107,13 +108,13 @@ User.where_assoc_exists(:posts).or(User.where_assoc_exists(:comments))
|
|
107
108
|
```
|
108
109
|
```sql
|
109
110
|
SELECT "users".* FROM "users"
|
110
|
-
WHERE (
|
111
|
+
WHERE (EXISTS (
|
111
112
|
SELECT 1 FROM "posts"
|
112
113
|
WHERE "posts"."author_id" = "users"."id"
|
113
|
-
)
|
114
|
+
) OR EXISTS (
|
114
115
|
SELECT 1 FROM "comments"
|
115
116
|
WHERE "comments"."author_id" = "users"."id"
|
116
|
-
))
|
117
|
+
))
|
117
118
|
```
|
118
119
|
|
119
120
|
---
|
data/README.md
CHANGED
@@ -37,9 +37,8 @@ These methods have many advantages over the alternative ways of achieving the si
|
|
37
37
|
|
38
38
|
## Installation
|
39
39
|
|
40
|
-
Rails 4.1 to
|
41
|
-
|
42
|
-
The gem only depends on the `activerecord` gem.
|
40
|
+
Rails 4.1 to 7.0 are supported with Ruby 2.1 to 3.1. Tested against SQLite3, PostgreSQL and MySQL. The gem
|
41
|
+
only depends on the `activerecord` gem.
|
43
42
|
|
44
43
|
Add this line to your application's Gemfile:
|
45
44
|
|
@@ -51,7 +50,7 @@ And then execute:
|
|
51
50
|
|
52
51
|
$ bundle install
|
53
52
|
|
54
|
-
Or install it yourself
|
53
|
+
Or install it yourself with:
|
55
54
|
|
56
55
|
$ gem install activerecord_where_assoc
|
57
56
|
|
@@ -235,6 +234,19 @@ It is pretty complicated to support `#limit` and `#offset` of the `has_* :throug
|
|
235
234
|
|
236
235
|
Note that the support of `#limit` and `#offset` for the `:source` and `:through` parts is a feature. I consider `ActiveRecord` wrong for not handling them correctly.
|
237
236
|
|
237
|
+
## Another recommended gem
|
238
|
+
|
239
|
+
If you feel a need for this gem's feature, you may also be interested in another gem I made: [activerecord_follow_assoc](https://github.com/MaxLap/activerecord_follow_assoc).
|
240
|
+
|
241
|
+
It allows you to follow an association of your choice while building a query (a scope). You start querying posts, and then you change to querying the authors of those posts. For simple cases, it's possible that both `where_assoc` and `follow_assoc` can build the query your need, but each can handle different situations. Here is an example:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
# Find every posts that have comments by an admin
|
245
|
+
Post.where_assoc_exists([:comments, :author], &:admins)
|
246
|
+
```
|
247
|
+
|
248
|
+
This could be done with `follow_assoc`: `User.admins.follow_assoc(:comments, :post)`. But if you wanted conditions on a second association, then `follow_assoc` wouldn't work. On the other hand, if you received a scope on users and wanted their posts, then `follow_assoc` would be a nicer tool for the job. It all depends on the context where you need to do the query and what starting point you have.
|
249
|
+
|
238
250
|
## Development
|
239
251
|
|
240
252
|
After checking out the repo, run `bundle install` to install dependencies.
|
@@ -141,7 +141,7 @@ module ActiveRecordWhereAssoc
|
|
141
141
|
# Each step, we get all of the scoping lambdas that were defined on associations that apply for
|
142
142
|
# the reflection's target
|
143
143
|
# Basically, we start from the deepest part of the query and wrap it up
|
144
|
-
reflection_chain,
|
144
|
+
reflection_chain, constraints_chain = ActiveRecordCompat.chained_reflection_and_chained_constraints(final_reflection)
|
145
145
|
skip_next = false
|
146
146
|
|
147
147
|
reflection_chain.each_with_index do |reflection, i|
|
@@ -153,7 +153,7 @@ module ActiveRecordWhereAssoc
|
|
153
153
|
# the 2nd part of has_and_belongs_to_many is handled at the same time as the first.
|
154
154
|
skip_next = true if actually_has_and_belongs_to_many?(reflection)
|
155
155
|
|
156
|
-
init_scopes = initial_scopes_from_reflection(reflection_chain[i..-1],
|
156
|
+
init_scopes = initial_scopes_from_reflection(record_class, reflection_chain[i..-1], constraints_chain[i], options)
|
157
157
|
current_scopes = init_scopes.map do |alias_scope, current_scope, klass_scope|
|
158
158
|
current_scope = process_association_step_limits(current_scope, reflection, record_class, options)
|
159
159
|
|
@@ -197,13 +197,13 @@ module ActiveRecordWhereAssoc
|
|
197
197
|
end
|
198
198
|
|
199
199
|
# Can return multiple pairs for polymorphic belongs_to, one per table to look into
|
200
|
-
def self.initial_scopes_from_reflection(reflection_chain, assoc_scopes, options)
|
200
|
+
def self.initial_scopes_from_reflection(record_class, reflection_chain, assoc_scopes, options)
|
201
201
|
reflection = reflection_chain.first
|
202
202
|
actual_source_reflection = user_defined_actual_source_reflection(reflection)
|
203
203
|
|
204
204
|
on_poly_belongs_to = option_value(options, :poly_belongs_to) if poly_belongs_to?(actual_source_reflection)
|
205
205
|
|
206
|
-
classes_with_scope = classes_with_scope_for_reflection(reflection, options)
|
206
|
+
classes_with_scope = classes_with_scope_for_reflection(record_class, reflection, options)
|
207
207
|
|
208
208
|
assoc_scope_allowed_lim_off = assoc_scope_to_keep_lim_off_from(reflection)
|
209
209
|
|
@@ -225,18 +225,18 @@ module ActiveRecordWhereAssoc
|
|
225
225
|
# would be great, except we cannot add a given_conditions afterward because we are on the wrong "base class",
|
226
226
|
# and we can't do #merge because of the LEW crap.
|
227
227
|
# So we must do the joins ourself!
|
228
|
-
_wrapper, sub_join_contraints = wrapper_and_join_constraints(reflection)
|
228
|
+
_wrapper, sub_join_contraints = wrapper_and_join_constraints(record_class, reflection)
|
229
229
|
next_reflection = reflection_chain[1]
|
230
230
|
|
231
231
|
current_scope = current_scope.joins(<<-SQL)
|
232
232
|
INNER JOIN #{next_reflection.klass.quoted_table_name} ON #{sub_join_contraints.to_sql}
|
233
233
|
SQL
|
234
234
|
|
235
|
-
alias_scope,
|
235
|
+
alias_scope, join_constraints = wrapper_and_join_constraints(record_class, next_reflection, habtm_other_reflection: reflection)
|
236
236
|
elsif on_poly_belongs_to
|
237
|
-
alias_scope,
|
237
|
+
alias_scope, join_constraints = wrapper_and_join_constraints(record_class, reflection, poly_belongs_to_klass: klass)
|
238
238
|
else
|
239
|
-
alias_scope,
|
239
|
+
alias_scope, join_constraints = wrapper_and_join_constraints(record_class, reflection)
|
240
240
|
end
|
241
241
|
|
242
242
|
assoc_scopes.each do |callable|
|
@@ -256,7 +256,7 @@ module ActiveRecordWhereAssoc
|
|
256
256
|
current_scope = current_scope.merge(relation)
|
257
257
|
end
|
258
258
|
|
259
|
-
[alias_scope, current_scope.where(
|
259
|
+
[alias_scope, current_scope.where(join_constraints), klass_scope]
|
260
260
|
end
|
261
261
|
end
|
262
262
|
|
@@ -272,7 +272,7 @@ module ActiveRecordWhereAssoc
|
|
272
272
|
user_defined_actual_source_reflection(reflection).scope
|
273
273
|
end
|
274
274
|
|
275
|
-
def self.classes_with_scope_for_reflection(reflection, options)
|
275
|
+
def self.classes_with_scope_for_reflection(record_class, reflection, options)
|
276
276
|
actual_source_reflection = user_defined_actual_source_reflection(reflection)
|
277
277
|
|
278
278
|
if poly_belongs_to?(actual_source_reflection)
|
@@ -283,7 +283,15 @@ module ActiveRecordWhereAssoc
|
|
283
283
|
else
|
284
284
|
case on_poly_belongs_to
|
285
285
|
when :pluck
|
286
|
-
|
286
|
+
model_for_ids = actual_source_reflection.active_record
|
287
|
+
|
288
|
+
if model_for_ids.abstract_class
|
289
|
+
# When the reflection is defined on an abstract model, we fallback to the model
|
290
|
+
# on which this was called
|
291
|
+
model_for_ids = record_class
|
292
|
+
end
|
293
|
+
|
294
|
+
class_names = model_for_ids.distinct.pluck(actual_source_reflection.foreign_type)
|
287
295
|
class_names.compact.map!(&:safe_constantize).compact
|
288
296
|
when Array, Hash
|
289
297
|
array = on_poly_belongs_to.to_a
|
@@ -391,7 +399,7 @@ module ActiveRecordWhereAssoc
|
|
391
399
|
alias_scope
|
392
400
|
end
|
393
401
|
|
394
|
-
def self.wrapper_and_join_constraints(reflection, options = {})
|
402
|
+
def self.wrapper_and_join_constraints(record_class, reflection, options = {})
|
395
403
|
poly_belongs_to_klass = options[:poly_belongs_to_klass]
|
396
404
|
join_keys = ActiveRecordCompat.join_keys(reflection, poly_belongs_to_klass)
|
397
405
|
|
@@ -400,6 +408,12 @@ module ActiveRecordWhereAssoc
|
|
400
408
|
|
401
409
|
table = (poly_belongs_to_klass || reflection.klass).arel_table
|
402
410
|
foreign_klass = reflection.send(:actual_source_reflection).active_record
|
411
|
+
if foreign_klass.abstract_class
|
412
|
+
# When the reflection is defined on an abstract model, we fallback to the model
|
413
|
+
# on which this was called
|
414
|
+
foreign_klass = record_class
|
415
|
+
end
|
416
|
+
|
403
417
|
foreign_table = foreign_klass.arel_table
|
404
418
|
|
405
419
|
habtm_other_reflection = options[:habtm_other_reflection]
|
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.1.
|
4
|
+
version: 1.1.3
|
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:
|
11
|
+
date: 2022-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -190,7 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
190
|
- !ruby/object:Gem::Version
|
191
191
|
version: '0'
|
192
192
|
requirements: []
|
193
|
-
rubygems_version: 3.
|
193
|
+
rubygems_version: 3.3.7
|
194
194
|
signing_key:
|
195
195
|
specification_version: 4
|
196
196
|
summary: Make ActiveRecord do conditions on your associations
|