activerecord-refined 0.3.0 → 0.3.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 +4 -4
- data/.github/workflows/push_gem.yml +45 -0
- data/.github/workflows/test.yml +23 -0
- data/.gitignore +2 -0
- data/Gemfile +8 -0
- data/README.md +104 -8
- data/Rakefile +17 -0
- data/activerecord-refined.gemspec +1 -1
- data/examples/aggregations.rb +91 -0
- data/examples/complex_joins.rb +69 -0
- data/lib/active_record/refined/ast.rb +171 -54
- data/lib/active_record/refined.rb +1 -7
- data/lib/activerecord-refined/version.rb +1 -1
- data/test/test_block_syntax.rb +217 -72
- data/test/test_helper.rb +85 -2
- metadata +6 -2
data/test/test_block_syntax.rb
CHANGED
|
@@ -2,31 +2,31 @@ require_relative 'test_helper'
|
|
|
2
2
|
|
|
3
3
|
class TestBlockSyntax < Minitest::Test
|
|
4
4
|
def test_equal
|
|
5
|
-
|
|
5
|
+
assert_sql(/WHERE "users"."name" = 'matz'/, User.where { :name == 'matz' }.to_sql)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def test_not_equal
|
|
9
|
-
|
|
9
|
+
assert_sql(/WHERE "users"."name" != 'nobu'/, User.where { :name != 'nobu' }.to_sql)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def test_greater_than
|
|
13
|
-
|
|
13
|
+
assert_sql(/WHERE "users"."age" > 3/, User.where { :age > 3 }.to_sql)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def test_greater_than_or_equal
|
|
17
|
-
|
|
17
|
+
assert_sql(/WHERE "users"."age" >= 18/, User.where { :age >= 18 }.to_sql)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def test_less_than
|
|
21
|
-
|
|
21
|
+
assert_sql(/WHERE "users"."age" < 60/, User.where { :age < 60 }.to_sql)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def test_less_than_or_equal
|
|
25
|
-
|
|
25
|
+
assert_sql(/WHERE "users"."age" <= 35/, User.where { :age <= 35 }.to_sql)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def test_like
|
|
29
|
-
|
|
29
|
+
assert_sql(/WHERE "users"."name" LIKE 'tender%'/, User.where { :name.like?('tender%') }.to_sql)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def test_outside_of_where_block
|
|
@@ -34,149 +34,279 @@ class TestBlockSyntax < Minitest::Test
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def test_and
|
|
37
|
-
|
|
37
|
+
assert_sql(/WHERE "users"."name" = 'matz' AND "users"."age" > 18/,
|
|
38
38
|
User.where { (:name == 'matz') & (:age > 18) }.to_sql)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def test_or
|
|
42
|
-
|
|
42
|
+
assert_sql(/WHERE \(?\"users\".\"name\" = 'matz' OR \"users\".\"name\" = 'nobu'\)?/,
|
|
43
43
|
User.where { (:name == 'matz') | (:name == 'nobu') }.to_sql)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def test_not
|
|
47
|
-
|
|
47
|
+
assert_sql(/WHERE NOT \(?\"users\".\"name\" = 'matz'\)?/,
|
|
48
48
|
User.where { !(:name == 'matz') }.to_sql)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def test_complex_combination
|
|
52
52
|
sql = User.where { ((:name == 'matz') & (:age > 18)) | !(:name == 'nobu') }.to_sql
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
assert_sql(/"users"."name" = 'matz'/, sql)
|
|
54
|
+
assert_sql(/"users"."age" > 18/, sql)
|
|
55
|
+
assert_sql(/NOT/, sql)
|
|
56
|
+
assert_sql(/OR/, sql)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
User.where { :
|
|
59
|
+
def test_like_qualified
|
|
60
|
+
assert_sql(/WHERE "users"."name" LIKE 'tender%'/,
|
|
61
|
+
User.where { :users[:name].like?('tender%') }.to_sql)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def test_not_like
|
|
65
|
-
|
|
66
|
-
User.where {
|
|
65
|
+
assert_sql(/WHERE NOT \("users"."name" LIKE 'tender%'\)/,
|
|
66
|
+
User.where { !:name.like?('tender%') }.to_sql)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_start_with
|
|
70
|
+
assert_sql(/WHERE "users"."name" LIKE 'tender%' ESCAPE '\\'/,
|
|
71
|
+
User.where { :name.start_with?('tender') }.to_sql)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_end_with
|
|
75
|
+
assert_sql(/WHERE "users"."name" LIKE '%love' ESCAPE '\\'/,
|
|
76
|
+
User.where { :name.end_with?('love') }.to_sql)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_include
|
|
80
|
+
assert_sql(/WHERE "users"."name" LIKE '%der%' ESCAPE '\\'/,
|
|
81
|
+
User.where { :name.include?('der') }.to_sql)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def test_start_with_escapes_wildcards
|
|
85
|
+
assert_sql(/WHERE "users"."name" LIKE '100\\%\\_%' ESCAPE '\\'/,
|
|
86
|
+
User.where { :name.start_with?('100%_') }.to_sql)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_include_escapes_wildcards
|
|
90
|
+
assert_sql(/WHERE "users"."name" LIKE '%100\\%%' ESCAPE '\\'/,
|
|
91
|
+
User.where { :name.include?('100%') }.to_sql)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_regexp
|
|
95
|
+
skip_without_regexp_support
|
|
96
|
+
assert_sql(/WHERE "users"."name" #{regexp_operator} '\^ma'/,
|
|
97
|
+
User.where { :name =~ '^ma' }.to_sql)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_not_regexp
|
|
101
|
+
skip_without_regexp_support
|
|
102
|
+
assert_sql(/WHERE "users"."name" #{not_regexp_operator} '\^ma'/,
|
|
103
|
+
User.where { :name !~ '^ma' }.to_sql)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_regexp_qualified
|
|
107
|
+
skip_without_regexp_support
|
|
108
|
+
assert_sql(/WHERE "users"."name" #{regexp_operator} '\^ma'/,
|
|
109
|
+
User.where { :users[:name] =~ '^ma' }.to_sql)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_regexp_on_function
|
|
113
|
+
skip_without_regexp_support
|
|
114
|
+
assert_sql(/WHERE UPPER\("users"."name"\) #{regexp_operator} '\^MA'/,
|
|
115
|
+
User.where { upper(:name) =~ '^MA' }.to_sql)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_regexp_literal
|
|
119
|
+
skip_without_regexp_support
|
|
120
|
+
assert_sql(/WHERE "users"."name" #{regexp_operator} 'love\$'/,
|
|
121
|
+
User.where { :name =~ /love$/ }.to_sql)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Rejected while the block runs, so this holds on every adapter.
|
|
125
|
+
def test_regexp_literal_with_options_is_rejected
|
|
126
|
+
assert_raises(ArgumentError) { User.where { :name =~ /^ma/i } }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def test_between
|
|
130
|
+
assert_sql(/WHERE "users"."age" BETWEEN 18 AND 65/,
|
|
131
|
+
User.where { :age.between?(18, 65) }.to_sql)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_in_range
|
|
135
|
+
assert_sql(/WHERE "users"."age" BETWEEN 18 AND 65/,
|
|
136
|
+
User.where { :age.in?(18..65) }.to_sql)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_in_endless_range
|
|
140
|
+
assert_sql(/WHERE "users"."age" >= 18/,
|
|
141
|
+
User.where { :age.in?(18..) }.to_sql)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_in_exclusive_range
|
|
145
|
+
assert_sql(/WHERE "users"."age" >= 18 AND "users"."age" < 65/,
|
|
146
|
+
User.where { :age.in?(18...65) }.to_sql)
|
|
67
147
|
end
|
|
68
148
|
|
|
69
149
|
def test_not_between
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
User.where { :age != (18..65) }.to_sql)
|
|
150
|
+
assert_sql(/WHERE NOT \("users"."age" BETWEEN 18 AND 65\)/,
|
|
151
|
+
User.where { !:age.between?(18, 65) }.to_sql)
|
|
73
152
|
end
|
|
74
153
|
|
|
75
154
|
def test_is_null
|
|
76
|
-
|
|
155
|
+
assert_sql(/WHERE "users"."name" IS NULL/,
|
|
77
156
|
User.where { :name.null? }.to_sql)
|
|
78
157
|
end
|
|
79
158
|
|
|
80
159
|
def test_is_null_qualified
|
|
81
|
-
|
|
160
|
+
assert_sql(/WHERE "users"."name" IS NULL/,
|
|
82
161
|
User.where { :users[:name].null? }.to_sql)
|
|
83
162
|
end
|
|
84
163
|
|
|
85
164
|
def test_in
|
|
86
|
-
|
|
87
|
-
User.where { :age
|
|
165
|
+
assert_sql(/WHERE "users"."age" IN \(1, 2, 3\)/,
|
|
166
|
+
User.where { :age.in?([1, 2, 3]) }.to_sql)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def test_in_qualified
|
|
170
|
+
assert_sql(/WHERE "users"."age" IN \(1, 2, 3\)/,
|
|
171
|
+
User.where { :users[:age].in?([1, 2, 3]) }.to_sql)
|
|
88
172
|
end
|
|
89
173
|
|
|
90
174
|
def test_not_in
|
|
91
|
-
|
|
92
|
-
User.where {
|
|
175
|
+
assert_sql(/WHERE NOT \("users"."age" IN \(1, 2, 3\)\)/,
|
|
176
|
+
User.where { !:age.in?([1, 2, 3]) }.to_sql)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# == passes a Range or an Array through as a value rather than expanding it,
|
|
180
|
+
# so that it compares against a PostgreSQL range or array column. The SQL
|
|
181
|
+
# literal depends on the column type, so assert on the Arel node instead.
|
|
182
|
+
def test_equal_range_is_an_equality
|
|
183
|
+
node = ActiveRecord::Refined::AST::Comparison.new(:period, :==, 18..65).
|
|
184
|
+
to_arel(User.arel_table)
|
|
185
|
+
assert_instance_of(Arel::Nodes::Equality, node)
|
|
186
|
+
assert_equal(18..65, node.right.value)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def test_equal_array_is_an_equality
|
|
190
|
+
node = ActiveRecord::Refined::AST::Comparison.new(:tags, :==, [1, 2, 3]).
|
|
191
|
+
to_arel(User.arel_table)
|
|
192
|
+
assert_instance_of(Arel::Nodes::Equality, node)
|
|
193
|
+
assert_equal([1, 2, 3], node.right.value)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def test_not_equal_array_is_an_inequality
|
|
197
|
+
node = ActiveRecord::Refined::AST::Comparison.new(:tags, :!=, [1, 2, 3]).
|
|
198
|
+
to_arel(User.arel_table)
|
|
199
|
+
assert_instance_of(Arel::Nodes::NotEqual, node)
|
|
200
|
+
assert_equal([1, 2, 3], node.right.value)
|
|
93
201
|
end
|
|
94
202
|
|
|
95
203
|
def test_qualified_column
|
|
96
|
-
|
|
204
|
+
assert_sql(/WHERE "users"."name" = 'matz'/,
|
|
97
205
|
User.where { :users[:name] == 'matz' }.to_sql)
|
|
98
206
|
end
|
|
99
207
|
|
|
100
208
|
def test_column_to_column_comparison
|
|
101
209
|
sql = User.where { :users[:name] == :users[:age] }.to_sql
|
|
102
|
-
|
|
210
|
+
assert_sql(/"users"."name" = "users"."age"/, sql)
|
|
103
211
|
end
|
|
104
212
|
|
|
105
213
|
def test_joins_with_block
|
|
106
214
|
sql = Author.joins(:posts) { :posts[:author_id] == :authors[:id] }.to_sql
|
|
107
|
-
|
|
215
|
+
assert_sql(/INNER JOIN "posts" ON "posts"."author_id" = "authors"."id"/, sql)
|
|
108
216
|
end
|
|
109
217
|
|
|
110
218
|
def test_left_outer_joins_with_block
|
|
111
219
|
sql = Author.left_outer_joins(:posts) { :posts[:author_id] == :authors[:id] }.to_sql
|
|
112
|
-
|
|
220
|
+
assert_sql(/LEFT OUTER JOIN "posts" ON "posts"."author_id" = "authors"."id"/, sql)
|
|
113
221
|
end
|
|
114
222
|
|
|
115
223
|
def test_select_aggregate
|
|
116
|
-
|
|
224
|
+
assert_sql(/SELECT SUM\("users"."age"\)/,
|
|
117
225
|
User.select { :age.sum }.to_sql)
|
|
118
226
|
end
|
|
119
227
|
|
|
120
228
|
def test_select_aggregate_qualified
|
|
121
|
-
|
|
229
|
+
assert_sql(/SELECT COUNT\("users"."id"\)/,
|
|
122
230
|
User.select { :users[:id].count }.to_sql)
|
|
123
231
|
end
|
|
124
232
|
|
|
125
233
|
def test_select_average
|
|
126
|
-
|
|
234
|
+
assert_sql(/SELECT AVG\("users"."age"\)/,
|
|
127
235
|
User.select { :age.average }.to_sql)
|
|
128
236
|
end
|
|
129
237
|
|
|
130
238
|
def test_select_maximum
|
|
131
|
-
|
|
239
|
+
assert_sql(/SELECT MAX\("users"."age"\)/,
|
|
132
240
|
User.select { :age.maximum }.to_sql)
|
|
133
241
|
end
|
|
134
242
|
|
|
135
243
|
def test_select_minimum
|
|
136
|
-
|
|
244
|
+
assert_sql(/SELECT MIN\("users"."age"\)/,
|
|
137
245
|
User.select { :age.minimum }.to_sql)
|
|
138
246
|
end
|
|
139
247
|
|
|
140
248
|
def test_having_aggregate
|
|
141
249
|
sql = User.group(:name).having { :age.sum > 100 }.to_sql
|
|
142
|
-
|
|
143
|
-
|
|
250
|
+
assert_sql(/GROUP BY "users"."name"/, sql)
|
|
251
|
+
assert_sql(/HAVING SUM\("users"."age"\) > 100/, sql)
|
|
144
252
|
end
|
|
145
253
|
|
|
146
254
|
def test_select_avg_function
|
|
147
|
-
|
|
255
|
+
assert_sql(/SELECT AVG\("users"."age"\)/,
|
|
148
256
|
User.select { avg(:age) }.to_sql)
|
|
149
257
|
end
|
|
150
258
|
|
|
151
259
|
def test_select_count_function
|
|
152
|
-
|
|
260
|
+
assert_sql(/SELECT COUNT\("users"."id"\)/,
|
|
153
261
|
User.select { count(:id) }.to_sql)
|
|
154
262
|
end
|
|
155
263
|
|
|
264
|
+
def test_select_count_star
|
|
265
|
+
assert_sql(/SELECT COUNT\(\*\)/,
|
|
266
|
+
User.select { count(:*) }.to_sql)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def test_select_count_star_alias
|
|
270
|
+
assert_sql(/SELECT COUNT\(\*\) AS cnt/,
|
|
271
|
+
User.select { count(:*).as(:cnt) }.to_sql)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def test_having_count_star
|
|
275
|
+
sql = Author.joins(:posts) { :posts[:author_id] == :authors[:id] }.
|
|
276
|
+
group { :authors[:id] }.
|
|
277
|
+
having { count(:*) > 1 }.to_sql
|
|
278
|
+
assert_sql(/HAVING COUNT\(\*\) > 1/, sql)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def test_order_count_star
|
|
282
|
+
assert_sql(/ORDER BY COUNT\(\*\) DESC/,
|
|
283
|
+
User.group(:name).order { count(:*).desc }.to_sql)
|
|
284
|
+
end
|
|
285
|
+
|
|
156
286
|
def test_select_sum_function
|
|
157
|
-
|
|
287
|
+
assert_sql(/SELECT SUM\("users"."age"\)/,
|
|
158
288
|
User.select { sum(:age) }.to_sql)
|
|
159
289
|
end
|
|
160
290
|
|
|
161
291
|
def test_select_min_function
|
|
162
|
-
|
|
292
|
+
assert_sql(/SELECT MIN\("users"."age"\)/,
|
|
163
293
|
User.select { min(:age) }.to_sql)
|
|
164
294
|
end
|
|
165
295
|
|
|
166
296
|
def test_select_max_function
|
|
167
|
-
|
|
297
|
+
assert_sql(/SELECT MAX\("users"."age"\)/,
|
|
168
298
|
User.select { max(:age) }.to_sql)
|
|
169
299
|
end
|
|
170
300
|
|
|
171
301
|
def test_function_qualified_column
|
|
172
|
-
|
|
302
|
+
assert_sql(/SELECT AVG\("users"."age"\)/,
|
|
173
303
|
User.select { avg(:users[:age]) }.to_sql)
|
|
174
304
|
end
|
|
175
305
|
|
|
176
306
|
def test_having_function
|
|
177
307
|
sql = User.group(:name).having { sum(:age) > 100 }.to_sql
|
|
178
|
-
|
|
179
|
-
|
|
308
|
+
assert_sql(/GROUP BY "users"."name"/, sql)
|
|
309
|
+
assert_sql(/HAVING SUM\("users"."age"\) > 100/, sql)
|
|
180
310
|
end
|
|
181
311
|
|
|
182
312
|
def test_function_and_method_syntax_match
|
|
@@ -185,123 +315,138 @@ class TestBlockSyntax < Minitest::Test
|
|
|
185
315
|
end
|
|
186
316
|
|
|
187
317
|
def test_upper_function
|
|
188
|
-
|
|
318
|
+
assert_sql(/SELECT UPPER\("users"."name"\)/,
|
|
189
319
|
User.select { upper(:name) }.to_sql)
|
|
190
320
|
end
|
|
191
321
|
|
|
192
322
|
def test_lower_function
|
|
193
|
-
|
|
323
|
+
assert_sql(/SELECT LOWER\("users"."name"\)/,
|
|
194
324
|
User.select { lower(:name) }.to_sql)
|
|
195
325
|
end
|
|
196
326
|
|
|
197
327
|
def test_length_function_in_where
|
|
198
|
-
|
|
328
|
+
assert_sql(/WHERE LENGTH\("users"."name"\) > 3/,
|
|
199
329
|
User.where { length(:name) > 3 }.to_sql)
|
|
200
330
|
end
|
|
201
331
|
|
|
202
332
|
def test_coalesce_function_with_literal
|
|
203
|
-
|
|
333
|
+
assert_sql(/SELECT COALESCE\("users"."name", 'unknown'\)/,
|
|
204
334
|
User.select { coalesce(:name, 'unknown') }.to_sql)
|
|
205
335
|
end
|
|
206
336
|
|
|
207
337
|
def test_function_comparison
|
|
208
|
-
|
|
338
|
+
assert_sql(/WHERE UPPER\("users"."name"\) = 'MATZ'/,
|
|
209
339
|
User.where { upper(:name) == 'MATZ' }.to_sql)
|
|
210
340
|
end
|
|
211
341
|
|
|
342
|
+
def test_function_like
|
|
343
|
+
assert_sql(/WHERE UPPER\("users"."name"\) LIKE 'MA%'/,
|
|
344
|
+
User.where { upper(:name).like?('MA%') }.to_sql)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def test_function_in
|
|
348
|
+
assert_sql(/WHERE UPPER\("users"."name"\) IN \('MATZ', 'NOBU'\)/,
|
|
349
|
+
User.where { upper(:name).in?(%w[MATZ NOBU]) }.to_sql)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def test_aggregate_in
|
|
353
|
+
assert_sql(/HAVING SUM\("users"."age"\) BETWEEN 1 AND 10/,
|
|
354
|
+
User.group(:name).having { :age.sum.in?(1..10) }.to_sql)
|
|
355
|
+
end
|
|
356
|
+
|
|
212
357
|
def test_nested_function
|
|
213
|
-
|
|
358
|
+
assert_sql(/SELECT UPPER\(COALESCE\("users"."name", 'x'\)\)/,
|
|
214
359
|
User.select { upper(coalesce(:name, 'x')) }.to_sql)
|
|
215
360
|
end
|
|
216
361
|
|
|
217
362
|
def test_function_qualified_column_arg
|
|
218
|
-
|
|
363
|
+
assert_sql(/SELECT UPPER\("users"."name"\)/,
|
|
219
364
|
User.select { upper(:users[:name]) }.to_sql)
|
|
220
365
|
end
|
|
221
366
|
|
|
222
367
|
def test_select_multiple_fields
|
|
223
|
-
|
|
368
|
+
assert_sql(/SELECT UPPER\("users"."name"\), "users"."age"/,
|
|
224
369
|
User.select { [upper(:name), :age] }.to_sql)
|
|
225
370
|
end
|
|
226
371
|
|
|
227
372
|
def test_select_multiple_columns
|
|
228
|
-
|
|
373
|
+
assert_sql(/SELECT "users"."name", "users"."age"/,
|
|
229
374
|
User.select { [:name, :age] }.to_sql)
|
|
230
375
|
end
|
|
231
376
|
|
|
232
377
|
def test_select_multiple_with_aggregate
|
|
233
|
-
|
|
378
|
+
assert_sql(/SELECT "users"."name", SUM\("users"."age"\)/,
|
|
234
379
|
User.select { [:name, sum(:age)] }.to_sql)
|
|
235
380
|
end
|
|
236
381
|
|
|
237
382
|
def test_select_function_with_alias
|
|
238
|
-
|
|
383
|
+
assert_sql(/SELECT UPPER\("users"."name"\) AS upper_name, "users"."age"/,
|
|
239
384
|
User.select { [upper(:name).as(:upper_name), :age] }.to_sql)
|
|
240
385
|
end
|
|
241
386
|
|
|
242
387
|
def test_select_column_alias
|
|
243
|
-
|
|
388
|
+
assert_sql(/SELECT "users"."name" AS n/,
|
|
244
389
|
User.select { :name.as(:n) }.to_sql)
|
|
245
390
|
end
|
|
246
391
|
|
|
247
392
|
def test_select_qualified_column_alias
|
|
248
|
-
|
|
393
|
+
assert_sql(/SELECT "users"."name" AS n/,
|
|
249
394
|
User.select { :users[:name].as(:n) }.to_sql)
|
|
250
395
|
end
|
|
251
396
|
|
|
252
397
|
def test_select_aggregate_alias
|
|
253
|
-
|
|
398
|
+
assert_sql(/SELECT COUNT\("users"."id"\) AS cnt/,
|
|
254
399
|
User.select { count(:id).as(:cnt) }.to_sql)
|
|
255
400
|
end
|
|
256
401
|
|
|
257
402
|
def test_order_default_asc
|
|
258
|
-
|
|
403
|
+
assert_sql(/ORDER BY "users"."age"/,
|
|
259
404
|
User.order { :age }.to_sql)
|
|
260
405
|
end
|
|
261
406
|
|
|
262
407
|
def test_order_desc
|
|
263
|
-
|
|
408
|
+
assert_sql(/ORDER BY "users"."age" DESC/,
|
|
264
409
|
User.order { :age.desc }.to_sql)
|
|
265
410
|
end
|
|
266
411
|
|
|
267
412
|
def test_order_asc
|
|
268
|
-
|
|
413
|
+
assert_sql(/ORDER BY "users"."age" ASC/,
|
|
269
414
|
User.order { :age.asc }.to_sql)
|
|
270
415
|
end
|
|
271
416
|
|
|
272
417
|
def test_order_multiple
|
|
273
|
-
|
|
418
|
+
assert_sql(/ORDER BY "users"."age" DESC, "users"."name" ASC/,
|
|
274
419
|
User.order { [:age.desc, :name.asc] }.to_sql)
|
|
275
420
|
end
|
|
276
421
|
|
|
277
422
|
def test_order_qualified_column
|
|
278
|
-
|
|
423
|
+
assert_sql(/ORDER BY "users"."name" DESC/,
|
|
279
424
|
User.order { :users[:name].desc }.to_sql)
|
|
280
425
|
end
|
|
281
426
|
|
|
282
427
|
def test_group_single
|
|
283
|
-
|
|
428
|
+
assert_sql(/GROUP BY "users"."name"/,
|
|
284
429
|
User.group { :name }.to_sql)
|
|
285
430
|
end
|
|
286
431
|
|
|
287
432
|
def test_group_multiple
|
|
288
|
-
|
|
433
|
+
assert_sql(/GROUP BY "users"."name", "users"."age"/,
|
|
289
434
|
User.group { [:name, :age] }.to_sql)
|
|
290
435
|
end
|
|
291
436
|
|
|
292
437
|
def test_group_qualified_column
|
|
293
|
-
|
|
438
|
+
assert_sql(/GROUP BY "users"."name"/,
|
|
294
439
|
User.group { :users[:name] }.to_sql)
|
|
295
440
|
end
|
|
296
441
|
|
|
297
442
|
def test_group_with_having
|
|
298
443
|
sql = User.group { :name }.having { sum(:age) > 100 }.to_sql
|
|
299
|
-
|
|
300
|
-
|
|
444
|
+
assert_sql(/GROUP BY "users"."name"/, sql)
|
|
445
|
+
assert_sql(/HAVING SUM\("users"."age"\) > 100/, sql)
|
|
301
446
|
end
|
|
302
447
|
|
|
303
448
|
def test_default_where_syntax
|
|
304
|
-
|
|
449
|
+
assert_sql(/WHERE "users"."name" = 'Ruby' AND "users"."age" = 19/,
|
|
305
450
|
User.where(name: 'Ruby', age: 19).to_sql)
|
|
306
451
|
end
|
|
307
452
|
end
|
data/test/test_helper.rb
CHANGED
|
@@ -5,9 +5,89 @@ require 'minitest/autorun'
|
|
|
5
5
|
Bundler.require
|
|
6
6
|
|
|
7
7
|
require 'active_record'
|
|
8
|
+
require 'etc'
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
# Which database to generate SQL for. The tests only build queries, so any
|
|
11
|
+
# server reachable without further setup will do; the devcontainer runs
|
|
12
|
+
# PostgreSQL and MariaDB on localhost with a passwordless account named after
|
|
13
|
+
# the container user.
|
|
14
|
+
#
|
|
15
|
+
# rake test # sqlite3
|
|
16
|
+
# ADAPTER=postgresql rake test
|
|
17
|
+
# ADAPTER=mysql2 rake test
|
|
18
|
+
# rake test:all # all three in turn
|
|
19
|
+
ADAPTER = ENV.fetch('ADAPTER', 'sqlite3')
|
|
20
|
+
|
|
21
|
+
DATABASE_NAME = 'activerecord_refined_test'.freeze
|
|
22
|
+
|
|
23
|
+
DATABASE_CONFIG =
|
|
24
|
+
case ADAPTER
|
|
25
|
+
when 'sqlite3'
|
|
26
|
+
{adapter: 'sqlite3', database: ':memory:'}
|
|
27
|
+
when 'postgresql', 'mysql2'
|
|
28
|
+
{
|
|
29
|
+
adapter: ADAPTER,
|
|
30
|
+
host: ENV.fetch('DB_HOST', '127.0.0.1'),
|
|
31
|
+
username: ENV.fetch('DB_USERNAME') { Etc.getlogin },
|
|
32
|
+
password: ENV['DB_PASSWORD'],
|
|
33
|
+
database: DATABASE_NAME,
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
raise ArgumentError, "unknown ADAPTER: #{ADAPTER}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The server databases persist between runs, so create one on first use.
|
|
40
|
+
unless ADAPTER == 'sqlite3'
|
|
41
|
+
maintenance_database = ADAPTER == 'postgresql' ? 'postgres' : 'mysql'
|
|
42
|
+
ActiveRecord::Base.establish_connection(
|
|
43
|
+
DATABASE_CONFIG.merge(database: maintenance_database))
|
|
44
|
+
begin
|
|
45
|
+
ActiveRecord::Base.lease_connection.create_database(DATABASE_NAME)
|
|
46
|
+
rescue ActiveRecord::DatabaseAlreadyExists
|
|
47
|
+
end
|
|
48
|
+
ActiveRecord::Base.remove_connection
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
ActiveRecord::Base.establish_connection(DATABASE_CONFIG)
|
|
52
|
+
|
|
53
|
+
module SqlAssertions
|
|
54
|
+
# Arel spells the regexp match differently per adapter, and raises on the
|
|
55
|
+
# ones missing from this list.
|
|
56
|
+
REGEXP_OPERATORS = {
|
|
57
|
+
'postgresql' => ['~', '!~'],
|
|
58
|
+
'mysql2' => ['REGEXP', 'NOT REGEXP'],
|
|
59
|
+
}.freeze
|
|
60
|
+
|
|
61
|
+
def skip_without_regexp_support
|
|
62
|
+
skip "#{ADAPTER} has no regexp operator" unless REGEXP_OPERATORS.key?(ADAPTER)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def regexp_operator
|
|
66
|
+
Regexp.escape(REGEXP_OPERATORS.fetch(ADAPTER).first)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def not_regexp_operator
|
|
70
|
+
Regexp.escape(REGEXP_OPERATORS.fetch(ADAPTER).last)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# MySQL quotes identifiers with backticks instead of double quotes, and
|
|
74
|
+
# doubles the backslashes inside string literals because backslash is itself
|
|
75
|
+
# an escape character there. Normalising both lets a single expectation
|
|
76
|
+
# cover every adapter.
|
|
77
|
+
def normalize_sql(sql)
|
|
78
|
+
sql.tr('`', '"').gsub("\\\\") { "\\" }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Takes a relation or the SQL string of one.
|
|
82
|
+
def assert_sql(pattern, relation_or_sql)
|
|
83
|
+
sql = relation_or_sql.respond_to?(:to_sql) ? relation_or_sql.to_sql : relation_or_sql
|
|
84
|
+
assert_match(pattern, normalize_sql(sql))
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Minitest::Test
|
|
89
|
+
include SqlAssertions
|
|
90
|
+
end
|
|
11
91
|
|
|
12
92
|
class User < ActiveRecord::Base
|
|
13
93
|
end
|
|
@@ -22,6 +102,9 @@ end
|
|
|
22
102
|
|
|
23
103
|
class CreateAllTables < ActiveRecord::Migration[8.1]
|
|
24
104
|
def up
|
|
105
|
+
drop_table(:users, if_exists: true)
|
|
106
|
+
drop_table(:authors, if_exists: true)
|
|
107
|
+
drop_table(:posts, if_exists: true)
|
|
25
108
|
create_table(:users) {|t| t.string :name; t.integer :age}
|
|
26
109
|
create_table(:authors) {|t| t.string :name}
|
|
27
110
|
create_table(:posts) {|t| t.string :title; t.integer :author_id}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-refined
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shugo Maeda
|
|
@@ -80,12 +80,16 @@ executables: []
|
|
|
80
80
|
extensions: []
|
|
81
81
|
extra_rdoc_files: []
|
|
82
82
|
files:
|
|
83
|
+
- .github/workflows/push_gem.yml
|
|
84
|
+
- .github/workflows/test.yml
|
|
83
85
|
- .gitignore
|
|
84
86
|
- Gemfile
|
|
85
87
|
- LICENSE.txt
|
|
86
88
|
- README.md
|
|
87
89
|
- Rakefile
|
|
88
90
|
- activerecord-refined.gemspec
|
|
91
|
+
- examples/aggregations.rb
|
|
92
|
+
- examples/complex_joins.rb
|
|
89
93
|
- lib/active_record/refined.rb
|
|
90
94
|
- lib/active_record/refined/ast.rb
|
|
91
95
|
- lib/activerecord-refined.rb
|
|
@@ -113,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
113
117
|
requirements: []
|
|
114
118
|
rubygems_version: 4.1.0.dev
|
|
115
119
|
specification_version: 4
|
|
116
|
-
summary: ActiveRecord + Ruby
|
|
120
|
+
summary: "ActiveRecord + Ruby 4.1 Proc#refined"
|
|
117
121
|
test_files:
|
|
118
122
|
- test/test_block_syntax.rb
|
|
119
123
|
- test/test_helper.rb
|