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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c33267f5201fd0465cceec484dd0a2c91b3511b47289f500311e0d3a3946b28f
4
- data.tar.gz: 0b1ef81c3b3106a0226ed72494523ce3a2ebb005f4b1401d6cca3917a78bfa26
3
+ metadata.gz: fd0fae20ed7a2ae8fac19fda0df7f70b9b6a842a6519684c66c571dbf2f57732
4
+ data.tar.gz: f570a564494a52517dde7aad62c79b571ab3d1e49d651f32ac2bb86b301fa4f6
5
5
  SHA512:
6
- metadata.gz: 156df4226e93a9d5a8e900a71b518d42ff984a47a8917d7d807429d3b473a5f8db7d392496c8f4dfda7f0746689b9127547d238b2f875846235c9075e4789f7b
7
- data.tar.gz: fbea2c6d875f87c5b04d740fb6878d1f9ece9e11ba0053d38bbe6c8cb6311b194be770ebd402bc59a2baad10c3ecac8ee9f5e2bad8f6147ea152d8e8c2a96518
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 ((EXISTS (
111
+ WHERE (EXISTS (
111
112
  SELECT 1 FROM "posts"
112
113
  WHERE "posts"."author_id" = "users"."id"
113
- )) OR (EXISTS (
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 6.1 are supported with Ruby 2.1 to 2.7.
41
- Tested against SQLite3, PostgreSQL and MySQL.
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 as:
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, constaints_chain = ActiveRecordCompat.chained_reflection_and_chained_constraints(final_reflection)
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], constaints_chain[i], options)
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, join_constaints = wrapper_and_join_constraints(next_reflection, habtm_other_reflection: reflection)
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, join_constaints = wrapper_and_join_constraints(reflection, poly_belongs_to_klass: klass)
237
+ alias_scope, join_constraints = wrapper_and_join_constraints(record_class, reflection, poly_belongs_to_klass: klass)
238
238
  else
239
- alias_scope, join_constaints = wrapper_and_join_constraints(reflection)
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(join_constaints), klass_scope]
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
- class_names = actual_source_reflection.active_record.distinct.pluck(actual_source_reflection.foreign_type)
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]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordWhereAssoc
4
- VERSION = "1.1.2".freeze
4
+ VERSION = "1.1.3".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.1.2
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: 2020-12-24 00:00:00.000000000 Z
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.0.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