arel_extensions 1.6.0 → 2.0.0.rc3

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +2 -1
  3. data/.gitignore +6 -7
  4. data/.rubocop.yml +3 -67
  5. data/.travis/oracle/download.js +152 -0
  6. data/.travis/oracle/download.sh +30 -0
  7. data/.travis/oracle/download_ojdbc.js +116 -0
  8. data/.travis/oracle/install.sh +34 -0
  9. data/.travis/setup_accounts.sh +9 -0
  10. data/.travis/sqlite3/extension-functions.sh +6 -0
  11. data/.travis.yml +223 -0
  12. data/Gemfile +28 -2
  13. data/README.md +91 -258
  14. data/Rakefile +30 -48
  15. data/TODO +1 -0
  16. data/appveyor.yml +22 -60
  17. data/arel_extensions.gemspec +14 -13
  18. data/functions.html +3 -3
  19. data/gemfiles/rails3.gemfile +20 -0
  20. data/gemfiles/rails4.gemfile +29 -0
  21. data/gemfiles/rails5_0.gemfile +29 -0
  22. data/gemfiles/rails5_1_4.gemfile +14 -14
  23. data/gemfiles/rails5_2.gemfile +14 -16
  24. data/init/mssql.sql +4 -4
  25. data/init/mysql.sql +38 -38
  26. data/init/oracle.sql +0 -0
  27. data/init/postgresql.sql +25 -24
  28. data/init/sqlite.sql +0 -0
  29. data/lib/arel_extensions/attributes.rb +3 -7
  30. data/lib/arel_extensions/boolean_functions.rb +14 -53
  31. data/lib/arel_extensions/common_sql_functions.rb +17 -16
  32. data/lib/arel_extensions/comparators.rb +28 -29
  33. data/lib/arel_extensions/date_duration.rb +13 -17
  34. data/lib/arel_extensions/insert_manager.rb +15 -18
  35. data/lib/arel_extensions/math.rb +53 -55
  36. data/lib/arel_extensions/math_functions.rb +39 -46
  37. data/lib/arel_extensions/nodes/abs.rb +1 -0
  38. data/lib/arel_extensions/nodes/blank.rb +2 -1
  39. data/lib/arel_extensions/nodes/case.rb +19 -20
  40. data/lib/arel_extensions/nodes/cast.rb +8 -10
  41. data/lib/arel_extensions/nodes/ceil.rb +1 -1
  42. data/lib/arel_extensions/nodes/coalesce.rb +4 -3
  43. data/lib/arel_extensions/nodes/collate.rb +10 -9
  44. data/lib/arel_extensions/nodes/concat.rb +18 -9
  45. data/lib/arel_extensions/nodes/date_diff.rb +26 -42
  46. data/lib/arel_extensions/nodes/duration.rb +3 -0
  47. data/lib/arel_extensions/nodes/find_in_set.rb +1 -0
  48. data/lib/arel_extensions/nodes/floor.rb +1 -1
  49. data/lib/arel_extensions/nodes/format.rb +8 -35
  50. data/lib/arel_extensions/nodes/formatted_number.rb +23 -22
  51. data/lib/arel_extensions/nodes/function.rb +37 -46
  52. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  53. data/lib/arel_extensions/nodes/json.rb +39 -52
  54. data/lib/arel_extensions/nodes/length.rb +0 -5
  55. data/lib/arel_extensions/nodes/levenshtein_distance.rb +1 -1
  56. data/lib/arel_extensions/nodes/locate.rb +2 -1
  57. data/lib/arel_extensions/nodes/log10.rb +2 -1
  58. data/lib/arel_extensions/nodes/matches.rb +8 -6
  59. data/lib/arel_extensions/nodes/md5.rb +1 -0
  60. data/lib/arel_extensions/nodes/power.rb +5 -5
  61. data/lib/arel_extensions/nodes/rand.rb +1 -0
  62. data/lib/arel_extensions/nodes/repeat.rb +5 -3
  63. data/lib/arel_extensions/nodes/replace.rb +8 -16
  64. data/lib/arel_extensions/nodes/round.rb +6 -5
  65. data/lib/arel_extensions/nodes/soundex.rb +15 -15
  66. data/lib/arel_extensions/nodes/std.rb +21 -18
  67. data/lib/arel_extensions/nodes/substring.rb +16 -8
  68. data/lib/arel_extensions/nodes/then.rb +1 -1
  69. data/lib/arel_extensions/nodes/trim.rb +6 -4
  70. data/lib/arel_extensions/nodes/union.rb +8 -5
  71. data/lib/arel_extensions/nodes/union_all.rb +7 -4
  72. data/lib/arel_extensions/nodes/wday.rb +4 -0
  73. data/lib/arel_extensions/nodes.rb +1 -1
  74. data/lib/arel_extensions/null_functions.rb +5 -19
  75. data/lib/arel_extensions/predications.rb +44 -45
  76. data/lib/arel_extensions/railtie.rb +5 -5
  77. data/lib/arel_extensions/set_functions.rb +7 -5
  78. data/lib/arel_extensions/string_functions.rb +35 -91
  79. data/lib/arel_extensions/tasks.rb +6 -6
  80. data/lib/arel_extensions/version.rb +1 -1
  81. data/lib/arel_extensions/visitors/ibm_db.rb +31 -24
  82. data/lib/arel_extensions/visitors/mssql.rb +194 -440
  83. data/lib/arel_extensions/visitors/mysql.rb +212 -368
  84. data/lib/arel_extensions/visitors/oracle.rb +179 -236
  85. data/lib/arel_extensions/visitors/oracle12.rb +31 -18
  86. data/lib/arel_extensions/visitors/postgresql.rb +173 -271
  87. data/lib/arel_extensions/visitors/sqlite.rb +127 -157
  88. data/lib/arel_extensions/visitors/to_sql.rb +238 -300
  89. data/lib/arel_extensions/visitors.rb +62 -83
  90. data/lib/arel_extensions.rb +31 -235
  91. data/test/database.yml +10 -20
  92. data/test/helper.rb +18 -0
  93. data/test/real_db_test.rb +118 -121
  94. data/test/support/fake_record.rb +3 -11
  95. data/test/test_comparators.rb +17 -14
  96. data/test/visitors/test_bulk_insert_oracle.rb +12 -12
  97. data/test/visitors/test_bulk_insert_sqlite.rb +14 -13
  98. data/test/visitors/test_bulk_insert_to_sql.rb +13 -11
  99. data/test/visitors/test_oracle.rb +55 -55
  100. data/test/visitors/test_to_sql.rb +226 -419
  101. data/test/with_ar/all_agnostic_test.rb +370 -773
  102. data/test/with_ar/insert_agnostic_test.rb +22 -28
  103. data/test/with_ar/test_bulk_sqlite.rb +17 -18
  104. data/test/with_ar/test_math_sqlite.rb +27 -27
  105. data/test/with_ar/test_string_mysql.rb +34 -32
  106. data/test/with_ar/test_string_sqlite.rb +35 -31
  107. metadata +38 -52
  108. data/.github/workflows/publish.yml +0 -30
  109. data/.github/workflows/release.yml +0 -30
  110. data/.github/workflows/ruby.yml +0 -452
  111. data/CONTRIBUTING.md +0 -102
  112. data/Makefile +0 -18
  113. data/NEWS.md +0 -116
  114. data/bin/build +0 -15
  115. data/bin/publish +0 -8
  116. data/dev/arelx.dockerfile +0 -41
  117. data/dev/compose.yaml +0 -69
  118. data/dev/postgres.dockerfile +0 -5
  119. data/dev/rbenv +0 -189
  120. data/gemfiles/rails5.gemfile +0 -29
  121. data/gemfiles/rails6.gemfile +0 -34
  122. data/gemfiles/rails6_1.gemfile +0 -42
  123. data/gemfiles/rails7.gemfile +0 -42
  124. data/gemfiles/rails7_1.gemfile +0 -41
  125. data/gemfiles/rails7_2.gemfile +0 -41
  126. data/gemfiles/rails8.gemfile +0 -40
  127. data/gemfiles/rails8_1.gemfile +0 -41
  128. data/gemspecs/arel_extensions-v1.gemspec +0 -27
  129. data/gemspecs/arel_extensions-v2.gemspec +0 -27
  130. data/generate_gems.sh +0 -15
  131. data/lib/arel_extensions/aliases.rb +0 -14
  132. data/lib/arel_extensions/constants.rb +0 -13
  133. data/lib/arel_extensions/helpers.rb +0 -61
  134. data/lib/arel_extensions/nodes/aggregate_function.rb +0 -13
  135. data/lib/arel_extensions/nodes/byte_size.rb +0 -11
  136. data/lib/arel_extensions/nodes/char_length.rb +0 -11
  137. data/lib/arel_extensions/nodes/formatted_date.rb +0 -42
  138. data/lib/arel_extensions/nodes/rollup.rb +0 -36
  139. data/lib/arel_extensions/nodes/select.rb +0 -10
  140. data/lib/arel_extensions/nodes/sum.rb +0 -7
  141. data/lib/arel_extensions/visitors/convert_format.rb +0 -37
  142. data/lib/arel_extensions/warning.rb +0 -42
  143. data/test/arelx_test_helper.rb +0 -94
  144. data/test/config_loader.rb +0 -9
  145. data/version_v1.rb +0 -3
  146. data/version_v2.rb +0 -3
data/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  # Arel Extensions
2
2
 
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)
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)
5
6
  ![](http://img.shields.io/badge/license-MIT-brightgreen.svg)
6
7
 
7
8
  Gem: [![Latest Release](https://img.shields.io/gem/v/arel_extensions.svg)](https://rubygems.org/gems/arel_extensions)
8
9
  [![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?type=total)](https://rubygems.org/gems/arel_extensions)
9
10
  [![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?label=downloads-current-version)](https://rubygems.org/gems/arel_extensions)
10
11
 
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.
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.
13
14
  It allows to use more advanced SQL functions for any supported RDBMS.
14
15
 
15
16
 
@@ -18,11 +19,6 @@ It allows to use more advanced SQL functions for any supported RDBMS.
18
19
  Arel 6 (Rails 4) or Arel 7+ (Rails 5).
19
20
  [Arel Repository](http://github.com/rails/arel)
20
21
 
21
- or
22
-
23
- Rails 6
24
- [Rails Repository](http://github.com/rails/rails)
25
-
26
22
  ## Usage
27
23
 
28
24
  Most of the features will work just by adding the gem to your Gemfiles. To make sure to get all the features for any dbms, you should execute the next line as soon as you get your connection to your DB:
@@ -31,13 +27,12 @@ Most of the features will work just by adding the gem to your Gemfiles. To make
31
27
  ArelExtensions::CommonSqlFunctions.new(ActiveRecord::Base.connection).add_sql_functions()
32
28
  ```
33
29
 
34
- It will add common SQL features in your DB to align it with current routines. Technically, it will execute SQL scripts from init folder.
30
+ It will add common SQL features in your DB to align ti with current routines. Technically, it will execute SQL scripts from init folder.
35
31
 
36
32
 
37
33
  ## Examples
38
34
 
39
- In the following examples
40
- `t` is an `Arel::Table` for table `my_table` (i.e., `t = Arel::Table.new('my_table')`).
35
+ t is an Arel::Table for table my_table
41
36
 
42
37
  ## Comparators
43
38
 
@@ -51,7 +46,7 @@ In the following examples
51
46
  # => my_table.nb > 42
52
47
  ```
53
48
 
54
- Other operators: <, >=, <=, =~
49
+ Other operators : <, >=, <=, =~
55
50
 
56
51
 
57
52
  ## Maths
@@ -74,12 +69,12 @@ With Arel Extensions:
74
69
  # => SUM(my_table.nb) + 42
75
70
  ```
76
71
 
77
- Other functions: ABS, RAND, ROUND, FLOOR, CEIL, FORMAT
72
+ Other functions : ABS, RAND, ROUND, FLOOR, CEIL, FORMAT
78
73
 
79
74
  For Example:
80
- ```ruby
75
+ ```ruby
81
76
  t[:price].format_number("%07.2f €","fr_FR")
82
- # equivalent to 'sprintf("%07.2f €",price)' plus locale management
77
+ # equivalent to 'sprintf("%07.2f €",price)' plus locale management
83
78
  ```
84
79
 
85
80
  ## String operations
@@ -95,29 +90,14 @@ t[:price].format_number("%07.2f €","fr_FR")
95
90
  # => TRIM(TRIM(TRIM(COALESCE(my_table.name, '')), '\t'), '\n') = ''
96
91
 
97
92
  (t[:name] =~ /\A[a-d_]+/).to_sql
98
- # => my_table.name REGEXP '^[a-d_]+'
99
- ```
100
-
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 ')
93
+ # => my_table.name REGEXP '\^[a-d_]+'
107
94
  ```
108
95
 
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
96
+ Other functions : SOUNDEX, LENGTH, REPLACE, LOCATE, SUBSTRING, TRIM
117
97
 
118
98
  ### String Array operations
119
99
 
120
- `t[:list]` is a classical varchar containing a comma separated list (`"1,2,3,4"`).
100
+ ```t[:list]``` is a classical varchar containing a comma separated list ("1,2,3,4")
121
101
 
122
102
  ```ruby
123
103
  (t[:list] & 3).to_sql
@@ -145,77 +125,12 @@ t[:birthdate].month.to_sql
145
125
 
146
126
  t[:birthdate].year.to_sql
147
127
  # => 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
128
 
156
- # comparison
157
- t[:birthdate] >= '2014-03-03 10:10:10'
158
- ```
159
-
160
- ### Format and Time Zone Conversion
161
-
162
- `format` has two forms:
163
-
164
- ```ruby
165
129
  t[:birthdate].format('%Y-%m-%d').to_sql
166
130
  # => DATE_FORMAT(my_table.birthdate, '%Y-%m-%d')
167
131
  ```
168
132
 
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 overlapping 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
-
218
- ## Unions
133
+ ## Unions
219
134
 
220
135
  ```ruby
221
136
  (t.where(t[:name].eq('str')) + t.where(t[:name].eq('test'))).to_sql
@@ -228,7 +143,7 @@ Arel-extensions allows to use functions on case clause
228
143
 
229
144
  ```ruby
230
145
  t[:name].when("smith").then(1).when("doe").then(2).else(0).sum.to_sql
231
- # => SUM(CASE "my_table"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END)
146
+ # => SUM(CASE "my_table"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END)
232
147
  ```
233
148
 
234
149
  ## Cast Function
@@ -244,15 +159,15 @@ t[:id].cast('char').to_sql
244
159
  ## Stored Procedures and User-defined functions
245
160
 
246
161
  To optimize queries, some classical functions are defined in databases missing any alternative native functions.
247
- Examples:
248
- - `FIND_IN_SET`
162
+ Examples :
163
+ - FIND_IN_SET
249
164
 
250
165
  ## BULK INSERT / UPSERT
251
166
 
252
167
  Arel Extensions improves InsertManager by adding bulk_insert method, which allows to insert multiple rows in one insert.
253
168
 
254
169
 
255
- ```ruby
170
+ ```
256
171
  @cols = ['id', 'name', 'comments', 'created_at']
257
172
  @data = [
258
173
  [23, 'name1', "sdfdsfdsfsdf", '2016-01-01'],
@@ -294,7 +209,7 @@ User.connection.execute(insert_manager.to_sql)
294
209
  <td class="tg-yw4l">CEIL<br>column.ceil</td>
295
210
  <td class="ok">✔</td>
296
211
  <td class="ok">✔</td>
297
- <td class="tg-j6lv">CASE + CAST</td>
212
+ <td class="tg-j6lv">CASE + ROUND</td>
298
213
  <td class="ok">✔</td>
299
214
  <td class="tg-j6lv">CEILING()</td>
300
215
  <td class="tg-j6lv">CEILING()</td>
@@ -303,19 +218,10 @@ User.connection.execute(insert_manager.to_sql)
303
218
  <td class="tg-yw4l">FLOOR<br>column.floor</td>
304
219
  <td class="ok">✔</td>
305
220
  <td class="ok">✔</td>
306
- <td class="tg-j6lv">CASE + CAST</td>
307
- <td class="ok">✔</td>
308
- <td class="ok">✔</td>
309
- <td class="ok">✔</td>
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>
221
+ <td class="tg-j6lv">CASE + ROUND</td>
315
222
  <td class="ok">✔</td>
316
223
  <td class="ok">✔</td>
317
224
  <td class="ok">✔</td>
318
- <td class="ko">not implemented</td>
319
225
  </tr>
320
226
  <tr>
321
227
  <td class="tg-yw4l">RAND<br>Arel.rand</td>
@@ -345,53 +251,26 @@ User.connection.execute(insert_manager.to_sql)
345
251
  <td class="ok">✔</td>
346
252
  </tr>
347
253
  <tr>
348
- <th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
349
- <td class="tg-yw4l">CONCAT<br>column + "string"</td>
350
- <td class="ok">✔</td>
351
- <td class="ok">✔</td>
352
- <td class="tg-j6lv"> ||</td>
353
- <td class="ok">✔</td>
354
- <td class="tg-j6lv">+</td>
355
- <td class="ok">✔</td>
356
- </tr>
357
- <tr>
358
- <td class="tg-yw4l">FIND_IN_SET<br>column &amp; ("l")</td>
359
- <td class="ok">✔</td>
360
- <td class="ok">✔</td>
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>
254
+ <td class="tg-yw4l">POSIX FORMATTING<br>column.format_number("$ %7.2f","en_US")</td>
369
255
  <td class="ok">✔</td>
370
256
  <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>
375
- <tr>
376
- <td class="tg-yw4l">LENGTH<br>column.char_length</td>
377
- <td class="ok">✔</td>
378
257
  <td class="ok">✔</td>
379
258
  <td class="ok">✔</td>
380
259
  <td class="ok">✔</td>
381
- <td class="tg-j6lv">✔</td>
382
- <td class="ok">not implemented</td>
260
+ <td class="ko">not implemented</td>
383
261
  </tr>
384
262
  <tr>
385
- <td class="tg-yw4l">LENGTH (aka octet_length)<br>column.byte_size</td>
263
+ <th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
264
+ <td class="tg-yw4l">CONCAT<br>column + "string"</td>
386
265
  <td class="ok">✔</td>
387
266
  <td class="ok">✔</td>
267
+ <td class="tg-j6lv"> ||</td>
388
268
  <td class="ok">✔</td>
269
+ <td class="tg-j6lv">+</td>
389
270
  <td class="ok">✔</td>
390
- <td class="tg-j6lv">✔</td>
391
- <td class="ok">not implemented</td>
392
271
  </tr>
393
272
  <tr>
394
- <td class="tg-yw4l">LENGTH<br>column.length<br>⚠️ not portable, prefer char_length or byte_size</td>
273
+ <td class="tg-yw4l">LENGTH<br>column.length</td>
395
274
  <td class="ok">✔</td>
396
275
  <td class="ok">✔</td>
397
276
  <td class="ok">✔</td>
@@ -409,70 +288,34 @@ User.connection.execute(insert_manager.to_sql)
409
288
  <td class="ok">✔</td>
410
289
  </tr>
411
290
  <tr>
412
- <td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
291
+ <td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
413
292
  <td class="ok">✔</td>
414
- <td class="tg-j6lv">unaccent required</td>
415
- <td class="tg-j6lv">not supported</td>
293
+ <td class="tg-j6lv">SUBSTR()</td>
294
+ <td class="tg-j6lv">SUBSTR()</td>
295
+ <td class="tg-j6lv">SUBSTR()</td>
416
296
  <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 Insensitive<br>column.ai_matches('blah')</td>
422
- <td class="ok">not supported</td>
423
- <td class="tg-j6lv">not supported</td>
424
- <td class="tg-j6lv">not supported</td>
425
- <td class="ok">not supported</td>
426
- <td class="tg-j6lv">✔</td>
427
- <td class="tg-j6lv">?</td>
428
- </tr>
429
- <tr>
430
- <td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
431
- <td class="ok">not supported</td>
432
- <td class="tg-j6lv">✔</td>
433
- <td class="tg-j6lv">✔</td>
434
297
  <td class="ok">✔</td>
435
- <td class="tg-j6lv">✔</td>
436
- <td class="tg-j6lv">?</td>
437
298
  </tr>
438
299
  <tr>
439
- <td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
440
- <td class="ok">✔</td>
441
- <td class="tg-j6lv">✔</td>
442
- <td class="tg-j6lv">not supported</td>
300
+ <td class="tg-yw4l">FIND_IN_SET<br>column &amp; ("l")</td>
443
301
  <td class="ok">✔</td>
444
- <td class="tg-j6lv">✔</td>
445
- <td class="tg-j6lv">?</td>
446
- </tr>
447
- <tr>
448
- <td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
449
302
  <td class="ok">✔</td>
450
- <td class="ok">✔<br></td>
451
- <td class="tg-3oug">require pcre.so</td>
452
- <td class="tg-j6lv">NOT REGEXP_LIKE </td>
453
- <td class="tg-j6lv">NOT LIKE</td>
303
+ <td class="tg-orpl">Ruby function</td>
454
304
  <td class="ok">✔</td>
455
- </tr>
456
- <tr>
457
- <td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
458
305
  <td class="ok">✔</td>
459
306
  <td class="ok">✔</td>
460
- <td class="tg-3oug">require pcre.so</td>
461
- <td class="tg-j6lv">REGEXP_LIKE</td>
462
- <td class="tg-j6lv">LIKE</td>
463
- <td class="ok">✔</td>
464
307
  </tr>
465
308
  <tr>
466
- <td class="tg-yw4l">REPLACE<br>column.replace("s","X")</td>
467
- <td class="ok">✔</td>
309
+ <td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
468
310
  <td class="ok">✔</td>
311
+ <td class="tg-3oug">require fuzzystrmatch</td>
469
312
  <td class="ok">✔</td>
470
313
  <td class="ok">✔</td>
471
314
  <td class="ok">✔</td>
472
315
  <td class="ok">✔</td>
473
316
  </tr>
474
317
  <tr>
475
- <td class="tg-yw4l">REPLACE<br>column.replace(/re/,"X")</td>
318
+ <td class="tg-yw4l">REPLACE<br>column.replace("s","X")</td>
476
319
  <td class="ok">✔</td>
477
320
  <td class="ok">✔</td>
478
321
  <td class="ok">✔</td>
@@ -481,22 +324,31 @@ User.connection.execute(insert_manager.to_sql)
481
324
  <td class="ok">✔</td>
482
325
  </tr>
483
326
  <tr>
484
- <td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
327
+ <td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
485
328
  <td class="ok">✔</td>
486
- <td class="tg-3oug">require fuzzystrmatch</td>
487
329
  <td class="ok">✔</td>
330
+ <td class="tg-3oug">require pcre.so</td>
331
+ <td class="tg-j6lv">REGEXP_LIKE</td>
332
+ <td class="tg-j6lv">LIKE</td>
488
333
  <td class="ok">✔</td>
334
+ </tr>
335
+ <tr>
336
+ <td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
489
337
  <td class="ok">✔</td>
338
+ <td class="ok">✔<br></td>
339
+ <td class="tg-3oug">require pcre.so</td>
340
+ <td class="tg-j6lv">NOT REGEXP_LIKE </td>
341
+ <td class="tg-j6lv">NOT LIKE</td>
490
342
  <td class="ok">✔</td>
491
343
  </tr>
492
344
  <tr>
493
- <td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
494
- <td class="ok">✔</td>
495
- <td class="tg-j6lv">SUBSTR()</td>
496
- <td class="tg-j6lv">SUBSTR()</td>
497
- <td class="tg-j6lv">SUBSTR()</td>
345
+ <td class="tg-yw4l">ILIKE (in Arel6)<br/>column.imatches('%pattern')</td>
346
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
498
347
  <td class="ok">✔</td>
499
348
  <td class="ok">✔</td>
349
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
350
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
351
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
500
352
  </tr>
501
353
  <tr>
502
354
  <td class="tg-yw4l">TRIM (leading)<br>column.trim("LEADING","M")</td>
@@ -525,6 +377,43 @@ User.connection.execute(insert_manager.to_sql)
525
377
  <td class="tg-j6lv">LTRIM(RTRIM())</td>
526
378
  <td class="tg-j6lv">TRIM()</td>
527
379
  </tr>
380
+ <tr>
381
+ <td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
382
+ <td class="ok">✔</td>
383
+ <td class="tg-j6lv">unaccent required</td>
384
+ <td class="tg-j6lv">not supported</td>
385
+ <td class="ok">✔</td>
386
+ <td class="tg-j6lv">✔</td>
387
+ <td class="tg-j6lv">?</td>
388
+ </tr>
389
+ <tr>
390
+ <td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
391
+ <td class="ok">not supported</td>
392
+ <td class="tg-j6lv">not supported</td>
393
+ <td class="tg-j6lv">not supported</td>
394
+ <td class="ok">not supported</td>
395
+ <td class="tg-j6lv">✔</td>
396
+ <td class="tg-j6lv">?</td>
397
+ </tr>
398
+ <tr>
399
+ <td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
400
+ <td class="ok">not supported</td>
401
+ <td class="tg-j6lv">✔</td>
402
+ <td class="tg-j6lv">✔</td>
403
+ <td class="ok">✔</td>
404
+ <td class="tg-j6lv">✔</td>
405
+ <td class="tg-j6lv">?</td>
406
+ </tr>
407
+ <tr>
408
+ <td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
409
+ <td class="ok">✔</td>
410
+ <td class="tg-j6lv">✔</td>
411
+ <td class="tg-j6lv">not supported</td>
412
+ <td class="ok">✔</td>
413
+ <td class="tg-j6lv">✔</td>
414
+ <td class="tg-j6lv">?</td>
415
+ </tr>
416
+
528
417
  <tr>
529
418
  <th class="tg-4rp9" rowspan="6"><div>Date functions</div></th>
530
419
  <td class="tg-yw4l">DATEADD<br>column + 2.year<br></td>
@@ -581,16 +470,7 @@ User.connection.execute(insert_manager.to_sql)
581
470
  <td class="ok">✔</td>
582
471
  </tr>
583
472
  <tr>
584
- <th class="tg-72dn" rowspan="13"><div>Comparators functions</div></th>
585
- <td class="tg-yw4l">BLANK<br>column.blank<br></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>
473
+ <th class="tg-72dn" rowspan="8"><div>Comparators functions</div></th>
594
474
  <td class="tg-yw4l">COALESCE<br>column.coalesce(var)</td>
595
475
  <td class="ok">✔</td>
596
476
  <td class="ok">✔</td>
@@ -599,24 +479,6 @@ User.connection.execute(insert_manager.to_sql)
599
479
  <td class="ok">✔</td>
600
480
  <td class="ok">✔</td>
601
481
  </tr>
602
- <tr>
603
- <td class="tg-yw4l">COALESCE_BLANK<br>column.coalesce_blank(var)</td>
604
- <td class="ok">✔</td>
605
- <td class="ok">✔</td>
606
- <td class="ok">✔</td>
607
- <td class="ok">✔</td>
608
- <td class="ok">✔</td>
609
- <td class="ok">✔</td>
610
- </tr>
611
- <tr>
612
- <td class="tg-yw4l">IF_PRESENT</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
482
  <tr>
621
483
  <td class="tg-yw4l">ISNULL<br>column.isnull()</td>
622
484
  <td class="tg-j6lv">IFNULL()</td>
@@ -626,24 +488,6 @@ User.connection.execute(insert_manager.to_sql)
626
488
  <td class="ok">✔</td>
627
489
  <td class="ok">✔</td>
628
490
  </tr>
629
- <tr>
630
- <td class="tg-yw4l">NOT_BLANK<br>column.not_blank<br></td>
631
- <td class="ok">✔</td>
632
- <td class="ok">✔</td>
633
- <td class="ok">✔</td>
634
- <td class="ok">✔</td>
635
- <td class="ok">✔</td>
636
- <td class="ok">✔</td>
637
- </tr>
638
- <tr>
639
- <td class="tg-yw4l">PRESENT<br>column.present<br>alias to NOT_BLANK<br></td>
640
- <td class="ok">✔</td>
641
- <td class="ok">✔</td>
642
- <td class="ok">✔</td>
643
- <td class="ok">✔</td>
644
- <td class="ok">✔</td>
645
- <td class="ok">✔</td>
646
- </tr>
647
491
  <tr>
648
492
  <td class="tg-yw4l">==<br>column == integer</td>
649
493
  <td class="ok">✔</td>
@@ -747,14 +591,3 @@ User.connection.execute(insert_manager.to_sql)
747
591
  </tr>
748
592
  </tbody>
749
593
  </table>
750
-
751
- ## Version Compatibility
752
-
753
- <table>
754
- <tr><th>Ruby</th> <th>Rails</th> <th>Arel Extensions</th></tr>
755
- <tr><td>3.1</td> <td>6.1</td> <td>2</td></tr>
756
- <tr><td>3.0</td> <td>6.1</td> <td>2</td></tr>
757
- <tr><td>2.7</td> <td>6.1, 6.0</td> <td>2</td></tr>
758
- <tr><td>2.5</td> <td>6.1, 6.0</td> <td>2</td></tr>
759
- <tr><td>2.5</td> <td>5.2</td> <td>1</td></tr>
760
- </table>
data/Rakefile CHANGED
@@ -1,57 +1,39 @@
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'
7
- task default: [:test]
6
+ desc "Default Task"
7
+ task default: [ :test ]
8
8
 
9
9
  Rake::TestTask.new(:test) do |t|
10
- t.libs << 'lib'
11
- t.libs << 'test'
12
- t.pattern = 'test/**/test_*.rb'
13
- t.warning = true
14
- t.verbose = true
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/test_*.rb'
13
+ t.warning = true
14
+ t.verbose = true
15
15
  end
16
16
 
17
- namespace :test do
18
- Rake::TestTask.new('to_sql' => []) { |t|
19
- t.libs << 'lib'
20
- t.libs << 'test'
21
- t.pattern = 'test/visitors/test_to_sql.rb'
22
- t.warning = true
23
- t.verbose = true
24
- t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
25
- }
26
- end
27
-
28
- %w[mysql postgresql sqlite ibm_db oracle mssql].each do |adapter|
29
- namespace :test do
30
- Rake::TestTask.new(adapter => "#{adapter}:env") { |t|
31
- t.libs << 'lib'
32
- t.libs << 'test'
33
- t.pattern = 'test/with_ar/*_agnostic_test.rb'
34
- t.warning = false
35
- t.verbose = true
36
- t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
37
- }
38
- end
39
-
40
- namespace adapter do
41
- task test: "test_#{adapter}"
42
- task isolated_test: "isolated_test_#{adapter}"
43
-
44
- # Set the connection environment for the adapter
45
- task(:env) { ENV['DB'] = adapter }
46
- end
47
-
48
- # Make sure the adapter test evaluates the env setting task
49
- task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
50
- end
51
-
52
- # Useful shorthands.
53
- namespace :test do
54
- task :sql => :to_sql
55
- task :pg => :postgresql
56
- task :postgres => :postgresql
17
+ %w(mysql postgresql sqlite ibm_db oracle mssql).each do |adapter|
18
+ namespace :test do
19
+ Rake::TestTask.new(adapter => "#{adapter}:env") { |t|
20
+ t.libs << 'lib'
21
+ t.libs << 'test'
22
+ t.pattern = 'test/with_ar/*_agnostic_test.rb'
23
+ t.warning = true
24
+ t.verbose = true
25
+ t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
26
+ }
27
+ end
28
+
29
+ namespace adapter do
30
+ task :test => "test_#{adapter}"
31
+ task :isolated_test => "isolated_test_#{adapter}"
32
+
33
+ # Set the connection environment for the adapter
34
+ task(:env) { ENV['DB'] = adapter }
35
+ end
36
+
37
+ # Make sure the adapter test evaluates the env setting task
38
+ task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
57
39
  end
data/TODO CHANGED
@@ -1,6 +1,7 @@
1
1
  Code quality:
2
2
  + codeclimate.com
3
3
  + gemnasium
4
+ + appveyor.com (windows)
4
5
  + hakiri.io
5
6
  - coveralls.io
6
7
  - inch-ci.org