torque-postgresql 1.1.8 → 2.0.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/torque/postgresql.rb +0 -2
  3. data/lib/torque/postgresql/adapter.rb +0 -1
  4. data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
  5. data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
  6. data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
  7. data/lib/torque/postgresql/adapter/schema_dumper.rb +71 -11
  8. data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
  9. data/lib/torque/postgresql/associations.rb +0 -3
  10. data/lib/torque/postgresql/associations/association.rb +0 -4
  11. data/lib/torque/postgresql/associations/association_scope.rb +18 -60
  12. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +12 -15
  13. data/lib/torque/postgresql/associations/preloader.rb +0 -32
  14. data/lib/torque/postgresql/associations/preloader/association.rb +13 -10
  15. data/lib/torque/postgresql/autosave_association.rb +4 -4
  16. data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
  17. data/lib/torque/postgresql/coder.rb +1 -2
  18. data/lib/torque/postgresql/config.rb +0 -6
  19. data/lib/torque/postgresql/inheritance.rb +13 -17
  20. data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
  21. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -38
  22. data/lib/torque/postgresql/relation.rb +11 -16
  23. data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
  24. data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
  25. data/lib/torque/postgresql/version.rb +1 -1
  26. data/spec/en.yml +19 -0
  27. data/spec/factories/authors.rb +6 -0
  28. data/spec/factories/comments.rb +13 -0
  29. data/spec/factories/posts.rb +6 -0
  30. data/spec/factories/tags.rb +5 -0
  31. data/spec/factories/texts.rb +5 -0
  32. data/spec/factories/users.rb +6 -0
  33. data/spec/factories/videos.rb +5 -0
  34. data/spec/mocks/cache_query.rb +16 -0
  35. data/spec/mocks/create_table.rb +35 -0
  36. data/spec/models/activity.rb +3 -0
  37. data/spec/models/activity_book.rb +4 -0
  38. data/spec/models/activity_post.rb +7 -0
  39. data/spec/models/activity_post/sample.rb +4 -0
  40. data/spec/models/author.rb +4 -0
  41. data/spec/models/author_journalist.rb +4 -0
  42. data/spec/models/comment.rb +3 -0
  43. data/spec/models/course.rb +2 -0
  44. data/spec/models/geometry.rb +2 -0
  45. data/spec/models/guest_comment.rb +4 -0
  46. data/spec/models/post.rb +6 -0
  47. data/spec/models/tag.rb +2 -0
  48. data/spec/models/text.rb +2 -0
  49. data/spec/models/time_keeper.rb +2 -0
  50. data/spec/models/user.rb +8 -0
  51. data/spec/models/video.rb +2 -0
  52. data/spec/schema.rb +141 -0
  53. data/spec/spec_helper.rb +59 -0
  54. data/spec/tests/arel_spec.rb +72 -0
  55. data/spec/tests/auxiliary_statement_spec.rb +593 -0
  56. data/spec/tests/belongs_to_many_spec.rb +240 -0
  57. data/spec/tests/coder_spec.rb +367 -0
  58. data/spec/tests/collector_spec.rb +59 -0
  59. data/spec/tests/distinct_on_spec.rb +65 -0
  60. data/spec/tests/enum_set_spec.rb +306 -0
  61. data/spec/tests/enum_spec.rb +621 -0
  62. data/spec/tests/geometric_builder_spec.rb +221 -0
  63. data/spec/tests/has_many_spec.rb +390 -0
  64. data/spec/tests/interval_spec.rb +167 -0
  65. data/spec/tests/lazy_spec.rb +24 -0
  66. data/spec/tests/period_spec.rb +954 -0
  67. data/spec/tests/quoting_spec.rb +24 -0
  68. data/spec/tests/range_spec.rb +36 -0
  69. data/spec/tests/relation_spec.rb +57 -0
  70. data/spec/tests/table_inheritance_spec.rb +403 -0
  71. metadata +103 -15
  72. data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
  73. data/lib/torque/postgresql/schema_dumper.rb +0 -101
@@ -0,0 +1,593 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'AuxiliaryStatement' do
4
+ before :each do
5
+ User.auxiliary_statements_list = {}
6
+ end
7
+
8
+ context 'on relation' do
9
+ let(:klass) { User }
10
+ let(:true_value) { 'TRUE' }
11
+ subject { klass.unscoped }
12
+
13
+ it 'has its method' do
14
+ expect(subject).to respond_to(:with)
15
+ end
16
+
17
+ it 'can perform simple queries' do
18
+ klass.send(:auxiliary_statement, :comments) do |cte|
19
+ cte.query Comment.all
20
+ cte.attributes content: :comment_content
21
+ end
22
+
23
+ result = 'WITH "comments" AS'
24
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
25
+ result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
26
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
27
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
28
+ end
29
+
30
+ it 'can perform more complex queries' do
31
+ klass.send(:auxiliary_statement, :comments) do |cte|
32
+ cte.query Comment.distinct_on(:user_id).order(:user_id, id: :desc)
33
+ cte.attributes content: :last_comment
34
+ end
35
+
36
+ result = 'WITH "comments" AS (SELECT DISTINCT ON ( "comments"."user_id" )'
37
+ result << ' "comments"."user_id", "comments"."content" AS last_comment'
38
+ result << ' FROM "comments" ORDER BY "comments"."user_id" ASC,'
39
+ result << ' "comments"."id" DESC) SELECT "users".*,'
40
+ result << ' "comments"."last_comment" FROM "users" INNER JOIN "comments"'
41
+ result << ' ON "comments"."user_id" = "users"."id"'
42
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
43
+ end
44
+
45
+ it 'accepts extra select columns' do
46
+ klass.send(:auxiliary_statement, :comments) do |cte|
47
+ cte.query Comment.all
48
+ cte.attributes content: :comment_content
49
+ end
50
+
51
+ result = 'WITH "comments" AS'
52
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content, "comments"."slug" AS comment_slug FROM "comments")'
53
+ result << ' SELECT "users".*, "comments"."comment_content", "comments"."comment_slug" FROM "users"'
54
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
55
+ expect(subject.with(:comments, select: {slug: :comment_slug}).arel.to_sql).to eql(result)
56
+ end
57
+
58
+ it 'accepts extra join columns' do
59
+ klass.send(:auxiliary_statement, :comments) do |cte|
60
+ cte.query Comment.all
61
+ cte.attributes content: :comment_content
62
+ end
63
+
64
+ result = 'WITH "comments" AS'
65
+ result << ' (SELECT "comments"."user_id", "comments"."active", "comments"."content" AS comment_content FROM "comments")'
66
+ result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
67
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id" AND "comments"."active" = "users"."active"'
68
+ expect(subject.with(:comments, join: {active: :active}).arel.to_sql).to eql(result)
69
+ end
70
+
71
+ it 'accepts extra conditions' do
72
+ klass.send(:auxiliary_statement, :comments) do |cte|
73
+ cte.query Comment.all
74
+ cte.attributes content: :comment_content
75
+ end
76
+
77
+ result = 'WITH "comments" AS'
78
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content'
79
+ result << ' FROM "comments" WHERE "comments"."active" = $1)'
80
+ result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
81
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
82
+ expect(subject.with(:comments, where: {active: true}).arel.to_sql).to eql(result)
83
+ end
84
+
85
+ it 'accepts scopes from both sides' do
86
+ klass.send(:auxiliary_statement, :comments) do |cte|
87
+ cte.query Comment.where(id: 1).all
88
+ cte.attributes content: :comment_content
89
+ end
90
+
91
+ query = subject.where(id: 2).with(:comments)
92
+
93
+ result = 'WITH "comments" AS'
94
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments"'
95
+ result << ' WHERE "comments"."id" = $1)'
96
+ result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
97
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
98
+ result << ' WHERE "users"."id" = $2'
99
+
100
+ expect(query.arel.to_sql).to eql(result)
101
+ expect(query.send(:bound_attributes).map(&:value_before_type_cast)).to eql([1, 2])
102
+ end
103
+
104
+ it 'accepts string as attributes' do
105
+ klass.send(:auxiliary_statement, :comments) do |cte|
106
+ cte.query Comment.all
107
+ cte.attributes sql('MAX(id)') => :comment_id
108
+ end
109
+
110
+ result = 'WITH "comments" AS'
111
+ result << ' (SELECT "comments"."user_id", MAX(id) AS comment_id FROM "comments")'
112
+ result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
113
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
114
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
115
+ end
116
+
117
+ it 'accepts complex string as attributes' do
118
+ klass.send(:auxiliary_statement, :comments) do |cte|
119
+ cte.query Comment.all
120
+ cte.attributes sql('ROW_NUMBER() OVER (PARTITION BY ORDER BY "comments"."id")') => :comment_id
121
+ end
122
+
123
+ result = 'WITH "comments" AS'
124
+ result << ' (SELECT "comments"."user_id", ROW_NUMBER() OVER (PARTITION BY ORDER BY "comments"."id") AS comment_id FROM "comments")'
125
+ result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
126
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
127
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
128
+ end
129
+
130
+ it 'accepts arel attribute as attributes' do
131
+ klass.send(:auxiliary_statement, :comments) do |cte|
132
+ cte.query Comment.all
133
+ cte.attributes col(:id).minimum => :comment_id
134
+ end
135
+
136
+ result = 'WITH "comments" AS'
137
+ result << ' (SELECT "comments"."user_id", MIN("comments"."id") AS comment_id FROM "comments")'
138
+ result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
139
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
140
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
141
+ end
142
+
143
+ it 'accepts custom join properties' do
144
+ klass.send(:auxiliary_statement, :comments) do |cte|
145
+ cte.query Comment.all
146
+ cte.attributes content: :comment_content
147
+ cte.join name: :id, 'a.col' => :col
148
+ end
149
+
150
+ result = 'WITH "comments" AS (SELECT "comments"."id", "comments"."col",'
151
+ result << ' "comments"."content" AS comment_content FROM "comments") SELECT "users".*,'
152
+ result << ' "comments"."comment_content" FROM "users" INNER JOIN "comments"'
153
+ result << ' ON "comments"."id" = "users"."name" AND "comments"."col" = "a"."col"'
154
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
155
+ end
156
+
157
+ it 'can perform other types of joins' do
158
+ klass.send(:auxiliary_statement, :comments) do |cte|
159
+ cte.query Comment.all
160
+ cte.attributes content: :comment_content
161
+ cte.join_type :left
162
+ end
163
+
164
+ result = 'WITH "comments" AS (SELECT "comments"."user_id",'
165
+ result << ' "comments"."content" AS comment_content FROM "comments") SELECT "users".*,'
166
+ result << ' "comments"."comment_content" FROM "users" LEFT OUTER JOIN "comments"'
167
+ result << ' ON "comments"."user_id" = "users"."id"'
168
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
169
+ end
170
+
171
+ it 'can manually define the association' do
172
+ klass.has_many :sample_comment, class_name: 'Comment', foreign_key: :a_user_id
173
+ klass.send(:auxiliary_statement, :comments) do |cte|
174
+ cte.query Comment.all
175
+ cte.through :sample_comment
176
+ cte.attributes content: :sample_content
177
+ end
178
+
179
+ result = 'WITH "comments" AS'
180
+ result << ' (SELECT "comments"."a_user_id", "comments"."content" AS sample_content FROM "comments")'
181
+ result << ' SELECT "users".*, "comments"."sample_content" FROM "users"'
182
+ result << ' INNER JOIN "comments" ON "comments"."a_user_id" = "users"."id"'
183
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
184
+ end
185
+
186
+ it 'accepts complex scopes from dependencies' do
187
+ klass.send(:auxiliary_statement, :comments1) do |cte|
188
+ cte.query Comment.where(id: 1).all
189
+ cte.attributes content: :comment_content1
190
+ end
191
+
192
+ klass.send(:auxiliary_statement, :comments2) do |cte|
193
+ cte.requires :comments1
194
+ cte.query Comment.where(id: 2).all
195
+ cte.attributes content: :comment_content2
196
+ end
197
+
198
+ query = subject.where(id: 3).with(:comments2)
199
+
200
+ result = 'WITH '
201
+ result << '"comments1" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content1 FROM "comments" WHERE "comments"."id" = $1), '
202
+ result << '"comments2" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content2 FROM "comments" WHERE "comments"."id" = $2)'
203
+ result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
204
+ result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
205
+ result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
206
+ result << ' WHERE "users"."id" = $3'
207
+
208
+ expect(query.arel.to_sql).to eql(result)
209
+ expect(query.send(:bound_attributes).map(&:value_before_type_cast)).to eql([1, 2, 3])
210
+ end
211
+
212
+ context 'with dependency' do
213
+ before :each do
214
+ klass.send(:auxiliary_statement, :comments1) do |cte|
215
+ cte.query Comment.all
216
+ cte.attributes content: :comment_content1
217
+ end
218
+
219
+ klass.send(:auxiliary_statement, :comments2) do |cte|
220
+ cte.requires :comments1
221
+ cte.query Comment.all
222
+ cte.attributes content: :comment_content2
223
+ end
224
+ end
225
+
226
+ it 'can requires another statement as dependency' do
227
+ result = 'WITH '
228
+ result << '"comments1" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content1 FROM "comments"), '
229
+ result << '"comments2" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content2 FROM "comments")'
230
+ result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
231
+ result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
232
+ result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
233
+ expect(subject.with(:comments2).arel.to_sql).to eql(result)
234
+ end
235
+
236
+ it 'can uses already already set dependent' do
237
+ result = 'WITH '
238
+ result << '"comments1" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content1 FROM "comments"), '
239
+ result << '"comments2" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content2 FROM "comments")'
240
+ result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
241
+ result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
242
+ result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
243
+ expect(subject.with(:comments1, :comments2).arel.to_sql).to eql(result)
244
+ end
245
+
246
+ it 'raises an error if the dependent does not exist' do
247
+ klass.send(:auxiliary_statement, :comments2) do |cte|
248
+ cte.requires :comments3
249
+ cte.query Comment.all
250
+ cte.attributes content: :comment_content2
251
+ end
252
+ expect{ subject.with(:comments2).arel.to_sql }.to raise_error(ArgumentError)
253
+ end
254
+ end
255
+
256
+ context 'query as string' do
257
+ it 'performs correctly' do
258
+ klass.send(:auxiliary_statement, :comments) do |cte|
259
+ cte.query :comments, 'SELECT * FROM comments'
260
+ cte.attributes content: :comment
261
+ cte.join id: :user_id
262
+ end
263
+
264
+ result = 'WITH "comments" AS (SELECT * FROM comments)'
265
+ result << ' SELECT "users".*, "comments"."comment" FROM "users"'
266
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
267
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
268
+ end
269
+
270
+ it 'accepts arguments to format the query' do
271
+ klass.send(:auxiliary_statement, :comments) do |cte|
272
+ cte.query :comments, 'SELECT * FROM comments WHERE active = %{active}'
273
+ cte.attributes content: :comment
274
+ cte.join id: :user_id
275
+ end
276
+
277
+ result = "WITH \"comments\" AS (SELECT * FROM comments WHERE active = #{true_value})"
278
+ result << ' SELECT "users".*, "comments"."comment" FROM "users"'
279
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
280
+ expect(subject.with(:comments, args: {active: true}).arel.to_sql).to eql(result)
281
+ end
282
+
283
+ it 'raises an error when join columns are not given' do
284
+ klass.send(:auxiliary_statement, :comments) do |cte|
285
+ cte.query :comments, 'SELECT * FROM comments'
286
+ cte.attributes content: :comment
287
+ end
288
+
289
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /join columns/)
290
+ end
291
+
292
+ it 'raises an error when not given the table name as first argument' do
293
+ klass.send(:auxiliary_statement, :comments) do |cte|
294
+ cte.query 'SELECT * FROM comments'
295
+ cte.attributes content: :comment
296
+ cte.join id: :user_id
297
+ end
298
+
299
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /table name/)
300
+ end
301
+ end
302
+
303
+ context 'query as proc' do
304
+ it 'performs correctly for result as relation' do
305
+ klass.send(:auxiliary_statement, :comments) do |cte|
306
+ cte.query :comments, -> { Comment.all }
307
+ cte.attributes content: :comment
308
+ cte.join id: :user_id
309
+ end
310
+
311
+ result = 'WITH "comments" AS'
312
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment FROM "comments")'
313
+ result << ' SELECT "users".*, "comments"."comment" FROM "users"'
314
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
315
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
316
+ end
317
+
318
+ it 'performs correctly for anything that has a call method' do
319
+ obj = Struct.new(:call, :arity).new('SELECT * FROM comments', 0)
320
+ klass.send(:auxiliary_statement, :comments) do |cte|
321
+ cte.query :comments, obj
322
+ cte.attributes content: :comment
323
+ cte.join id: :user_id
324
+ end
325
+
326
+ result = 'WITH "comments" AS (SELECT * FROM comments)'
327
+ result << ' SELECT "users".*, "comments"."comment" FROM "users"'
328
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
329
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
330
+ end
331
+
332
+ it 'performs correctly for result as string' do
333
+ klass.send(:auxiliary_statement, :comments) do |cte|
334
+ cte.query :comments, -> { 'SELECT * FROM comments' }
335
+ cte.attributes content: :comment
336
+ cte.join id: :user_id
337
+ end
338
+
339
+ result = 'WITH "comments" AS (SELECT * FROM comments)'
340
+ result << ' SELECT "users".*, "comments"."comment" FROM "users"'
341
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
342
+ expect(subject.with(:comments).arel.to_sql).to eql(result)
343
+ end
344
+
345
+ it 'performs correctly when the proc requires arguments' do
346
+ klass.send(:auxiliary_statement, :comments) do |cte|
347
+ cte.query :comments, -> (args) { Comment.where(id: args.id) }
348
+ cte.attributes content: :comment
349
+ cte.join id: :user_id
350
+ end
351
+
352
+ query = subject.with(:comments, args: {id: 1})
353
+
354
+ result = 'WITH "comments" AS'
355
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment'
356
+ result << ' FROM "comments" WHERE "comments"."id" = $1)'
357
+ result << ' SELECT "users".*, "comments"."comment" FROM "users"'
358
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
359
+
360
+ expect(query.arel.to_sql).to eql(result)
361
+ expect(query.send(:bound_attributes).map(&:value_before_type_cast)).to eql([1])
362
+ end
363
+
364
+ it 'raises an error when join columns are not given' do
365
+ klass.send(:auxiliary_statement, :comments) do |cte|
366
+ cte.query :comments, -> { Author.all }
367
+ cte.attributes content: :comment
368
+ end
369
+
370
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /join columns/)
371
+ end
372
+
373
+ it 'raises an error when not given the table name as first argument' do
374
+ klass.send(:auxiliary_statement, :comments) do |cte|
375
+ cte.query -> { Comment.all }
376
+ cte.attributes content: :comment
377
+ cte.join id: :user_id
378
+ end
379
+
380
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /table name/)
381
+ end
382
+
383
+ it 'raises an error when the result of the proc is an invalid type' do
384
+ klass.send(:auxiliary_statement, :comments) do |cte|
385
+ cte.query :comments, -> { false }
386
+ cte.attributes content: :comment
387
+ cte.join id: :user_id
388
+ end
389
+
390
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /query objects/)
391
+ end
392
+ end
393
+
394
+ context 'with inheritance' do
395
+ let(:base) { Activity }
396
+ let(:klass) { ActivityBook }
397
+
398
+ it 'accepts ancestors auxiliary statements' do
399
+ base.send(:auxiliary_statement, :authors) do |cte|
400
+ cte.query Author.all
401
+ cte.attributes name: :author_name
402
+ cte.join author_id: :id
403
+ end
404
+
405
+ result = 'WITH "authors" AS'
406
+ result << ' (SELECT "authors"."id", "authors"."name" AS author_name FROM "authors")'
407
+ result << ' SELECT "activity_books".*, "authors"."author_name" FROM "activity_books"'
408
+ result << ' INNER JOIN "authors" ON "authors"."id" = "activity_books"."author_id"'
409
+ expect(subject.with(:authors).arel.to_sql).to eql(result)
410
+ end
411
+
412
+ it 'can replace ancestors auxiliary statements' do
413
+ base.send(:auxiliary_statement, :authors) do |cte|
414
+ cte.query Author.all
415
+ cte.attributes name: :author_name
416
+ cte.join author_id: :id
417
+ end
418
+
419
+ klass.send(:auxiliary_statement, :authors) do |cte|
420
+ cte.query Author.all
421
+ cte.attributes type: :author_type
422
+ cte.join author_id: :id
423
+ end
424
+
425
+ result = 'WITH "authors" AS'
426
+ result << ' (SELECT "authors"."id", "authors"."type" AS author_type FROM "authors")'
427
+ result << ' SELECT "activity_books".*, "authors"."author_type" FROM "activity_books"'
428
+ result << ' INNER JOIN "authors" ON "authors"."id" = "activity_books"."author_id"'
429
+ expect(subject.with(:authors).arel.to_sql).to eql(result)
430
+ end
431
+
432
+ it 'raises an error when no class has the auxiliary statement' do
433
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError)
434
+ end
435
+ end
436
+
437
+ it 'works with count and does not add extra columns' do
438
+ klass.send(:auxiliary_statement, :comments) do |cte|
439
+ cte.query Comment.all
440
+ cte.attributes content: :comment_content
441
+ end
442
+
443
+ result = 'WITH "comments" AS'
444
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
445
+ result << ' SELECT COUNT(*) FROM "users"'
446
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
447
+
448
+ query = get_last_executed_query{ subject.with(:comments).count }
449
+ expect(query).to eql(result)
450
+ end
451
+
452
+ it 'works with sum and does not add extra columns' do
453
+ klass.send(:auxiliary_statement, :comments) do |cte|
454
+ cte.query Comment.all
455
+ cte.attributes id: :value
456
+ end
457
+
458
+ result = 'WITH "comments" AS'
459
+ result << ' (SELECT "comments"."user_id", "comments"."id" AS value FROM "comments")'
460
+ result << ' SELECT SUM("comments"."value") FROM "users"'
461
+ result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
462
+
463
+ query = get_last_executed_query{ subject.with(:comments).sum(comments: :value) }
464
+ expect(query).to eql(result)
465
+ end
466
+
467
+ it 'raises an error when using an invalid type of object as query' do
468
+ klass.send(:auxiliary_statement, :comments) do |cte|
469
+ cte.query :string, String
470
+ end
471
+
472
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /object types/)
473
+ end
474
+
475
+ it 'raises an error when traying to use a statement that is not defined' do
476
+ expect{ subject.with(:does_not_exist).arel.to_sql }.to raise_error(ArgumentError)
477
+ end
478
+
479
+ it 'raises an error when using an invalid type of join' do
480
+ klass.send(:auxiliary_statement, :comments) do |cte|
481
+ cte.query Comment.all
482
+ cte.attributes content: :comment_content
483
+ cte.join_type :invalid
484
+ end
485
+
486
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError)
487
+ end
488
+ end
489
+
490
+ context 'on model' do
491
+ subject { User }
492
+
493
+ it 'has its configurator' do
494
+ expect(subject.protected_methods).to include(:cte)
495
+ expect(subject.protected_methods).to include(:auxiliary_statement)
496
+ end
497
+
498
+ it 'allows configurate new auxiliary statements' do
499
+ subject.send(:auxiliary_statement, :cte1)
500
+ expect(subject.auxiliary_statements_list).to include(:cte1)
501
+ expect(subject.const_defined?('Cte1_AuxiliaryStatement')).to be_truthy
502
+ end
503
+
504
+ it 'has its query method' do
505
+ expect(subject).to respond_to(:with)
506
+ end
507
+
508
+ it 'returns a relation when using the method' do
509
+ subject.send(:auxiliary_statement, :comments) do |cte|
510
+ cte.query Comment.all
511
+ cte.attributes content: :comment_content
512
+ end
513
+ expect(subject.with(:comments)).to be_a(ActiveRecord::Relation)
514
+ end
515
+ end
516
+
517
+ context 'on external' do
518
+ let(:klass) { Torque::PostgreSQL::AuxiliaryStatement }
519
+ subject { User }
520
+
521
+ it 'has the external method available' do
522
+ expect(klass).to respond_to(:create)
523
+ end
524
+
525
+ it 'accepts simple auxiliary statement definition' do
526
+ sample = klass.create(Comment.all)
527
+ query = subject.with(sample, select: {content: :comment_content}).arel.to_sql
528
+
529
+ result = 'WITH "comment" AS'
530
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
531
+ result << ' SELECT "users".*, "comment"."comment_content" FROM "users"'
532
+ result << ' INNER JOIN "comment" ON "comment"."user_id" = "users"."id"'
533
+ expect(query).to eql(result)
534
+ end
535
+
536
+ it 'accepts a hash auxiliary statement definition' do
537
+ sample = klass.create(query: Comment.all, select: {content: :comment_content})
538
+ query = subject.with(sample).arel.to_sql
539
+
540
+ result = 'WITH "comment" AS'
541
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
542
+ result << ' SELECT "users".*, "comment"."comment_content" FROM "users"'
543
+ result << ' INNER JOIN "comment" ON "comment"."user_id" = "users"."id"'
544
+ expect(query).to eql(result)
545
+ end
546
+
547
+ it 'accepts a block when creating the auxiliary statement' do
548
+ sample = klass.create(:all_comments) do |cte|
549
+ cte.query Comment.all
550
+ cte.select content: :comment_content
551
+ end
552
+
553
+ result = 'WITH "all_comments" AS'
554
+ result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
555
+ result << ' SELECT "users".*, "all_comments"."comment_content" FROM "users"'
556
+ result << ' INNER JOIN "all_comments" ON "all_comments"."user_id" = "users"."id"'
557
+
558
+ query = subject.with(sample).arel.to_sql
559
+ expect(query).to eql(result)
560
+ end
561
+ end
562
+
563
+ context 'on settings' do
564
+ let(:base) { User }
565
+ let(:statement_klass) do
566
+ base.send(:auxiliary_statement, :statement)
567
+ base::Statement_AuxiliaryStatement
568
+ end
569
+
570
+ subject do
571
+ Torque::PostgreSQL::AuxiliaryStatement::Settings.new(base, statement_klass)
572
+ end
573
+
574
+ it 'has access to base' do
575
+ expect(subject.base).to eql(User)
576
+ expect(subject.base_table).to be_a(Arel::Table)
577
+ end
578
+
579
+ it 'has access to statement table' do
580
+ expect(subject.table_name).to eql('statement')
581
+ expect(subject.table).to be_a(Arel::Table)
582
+ end
583
+
584
+ it 'has access to the query arel table' do
585
+ subject.query Comment.all
586
+ expect(subject.query_table).to be_a(Arel::Table)
587
+ end
588
+
589
+ it 'raises an error when trying to access query table before defining the query' do
590
+ expect{ subject.with(:comments).arel.to_sql }.to raise_error(StandardError)
591
+ end
592
+ end
593
+ end