arel_extensions 2.0.21 → 2.2.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.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -2
  3. data/.github/workflows/publish.yml +29 -0
  4. data/.github/workflows/release.yml +30 -0
  5. data/.github/workflows/ruby.yml +377 -80
  6. data/.gitignore +7 -6
  7. data/.rubocop.yml +62 -1
  8. data/CONTRIBUTING.md +102 -0
  9. data/Gemfile +2 -23
  10. data/NEWS.md +89 -0
  11. data/README.md +228 -84
  12. data/Rakefile +11 -4
  13. data/TODO +0 -1
  14. data/appveyor.yml +60 -22
  15. data/arel_extensions.gemspec +11 -12
  16. data/bin/build +15 -0
  17. data/bin/compose +6 -0
  18. data/bin/publish +8 -0
  19. data/dev/arelx.dockerfile +44 -0
  20. data/dev/compose.yaml +71 -0
  21. data/dev/postgres.dockerfile +5 -0
  22. data/dev/rbenv +189 -0
  23. data/gemfiles/rails3.gemfile +10 -10
  24. data/gemfiles/rails4_2.gemfile +38 -0
  25. data/gemfiles/rails5.gemfile +29 -0
  26. data/gemfiles/rails5_1_4.gemfile +13 -13
  27. data/gemfiles/rails5_2.gemfile +16 -14
  28. data/gemfiles/rails6.gemfile +18 -15
  29. data/gemfiles/rails6_1.gemfile +18 -15
  30. data/gemfiles/rails7.gemfile +33 -0
  31. data/gemfiles/rails7_1.gemfile +33 -0
  32. data/gemfiles/rails7_2.gemfile +33 -0
  33. data/gemspecs/arel_extensions-v1.gemspec +12 -13
  34. data/gemspecs/arel_extensions-v2.gemspec +11 -12
  35. data/init/mssql.sql +0 -0
  36. data/init/mysql.sql +0 -0
  37. data/init/oracle.sql +0 -0
  38. data/init/postgresql.sql +0 -0
  39. data/init/sqlite.sql +0 -0
  40. data/lib/arel_extensions/aliases.rb +14 -0
  41. data/lib/arel_extensions/attributes.rb +10 -2
  42. data/lib/arel_extensions/boolean_functions.rb +2 -4
  43. data/lib/arel_extensions/common_sql_functions.rb +12 -12
  44. data/lib/arel_extensions/comparators.rb +14 -14
  45. data/lib/arel_extensions/date_duration.rb +14 -9
  46. data/lib/arel_extensions/helpers.rb +62 -0
  47. data/lib/arel_extensions/insert_manager.rb +19 -17
  48. data/lib/arel_extensions/math.rb +48 -45
  49. data/lib/arel_extensions/math_functions.rb +18 -18
  50. data/lib/arel_extensions/nodes/abs.rb +0 -0
  51. data/lib/arel_extensions/nodes/aggregate_function.rb +0 -0
  52. data/lib/arel_extensions/nodes/blank.rb +1 -1
  53. data/lib/arel_extensions/nodes/case.rb +10 -12
  54. data/lib/arel_extensions/nodes/cast.rb +6 -6
  55. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  56. data/lib/arel_extensions/nodes/change_case.rb +0 -0
  57. data/lib/arel_extensions/nodes/coalesce.rb +1 -1
  58. data/lib/arel_extensions/nodes/collate.rb +9 -9
  59. data/lib/arel_extensions/nodes/concat.rb +2 -2
  60. data/lib/arel_extensions/nodes/date_diff.rb +33 -14
  61. data/lib/arel_extensions/nodes/duration.rb +0 -0
  62. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  63. data/lib/arel_extensions/nodes/floor.rb +0 -0
  64. data/lib/arel_extensions/nodes/format.rb +3 -2
  65. data/lib/arel_extensions/nodes/formatted_date.rb +42 -0
  66. data/lib/arel_extensions/nodes/formatted_number.rb +2 -2
  67. data/lib/arel_extensions/nodes/function.rb +22 -26
  68. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  69. data/lib/arel_extensions/nodes/json.rb +15 -9
  70. data/lib/arel_extensions/nodes/length.rb +6 -0
  71. data/lib/arel_extensions/nodes/levenshtein_distance.rb +1 -1
  72. data/lib/arel_extensions/nodes/locate.rb +1 -1
  73. data/lib/arel_extensions/nodes/log10.rb +0 -0
  74. data/lib/arel_extensions/nodes/matches.rb +1 -1
  75. data/lib/arel_extensions/nodes/md5.rb +0 -0
  76. data/lib/arel_extensions/nodes/power.rb +0 -0
  77. data/lib/arel_extensions/nodes/rand.rb +0 -0
  78. data/lib/arel_extensions/nodes/repeat.rb +2 -2
  79. data/lib/arel_extensions/nodes/replace.rb +2 -10
  80. data/lib/arel_extensions/nodes/rollup.rb +36 -0
  81. data/lib/arel_extensions/nodes/round.rb +0 -0
  82. data/lib/arel_extensions/nodes/select.rb +10 -0
  83. data/lib/arel_extensions/nodes/soundex.rb +2 -2
  84. data/lib/arel_extensions/nodes/std.rb +0 -0
  85. data/lib/arel_extensions/nodes/substring.rb +1 -1
  86. data/lib/arel_extensions/nodes/sum.rb +0 -0
  87. data/lib/arel_extensions/nodes/then.rb +1 -1
  88. data/lib/arel_extensions/nodes/trim.rb +2 -2
  89. data/lib/arel_extensions/nodes/union.rb +5 -5
  90. data/lib/arel_extensions/nodes/union_all.rb +4 -4
  91. data/lib/arel_extensions/nodes/wday.rb +0 -0
  92. data/lib/arel_extensions/nodes.rb +0 -0
  93. data/lib/arel_extensions/null_functions.rb +16 -0
  94. data/lib/arel_extensions/predications.rb +10 -10
  95. data/lib/arel_extensions/railtie.rb +1 -1
  96. data/lib/arel_extensions/set_functions.rb +3 -3
  97. data/lib/arel_extensions/string_functions.rb +19 -10
  98. data/lib/arel_extensions/tasks.rb +2 -2
  99. data/lib/arel_extensions/version.rb +1 -1
  100. data/lib/arel_extensions/visitors/convert_format.rb +0 -0
  101. data/lib/arel_extensions/visitors/ibm_db.rb +20 -20
  102. data/lib/arel_extensions/visitors/mssql.rb +394 -169
  103. data/lib/arel_extensions/visitors/mysql.rb +238 -151
  104. data/lib/arel_extensions/visitors/oracle.rb +170 -131
  105. data/lib/arel_extensions/visitors/oracle12.rb +16 -16
  106. data/lib/arel_extensions/visitors/postgresql.rb +170 -140
  107. data/lib/arel_extensions/visitors/sqlite.rb +88 -87
  108. data/lib/arel_extensions/visitors/to_sql.rb +185 -156
  109. data/lib/arel_extensions/visitors.rb +73 -60
  110. data/lib/arel_extensions.rb +173 -36
  111. data/test/arelx_test_helper.rb +49 -1
  112. data/test/database.yml +13 -7
  113. data/test/real_db_test.rb +101 -83
  114. data/test/support/fake_record.rb +8 -2
  115. data/test/test_comparators.rb +5 -5
  116. data/test/visitors/test_bulk_insert_oracle.rb +5 -5
  117. data/test/visitors/test_bulk_insert_sqlite.rb +5 -5
  118. data/test/visitors/test_bulk_insert_to_sql.rb +5 -5
  119. data/test/visitors/test_oracle.rb +14 -14
  120. data/test/visitors/test_to_sql.rb +121 -93
  121. data/test/with_ar/all_agnostic_test.rb +630 -320
  122. data/test/with_ar/insert_agnostic_test.rb +25 -18
  123. data/test/with_ar/test_bulk_sqlite.rb +11 -7
  124. data/test/with_ar/test_math_sqlite.rb +18 -14
  125. data/test/with_ar/test_string_mysql.rb +26 -22
  126. data/test/with_ar/test_string_sqlite.rb +26 -22
  127. data/version_v1.rb +1 -1
  128. data/version_v2.rb +1 -1
  129. metadata +24 -26
  130. data/.travis/oracle/download.js +0 -152
  131. data/.travis/oracle/download.sh +0 -30
  132. data/.travis/oracle/download_ojdbc.js +0 -116
  133. data/.travis/oracle/install.sh +0 -34
  134. data/.travis/setup_accounts.sh +0 -9
  135. data/.travis/sqlite3/extension-functions.sh +0 -6
  136. data/.travis.yml +0 -193
  137. data/gemfiles/rails4.gemfile +0 -29
  138. data/gemfiles/rails5_0.gemfile +0 -29
data/README.md CHANGED
@@ -1,16 +1,15 @@
1
1
  # Arel Extensions
2
2
 
3
- [![Travis Build Status](https://img.shields.io/travis/Faveod/arel-extensions.svg?label=Travis%20build)](http://travis-ci.org/Faveod/arel-extensions)
4
- [![AppVeyor Build Status](https://img.shields.io/appveyor/ci/yazfav/arel-extensions.svg?label=AppVeyor%20build)](https://ci.appveyor.com/project/yazfav/arel-extensions)
5
- [![Security](https://hakiri.io/github/Faveod/arel-extensions/master.svg)](https://hakiri.io/github/Faveod/arel-extensions/master)
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/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 (ruby to SQL) to Arel.
13
- It aims to ensure pure ruby syntax for the biggest number of usual cases.
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
- t is an Arel::Table for table my_table
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 : ABS, RAND, ROUND, FLOOR, CEIL, FORMAT
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 '\^[a-d_]+'
98
+ # => my_table.name REGEXP '^[a-d_]+'
99
99
  ```
100
100
 
101
- Other functions : SOUNDEX, LENGTH, REPLACE, LOCATE, SUBSTRING, TRIM
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
- ```t[:list]``` is a classical varchar containing a comma separated list ("1,2,3,4")
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
@@ -130,11 +145,76 @@ t[:birthdate].month.to_sql
130
145
 
131
146
  t[:birthdate].year.to_sql
132
147
  # => YEAR(my_table.birthdate)
148
+ ```
149
+
150
+ ### Datetime
151
+
152
+ ```ruby
153
+ # datetime difference
154
+ t[:birthdate] - Time.utc(2014, 3, 3, 12, 41, 18)
155
+
156
+ # comparison
157
+ t[:birthdate] >= '2014-03-03 10:10:10'
158
+ ```
133
159
 
160
+ ### Format and Time Zone Conversion
161
+
162
+ `format` has two forms:
163
+
164
+ ```ruby
134
165
  t[:birthdate].format('%Y-%m-%d').to_sql
135
166
  # => DATE_FORMAT(my_table.birthdate, '%Y-%m-%d')
136
167
  ```
137
168
 
169
+ Which formats the datetime without any time zone conversion.
170
+ The second form accepts 2 kinds of values:
171
+
172
+ 1. String:
173
+
174
+ ```ruby
175
+ t[:birthdate].format('%Y/%m/%d %H:%M:%S', 'posix/Pacific/Tahiti')
176
+ # => DATE_FORMAT(CONVERT_TZ(CAST(my_table.birthdate AS datetime), 'UTC', 'posix/Pacific/Tahiti'), '%Y/%m/%d %H:%i:%S') ## MySQL
177
+ # => TO_CHAR(CAST(my_table.birthdate AS timestamp with time zone) AT TIME ZONE 'UTC' AT TIME ZONE 'posix/Pacific/Tahiti', 'YYYY/MM/DD HH24:MI:SS') ## PostgreSQL
178
+ # => CONVERT(datetime, my_table.birthdate) AT TIME ZONE 'UTC' AT TIME ZONE N'posix/Pacific/Tahiti' ## SQL Server (& truncated for clarity)
179
+ # ^^^^^^^^^^^^^^^^^^^^ 🚨 Invalid timezone for SQL Server. Explanation below.
180
+ ```
181
+
182
+ which will convert the datetime field to the supplied time zone. This generally
183
+ means that you're letting the RDBMS decide or infer what is the timezone of the
184
+ column before conversion to the supplied timezone.
185
+
186
+ 1. Hash of the form `{ src_time_zone => dst_time_zone }`:
187
+
188
+ ```ruby
189
+ t[:birthdate].format('%Y/%m/%d %H:%M:%S', { 'posix/Europe/Paris' => 'posix/Pacific/Tahiti' })
190
+ ```
191
+
192
+ which will explicitly indicate the original timestamp that should be considered
193
+ by the RDBMS.
194
+
195
+ Warning:
196
+
197
+ - ⚠️ Time Zone names are specific to each RDBMS. While `PostgreSQL` and `MySQL`
198
+ have overlaping names (the ones prefixed with `posix`), you should always
199
+ read your vendor's documentation. `SQL Server` is a black sheep and has its
200
+ own conventions.
201
+ - ⚠️ Daylight saving is managed by the RDBMS vendor. Choose the approptiate time
202
+ zone name that enforces proper daylight saving conversions.
203
+ - ☣️ Choosing `GMT+offset` will certainly bypass daylight saving computations.
204
+ - ☣️ Choosing abbreviate forms like `CET`, which stands for `Central European
205
+ Time` will behave differently on `PostgreSQL` and `MySQL`. Don't assume
206
+ uniform behavior, or even a _rational_ one.
207
+ - ⚠️ Pay attention to the type of the `datetime` column you're working with. For
208
+ example, in Postgres, a `datetime` can be one of the following types:
209
+ 1. `timestamp with time zone`
210
+ 2. `timestamp without time zone`
211
+ In the first case, you don't need to supply a conversion hash because postgres
212
+ knows how to convert it to the desired time zone. However, if you do the same
213
+ for the second case, you might get surprises, especially if your Postgres
214
+ installation's default timezone is not `UTC`.
215
+ - ⚠️ SQLite is not supported.
216
+ - 🚨 Always test against your setup 🚨
217
+
138
218
  ## Unions
139
219
 
140
220
  ```ruby
@@ -164,15 +244,15 @@ t[:id].cast('char').to_sql
164
244
  ## Stored Procedures and User-defined functions
165
245
 
166
246
  To optimize queries, some classical functions are defined in databases missing any alternative native functions.
167
- Examples :
168
- - FIND_IN_SET
247
+ Examples:
248
+ - `FIND_IN_SET`
169
249
 
170
250
  ## BULK INSERT / UPSERT
171
251
 
172
252
  Arel Extensions improves InsertManager by adding bulk_insert method, which allows to insert multiple rows in one insert.
173
253
 
174
254
 
175
- ```
255
+ ```ruby
176
256
  @cols = ['id', 'name', 'comments', 'created_at']
177
257
  @data = [
178
258
  [23, 'name1', "sdfdsfdsfsdf", '2016-01-01'],
@@ -228,6 +308,15 @@ User.connection.execute(insert_manager.to_sql)
228
308
  <td class="ok">✔</td>
229
309
  <td class="ok">✔</td>
230
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>
231
320
  <tr>
232
321
  <td class="tg-yw4l">RAND<br>Arel.rand</td>
233
322
  <td class="ok">✔</td>
@@ -256,24 +345,33 @@ User.connection.execute(insert_manager.to_sql)
256
345
  <td class="ok">✔</td>
257
346
  </tr>
258
347
  <tr>
259
- <td class="tg-yw4l">POSIX FORMATTING<br>column.format_number("$ %7.2f","en_US")</td>
260
- <td class="ok">✔</td>
348
+ <th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
349
+ <td class="tg-yw4l">CONCAT<br>column + "string"</td>
261
350
  <td class="ok">✔</td>
262
351
  <td class="ok">✔</td>
352
+ <td class="tg-j6lv"> ||</td>
263
353
  <td class="ok">✔</td>
354
+ <td class="tg-j6lv">+</td>
264
355
  <td class="ok">✔</td>
265
- <td class="ko">not implemented</td>
266
356
  </tr>
267
357
  <tr>
268
- <th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
269
- <td class="tg-yw4l">CONCAT<br>column + "string"</td>
358
+ <td class="tg-yw4l">FIND_IN_SET<br>column &amp; ("l")</td>
270
359
  <td class="ok">✔</td>
271
360
  <td class="ok">✔</td>
272
- <td class="tg-j6lv"> ||</td>
361
+ <td class="tg-orpl">Ruby function</td>
362
+ <td class="ok">✔</td>
273
363
  <td class="ok">✔</td>
274
- <td class="tg-j6lv">+</td>
275
364
  <td class="ok">✔</td>
276
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>
369
+ <td class="ok">✔</td>
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>
374
+ </tr>
277
375
  <tr>
278
376
  <td class="tg-yw4l">LENGTH<br>column.length</td>
279
377
  <td class="ok">✔</td>
@@ -293,30 +391,57 @@ User.connection.execute(insert_manager.to_sql)
293
391
  <td class="ok">✔</td>
294
392
  </tr>
295
393
  <tr>
296
- <td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
297
- <td class="ok">✔</td>
298
- <td class="tg-j6lv">SUBSTR()</td>
299
- <td class="tg-j6lv">SUBSTR()</td>
300
- <td class="tg-j6lv">SUBSTR()</td>
394
+ <td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
301
395
  <td class="ok">✔</td>
396
+ <td class="tg-j6lv">unaccent required</td>
397
+ <td class="tg-j6lv">not supported</td>
302
398
  <td class="ok">✔</td>
399
+ <td class="tg-j6lv">✔</td>
400
+ <td class="tg-j6lv">?</td>
303
401
  </tr>
304
402
  <tr>
305
- <td class="tg-yw4l">FIND_IN_SET<br>column &amp; ("l")</td>
306
- <td class="ok">✔</td>
307
- <td class="ok">✔</td>
308
- <td class="tg-orpl">Ruby function</td>
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>
309
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>
310
422
  <td class="ok">✔</td>
423
+ <td class="tg-j6lv">✔</td>
424
+ <td class="tg-j6lv">not supported</td>
311
425
  <td class="ok">✔</td>
426
+ <td class="tg-j6lv">✔</td>
427
+ <td class="tg-j6lv">?</td>
312
428
  </tr>
313
429
  <tr>
314
- <td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
430
+ <td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
315
431
  <td class="ok">✔</td>
316
- <td class="tg-3oug">require fuzzystrmatch</td>
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>
317
436
  <td class="ok">✔</td>
437
+ </tr>
438
+ <tr>
439
+ <td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
318
440
  <td class="ok">✔</td>
319
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>
320
445
  <td class="ok">✔</td>
321
446
  </tr>
322
447
  <tr>
@@ -329,31 +454,31 @@ User.connection.execute(insert_manager.to_sql)
329
454
  <td class="ok">✔</td>
330
455
  </tr>
331
456
  <tr>
332
- <td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
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>
333
461
  <td class="ok">✔</td>
334
462
  <td class="ok">✔</td>
335
- <td class="tg-3oug">require pcre.so</td>
336
- <td class="tg-j6lv">REGEXP_LIKE</td>
337
- <td class="tg-j6lv">LIKE</td>
338
463
  <td class="ok">✔</td>
339
464
  </tr>
340
465
  <tr>
341
- <td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
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>
342
471
  <td class="ok">✔</td>
343
- <td class="ok">✔<br></td>
344
- <td class="tg-3oug">require pcre.so</td>
345
- <td class="tg-j6lv">NOT REGEXP_LIKE </td>
346
- <td class="tg-j6lv">NOT LIKE</td>
347
472
  <td class="ok">✔</td>
348
473
  </tr>
349
474
  <tr>
350
- <td class="tg-yw4l">ILIKE (in Arel6)<br/>column.imatches('%pattern')</td>
351
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
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>
352
480
  <td class="ok">✔</td>
353
481
  <td class="ok">✔</td>
354
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
355
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
356
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
357
482
  </tr>
358
483
  <tr>
359
484
  <td class="tg-yw4l">TRIM (leading)<br>column.trim("LEADING","M")</td>
@@ -382,43 +507,6 @@ User.connection.execute(insert_manager.to_sql)
382
507
  <td class="tg-j6lv">LTRIM(RTRIM())</td>
383
508
  <td class="tg-j6lv">TRIM()</td>
384
509
  </tr>
385
- <tr>
386
- <td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
387
- <td class="ok">✔</td>
388
- <td class="tg-j6lv">unaccent required</td>
389
- <td class="tg-j6lv">not supported</td>
390
- <td class="ok">✔</td>
391
- <td class="tg-j6lv">✔</td>
392
- <td class="tg-j6lv">?</td>
393
- </tr>
394
- <tr>
395
- <td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
396
- <td class="ok">not supported</td>
397
- <td class="tg-j6lv">not supported</td>
398
- <td class="tg-j6lv">not supported</td>
399
- <td class="ok">not supported</td>
400
- <td class="tg-j6lv">✔</td>
401
- <td class="tg-j6lv">?</td>
402
- </tr>
403
- <tr>
404
- <td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
405
- <td class="ok">not supported</td>
406
- <td class="tg-j6lv">✔</td>
407
- <td class="tg-j6lv">✔</td>
408
- <td class="ok">✔</td>
409
- <td class="tg-j6lv">✔</td>
410
- <td class="tg-j6lv">?</td>
411
- </tr>
412
- <tr>
413
- <td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
414
- <td class="ok">✔</td>
415
- <td class="tg-j6lv">✔</td>
416
- <td class="tg-j6lv">not supported</td>
417
- <td class="ok">✔</td>
418
- <td class="tg-j6lv">✔</td>
419
- <td class="tg-j6lv">?</td>
420
- </tr>
421
-
422
510
  <tr>
423
511
  <th class="tg-4rp9" rowspan="6"><div>Date functions</div></th>
424
512
  <td class="tg-yw4l">DATEADD<br>column + 2.year<br></td>
@@ -475,7 +563,16 @@ User.connection.execute(insert_manager.to_sql)
475
563
  <td class="ok">✔</td>
476
564
  </tr>
477
565
  <tr>
478
- <th class="tg-72dn" rowspan="8"><div>Comparators functions</div></th>
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>
479
576
  <td class="tg-yw4l">COALESCE<br>column.coalesce(var)</td>
480
577
  <td class="ok">✔</td>
481
578
  <td class="ok">✔</td>
@@ -484,6 +581,24 @@ User.connection.execute(insert_manager.to_sql)
484
581
  <td class="ok">✔</td>
485
582
  <td class="ok">✔</td>
486
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>
487
602
  <tr>
488
603
  <td class="tg-yw4l">ISNULL<br>column.isnull()</td>
489
604
  <td class="tg-j6lv">IFNULL()</td>
@@ -493,6 +608,24 @@ User.connection.execute(insert_manager.to_sql)
493
608
  <td class="ok">✔</td>
494
609
  <td class="ok">✔</td>
495
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>
496
629
  <tr>
497
630
  <td class="tg-yw4l">==<br>column == integer</td>
498
631
  <td class="ok">✔</td>
@@ -596,3 +729,14 @@ User.connection.execute(insert_manager.to_sql)
596
729
  </tr>
597
730
  </tbody>
598
731
  </table>
732
+
733
+ ## Version Compatibility
734
+
735
+ <table>
736
+ <tr><th>Ruby</th> <th>Rails</th> <th>Arel Extensions</th></tr>
737
+ <tr><td>3.1</td> <td>6.1</td> <td>2</td></tr>
738
+ <tr><td>3.0</td> <td>6.1</td> <td>2</td></tr>
739
+ <tr><td>2.7</td> <td>6.1, 6.0</td> <td>2</td></tr>
740
+ <tr><td>2.5</td> <td>6.1, 6.0</td> <td>2</td></tr>
741
+ <tr><td>2.5</td> <td>5.2</td> <td>1</td></tr>
742
+ </table>
data/Rakefile CHANGED
@@ -1,9 +1,9 @@
1
1
  require 'bundler'
2
- Bundler::GemHelper.install_tasks name: "arel_extensions"
2
+ Bundler::GemHelper.install_tasks name: 'arel_extensions'
3
3
 
4
4
  require 'rake/testtask'
5
5
 
6
- desc "Default Task"
6
+ desc 'Default Task'
7
7
  task default: [:test]
8
8
 
9
9
  Rake::TestTask.new(:test) do |t|
@@ -21,7 +21,7 @@ namespace :test do
21
21
  t.pattern = 'test/visitors/test_to_sql.rb'
22
22
  t.warning = true
23
23
  t.verbose = true
24
- t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
24
+ t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
25
25
  }
26
26
  end
27
27
 
@@ -33,7 +33,7 @@ end
33
33
  t.pattern = 'test/with_ar/*_agnostic_test.rb'
34
34
  t.warning = false
35
35
  t.verbose = true
36
- t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
36
+ t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
37
37
  }
38
38
  end
39
39
 
@@ -48,3 +48,10 @@ end
48
48
  # Make sure the adapter test evaluates the env setting task
49
49
  task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
50
50
  end
51
+
52
+ # Useful shorthands.
53
+ namespace :test do
54
+ task :sql => :to_sql
55
+ task :pg => :postgresql
56
+ task :postgres => :postgresql
57
+ end
data/TODO CHANGED
@@ -1,7 +1,6 @@
1
1
  Code quality:
2
2
  + codeclimate.com
3
3
  + gemnasium
4
- + appveyor.com (windows)
5
4
  + hakiri.io
6
5
  - coveralls.io
7
6
  - inch-ci.org
data/appveyor.yml CHANGED
@@ -1,44 +1,82 @@
1
+ image: Visual Studio 2015
2
+
1
3
  version: "{build}"
2
4
 
3
5
  cache:
4
6
  - vendor/bundle
5
7
 
6
- branches:
7
- only:
8
- - master
9
-
10
- services:
11
- - mssql2014
12
-
8
+ environment:
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
19
+ - RUBY_VERSION: 25-x64
20
+ RAILS: 5_2
21
+ SQL: MSSQL$SQL2012SP1
22
+ - RUBY_VERSION: 25-x64
23
+ RAILS: 5_2
24
+ SQL: MSSQL$SQL2014
25
+ - RUBY_VERSION: 25-x64
26
+ RAILS: 5_2
27
+ SQL: MSSQL$SQL2016
28
+ - RUBY_VERSION: 25-x64
29
+ RAILS: 6
30
+ SQL: MSSQL$SQL2012SP1
31
+ - RUBY_VERSION: 25-x64
32
+ RAILS: 6
33
+ SQL: MSSQL$SQL2014
34
+ - RUBY_VERSION: 25-x64
35
+ RAILS: 6
36
+ SQL: MSSQL$SQL2016
37
+ - RUBY_VERSION: 25-x64
38
+ RAILS: 6_1
39
+ SQL: MSSQL$SQL2012SP1
40
+ - RUBY_VERSION: 25-x64
41
+ RAILS: 6_1
42
+ SQL: MSSQL$SQL2014
43
+ - RUBY_VERSION: 25-x64
44
+ RAILS: 6_1
45
+ SQL: MSSQL$SQL2016
13
46
 
14
47
  install:
15
48
  - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
49
+ - gem update --system
50
+ - gem install rubygems-update && update_rubygems
16
51
  - bundle config --local path vendor/bundle
52
+ - cp ./gemfiles/rails%RAILS%.gemfile ./Gemfile
53
+ - bundle config set gemfile ./gemfiles/rails%RAILS%.gemfile
17
54
  - bundle install
18
55
 
19
- # Disable normal Windows builds in favor of our test script.
20
56
  build: off
21
57
 
22
-
23
58
  before_test:
24
59
  - ruby -v
25
60
  - gem -v
26
61
  - bundle -v
27
62
 
28
63
  test_script:
29
- - ruby --version
30
- - gem --version
31
- - bundler --version
32
- - bundle exec rake test
33
- - ps: Start-Service 'MSSQL$SQL2014'
34
- - timeout /t 4 /nobreak > NUL
64
+ - ps: Get-Service '*SQL*'
65
+ - net start %SQL%
35
66
  - bundle exec rake test:mssql
36
67
 
37
- environment:
68
+ for:
69
+ -
38
70
  matrix:
39
- - RUBY_VERSION: 200
40
- - RUBY_VERSION: 21
41
- - RUBY_VERSION: 22
42
- - RUBY_VERSION: 23
43
- - RUBY_VERSION: 23-x64
44
-
71
+ except:
72
+ - RAILS: 5_2
73
+ install:
74
+ - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
75
+ - cp ./gemspecs/arel_extensions-v2.gemspec ./arel_extensions.gemspec
76
+ - cp ./version_v2.rb lib/arel_extensions/version.rb
77
+ - cp ./gemfiles/rails%RAILS%.gemfile ./Gemfile
78
+ - gem update --system
79
+ - gem install rubygems-update && update_rubygems
80
+ - bundle config --local path vendor/bundle
81
+ - bundle config set gemfile ./gemfiles/rails%RAILS%.gemfile
82
+ - bundle install
@@ -1,28 +1,27 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
- require "arel_extensions/version"
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'arel_extensions/version'
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "arel_extensions"
5
+ s.name = 'arel_extensions'
6
6
  s.version = ArelExtensions::VERSION
7
7
  s.platform = Gem::Platform::RUBY
8
- s.authors = ["Yann Azoury", "Félix Bellanger", "Julien Delporte"]
9
- s.email = ["yann.azoury@faveod.com", "felix.bellanger@faveod.com", "julien.delporte@faveod.com"]
10
- s.homepage = "https://github.com/Faveod/arel-extensions"
11
- s.description = "Adds new features to Arel"
12
- s.summary = "Extending Arel"
8
+ s.authors = ['Yann Azoury', 'Félix Bellanger', 'Julien Delporte']
9
+ s.email = ['yann.azoury@faveod.com', 'felix.bellanger@faveod.com', 'julien.delporte@faveod.com']
10
+ s.homepage = 'https://github.com/Faveod/arel-extensions'
11
+ s.description = 'Adds new features to Arel'
12
+ s.summary = 'Extending Arel'
13
13
  s.license = 'MIT'
14
14
 
15
- s.rdoc_options = ["--main", "README.md"]
16
- s.extra_rdoc_files = ["MIT-LICENSE.txt", "README.md", 'functions.html']
15
+ s.rdoc_options = ['--main', 'README.md']
16
+ s.extra_rdoc_files = ['MIT-LICENSE.txt', 'README.md', 'functions.html']
17
17
 
18
18
  # Manifest
19
19
  s.files = `git ls-files`.split("\n")
20
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
- s.require_paths = ["lib"]
21
+ s.require_paths = ['lib']
22
22
 
23
23
  s.add_dependency('activerecord', '>= 6.0')
24
24
 
25
25
  s.add_development_dependency('minitest', '~> 5.9')
26
- s.add_development_dependency('rdoc', '~> 4.0')
27
26
  s.add_development_dependency('rake', '~> 12.3.3')
28
27
  end