arel_extensions 2.1.5 → 2.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +156 -153
- data/NEWS.md +32 -0
- data/README.md +151 -83
- data/appveyor.yml +9 -0
- data/arel_extensions.gemspec +0 -1
- data/gemfiles/{rails4.gemfile → rails4_2.gemfile} +12 -3
- data/gemfiles/{rails5_0.gemfile → rails5.gemfile} +0 -0
- data/gemfiles/rails6_1.gemfile +1 -1
- data/gemfiles/rails7.gemfile +1 -1
- data/gemspecs/arel_extensions-v1.gemspec +0 -1
- data/gemspecs/arel_extensions-v2.gemspec +0 -1
- data/lib/arel_extensions/date_duration.rb +5 -0
- data/lib/arel_extensions/nodes/case.rb +4 -3
- data/lib/arel_extensions/nodes/formatted_date.rb +42 -0
- data/lib/arel_extensions/nodes/rollup.rb +36 -0
- data/lib/arel_extensions/nodes/select.rb +10 -0
- data/lib/arel_extensions/null_functions.rb +16 -0
- data/lib/arel_extensions/string_functions.rb +1 -0
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors/mssql.rb +97 -0
- data/lib/arel_extensions/visitors/mysql.rb +89 -21
- data/lib/arel_extensions/visitors/oracle.rb +21 -0
- data/lib/arel_extensions/visitors/postgresql.rb +4 -0
- data/lib/arel_extensions/visitors/to_sql.rb +20 -0
- data/lib/arel_extensions/visitors.rb +8 -0
- data/lib/arel_extensions.rb +16 -0
- data/test/with_ar/all_agnostic_test.rb +249 -129
- data/version_v1.rb +1 -1
- data/version_v2.rb +1 -1
- metadata +9 -19
data/README.md
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
# Arel Extensions
|
2
2
|
|
3
3
|
![GitHub workflow](https://github.com/Faveod/arel-extensions/actions/workflows/ruby.yml/badge.svg)
|
4
|
-
[![AppVeyor Build Status](https://img.shields.io/appveyor/ci/
|
5
|
-
[![Security](https://hakiri.io/github/Faveod/arel-extensions/master.svg)](https://hakiri.io/github/Faveod/arel-extensions/master)
|
4
|
+
[![AppVeyor Build Status](https://img.shields.io/appveyor/ci/jdelporte/arel-extensions.svg?label=AppVeyor%20build)](https://ci.appveyor.com/project/jdelporte/arel-extensions)
|
6
5
|
![](http://img.shields.io/badge/license-MIT-brightgreen.svg)
|
7
6
|
|
8
7
|
Gem: [![Latest Release](https://img.shields.io/gem/v/arel_extensions.svg)](https://rubygems.org/gems/arel_extensions)
|
9
8
|
[![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?type=total)](https://rubygems.org/gems/arel_extensions)
|
10
9
|
[![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?label=downloads-current-version)](https://rubygems.org/gems/arel_extensions)
|
11
10
|
|
12
|
-
Arel Extensions adds shortcuts, fixes and new ORM mappings (
|
13
|
-
It aims to ensure pure
|
11
|
+
Arel Extensions adds shortcuts, fixes and new ORM mappings (Ruby to SQL) to Arel.
|
12
|
+
It aims to ensure pure Ruby syntax for most usual cases.
|
14
13
|
It allows to use more advanced SQL functions for any supported RDBMS.
|
15
14
|
|
16
15
|
|
@@ -37,7 +36,8 @@ It will add common SQL features in your DB to align ti with current routines. Te
|
|
37
36
|
|
38
37
|
## Examples
|
39
38
|
|
40
|
-
|
39
|
+
In the following examples
|
40
|
+
`t` is an `Arel::Table` for table `my_table` (i.e., `t = Arel::Table.new('my_table')`).
|
41
41
|
|
42
42
|
## Comparators
|
43
43
|
|
@@ -51,7 +51,7 @@ t is an Arel::Table for table my_table
|
|
51
51
|
# => my_table.nb > 42
|
52
52
|
```
|
53
53
|
|
54
|
-
Other operators
|
54
|
+
Other operators: <, >=, <=, =~
|
55
55
|
|
56
56
|
|
57
57
|
## Maths
|
@@ -74,7 +74,7 @@ With Arel Extensions:
|
|
74
74
|
# => SUM(my_table.nb) + 42
|
75
75
|
```
|
76
76
|
|
77
|
-
Other functions
|
77
|
+
Other functions: ABS, RAND, ROUND, FLOOR, CEIL, FORMAT
|
78
78
|
|
79
79
|
For Example:
|
80
80
|
```ruby
|
@@ -95,14 +95,29 @@ t[:price].format_number("%07.2f €","fr_FR")
|
|
95
95
|
# => TRIM(TRIM(TRIM(COALESCE(my_table.name, '')), '\t'), '\n') = ''
|
96
96
|
|
97
97
|
(t[:name] =~ /\A[a-d_]+/).to_sql
|
98
|
-
# => my_table.name REGEXP '
|
98
|
+
# => my_table.name REGEXP '^[a-d_]+'
|
99
99
|
```
|
100
100
|
|
101
|
-
|
101
|
+
The `replace` function supports string and regex patterns.
|
102
|
+
For instance
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
t[:email].replace('@', ' at ').replace('.', ' dot ').to_sql
|
106
|
+
# => REPLACE(REPLACE(`my_table`.`email`, '@', ' at '), '.', ' dot ')
|
107
|
+
```
|
108
|
+
|
109
|
+
Captures are supported when using regex patterns. The replace string may then reference the capture groups using `\1`, `\2`, etc. For instance
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
t[:email].replace(/^(.*)@(.*)$/, 'user: \1, host: \2').to_sql
|
113
|
+
# => REGEXP_REPLACE(`my_table`.`email`, '(?-mix:^(.*)@(.*)$)', 'user: \\1, host: \\2')
|
114
|
+
```
|
115
|
+
|
116
|
+
Other functions: SOUNDEX, LENGTH, REPLACE, LOCATE, SUBSTRING, TRIM
|
102
117
|
|
103
118
|
### String Array operations
|
104
119
|
|
105
|
-
|
120
|
+
`t[:list]` is a classical varchar containing a comma separated list (`"1,2,3,4"`).
|
106
121
|
|
107
122
|
```ruby
|
108
123
|
(t[:list] & 3).to_sql
|
@@ -229,15 +244,15 @@ t[:id].cast('char').to_sql
|
|
229
244
|
## Stored Procedures and User-defined functions
|
230
245
|
|
231
246
|
To optimize queries, some classical functions are defined in databases missing any alternative native functions.
|
232
|
-
Examples
|
233
|
-
- FIND_IN_SET
|
247
|
+
Examples:
|
248
|
+
- `FIND_IN_SET`
|
234
249
|
|
235
250
|
## BULK INSERT / UPSERT
|
236
251
|
|
237
252
|
Arel Extensions improves InsertManager by adding bulk_insert method, which allows to insert multiple rows in one insert.
|
238
253
|
|
239
254
|
|
240
|
-
```
|
255
|
+
```ruby
|
241
256
|
@cols = ['id', 'name', 'comments', 'created_at']
|
242
257
|
@data = [
|
243
258
|
[23, 'name1', "sdfdsfdsfsdf", '2016-01-01'],
|
@@ -293,6 +308,15 @@ User.connection.execute(insert_manager.to_sql)
|
|
293
308
|
<td class="ok">✔</td>
|
294
309
|
<td class="ok">✔</td>
|
295
310
|
</tr>
|
311
|
+
<tr>
|
312
|
+
<td class="tg-yw4l">POSIX FORMATTING<br>column.format_number("$ %7.2f","en_US")</td>
|
313
|
+
<td class="ok">✔</td>
|
314
|
+
<td class="ok">✔</td>
|
315
|
+
<td class="ok">✔</td>
|
316
|
+
<td class="ok">✔</td>
|
317
|
+
<td class="ok">✔</td>
|
318
|
+
<td class="ko">not implemented</td>
|
319
|
+
</tr>
|
296
320
|
<tr>
|
297
321
|
<td class="tg-yw4l">RAND<br>Arel.rand</td>
|
298
322
|
<td class="ok">✔</td>
|
@@ -321,23 +345,32 @@ User.connection.execute(insert_manager.to_sql)
|
|
321
345
|
<td class="ok">✔</td>
|
322
346
|
</tr>
|
323
347
|
<tr>
|
324
|
-
<
|
325
|
-
<td class="
|
348
|
+
<th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
|
349
|
+
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
326
350
|
<td class="ok">✔</td>
|
327
351
|
<td class="ok">✔</td>
|
352
|
+
<td class="tg-j6lv"> ||</td>
|
328
353
|
<td class="ok">✔</td>
|
354
|
+
<td class="tg-j6lv">+</td>
|
329
355
|
<td class="ok">✔</td>
|
330
|
-
<td class="ko">not implemented</td>
|
331
356
|
</tr>
|
332
357
|
<tr>
|
333
|
-
<
|
334
|
-
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
358
|
+
<td class="tg-yw4l">FIND_IN_SET<br>column & ("l")</td>
|
335
359
|
<td class="ok">✔</td>
|
336
360
|
<td class="ok">✔</td>
|
337
|
-
<td class="tg-
|
361
|
+
<td class="tg-orpl">Ruby function</td>
|
362
|
+
<td class="ok">✔</td>
|
363
|
+
<td class="ok">✔</td>
|
364
|
+
<td class="ok">✔</td>
|
365
|
+
</tr>
|
366
|
+
<tr>
|
367
|
+
<td class="tg-yw4l">ILIKE (in Arel6)<br/>column.imatches('%pattern')</td>
|
368
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
338
369
|
<td class="ok">✔</td>
|
339
|
-
<td class="tg-j6lv">+</td>
|
340
370
|
<td class="ok">✔</td>
|
371
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
372
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
373
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
341
374
|
</tr>
|
342
375
|
<tr>
|
343
376
|
<td class="tg-yw4l">LENGTH<br>column.length</td>
|
@@ -358,30 +391,57 @@ User.connection.execute(insert_manager.to_sql)
|
|
358
391
|
<td class="ok">✔</td>
|
359
392
|
</tr>
|
360
393
|
<tr>
|
361
|
-
<td class="tg-yw4l">
|
362
|
-
<td class="ok">✔</td>
|
363
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
364
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
365
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
394
|
+
<td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
|
366
395
|
<td class="ok">✔</td>
|
396
|
+
<td class="tg-j6lv">unaccent required</td>
|
397
|
+
<td class="tg-j6lv">not supported</td>
|
367
398
|
<td class="ok">✔</td>
|
399
|
+
<td class="tg-j6lv">✔</td>
|
400
|
+
<td class="tg-j6lv">?</td>
|
368
401
|
</tr>
|
369
402
|
<tr>
|
370
|
-
<td class="tg-yw4l">
|
371
|
-
<td class="ok"
|
372
|
-
<td class="
|
373
|
-
<td class="tg-
|
403
|
+
<td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
|
404
|
+
<td class="ok">not supported</td>
|
405
|
+
<td class="tg-j6lv">not supported</td>
|
406
|
+
<td class="tg-j6lv">not supported</td>
|
407
|
+
<td class="ok">not supported</td>
|
408
|
+
<td class="tg-j6lv">✔</td>
|
409
|
+
<td class="tg-j6lv">?</td>
|
410
|
+
</tr>
|
411
|
+
<tr>
|
412
|
+
<td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
|
413
|
+
<td class="ok">not supported</td>
|
414
|
+
<td class="tg-j6lv">✔</td>
|
415
|
+
<td class="tg-j6lv">✔</td>
|
374
416
|
<td class="ok">✔</td>
|
417
|
+
<td class="tg-j6lv">✔</td>
|
418
|
+
<td class="tg-j6lv">?</td>
|
419
|
+
</tr>
|
420
|
+
<tr>
|
421
|
+
<td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
|
375
422
|
<td class="ok">✔</td>
|
423
|
+
<td class="tg-j6lv">✔</td>
|
424
|
+
<td class="tg-j6lv">not supported</td>
|
376
425
|
<td class="ok">✔</td>
|
426
|
+
<td class="tg-j6lv">✔</td>
|
427
|
+
<td class="tg-j6lv">?</td>
|
377
428
|
</tr>
|
378
429
|
<tr>
|
379
|
-
<td class="tg-yw4l">
|
430
|
+
<td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
|
380
431
|
<td class="ok">✔</td>
|
381
|
-
<td class="
|
432
|
+
<td class="ok">✔<br></td>
|
433
|
+
<td class="tg-3oug">require pcre.so</td>
|
434
|
+
<td class="tg-j6lv">NOT REGEXP_LIKE </td>
|
435
|
+
<td class="tg-j6lv">NOT LIKE</td>
|
382
436
|
<td class="ok">✔</td>
|
437
|
+
</tr>
|
438
|
+
<tr>
|
439
|
+
<td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
|
383
440
|
<td class="ok">✔</td>
|
384
441
|
<td class="ok">✔</td>
|
442
|
+
<td class="tg-3oug">require pcre.so</td>
|
443
|
+
<td class="tg-j6lv">REGEXP_LIKE</td>
|
444
|
+
<td class="tg-j6lv">LIKE</td>
|
385
445
|
<td class="ok">✔</td>
|
386
446
|
</tr>
|
387
447
|
<tr>
|
@@ -394,31 +454,31 @@ User.connection.execute(insert_manager.to_sql)
|
|
394
454
|
<td class="ok">✔</td>
|
395
455
|
</tr>
|
396
456
|
<tr>
|
397
|
-
<td class="tg-yw4l">
|
457
|
+
<td class="tg-yw4l">REPLACE<br>column.replace(/re/,"X")</td>
|
458
|
+
<td class="ok">✔</td>
|
459
|
+
<td class="ok">✔</td>
|
460
|
+
<td class="ok">✔</td>
|
398
461
|
<td class="ok">✔</td>
|
399
462
|
<td class="ok">✔</td>
|
400
|
-
<td class="tg-3oug">require pcre.so</td>
|
401
|
-
<td class="tg-j6lv">REGEXP_LIKE</td>
|
402
|
-
<td class="tg-j6lv">LIKE</td>
|
403
463
|
<td class="ok">✔</td>
|
404
464
|
</tr>
|
405
465
|
<tr>
|
406
|
-
<td class="tg-yw4l">
|
466
|
+
<td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
|
467
|
+
<td class="ok">✔</td>
|
468
|
+
<td class="tg-3oug">require fuzzystrmatch</td>
|
469
|
+
<td class="ok">✔</td>
|
470
|
+
<td class="ok">✔</td>
|
407
471
|
<td class="ok">✔</td>
|
408
|
-
<td class="ok">✔<br></td>
|
409
|
-
<td class="tg-3oug">require pcre.so</td>
|
410
|
-
<td class="tg-j6lv">NOT REGEXP_LIKE </td>
|
411
|
-
<td class="tg-j6lv">NOT LIKE</td>
|
412
472
|
<td class="ok">✔</td>
|
413
473
|
</tr>
|
414
474
|
<tr>
|
415
|
-
<td class="tg-yw4l">
|
416
|
-
<td class="
|
475
|
+
<td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
|
476
|
+
<td class="ok">✔</td>
|
477
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
478
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
479
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
417
480
|
<td class="ok">✔</td>
|
418
481
|
<td class="ok">✔</td>
|
419
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
420
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
421
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
422
482
|
</tr>
|
423
483
|
<tr>
|
424
484
|
<td class="tg-yw4l">TRIM (leading)<br>column.trim("LEADING","M")</td>
|
@@ -447,43 +507,6 @@ User.connection.execute(insert_manager.to_sql)
|
|
447
507
|
<td class="tg-j6lv">LTRIM(RTRIM())</td>
|
448
508
|
<td class="tg-j6lv">TRIM()</td>
|
449
509
|
</tr>
|
450
|
-
<tr>
|
451
|
-
<td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
|
452
|
-
<td class="ok">✔</td>
|
453
|
-
<td class="tg-j6lv">unaccent required</td>
|
454
|
-
<td class="tg-j6lv">not supported</td>
|
455
|
-
<td class="ok">✔</td>
|
456
|
-
<td class="tg-j6lv">✔</td>
|
457
|
-
<td class="tg-j6lv">?</td>
|
458
|
-
</tr>
|
459
|
-
<tr>
|
460
|
-
<td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
|
461
|
-
<td class="ok">not supported</td>
|
462
|
-
<td class="tg-j6lv">not supported</td>
|
463
|
-
<td class="tg-j6lv">not supported</td>
|
464
|
-
<td class="ok">not supported</td>
|
465
|
-
<td class="tg-j6lv">✔</td>
|
466
|
-
<td class="tg-j6lv">?</td>
|
467
|
-
</tr>
|
468
|
-
<tr>
|
469
|
-
<td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
|
470
|
-
<td class="ok">not supported</td>
|
471
|
-
<td class="tg-j6lv">✔</td>
|
472
|
-
<td class="tg-j6lv">✔</td>
|
473
|
-
<td class="ok">✔</td>
|
474
|
-
<td class="tg-j6lv">✔</td>
|
475
|
-
<td class="tg-j6lv">?</td>
|
476
|
-
</tr>
|
477
|
-
<tr>
|
478
|
-
<td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
|
479
|
-
<td class="ok">✔</td>
|
480
|
-
<td class="tg-j6lv">✔</td>
|
481
|
-
<td class="tg-j6lv">not supported</td>
|
482
|
-
<td class="ok">✔</td>
|
483
|
-
<td class="tg-j6lv">✔</td>
|
484
|
-
<td class="tg-j6lv">?</td>
|
485
|
-
</tr>
|
486
|
-
|
487
510
|
<tr>
|
488
511
|
<th class="tg-4rp9" rowspan="6"><div>Date functions</div></th>
|
489
512
|
<td class="tg-yw4l">DATEADD<br>column + 2.year<br></td>
|
@@ -540,7 +563,16 @@ User.connection.execute(insert_manager.to_sql)
|
|
540
563
|
<td class="ok">✔</td>
|
541
564
|
</tr>
|
542
565
|
<tr>
|
543
|
-
<th class="tg-72dn" rowspan="
|
566
|
+
<th class="tg-72dn" rowspan="13"><div>Comparators functions</div></th>
|
567
|
+
<td class="tg-yw4l">BLANK<br>column.blank<br></td>
|
568
|
+
<td class="ok">✔</td>
|
569
|
+
<td class="ok">✔</td>
|
570
|
+
<td class="ok">✔</td>
|
571
|
+
<td class="ok">✔</td>
|
572
|
+
<td class="ok">✔</td>
|
573
|
+
<td class="ok">✔</td>
|
574
|
+
</tr>
|
575
|
+
<tr>
|
544
576
|
<td class="tg-yw4l">COALESCE<br>column.coalesce(var)</td>
|
545
577
|
<td class="ok">✔</td>
|
546
578
|
<td class="ok">✔</td>
|
@@ -549,6 +581,24 @@ User.connection.execute(insert_manager.to_sql)
|
|
549
581
|
<td class="ok">✔</td>
|
550
582
|
<td class="ok">✔</td>
|
551
583
|
</tr>
|
584
|
+
<tr>
|
585
|
+
<td class="tg-yw4l">COALESCE_BLANK<br>column.coalesce_blank(var)</td>
|
586
|
+
<td class="ok">✔</td>
|
587
|
+
<td class="ok">✔</td>
|
588
|
+
<td class="ok">✔</td>
|
589
|
+
<td class="ok">✔</td>
|
590
|
+
<td class="ok">✔</td>
|
591
|
+
<td class="ok">✔</td>
|
592
|
+
</tr>
|
593
|
+
<tr>
|
594
|
+
<td class="tg-yw4l">IF_PRESENT</td>
|
595
|
+
<td class="ok">✔</td>
|
596
|
+
<td class="ok">✔</td>
|
597
|
+
<td class="ok">✔</td>
|
598
|
+
<td class="ok">✔</td>
|
599
|
+
<td class="ok">✔</td>
|
600
|
+
<td class="ok">✔</td>
|
601
|
+
</tr>
|
552
602
|
<tr>
|
553
603
|
<td class="tg-yw4l">ISNULL<br>column.isnull()</td>
|
554
604
|
<td class="tg-j6lv">IFNULL()</td>
|
@@ -558,6 +608,24 @@ User.connection.execute(insert_manager.to_sql)
|
|
558
608
|
<td class="ok">✔</td>
|
559
609
|
<td class="ok">✔</td>
|
560
610
|
</tr>
|
611
|
+
<tr>
|
612
|
+
<td class="tg-yw4l">NOT_BLANK<br>column.not_blank<br></td>
|
613
|
+
<td class="ok">✔</td>
|
614
|
+
<td class="ok">✔</td>
|
615
|
+
<td class="ok">✔</td>
|
616
|
+
<td class="ok">✔</td>
|
617
|
+
<td class="ok">✔</td>
|
618
|
+
<td class="ok">✔</td>
|
619
|
+
</tr>
|
620
|
+
<tr>
|
621
|
+
<td class="tg-yw4l">PRESENT<br>column.present<br>alias to NOT_BLANK<br></td>
|
622
|
+
<td class="ok">✔</td>
|
623
|
+
<td class="ok">✔</td>
|
624
|
+
<td class="ok">✔</td>
|
625
|
+
<td class="ok">✔</td>
|
626
|
+
<td class="ok">✔</td>
|
627
|
+
<td class="ok">✔</td>
|
628
|
+
</tr>
|
561
629
|
<tr>
|
562
630
|
<td class="tg-yw4l">==<br>column == integer</td>
|
563
631
|
<td class="ok">✔</td>
|
data/appveyor.yml
CHANGED
@@ -7,6 +7,15 @@ cache:
|
|
7
7
|
|
8
8
|
environment:
|
9
9
|
matrix:
|
10
|
+
- RUBY_VERSION: 25-x64
|
11
|
+
RAILS: 4_2
|
12
|
+
SQL: MSSQL$SQL2012SP1
|
13
|
+
- RUBY_VERSION: 25-x64
|
14
|
+
RAILS: 4_2
|
15
|
+
SQL: MSSQL$SQL2014
|
16
|
+
- RUBY_VERSION: 25-x64
|
17
|
+
RAILS: 4_2
|
18
|
+
SQL: MSSQL$SQL2016
|
10
19
|
- RUBY_VERSION: 25-x64
|
11
20
|
RAILS: 5_2
|
12
21
|
SQL: MSSQL$SQL2012SP1
|
data/arel_extensions.gemspec
CHANGED
@@ -3,6 +3,13 @@ source 'https://rubygems.org'
|
|
3
3
|
gem 'arel', '~> 6.0'
|
4
4
|
|
5
5
|
group :development, :test do
|
6
|
+
# We need to explicitly include bigdecimal for ruby 2.7 .
|
7
|
+
# See https://github.com/ruby/bigdecimal for details.
|
8
|
+
rb_version = Gem::Version.new(RUBY_VERSION)
|
9
|
+
if Gem::Version.new('2.7') <= rb_version && rb_version < Gem::Version.new('2.8')
|
10
|
+
gem 'bigdecimal', '~> 1.3.5', platforms: %i[mri mingw x64_mingw mswin]
|
11
|
+
end
|
12
|
+
|
6
13
|
gem 'activesupport', '~> 4.0'
|
7
14
|
gem 'activemodel', '~> 4.0'
|
8
15
|
gem 'activerecord', '~> 4.0'
|
@@ -11,15 +18,17 @@ group :development, :test do
|
|
11
18
|
gem 'mysql2', '0.4.10', platforms: %i[mri mswin mingw]
|
12
19
|
gem 'pg', '< 1.0.0', platforms: %i[mri mingw]
|
13
20
|
|
14
|
-
gem 'tiny_tds', platforms: %i[mri mingw mswin]
|
15
|
-
gem 'activerecord-sqlserver-adapter', '~> 4.2.0', platforms: %i[mri mingw mswin]
|
21
|
+
gem 'tiny_tds', platforms: %i[mri mingw x64_mingw mswin]
|
22
|
+
gem 'activerecord-sqlserver-adapter', '~> 4.2.0', platforms: %i[mri mingw x64_mingw mswin]
|
16
23
|
|
17
24
|
gem 'ruby-oci8', platforms: %i[mri mswin mingw] if ENV.has_key? 'ORACLE_HOME'
|
18
25
|
gem 'activerecord-oracle_enhanced-adapter', '~> 1.6.0' if ENV.has_key? 'ORACLE_HOME'
|
19
26
|
|
20
27
|
# for JRuby
|
21
28
|
gem 'activerecord-jdbc-adapter', '~> 1.3', platforms: :jruby
|
22
|
-
gem 'jdbc-sqlite3', platforms: :jruby
|
29
|
+
gem 'jdbc-sqlite3', '~> 3.28', platforms: :jruby
|
30
|
+
gem 'jdbc-postgres', '~> 42.2', platforms: :jruby
|
31
|
+
gem 'jdbc-mysql', '~> 5.1', platforms: :jruby
|
23
32
|
gem 'activerecord-jdbcsqlite3-adapter', platforms: :jruby
|
24
33
|
gem 'activerecord-jdbcmysql-adapter', platforms: :jruby
|
25
34
|
gem 'activerecord-jdbcpostgresql-adapter', platforms: :jruby
|
File without changes
|
data/gemfiles/rails6_1.gemfile
CHANGED
@@ -9,7 +9,7 @@ group :development, :test do
|
|
9
9
|
gem 'activerecord', '~> 6.1.0'
|
10
10
|
|
11
11
|
gem 'sqlite3', '~> 1.4', platforms: [:mri]
|
12
|
-
gem 'mysql2', '0.5
|
12
|
+
gem 'mysql2', '~>0.5', platforms: [:mri]
|
13
13
|
gem 'pg', '~> 1.1', platforms: [:mri]
|
14
14
|
|
15
15
|
gem 'tiny_tds', platforms: %i[mri mingw x64_mingw mswin]
|
data/gemfiles/rails7.gemfile
CHANGED
@@ -9,7 +9,7 @@ group :development, :test do
|
|
9
9
|
gem 'activerecord', '~> 7.0.1'
|
10
10
|
|
11
11
|
gem 'sqlite3', '~> 1.4', platforms: [:mri]
|
12
|
-
gem 'mysql2', '0.5
|
12
|
+
gem 'mysql2', '~>0.5', platforms: [:mri]
|
13
13
|
gem 'pg', '~> 1.1', platforms: [:mri]
|
14
14
|
|
15
15
|
gem 'tiny_tds', platforms: %i[mri mingw x64_mingw mswin]
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'arel_extensions/nodes/format'
|
2
|
+
require 'arel_extensions/nodes/formatted_date'
|
2
3
|
require 'arel_extensions/nodes/duration'
|
3
4
|
require 'arel_extensions/nodes/wday'
|
4
5
|
|
@@ -43,5 +44,9 @@ module ArelExtensions
|
|
43
44
|
def format(tpl, time_zone = nil)
|
44
45
|
ArelExtensions::Nodes::Format.new [self, tpl, time_zone]
|
45
46
|
end
|
47
|
+
|
48
|
+
def format_date(tpl, time_zone = nil)
|
49
|
+
ArelExtensions::Nodes::FormattedDate.new [self, tpl, time_zone]
|
50
|
+
end
|
46
51
|
end
|
47
52
|
end
|
@@ -35,12 +35,13 @@ module ArelExtensions
|
|
35
35
|
include Arel::Predications
|
36
36
|
include Arel::OrderPredications
|
37
37
|
include ArelExtensions::Aliases
|
38
|
-
include ArelExtensions::Math
|
39
38
|
include ArelExtensions::Comparators
|
40
|
-
include ArelExtensions::
|
39
|
+
include ArelExtensions::DateDuration
|
40
|
+
include ArelExtensions::Math
|
41
41
|
include ArelExtensions::MathFunctions
|
42
|
-
include ArelExtensions::StringFunctions
|
43
42
|
include ArelExtensions::NullFunctions
|
43
|
+
include ArelExtensions::Predications
|
44
|
+
include ArelExtensions::StringFunctions
|
44
45
|
|
45
46
|
def return_type
|
46
47
|
obj = if @conditions.length > 0
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
module ArelExtensions
|
4
|
+
module Nodes
|
5
|
+
class FormattedDate < Function
|
6
|
+
RETURN_TYPE = :string
|
7
|
+
|
8
|
+
attr_accessor :col_type, :iso_format, :time_zone
|
9
|
+
|
10
|
+
def initialize expr
|
11
|
+
col = expr[0]
|
12
|
+
@iso_format = convert_format(expr[1])
|
13
|
+
@time_zone = expr[2]
|
14
|
+
@col_type = type_of_attribute(col)
|
15
|
+
super [col, convert_to_string_node(@iso_format)]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Address portability issues with some of the formats.
|
21
|
+
def convert_format(fmt)
|
22
|
+
s = StringScanner.new fmt
|
23
|
+
res = StringIO.new
|
24
|
+
while !s.eos?
|
25
|
+
res <<
|
26
|
+
case
|
27
|
+
when s.scan(/%D/) then '%m/%d/%y'
|
28
|
+
when s.scan(/%F/) then '%Y-%m-%d'
|
29
|
+
when s.scan(/%R/) then '%H:%M'
|
30
|
+
when s.scan(/%r/) then '%I:%M:%S %p'
|
31
|
+
when s.scan(/%T/) then '%H:%M:%S'
|
32
|
+
when s.scan(/%v/) then '%e-%b-%Y'
|
33
|
+
|
34
|
+
when s.scan(/[^%]+/) then s.matched
|
35
|
+
when s.scan(/./) then s.matched
|
36
|
+
end
|
37
|
+
end
|
38
|
+
res.string
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# The following is a patch to activerecord when it doesn't
|
2
|
+
# have RollUp defined, i.e. for rails < 5.2
|
3
|
+
|
4
|
+
begin
|
5
|
+
Arel::Nodes.const_get('RollUp')
|
6
|
+
rescue NameError => e
|
7
|
+
module Arel
|
8
|
+
module Nodes
|
9
|
+
class RollUp < Arel::Nodes::Unary
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Arel
|
15
|
+
module Visitors
|
16
|
+
class PostgreSQL
|
17
|
+
# Utilized by GroupingSet, Cube & RollUp visitors to
|
18
|
+
# handle grouping aggregation semantics
|
19
|
+
def grouping_array_or_grouping_element(o, collector)
|
20
|
+
if o.expr.is_a? Array
|
21
|
+
collector << "( "
|
22
|
+
visit o.expr, collector
|
23
|
+
collector << " )"
|
24
|
+
else
|
25
|
+
visit o.expr, collector
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def visit_Arel_Nodes_RollUp(o, collector)
|
30
|
+
collector << "ROLLUP"
|
31
|
+
grouping_array_or_grouping_element o, collector
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -3,6 +3,12 @@ require 'arel_extensions/nodes/is_null'
|
|
3
3
|
|
4
4
|
module ArelExtensions
|
5
5
|
module NullFunctions
|
6
|
+
|
7
|
+
# if_present returns nil if the the value is nil or blank
|
8
|
+
def if_present
|
9
|
+
Arel.when(self.cast(:string).present).then(self)
|
10
|
+
end
|
11
|
+
|
6
12
|
# ISNULL function lets you return an alternative value when an expression is NULL.
|
7
13
|
def is_null
|
8
14
|
ArelExtensions::Nodes::IsNull.new [self]
|
@@ -19,5 +25,15 @@ module ArelExtensions
|
|
19
25
|
args.unshift(self)
|
20
26
|
ArelExtensions::Nodes::Coalesce.new args
|
21
27
|
end
|
28
|
+
|
29
|
+
def coalesce_blank *args
|
30
|
+
res = Arel.when(self.cast(:string).present).then(self)
|
31
|
+
args[0...-1].each do |a|
|
32
|
+
val = a.is_a?(Arel::Nodes::Node) ? a : Arel.quoted(a)
|
33
|
+
res = res.when(val.present).then(a)
|
34
|
+
end
|
35
|
+
res = res.else(args[-1])
|
36
|
+
res
|
37
|
+
end
|
22
38
|
end
|
23
39
|
end
|