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 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