activerecord_where_assoc 1.1.2 → 1.1.3
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 +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
|