torque-postgresql 1.1.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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