where_exists 1.1.5 → 1.2.0

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
- SHA1:
3
- metadata.gz: 04a754bdba0efe110c9e700feda620618a9f0f35
4
- data.tar.gz: 194d5488d6809663b0be8eaba9f26f0ffb224aeb
2
+ SHA256:
3
+ metadata.gz: 8fe61ef5f7b676e2d9b3704b34f8101e7e84157060f2b11d56db8e99027765a9
4
+ data.tar.gz: e8de623c18fe2df6c50c809b6bf0b551eb66bf7ae2a36e0ca65cc18a44470b79
5
5
  SHA512:
6
- metadata.gz: 6a2d63d72377b8f062eee34f2bc228309ebe4201d88b319dda9b9316aa2e84de39d117d21c057f494b2ed2c79f12a2f90aae6ea39a1a168c7e3aeab02b5dfb8a
7
- data.tar.gz: 952e052725851bb53f8ade54661ec65d35bdf536836ce589c85bdd29c3cceba7d0dc1347d9bd2aa97d656dc85a01db38f6b0a2d756f535812e06cc63f3a90fea
6
+ metadata.gz: 9f4b7fe34573309d53e2e81255782a2d4f3dbfa190c9e975d5cc9eb6750f0b118d448401002786862932ed1fcda19cda6ee3387d5a7a036b7594d841872c907e
7
+ data.tar.gz: 389a33011c215ad3373388d20f5b49f186b80458dc18afee829c9ddf370d84b517e8ad16df300e041198b192f2884f9a4217cd6677179f7999591f5d74580f16
data/lib/where_exists.rb CHANGED
@@ -61,7 +61,9 @@ module WhereExists
61
61
  association_scope = association.scope
62
62
 
63
63
  if polymorphic
64
- associated_models = self.select("DISTINCT #{connection.quote_column_name(association.foreign_type)}").pluck(association.foreign_type).map(&:constantize)
64
+ associated_models = self.select("DISTINCT #{connection.quote_column_name(association.foreign_type)}").
65
+ where("#{connection.quote_column_name(association.foreign_type)} IS NOT NULL").pluck(association.foreign_type).
66
+ uniq.map(&:classify).map(&:constantize)
65
67
  else
66
68
  associated_models = [association.klass]
67
69
  end
@@ -82,8 +84,10 @@ module WhereExists
82
84
  query = query.instance_exec(&association_scope)
83
85
  end
84
86
  if polymorphic
85
- other_type = connection.quote(associated_model.name)
86
- query = query.where("#{self_type} = #{other_type}")
87
+ other_types = [associated_model.name, associated_model.table_name]
88
+ other_types << associated_model.polymorphic_name if associated_model.respond_to?(:polymorphic_name)
89
+
90
+ query = query.where("#{self_type} IN (?)", other_types.uniq)
87
91
  end
88
92
  queries.push query
89
93
  end
@@ -129,8 +133,14 @@ module WhereExists
129
133
 
130
134
  if association.options[:as]
131
135
  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}")
136
+ class_values = [self.name, self.table_name]
137
+ class_values << self.polymorphic_name if associated_model.respond_to?(:polymorphic_name)
138
+
139
+ result = result.where("#{other_types} IN (?)", class_values.uniq)
140
+ end
141
+
142
+ if association_scope
143
+ result = result.instance_exec(&association_scope)
134
144
  end
135
145
 
136
146
  if next_association[:association]
@@ -141,10 +151,6 @@ module WhereExists
141
151
  result = result.where(*where_parameters)
142
152
  end
143
153
 
144
- if association_scope
145
- result = result.instance_exec(&association_scope)
146
- end
147
-
148
154
  [result]
149
155
  end
150
156
 
@@ -155,11 +161,9 @@ module WhereExists
155
161
 
156
162
  primary_key = association.options[:primary_key] || self.primary_key
157
163
 
158
- join_table = [self.table_name, associated_model.table_name].sort.join("_")
159
-
160
164
  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")
165
+ join_ids = quote_table_and_column_name(association.join_table, association.foreign_key)
166
+ associated_join_ids = quote_table_and_column_name(association.join_table, association.association_foreign_key)
163
167
  associated_ids = quote_table_and_column_name(associated_model.table_name, associated_model.primary_key)
164
168
 
165
169
  result =
@@ -167,7 +171,7 @@ module WhereExists
167
171
  select("1").
168
172
  joins(
169
173
  <<-SQL
170
- INNER JOIN #{connection.quote_table_name(join_table)}
174
+ INNER JOIN #{connection.quote_table_name(association.join_table)}
171
175
  ON #{associated_ids} = #{associated_join_ids}
172
176
  SQL
173
177
  ).
@@ -1,3 +1,3 @@
1
1
  module WhereExists
2
- VERSION = "1.1.5"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -60,4 +60,21 @@ 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_table_name_based_lookup
65
+ first_entity = FirstPolymorphicEntity.create!
66
+ second_entity = SecondPolymorphicEntity.create! id: first_entity.id + 1
67
+
68
+ first_child = BelongsToPolymorphicChild.create!(polymorphic_entity_id: first_entity.id, polymorphic_entity_type: first_entity.class.table_name)
69
+ second_child = BelongsToPolymorphicChild.create!(polymorphic_entity_id: second_entity.id, polymorphic_entity_type: second_entity.class.table_name)
70
+ orphaned_child = BelongsToPolymorphicChild.create!(polymorphic_entity_id: second_entity.id, polymorphic_entity_type: first_entity.class.table_name)
71
+
72
+ result = BelongsToPolymorphicChild.where_exists(:polymorphic_entity)
73
+ assert_equal 2, result.length
74
+ assert_equal [first_child, second_child].map(&:id).sort, result.map(&:id).sort
75
+
76
+ result = BelongsToPolymorphicChild.where_not_exists(:polymorphic_entity)
77
+ assert_equal 1, result.length
78
+ assert_equal [orphaned_child].map(&:id).sort, result.map(&:id).sort
79
+ end
63
80
  end
data/test/db/test.db CHANGED
Binary file
@@ -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
@@ -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
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.5
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eugene Zolotarev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-15 00:00:00.000000000 Z
11
+ date: 2019-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -125,8 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubyforge_project:
129
- rubygems_version: 2.6.11
128
+ rubygems_version: 3.0.2
130
129
  signing_key:
131
130
  specification_version: 4
132
131
  summary: "#where_exists extension of ActiveRecord"