where_exists 1.1.4 → 1.2.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: 9264d4a29619fa91e69b9648228c2597564b087e81d11012bfe6e1708337ac7d
4
- data.tar.gz: cd071c68e39c82aa182c35f99e5871dd0ddeec3b3bbdb63e5aa12dbfc2ee088e
3
+ metadata.gz: 397b2a3fc406fb516d6f060ae46b25580a764c668476859737ee07fbd979c15b
4
+ data.tar.gz: 1c23ab279b89ede28edb71e3b951f7587ab2a7b27605bba607888f0b89e03c58
5
5
  SHA512:
6
- metadata.gz: 33621afb28c864019a32ef912bcda6edbd481ea26b4eabef5965796ce8bfd23f8e37f4ed56ddce72e34bf0f286e8759b1132ee39ffd95a3b329ad2cdbfce2fdb
7
- data.tar.gz: dfe4dee7dc35c9a5fe4a3bbe4441a4647da72d9f541c4f0c98f564df784e394b27da5776d1c9389b4e879b102bbd701af5db4052a32b80bd6b7e2d5264c9ba0c
6
+ metadata.gz: 454c6302e62cbc669c8d800bab1305b2e83402f46ed2499a4caf373cfaa23547cbaf261163fc28cd2fe224a31c7f5bb927a5baf09deb04c856de49c481b5fc75
7
+ data.tar.gz: b32e53db604284721d96afd9b1fa7ac204277dda4cc27f4851ac1918760980fa64c17392917ca770a712541f5ab132bedaa2f0f102ac6c20fa50354d16e18ad1
data/MIT-LICENSE CHANGED
File without changes
data/README.markdown ADDED
@@ -0,0 +1,168 @@
1
+ # Where Exists
2
+ **Rails way to harness the power of SQL EXISTS condition**<br>
3
+ [![Gem Version](https://badge.fury.io/rb/where_exists.svg)](http://badge.fury.io/rb/where_exists)
4
+
5
+ ## Description
6
+
7
+ <img src="http://i.imgur.com/psLfPoW.gif" alt="Exists" align="right" width="100" height="200">
8
+
9
+ This gem does exactly two things:
10
+
11
+ * Selects each model object for which there is a certain associated object
12
+ * Selects each model object for which there aren't any certain associated objects
13
+
14
+ It uses SQL [EXISTS condition](http://www.techonthenet.com/sql/exists.php) to do it fast, and extends ActiveRecord with `where_exists` and `where_not_exists` methods to make its usage simple and straightforward.
15
+
16
+ ## Quick start
17
+
18
+ Add gem to Gemfile:
19
+
20
+ gem 'where_exists'
21
+
22
+ and run `bundle install` as usual.
23
+
24
+ And now you have `where_exists` and `where_not_exists` methods available for your ActiveRecord models and relations.
25
+
26
+ Syntax:
27
+
28
+ ```ruby
29
+ Model.where_exists(association, additional_finder_parameters)
30
+ ```
31
+
32
+ Supported Rails versions: >= 4.2.
33
+
34
+ ## Example of usage
35
+
36
+ Given there is User model:
37
+
38
+ ```ruby
39
+ class User < ActiveRecord::Base
40
+ has_many :connections
41
+ has_many :groups, through: :connections
42
+ end
43
+ ```
44
+
45
+ And Group:
46
+
47
+ ```ruby
48
+ class Group < ActiveRecord::Base
49
+ has_many :connections
50
+ has_many :users, through: :connections
51
+ end
52
+ ```
53
+
54
+ And standard many-to-many Connection:
55
+
56
+ ```ruby
57
+ class Connection
58
+ belongs_to :user
59
+ belongs_to :group
60
+ end
61
+ ```
62
+
63
+ What I want to do is to:
64
+
65
+ * Select users who don't belong to given set of Groups (groups with ids `[4,5,6]`)
66
+ * Select users who belong to one set of Groups (`[1,2,3]`) and don't belong to another (`[4,5,6]`)
67
+ * Select users who don't belong to a Group
68
+
69
+ Also, I don't want to:
70
+
71
+ * Fetch a lot of data from database to manipulate it with Ruby code. I know that will be inefficient in terms of CPU and memory (Ruby is much slower than any commonly used DB engine, and typically I want to rely on DB engine to do the heavy lifting)
72
+ * I tried queries like `User.joins(:group).where(group_id: [1,2,3]).where.not(group_id: [4,5,6])` and they return wrong results (some users from the result set belong to groups 4,5,6 *as well as* 1,2,3)
73
+ * I don't want to do `join` merely for the sake of only checking for existence, because I know that that is a pretty complex (i.e. CPU/memory-intensive) operation for DB
74
+
75
+ <sub><sup>If you wonder how to do that without the gem (i.e. essentially by writing SQL EXISTS statement manually) see that [StackOverflow answer](http://stackoverflow.com/a/32016347/5029266) (disclosure: it's self-answered question of a contributor of this gem).</sup></sub>
76
+
77
+ And now you are able to do all these things (and more) as simple as:
78
+
79
+ > Select only users who don't belong to given set of Groups (groups with ids `[4,5,6]`)
80
+
81
+ ```ruby
82
+ # It's really neat, isn't it?
83
+ User.where_exists(:groups, id: [4,5,6])
84
+ ```
85
+
86
+ <sub><sup>Notice that the second argument is `where` parameters for Group model</sup></sub>
87
+
88
+ > Select only users who belong to one set of Groups (`[1,2,3]`) and don't belong to another (`[4,5,6]`)
89
+
90
+ ```ruby
91
+ # Chain-able like you expect them to be.
92
+ #
93
+ # Additional finder parameters is anything that
94
+ # could be fed to 'where' method.
95
+ #
96
+ # Let's use 'name' instead of 'id' here, for example.
97
+
98
+ User.where_exists(:groups, name: ['first','second','third']).
99
+ where_not_exists(:groups, name: ['fourth','fifth','sixth'])
100
+ ```
101
+
102
+ <sub><sup>It is possible to add as much attributes to the criteria as it is necessary, just as with regular `where(...)`</sub></sup>
103
+
104
+ > Select only users who don't belong to a Group
105
+
106
+ ```ruby
107
+ # And that's just its basic capabilities
108
+ User.where_not_exists(:groups)
109
+ ```
110
+
111
+ <sub><sup>Adding parameters (the second argument) to `where_not_exists` method is feasible as well, if you have such requirements.</sup></sub>
112
+
113
+
114
+ > Re-use existing scopes
115
+
116
+ ```ruby
117
+ User.where_exists(:groups) do |groups_scope|
118
+ groups_scope.activated_since(Time.now)
119
+ end
120
+
121
+ User.where_exists(:groups, &:approved)
122
+ ```
123
+ <sub><sup>If you pass a block to `where_exists`, the scope of the relation will be yielded to your block so you can re-use existing scopes.</sup></sub>
124
+
125
+
126
+
127
+ ## Additional capabilities
128
+
129
+ **Q**: Does it support both `has_many` and `belongs_to` association type?<br>
130
+ **A**: Yes.
131
+
132
+
133
+ **Q**: Does it support polymorphic associations?<br>
134
+ **A**: Yes, both ways.
135
+
136
+
137
+ **Q**: Does it support multi-level (recursive) `:through` associations?<br>
138
+ **A**: You bet. (Now you can forget complex EXISTS or JOIN statetements in a pretty wide variety of similar cases.)
139
+
140
+
141
+ **Q**: Does it support `where` parameters with interpolation, e.g. `parent.where_exists(:child, 'fieldA > ?', 1)`?<br>
142
+ **A**: Yes.
143
+
144
+
145
+ **Q**: Does it take into account default association condition, e.g. `has_many :drafts, -> { where published: nil }`?<br>
146
+ **A**: Yes.
147
+
148
+ ## Contributing
149
+
150
+ If you find that this gem lacks certain possibilities that you would have found useful, don't hesitate to create a [feature request](https://github.com/EugZol/where_exists/issues).
151
+
152
+ Also,
153
+
154
+ * Report bugs
155
+ * Submit pull request with new features or bug fixes
156
+ * Enhance or clarify the documentation that you are reading
157
+
158
+ To run tests: `bundle exec rake test`
159
+
160
+ ## License
161
+
162
+ This project uses MIT license. See [`MIT-LICENSE`](https://github.com/EugZol/where_exists/blob/master/MIT-LICENSE) file for full text.
163
+
164
+ ## Alternatives
165
+
166
+ One known alternative is https://github.com/MaxLap/activerecord_where_assoc
167
+
168
+ A comprehensive comparison is made by MaxLap here: https://github.com/MaxLap/activerecord_where_assoc/blob/master/ALTERNATIVES_PROBLEMS.md
data/Rakefile CHANGED
File without changes
data/lib/where_exists.rb CHANGED
@@ -20,7 +20,11 @@ module WhereExists
20
20
  not_string = "NOT "
21
21
  end
22
22
 
23
- self.where("#{not_string}(#{queries_sql})")
23
+ if queries_sql.empty?
24
+ does_exist ? self.none : self.all
25
+ else
26
+ self.where("#{not_string}(#{queries_sql})")
27
+ end
24
28
  end
25
29
 
26
30
  def build_exists_string(association_name, *where_parameters, &block)
@@ -61,7 +65,9 @@ module WhereExists
61
65
  association_scope = association.scope
62
66
 
63
67
  if polymorphic
64
- associated_models = self.select("DISTINCT #{connection.quote_column_name(association.foreign_type)}").pluck(association.foreign_type).map(&:constantize)
68
+ associated_models = self.select("DISTINCT #{connection.quote_column_name(association.foreign_type)}").
69
+ where("#{connection.quote_column_name(association.foreign_type)} IS NOT NULL").pluck(association.foreign_type).
70
+ uniq.map(&:classify).map(&:constantize)
65
71
  else
66
72
  associated_models = [association.klass]
67
73
  end
@@ -82,8 +88,10 @@ module WhereExists
82
88
  query = query.instance_exec(&association_scope)
83
89
  end
84
90
  if polymorphic
85
- other_type = connection.quote(associated_model.name)
86
- query = query.where("#{self_type} = #{other_type}")
91
+ other_types = [associated_model.name, associated_model.table_name]
92
+ other_types << associated_model.polymorphic_name if associated_model.respond_to?(:polymorphic_name)
93
+
94
+ query = query.where("#{self_type} IN (?)", other_types.uniq)
87
95
  end
88
96
  queries.push query
89
97
  end
@@ -129,8 +137,14 @@ module WhereExists
129
137
 
130
138
  if association.options[:as]
131
139
  other_types = quote_table_and_column_name(associated_model.table_name, association.type)
132
- self_class = connection.quote(self.name)
133
- result = result.where("#{other_types} = #{self_class}")
140
+ class_values = [self.name, self.table_name]
141
+ class_values << self.polymorphic_name if associated_model.respond_to?(:polymorphic_name)
142
+
143
+ result = result.where("#{other_types} IN (?)", class_values.uniq)
144
+ end
145
+
146
+ if association_scope
147
+ result = result.instance_exec(&association_scope)
134
148
  end
135
149
 
136
150
  if next_association[:association]
@@ -141,10 +155,6 @@ module WhereExists
141
155
  result = result.where(*where_parameters)
142
156
  end
143
157
 
144
- if association_scope
145
- result = result.instance_exec(&association_scope)
146
- end
147
-
148
158
  [result]
149
159
  end
150
160
 
@@ -155,11 +165,9 @@ module WhereExists
155
165
 
156
166
  primary_key = association.options[:primary_key] || self.primary_key
157
167
 
158
- join_table = [self.table_name, associated_model.table_name].sort.join("_")
159
-
160
168
  self_ids = quote_table_and_column_name(self.table_name, primary_key)
161
- join_ids = quote_table_and_column_name(join_table, association.foreign_key)
162
- associated_join_ids = quote_table_and_column_name(join_table, "#{associated_model.name.downcase}_id")
169
+ join_ids = quote_table_and_column_name(association.join_table, association.foreign_key)
170
+ associated_join_ids = quote_table_and_column_name(association.join_table, association.association_foreign_key)
163
171
  associated_ids = quote_table_and_column_name(associated_model.table_name, associated_model.primary_key)
164
172
 
165
173
  result =
@@ -167,7 +175,7 @@ module WhereExists
167
175
  select("1").
168
176
  joins(
169
177
  <<-SQL
170
- INNER JOIN #{connection.quote_table_name(join_table)}
178
+ INNER JOIN #{connection.quote_table_name(association.join_table)}
171
179
  ON #{associated_ids} = #{associated_join_ids}
172
180
  SQL
173
181
  ).
@@ -198,13 +206,13 @@ module WhereExists
198
206
 
199
207
  if next_association[:next_association] && next_association[:next_association][:association]
200
208
  subq = str.match(/\([^\(\)]+\)/mi)[0]
201
- str.sub!(subq,
209
+ str.sub!(subq) do
202
210
  "(#{subq} AND (#{loop_nested_association(
203
211
  next_association[:association],
204
212
  next_association[:next_association],
205
213
  true
206
214
  )}))"
207
- )
215
+ end
208
216
  end
209
217
 
210
218
  nested ? str : [query.where(str)]
@@ -1,3 +1,3 @@
1
1
  module WhereExists
2
- VERSION = "1.1.4"
2
+ VERSION = "1.2.3"
3
3
  end
@@ -60,4 +60,33 @@ class BelongsToPolymorphicTest < Minitest::Test
60
60
  assert_equal 1, result.length
61
61
  assert_equal orphaned_child.id, result.first.id
62
62
  end
63
+
64
+ def test_no_entities_or_empty_child_relation
65
+ result = BelongsToPolymorphicChild.where_not_exists(:polymorphic_entity)
66
+ assert_equal 0, result.length
67
+
68
+ _first_child = BelongsToPolymorphicChild.create!
69
+ result = BelongsToPolymorphicChild.where_not_exists(:polymorphic_entity)
70
+ assert_equal 1, result.length
71
+
72
+ result = BelongsToPolymorphicChild.where_exists(:polymorphic_entity)
73
+ assert_equal 0, result.length
74
+ end
75
+
76
+ def test_table_name_based_lookup
77
+ first_entity = FirstPolymorphicEntity.create!
78
+ second_entity = SecondPolymorphicEntity.create! id: first_entity.id + 1
79
+
80
+ first_child = BelongsToPolymorphicChild.create!(polymorphic_entity_id: first_entity.id, polymorphic_entity_type: first_entity.class.table_name)
81
+ second_child = BelongsToPolymorphicChild.create!(polymorphic_entity_id: second_entity.id, polymorphic_entity_type: second_entity.class.table_name)
82
+ orphaned_child = BelongsToPolymorphicChild.create!(polymorphic_entity_id: second_entity.id, polymorphic_entity_type: first_entity.class.table_name)
83
+
84
+ result = BelongsToPolymorphicChild.where_exists(:polymorphic_entity)
85
+ assert_equal 2, result.length
86
+ assert_equal [first_child, second_child].map(&:id).sort, result.map(&:id).sort
87
+
88
+ result = BelongsToPolymorphicChild.where_not_exists(:polymorphic_entity)
89
+ assert_equal 1, result.length
90
+ assert_equal [orphaned_child].map(&:id).sort, result.map(&:id).sort
91
+ end
63
92
  end
File without changes
data/test/db/test.db CHANGED
Binary file
File without changes
File without changes
@@ -35,10 +35,17 @@ class HasManyPolymorphicTest < Minitest::Test
35
35
  child = HasManyPolymorphicChild.create!
36
36
 
37
37
  irrelevant_entity = IrrelevantPolymorphicEntity.create!(children: [child])
38
- _relevant_entity = RelevantPolymorphicEntity.create!(id: irrelevant_entity.id)
38
+ relevant_entity = RelevantPolymorphicEntity.create!(id: irrelevant_entity.id)
39
+
40
+ assert_equal 0, RelevantPolymorphicEntity.where_exists(:children).length
41
+ assert_equal 1, IrrelevantPolymorphicEntity.where_exists(:children).length
42
+
43
+ child.update!(polymorphic_thing_type: RelevantPolymorphicEntity.table_name)
39
44
 
40
45
  result = RelevantPolymorphicEntity.where_exists(:children)
41
46
 
42
- assert_equal 0, result.length
47
+ assert_equal 0, IrrelevantPolymorphicEntity.where_exists(:children).length
48
+ assert_equal 1, result.length
49
+ assert_equal relevant_entity.id, result.first&.id
43
50
  end
44
51
  end
File without changes
@@ -1,34 +1,71 @@
1
1
  require_relative 'test_helper'
2
2
 
3
- ActiveRecord::Migration.create_table :projects, :force => true do |t|
3
+ ActiveRecord::Migration.create_table :projects, force: true do |t|
4
4
  t.string :name
5
5
  end
6
6
 
7
- ActiveRecord::Migration.create_table :tasks, :force => true do |t|
7
+ ActiveRecord::Migration.create_table :tasks, force: true do |t|
8
8
  t.string :name
9
9
  t.integer :project_id
10
10
  end
11
11
 
12
- ActiveRecord::Migration.create_table :line_items, :force => true do |t|
12
+ ActiveRecord::Migration.create_table :line_items, force: true do |t|
13
13
  t.string :name
14
14
  t.integer :invoice_id
15
15
  t.integer :task_id
16
16
  end
17
17
 
18
- ActiveRecord::Migration.create_table :work_details, :force => true do |t|
18
+ ActiveRecord::Migration.create_table :work_details, force: true do |t|
19
19
  t.string :name
20
20
  t.integer :line_item_id
21
21
  end
22
22
 
23
- ActiveRecord::Migration.create_table :invoices, :force => true do |t|
23
+ ActiveRecord::Migration.create_table :invoices, force: true do |t|
24
24
  t.string :name
25
25
  end
26
26
 
27
+ ActiveRecord::Migration.create_table :blobs, force: true do |t|
28
+ end
29
+
30
+ ActiveRecord::Migration.create_table :attachments, force: true do |t|
31
+ t.string :name, null: false
32
+ t.references :record, null: false, polymorphic: true, index: false
33
+ t.references :blob, null: false
34
+
35
+ t.datetime :created_at, null: false
36
+
37
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_attachments_uniqueness", unique: true
38
+ end
39
+
40
+ class Attachment < ActiveRecord::Base
41
+ belongs_to :record, polymorphic: true, touch: true
42
+ belongs_to :blob
43
+ end
44
+
45
+ class Blob < ActiveRecord::Base
46
+ has_many :attachments
47
+
48
+ scope :unattached, -> { left_joins(:attachments).where(Attachment.table_name => { blob_id: nil }) }
49
+
50
+ before_destroy(prepend: true) do
51
+ raise ActiveRecord::InvalidForeignKey if attachments.exists?
52
+ end
53
+ end
54
+
55
+
56
+
27
57
  class Project < ActiveRecord::Base
28
58
  has_many :tasks
29
59
  has_many :invoices, :through => :tasks
30
60
  has_many :project_line_items, :through => :tasks, :source => :line_items
31
61
  has_many :work_details, :through => :project_line_items
62
+
63
+ has_many :attachments, as: :record
64
+ has_many :blobs, through: :attachments, source: :blob
65
+ has_many :relevant_attachments, -> { where(name: "relevant") }, as: :record, class_name: "Attachment", inverse_of: :record, dependent: false
66
+ has_many :relevant_blobs, through: :relevant_attachments, class_name: "Blob", source: :blob
67
+ has_many :irrelevant_attachments, -> { where(name: "irrelevant") }, as: :record, class_name: "Attachment", inverse_of: :record, dependent: false
68
+ has_many :irrelevant_blobs, through: :irrelevant_attachments, class_name: "Blob", source: :blob
32
69
  end
33
70
 
34
71
  class Task < ActiveRecord::Base
@@ -36,6 +73,7 @@ class Task < ActiveRecord::Base
36
73
 
37
74
  has_many :invoices, :through => :line_items
38
75
  has_many :line_items
76
+ has_many :scoped_line_items, -> { where(name: 'relevant') }, class_name: 'LineItem'
39
77
  end
40
78
 
41
79
  class LineItem < ActiveRecord::Base
@@ -94,12 +132,16 @@ class HasManyThroughTest < Minitest::Test
94
132
  invoice = Invoice.create!(name: 'relevant')
95
133
  irrelevant_invoice = Invoice.create!(name: 'irrelevant')
96
134
 
97
- line_item = LineItem.create!(task: task, invoice: invoice)
98
- irrelevant_line_item = LineItem.create!(task: irrelevant_task, invoice: irrelevant_invoice)
135
+ line_item = LineItem.create!(name: 'relevant', task: task, invoice: invoice)
136
+ irrelevant_line_item = LineItem.create!(name: 'relevant', task: irrelevant_task, invoice: irrelevant_invoice)
99
137
 
100
138
  _work_detail = WorkDetail.create!(line_item: line_item, name: 'relevant')
101
139
  _irrelevant_work_detail = WorkDetail.create!(line_item: irrelevant_line_item, name: 'irrelevant')
102
140
 
141
+ blob = Blob.create!()
142
+ _relevant_attachment = Attachment.create!(name: 'relevant', blob: blob, record: project)
143
+ _irrelevant_attachment = Attachment.create!(name: 'irrelevant', blob: blob, record: irrelevant_project)
144
+
103
145
  result = Project.where_exists(:invoices, name: 'relevant')
104
146
 
105
147
  assert_equal 1, result.length
@@ -124,5 +166,28 @@ class HasManyThroughTest < Minitest::Test
124
166
 
125
167
  assert_equal 1, result.length
126
168
  assert_equal irrelevant_project.id, result.first.id
169
+
170
+ result = Task.where_exists(:scoped_line_items)
171
+
172
+ assert_equal 2, result.length
173
+
174
+ result = Project.where_exists(:relevant_blobs)
175
+
176
+ assert_equal 1, result.length
177
+ assert_equal project.id, result.first.id
178
+
179
+ result = Project.where_not_exists(:relevant_blobs)
180
+
181
+ assert_equal 1, result.length
182
+ assert_equal irrelevant_project.id, result.first.id
183
+
184
+ result = Project.where_exists(:blobs)
185
+
186
+ assert_equal 2, result.length
187
+
188
+ result = Project.where_not_exists(:blobs)
189
+
190
+ assert_equal 0, result.length
191
+
127
192
  end
128
193
  end
data/test/test_helper.rb CHANGED
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: where_exists
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eugene Zolotarev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-25 00:00:00.000000000 Z
11
+ date: 2021-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '4.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '6'
22
+ version: '6.2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,21 +29,21 @@ dependencies:
29
29
  version: '4.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '6'
32
+ version: '6.2'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: sqlite3
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '1.3'
39
+ version: '1.4'
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '1.3'
46
+ version: '1.4'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: minitest
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,7 @@ extensions: []
94
94
  extra_rdoc_files: []
95
95
  files:
96
96
  - MIT-LICENSE
97
+ - README.markdown
97
98
  - Rakefile
98
99
  - lib/where_exists.rb
99
100
  - lib/where_exists/version.rb
@@ -125,18 +126,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
126
  - !ruby/object:Gem::Version
126
127
  version: '0'
127
128
  requirements: []
128
- rubyforge_project:
129
- rubygems_version: 2.7.7
129
+ rubygems_version: 3.1.2
130
130
  signing_key:
131
131
  specification_version: 4
132
132
  summary: "#where_exists extension of ActiveRecord"
133
133
  test_files:
134
+ - test/has_and_belongs_to_many.rb
134
135
  - test/belongs_to_polymorphic_test.rb
135
- - test/belongs_to_test.rb
136
+ - test/has_many_through_test.rb
136
137
  - test/db/test.db
137
- - test/documentation_test.rb
138
- - test/has_and_belongs_to_many.rb
138
+ - test/test_helper.rb
139
139
  - test/has_many_polymorphic_test.rb
140
140
  - test/has_many_test.rb
141
- - test/has_many_through_test.rb
142
- - test/test_helper.rb
141
+ - test/documentation_test.rb
142
+ - test/belongs_to_test.rb