where_exists 1.1.5 → 1.2.0

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