where_exists 1.1.1 → 1.1.2

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: '09bda87b615e0962be005bda294cc0227e1d76ce'
4
- data.tar.gz: 4cb809c77b97dfaad246f1a90be28e75cc2bb5a2
2
+ SHA256:
3
+ metadata.gz: 6f1f20fb282b67f60382f0d48c47f506623c3941e6d003b07b217eaa42291b9d
4
+ data.tar.gz: a4d5235b9373ad42c223eb439a611da125cc35ab7107ff6cf0b4c045bbd4f29e
5
5
  SHA512:
6
- metadata.gz: b4ca719701af78dfc661a35cb815adf49d1206072f0fa3db2af6a411677f7394ad19547706f0f14265462955231847922821732caa9c64ca4e22186cefeb1127
7
- data.tar.gz: 6e147f5238c544d0691cfb982d38cc560b5baf6a97d82f5529b94cafed6683c9e1a0b7706b6d967e2cc0913b905906c0d3192d64e064407f60774cd6c309f94c
6
+ metadata.gz: dbffcaf8b56b8aaf0c4c6e18586e41fba2ece5a4373f5adb6f0d5e42aee574eaf8d2c1cfd60842fd07d8a633c14f597bc65a0ae78742fb8b03f75f5a8226a94b
7
+ data.tar.gz: 178fa0742ea16a40d0809d344cc42750a39157e24e2e4df40bb34148967d53b035248a093d4e82e890e219577fd618916fade84b56239d41da656623c63a3b3a
File without changes
data/Rakefile CHANGED
@@ -25,7 +25,7 @@ Rake::TestTask.new(:test) do |t|
25
25
  t.libs << 'lib'
26
26
  t.libs << 'test'
27
27
  t.pattern = 'test/**/*_test.rb'
28
- t.verbose = false
28
+ t.verbose = true
29
29
  end
30
30
 
31
31
 
@@ -12,10 +12,22 @@ module WhereExists
12
12
  protected
13
13
 
14
14
  def where_exists_or_not_exists(does_exist, association_name, where_parameters)
15
+ queries_sql = build_exists_string(association_name, *where_parameters)
16
+
17
+ if does_exist
18
+ not_string = ""
19
+ else
20
+ not_string = "NOT "
21
+ end
22
+
23
+ self.where("#{not_string}(#{queries_sql})")
24
+ end
25
+
26
+ def build_exists_string(association_name, *where_parameters)
15
27
  association = self.reflect_on_association(association_name)
16
28
 
17
29
  unless association
18
- raise ArgumentError.new("where_exists: association #{association_name.inspect} not found on #{self.name}")
30
+ raise ArgumentError.new("where_exists: association - #{association_name} - #{association_name.inspect} not found on #{self.name}")
19
31
  end
20
32
 
21
33
  case association.macro
@@ -34,16 +46,7 @@ module WhereExists
34
46
  end
35
47
  raise ArgumentError.new("where_exists: not supported association – #{inspection}")
36
48
  end
37
-
38
- if does_exist
39
- not_string = ""
40
- else
41
- not_string = "NOT "
42
- end
43
-
44
49
  queries_sql = queries.map { |query| "EXISTS (" + query.to_sql + ")" }.join(" OR ")
45
-
46
- self.where("#{not_string}(#{queries_sql})")
47
50
  end
48
51
 
49
52
  def where_exists_for_belongs_to_query(association, where_parameters)
@@ -82,16 +85,34 @@ module WhereExists
82
85
  queries
83
86
  end
84
87
 
85
- def where_exists_for_has_many_query(association, where_parameters)
86
- through = association.options[:through].present?
87
-
88
- association_scope = association.scope
89
-
90
- if through
91
- next_association = association.source_reflection
88
+ def where_exists_for_has_many_query(association, where_parameters, next_association = {})
89
+ if association.through_reflection
90
+ raise ArgumentError.new(association) unless association.source_reflection
91
+ next_association = {
92
+ association: association.source_reflection,
93
+ params: where_parameters,
94
+ next_association: next_association
95
+ }
92
96
  association = association.through_reflection
97
+
98
+ case association.macro
99
+ when :has_many, :has_one
100
+ return where_exists_for_has_many_query(association, {}, next_association)
101
+ when :has_and_belongs_to_many
102
+ return where_exists_for_habtm_query(association, {}, next_association)
103
+ else
104
+ inspection = nil
105
+ begin
106
+ inspection = association.macros.inspect
107
+ rescue
108
+ inspection = association.macro
109
+ end
110
+ raise ArgumentError.new("where_exists: not supported association – #{inspection}")
111
+ end
93
112
  end
94
113
 
114
+ association_scope = next_association[:scope] || association.scope
115
+
95
116
  associated_model = association.klass
96
117
  primary_key = association.options[:primary_key] || self.primary_key
97
118
 
@@ -106,13 +127,14 @@ module WhereExists
106
127
  result = result.where("#{other_types} = #{self_class}")
107
128
  end
108
129
 
109
- if through
110
- return [result.where_exists(next_association.name, *where_parameters)]
130
+ if next_association[:association]
131
+ return loop_nested_association(result, next_association)
111
132
  end
112
133
 
113
134
  if where_parameters != []
114
135
  result = result.where(*where_parameters)
115
136
  end
137
+
116
138
  if association_scope
117
139
  result = result.instance_exec(&association_scope)
118
140
  end
@@ -120,7 +142,7 @@ module WhereExists
120
142
  [result]
121
143
  end
122
144
 
123
- def where_exists_for_habtm_query(association, where_parameters)
145
+ def where_exists_for_habtm_query(association, where_parameters, next_association = {})
124
146
  association_scope = association.scope
125
147
 
126
148
  associated_model = association.klass
@@ -145,9 +167,14 @@ module WhereExists
145
167
  ).
146
168
  where("#{join_ids} = #{self_ids}")
147
169
 
170
+ if next_association[:association]
171
+ return loop_nested_association(result, next_association)
172
+ end
173
+
148
174
  if where_parameters != []
149
175
  result = result.where(*where_parameters)
150
176
  end
177
+
151
178
  if association_scope
152
179
  result = result.instance_exec(&association_scope)
153
180
  end
@@ -155,6 +182,28 @@ module WhereExists
155
182
  [result]
156
183
  end
157
184
 
185
+ def loop_nested_association(query, next_association = {}, nested = false)
186
+ str = query.klass.build_exists_string(
187
+ next_association[:association].name,
188
+ *[
189
+ *next_association[:params]
190
+ ],
191
+ )
192
+
193
+ if next_association[:next_association] && next_association[:next_association][:association]
194
+ subq = str.match(/\([^\(\)]+\)/mi)[0]
195
+ str.sub!(subq,
196
+ "(#{subq} AND (#{loop_nested_association(
197
+ next_association[:association],
198
+ next_association[:next_association],
199
+ true
200
+ )}))"
201
+ )
202
+ end
203
+
204
+ nested ? str : [query.where(str)]
205
+ end
206
+
158
207
  def quote_table_and_column_name(table_name, column_name)
159
208
  connection.quote_table_name(table_name) + '.' + connection.quote_column_name(column_name)
160
209
  end
@@ -1,3 +1,3 @@
1
1
  module WhereExists
2
- VERSION = "1.1.1"
2
+ VERSION = "1.1.2"
3
3
  end
File without changes
File without changes
Binary file
File without changes
File without changes
File without changes
File without changes
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require_relative 'test_helper'
2
2
 
3
3
  ActiveRecord::Migration.create_table :projects, :force => true do |t|
4
4
  t.string :name
@@ -15,6 +15,11 @@ ActiveRecord::Migration.create_table :line_items, :force => true do |t|
15
15
  t.integer :task_id
16
16
  end
17
17
 
18
+ ActiveRecord::Migration.create_table :work_details, :force => true do |t|
19
+ t.string :name
20
+ t.integer :line_item_id
21
+ end
22
+
18
23
  ActiveRecord::Migration.create_table :invoices, :force => true do |t|
19
24
  t.string :name
20
25
  end
@@ -23,6 +28,7 @@ class Project < ActiveRecord::Base
23
28
  has_many :tasks
24
29
  has_many :invoices, :through => :tasks
25
30
  has_many :project_line_items, :through => :tasks, :source => :line_items
31
+ has_many :work_details, :through => :project_line_items
26
32
  end
27
33
 
28
34
  class Task < ActiveRecord::Base
@@ -35,6 +41,11 @@ end
35
41
  class LineItem < ActiveRecord::Base
36
42
  belongs_to :invoice
37
43
  belongs_to :task
44
+ has_many :work_details
45
+ end
46
+
47
+ class WorkDetail < ActiveRecord::Base
48
+ belongs_to :line_item
38
49
  end
39
50
 
40
51
  class Invoice < ActiveRecord::Base
@@ -50,6 +61,8 @@ class HasManyThroughTest < Minitest::Test
50
61
  end
51
62
 
52
63
  def test_one_level_through
64
+ ActiveRecord::Base.descendants.each(&:delete_all)
65
+
53
66
  project = Project.create!
54
67
  irrelevant_project = Project.create!
55
68
 
@@ -70,8 +83,10 @@ class HasManyThroughTest < Minitest::Test
70
83
  end
71
84
 
72
85
  def test_deep_through
73
- project = Project.create!
74
- irrelevant_project = Project.create!
86
+ ActiveRecord::Base.descendants.each(&:delete_all)
87
+
88
+ project = Project.create! name: 'relevant'
89
+ irrelevant_project = Project.create! name: 'irrelevant'
75
90
 
76
91
  task = Task.create!(project: project)
77
92
  irrelevant_task = Task.create!(project: irrelevant_project)
@@ -79,8 +94,11 @@ class HasManyThroughTest < Minitest::Test
79
94
  invoice = Invoice.create!(name: 'relevant')
80
95
  irrelevant_invoice = Invoice.create!(name: 'irrelevant')
81
96
 
82
- _line_item = LineItem.create!(task: task, invoice: invoice)
83
- _irrelevant_line_item = LineItem.create!(task: irrelevant_task, invoice: irrelevant_invoice)
97
+ line_item = LineItem.create!(task: task, invoice: invoice)
98
+ irrelevant_line_item = LineItem.create!(task: irrelevant_task, invoice: irrelevant_invoice)
99
+
100
+ _work_detail = WorkDetail.create!(line_item: line_item, name: 'relevant')
101
+ _irrelevant_work_detail = WorkDetail.create!(line_item: irrelevant_line_item, name: 'irrelevant')
84
102
 
85
103
  result = Project.where_exists(:invoices, name: 'relevant')
86
104
 
@@ -96,5 +114,15 @@ class HasManyThroughTest < Minitest::Test
96
114
 
97
115
  assert_equal 1, result.length
98
116
  assert_equal irrelevant_project.id, result.first.id
117
+
118
+ result = Project.where_exists(:work_details, name: 'relevant')
119
+
120
+ assert_equal 1, result.length
121
+ assert_equal project.id, result.first.id
122
+
123
+ result = Project.where_not_exists(:work_details, name: 'relevant')
124
+
125
+ assert_equal 1, result.length
126
+ assert_equal irrelevant_project.id, result.first.id
99
127
  end
100
128
  end
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.1
4
+ version: 1.1.2
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-01-06 00:00:00.000000000 Z
11
+ date: 2018-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -48,16 +48,44 @@ dependencies:
48
48
  name: minitest
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: '5.10'
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ">="
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '5.10'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '12.3'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '12.3'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rdoc
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '6.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
59
87
  - !ruby/object:Gem::Version
60
- version: '0'
88
+ version: '6.0'
61
89
  description: Rails way to harness the power of SQL "EXISTS" statement
62
90
  email:
63
91
  - eugzol@gmail.com
@@ -98,17 +126,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
126
  version: '0'
99
127
  requirements: []
100
128
  rubyforge_project:
101
- rubygems_version: 2.6.11
129
+ rubygems_version: 2.7.7
102
130
  signing_key:
103
131
  specification_version: 4
104
132
  summary: "#where_exists extension of ActiveRecord"
105
133
  test_files:
106
- - test/db/test.db
134
+ - test/belongs_to_polymorphic_test.rb
107
135
  - test/belongs_to_test.rb
136
+ - test/db/test.db
108
137
  - test/documentation_test.rb
109
- - test/test_helper.rb
110
- - test/has_many_polymorphic_test.rb
111
138
  - test/has_and_belongs_to_many.rb
112
- - test/belongs_to_polymorphic_test.rb
139
+ - test/has_many_polymorphic_test.rb
113
140
  - test/has_many_test.rb
114
141
  - test/has_many_through_test.rb
142
+ - test/test_helper.rb