arel_extensions 2.4.0 → 2.5.0

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rubocop.yml +131 -5
  4. data/Makefile +31 -1
  5. data/NEWS.md +15 -0
  6. data/README.md +11 -2
  7. data/Rakefile +5 -3
  8. data/arel_extensions.gemspec +3 -5
  9. data/gemfiles/rails5.gemfile +15 -8
  10. data/gemfiles/rails5_1_4.gemfile +11 -9
  11. data/gemfiles/rails5_2.gemfile +16 -9
  12. data/gemfiles/rails6.gemfile +13 -7
  13. data/gemfiles/rails6_1.gemfile +13 -7
  14. data/gemfiles/rails7.gemfile +15 -9
  15. data/gemfiles/rails7_1.gemfile +15 -8
  16. data/gemfiles/rails7_2.gemfile +12 -6
  17. data/gemfiles/rails8.gemfile +7 -7
  18. data/gemfiles/rails8_1.gemfile +4 -4
  19. data/gemspecs/arel_extensions-v1.gemspec +3 -5
  20. data/gemspecs/arel_extensions-v2.gemspec +3 -5
  21. data/lib/arel_extensions/aliases.rb +3 -3
  22. data/lib/arel_extensions/attributes.rb +2 -0
  23. data/lib/arel_extensions/boolean_functions.rb +9 -7
  24. data/lib/arel_extensions/common_sql_functions.rb +5 -3
  25. data/lib/arel_extensions/comparators.rb +6 -4
  26. data/lib/arel_extensions/date_duration.rb +2 -0
  27. data/lib/arel_extensions/helpers.rb +17 -17
  28. data/lib/arel_extensions/insert_manager.rb +5 -1
  29. data/lib/arel_extensions/math.rb +40 -44
  30. data/lib/arel_extensions/math_functions.rb +28 -30
  31. data/lib/arel_extensions/nodes/aggregate_function.rb +4 -4
  32. data/lib/arel_extensions/nodes/blank.rb +6 -4
  33. data/lib/arel_extensions/nodes/case.rb +15 -12
  34. data/lib/arel_extensions/nodes/cast.rb +3 -5
  35. data/lib/arel_extensions/nodes/coalesce.rb +1 -1
  36. data/lib/arel_extensions/nodes/collate.rb +1 -1
  37. data/lib/arel_extensions/nodes/concat.rb +6 -6
  38. data/lib/arel_extensions/nodes/date_diff.rb +40 -49
  39. data/lib/arel_extensions/nodes/duration.rb +2 -2
  40. data/lib/arel_extensions/nodes/format.rb +2 -2
  41. data/lib/arel_extensions/nodes/formatted_date.rb +2 -2
  42. data/lib/arel_extensions/nodes/formatted_number.rb +2 -2
  43. data/lib/arel_extensions/nodes/function.rb +6 -4
  44. data/lib/arel_extensions/nodes/is_null.rb +2 -0
  45. data/lib/arel_extensions/nodes/json.rb +27 -15
  46. data/lib/arel_extensions/nodes/levenshtein_distance.rb +2 -2
  47. data/lib/arel_extensions/nodes/locate.rb +1 -1
  48. data/lib/arel_extensions/nodes/power.rb +2 -2
  49. data/lib/arel_extensions/nodes/rand.rb +2 -2
  50. data/lib/arel_extensions/nodes/repeat.rb +2 -2
  51. data/lib/arel_extensions/nodes/replace.rb +3 -3
  52. data/lib/arel_extensions/nodes/rollup.rb +4 -4
  53. data/lib/arel_extensions/nodes/round.rb +3 -3
  54. data/lib/arel_extensions/nodes/select.rb +1 -1
  55. data/lib/arel_extensions/nodes/std.rb +4 -4
  56. data/lib/arel_extensions/nodes/substring.rb +52 -2
  57. data/lib/arel_extensions/nodes/then.rb +1 -1
  58. data/lib/arel_extensions/nodes/trim.rb +2 -2
  59. data/lib/arel_extensions/nodes/union.rb +3 -3
  60. data/lib/arel_extensions/nodes/union_all.rb +3 -3
  61. data/lib/arel_extensions/nodes/wday.rb +2 -2
  62. data/lib/arel_extensions/null_functions.rb +5 -7
  63. data/lib/arel_extensions/predications.rb +22 -18
  64. data/lib/arel_extensions/string_functions.rb +26 -27
  65. data/lib/arel_extensions/version.rb +3 -1
  66. data/lib/arel_extensions/visitors/convert_format.rb +2 -2
  67. data/lib/arel_extensions/visitors/ibm_db.rb +5 -5
  68. data/lib/arel_extensions/visitors/mssql.rb +91 -92
  69. data/lib/arel_extensions/visitors/mysql.rb +89 -69
  70. data/lib/arel_extensions/visitors/oracle.rb +100 -104
  71. data/lib/arel_extensions/visitors/oracle12.rb +5 -6
  72. data/lib/arel_extensions/visitors/postgresql.rb +107 -94
  73. data/lib/arel_extensions/visitors/sqlite.rb +57 -65
  74. data/lib/arel_extensions/visitors/to_sql.rb +84 -74
  75. data/lib/arel_extensions/visitors.rb +6 -6
  76. data/lib/arel_extensions/warning.rb +5 -5
  77. data/lib/arel_extensions.rb +34 -27
  78. data/test/real_db_test.rb +31 -31
  79. data/test/visitors/test_to_sql.rb +2 -2
  80. data/test/with_ar/all_agnostic_test.rb +149 -15
  81. data/version_v1.rb +3 -1
  82. data/version_v2.rb +3 -1
  83. metadata +2 -30
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'arel'
2
4
  require 'arel_extensions/constants'
3
5
  require 'base64'
@@ -20,7 +22,7 @@ class Arel::Nodes::Casted
20
22
  # They forget to define hash.
21
23
  if ArelExtensions::AREL_VERSION < ArelExtensions::V10
22
24
  def hash
23
- [self.class, self.val, self.attribute].hash
25
+ [self.class, val, attribute].hash
24
26
  end
25
27
  end
26
28
  end
@@ -36,8 +38,8 @@ class Arel::Nodes::Grouping
36
38
  end
37
39
 
38
40
  class Arel::Nodes::Ordering
39
- def eql? other
40
- self.hash.eql? other.hash
41
+ def eql?(other)
42
+ hash.eql? other.hash
41
43
  end
42
44
  end
43
45
 
@@ -88,7 +90,7 @@ require 'arel_extensions/nodes/select'
88
90
  # adapter behave differently. It doesn't always proc, so we added this for
89
91
  # coverage.
90
92
  if defined?(Arel::Visitors::SQLServer)
91
- Arel::Visitors.const_set('MSSQL', Arel::Visitors::SQLServer)
93
+ Arel::Visitors.const_set(:MSSQL, Arel::Visitors::SQLServer)
92
94
  require 'arel_extensions/visitors/mssql'
93
95
  class Arel::Visitors::SQLServer
94
96
  include ArelExtensions::Visitors::MSSQL
@@ -96,11 +98,11 @@ if defined?(Arel::Visitors::SQLServer)
96
98
  end
97
99
 
98
100
  module Arel
99
- def self.column_of table_name, column_name
101
+ def self.column_of(table_name, column_name)
100
102
  ArelExtensions.column_of(table_name, column_name)
101
103
  end
102
104
 
103
- def self.duration s, expr
105
+ def self.duration(s, expr)
104
106
  ArelExtensions::Nodes::Duration.new("#{s}i", expr)
105
107
  end
106
108
 
@@ -109,11 +111,11 @@ module Arel
109
111
  Arel::Nodes::Equality.new(1, 0)
110
112
  end
111
113
 
112
- def self.grouping *v
114
+ def self.grouping(*v)
113
115
  Arel::Nodes::Grouping.new(*v)
114
116
  end
115
117
 
116
- def self.json *expr
118
+ def self.json(*expr)
117
119
  ArelExtensions::Nodes::Json.new(
118
120
  if expr.length == 1
119
121
  expr.first
@@ -148,7 +150,7 @@ module Arel
148
150
  Arel.quoted(nil)
149
151
  end
150
152
 
151
- def self.quoted *args
153
+ def self.quoted(*args)
152
154
  Arel::Nodes.build_quoted(*args)
153
155
  end
154
156
 
@@ -160,7 +162,7 @@ module Arel
160
162
  Arel::Nodes::RollUp.new(args)
161
163
  end
162
164
 
163
- def self.shorten s
165
+ def self.shorten(s)
164
166
  Base64.urlsafe_encode64(Digest::MD5.new.digest(s)).tr('=', '').tr('-', '_')
165
167
  end
166
168
 
@@ -169,9 +171,9 @@ module Arel
169
171
  Arel::Nodes::Equality.new(1, 1)
170
172
  end
171
173
 
172
- def self.tuple *v
174
+ def self.tuple(*v)
173
175
  tmp = Arel.grouping(nil)
174
- Arel.grouping v.map{|e| tmp.convert_to_node(e)}
176
+ Arel.grouping(v.map { |e| tmp.convert_to_node(e) })
175
177
  end
176
178
 
177
179
  # For instance
@@ -179,7 +181,7 @@ module Arel
179
181
  # ```
180
182
  # Arel.when(at[field].is_null).then(0).else(1)
181
183
  # ```
182
- def self.when condition
184
+ def self.when(condition)
183
185
  ArelExtensions::Nodes::Case.new.when(condition)
184
186
  end
185
187
  end
@@ -202,7 +204,7 @@ class Arel::Nodes::Function
202
204
 
203
205
  if ArelExtensions::ACTIVE_RECORD_VERSION >= ArelExtensions::V8_1
204
206
  attr_accessor :alias
205
- alias_method :old_initialize, :initialize
207
+ alias old_initialize initialize
206
208
 
207
209
  def initialize(expr, aliaz = nil)
208
210
  old_initialize(expr)
@@ -210,9 +212,9 @@ class Arel::Nodes::Function
210
212
  end
211
213
  end
212
214
 
213
- alias_method(:old_as, :as) rescue nil
214
- def as other
215
- res = Arel::Nodes::As.new(self.clone, Arel.sql(other))
215
+ alias old_as as rescue nil
216
+ def as(other)
217
+ res = Arel::Nodes::As.new(clone, Arel.sql(other))
216
218
  self.alias = Arel.sql(other)
217
219
  res
218
220
  end
@@ -234,7 +236,8 @@ class Arel::Nodes::Unary
234
236
  include ArelExtensions::MathFunctions
235
237
  include ArelExtensions::Comparators
236
238
  include ArelExtensions::Predications
237
- def eql? other
239
+
240
+ def eql?(other)
238
241
  hash == other.hash
239
242
  end
240
243
  end
@@ -246,7 +249,8 @@ class Arel::Nodes::Binary
246
249
  include ArelExtensions::Comparators
247
250
  include ArelExtensions::BooleanFunctions
248
251
  include ArelExtensions::Predications
249
- def eql? other
252
+
253
+ def eql?(other)
250
254
  hash == other.hash
251
255
  end
252
256
  end
@@ -267,12 +271,12 @@ class Arel::SelectManager
267
271
  include ArelExtensions::Nodes
268
272
 
269
273
  remove_method(:as) if method_defined?(:as)
270
- def as table_name
274
+ def as(table_name)
271
275
  Arel::Nodes::TableAlias.new(self, table_name)
272
276
  end
273
277
 
274
278
  # Install an alias, if present.
275
- def xas table_name
279
+ def xas(table_name)
276
280
  if table_name.present?
277
281
  as table_name
278
282
  else
@@ -286,12 +290,12 @@ class Arel::Nodes::As
286
290
  end
287
291
 
288
292
  class Arel::Table
289
- alias_method(:old_alias, :alias) rescue nil
293
+ alias old_alias alias rescue nil
290
294
 
291
295
  # activerecord 7.1 removed the alias. We might need to remove our dependency
292
296
  # on the alias if it proves problematic.
293
- if !self.respond_to?(:table_name)
294
- alias :table_name :name
297
+ if !respond_to?(:table_name)
298
+ alias table_name name
295
299
  end
296
300
 
297
301
  def alias(name = "#{self.name}_2")
@@ -306,14 +310,17 @@ end
306
310
  class Arel::Nodes::TableAlias
307
311
  def method_missing(*args)
308
312
  met = args.shift.to_sym
309
- if self.relation.respond_to?(met)
310
- self.relation.send(met, args)
313
+ if relation.respond_to?(met)
314
+ relation.send(met, args)
311
315
  else
312
316
  super(met, *args)
313
317
  end
314
318
  end
315
- end
316
319
 
320
+ def respond_to_missing?(method_name, include_private = false)
321
+ relation.respond_to?(method_name) || super
322
+ end
323
+ end
317
324
 
318
325
  class Arel::Attributes::Attribute
319
326
  def to_sql(engine = Arel::Table.engine)
data/test/real_db_test.rb CHANGED
@@ -72,24 +72,24 @@ class ListTest < Minitest::Test
72
72
  end
73
73
 
74
74
  def test_abs
75
- assert_equal 0, User.where(User.arel_table[:name].eq('Negatif')).select((User.arel_table[:age].abs).as('res')).first.res
76
- assert_equal 14, User.where(User.arel_table[:name].eq('Laure')).select(((User.arel_table[:age] - 39).abs).as('res')).first.res
75
+ assert_equal 0, User.where(User.arel_table[:name].eq('Negatif')).select(User.arel_table[:age].abs.as('res')).first.res
76
+ assert_equal 14, User.where(User.arel_table[:name].eq('Laure')).select((User.arel_table[:age] - 39).abs.as('res')).first.res
77
77
  end
78
78
 
79
79
  def test_ceil
80
80
  if !$sqlite || !$load_extension_disabled
81
- assert_equal 1, User.where(User.arel_table[:name].eq('Negatif')).select((User.arel_table[:score].ceil).as('res')).first.res
82
- assert_equal 66, User.where(User.arel_table[:name].eq('Arthur')).select((User.arel_table[:score].ceil).as('res')).first.res
81
+ assert_equal 1, User.where(User.arel_table[:name].eq('Negatif')).select(User.arel_table[:score].ceil.as('res')).first.res
82
+ assert_equal 66, User.where(User.arel_table[:name].eq('Arthur')).select(User.arel_table[:score].ceil.as('res')).first.res
83
83
  end
84
84
  end
85
85
 
86
86
  def test_coalesce
87
87
  if /pgsql/i.match?(@cnx.adapter_name)
88
- assert_equal 100, User.where(User.arel_table[:name].eq('Test')).select((User.arel_table[:age].coalesce(100)).as('res')).first.res
89
- assert_equal 'Camille', User.where(User.arel_table[:name].eq('Camille')).select((User.arel_table[:name].coalesce('Null', 'default')).as('res')).first.res
88
+ assert_equal 100, User.where(User.arel_table[:name].eq('Test')).select(User.arel_table[:age].coalesce(100).as('res')).first.res
89
+ assert_equal 'Camille', User.where(User.arel_table[:name].eq('Camille')).select(User.arel_table[:name].coalesce('Null', 'default').as('res')).first.res
90
90
  else
91
- assert_equal 'Camille', User.where(User.arel_table[:name].eq('Camille')).select((User.arel_table[:name].coalesce('Null', 20)).as('res')).first.res
92
- assert_equal 20, User.where(User.arel_table[:name].eq('Test')).select((User.arel_table[:age].coalesce(nil, 20)).as('res')).first.res
91
+ assert_equal 'Camille', User.where(User.arel_table[:name].eq('Camille')).select(User.arel_table[:name].coalesce('Null', 20).as('res')).first.res
92
+ assert_equal 20, User.where(User.arel_table[:name].eq('Test')).select(User.arel_table[:age].coalesce(nil, 20).as('res')).first.res
93
93
  end
94
94
  end
95
95
 
@@ -115,45 +115,45 @@ class ListTest < Minitest::Test
115
115
 
116
116
  def test_date_duration
117
117
  # Year
118
- assert_equal 2016, User.where(User.arel_table[:name].eq('Lucas')).select((User.arel_table[:created_at].year).as('res')).first.res.to_i
118
+ assert_equal 2016, User.where(User.arel_table[:name].eq('Lucas')).select(User.arel_table[:created_at].year.as('res')).first.res.to_i
119
119
  assert_equal 0, User.where(User.arel_table[:created_at].year.eq('2012')).count
120
120
  # Month
121
- assert_equal 5, User.where(User.arel_table[:name].eq('Camille')).select((User.arel_table[:created_at].month).as('res')).first.res.to_i
121
+ assert_equal 5, User.where(User.arel_table[:name].eq('Camille')).select(User.arel_table[:created_at].month.as('res')).first.res.to_i
122
122
  assert_equal 8, User.where(User.arel_table[:created_at].month.eq('05')).count
123
123
  # Week
124
- assert_equal 21, User.where(User.arel_table[:name].eq('Arthur')).select((User.arel_table[:created_at].week).as('res')).first.res.to_i
124
+ assert_equal 21, User.where(User.arel_table[:name].eq('Arthur')).select(User.arel_table[:created_at].week.as('res')).first.res.to_i
125
125
  assert_equal 8, User.where(User.arel_table[:created_at].month.eq('05')).count
126
126
  # Day
127
- assert_equal 23, User.where(User.arel_table[:name].eq('Laure')).select((User.arel_table[:created_at].day).as('res')).first.res.to_i
127
+ assert_equal 23, User.where(User.arel_table[:name].eq('Laure')).select(User.arel_table[:created_at].day.as('res')).first.res.to_i
128
128
  assert_equal 0, User.where(User.arel_table[:created_at].day.eq('05')).count
129
129
  end
130
130
 
131
131
  def test_length
132
- assert_equal 7, User.where(User.arel_table[:name].eq('Camille')).select((User.arel_table[:name].length).as('res')).first.res
133
- assert_equal 5, User.where(User.arel_table[:name].eq('Laure')).select((User.arel_table[:name].length).as('res')).first.res
132
+ assert_equal 7, User.where(User.arel_table[:name].eq('Camille')).select(User.arel_table[:name].length.as('res')).first.res
133
+ assert_equal 5, User.where(User.arel_table[:name].eq('Laure')).select(User.arel_table[:name].length.as('res')).first.res
134
134
  end
135
135
 
136
136
  def test_locate
137
137
  if !$sqlite || !$load_extension_disabled
138
- assert_equal 1, User.where(User.arel_table[:name].eq('Camille')).select((User.arel_table[:name].locate('C')).as('res')).first.res
139
- assert_equal 0, User.where(User.arel_table[:name].eq('Lucas')).select((User.arel_table[:name].locate('z')).as('res')).first.res
140
- assert_equal 5, User.where(User.arel_table[:name].eq('Lucas')).select((User.arel_table[:name].locate('s')).as('res')).first.res
138
+ assert_equal 1, User.where(User.arel_table[:name].eq('Camille')).select(User.arel_table[:name].locate('C').as('res')).first.res
139
+ assert_equal 0, User.where(User.arel_table[:name].eq('Lucas')).select(User.arel_table[:name].locate('z').as('res')).first.res
140
+ assert_equal 5, User.where(User.arel_table[:name].eq('Lucas')).select(User.arel_table[:name].locate('s').as('res')).first.res
141
141
  end
142
142
  end
143
143
 
144
144
  def test_isnull
145
145
  if /pgsql/i.match?(ActiveRecord::Base.connection.adapter_name)
146
- assert_equal 100, User.where(User.arel_table[:name].eq('Test')).select((User.arel_table[:age].isnull(100)).as('res')).first.res
146
+ assert_equal 100, User.where(User.arel_table[:name].eq('Test')).select(User.arel_table[:age].isnull(100).as('res')).first.res
147
147
  else
148
- assert_equal 'default', User.where(User.arel_table[:name].eq('Test')).select((User.arel_table[:age].isnull('default')).as('res')).first.res
149
- assert_equal 'Test', User.where((User.arel_table[:age].isnull('default')).eq('default')).select(User.arel_table[:name]).first.name.to_s
148
+ assert_equal 'default', User.where(User.arel_table[:name].eq('Test')).select(User.arel_table[:age].isnull('default').as('res')).first.res
149
+ assert_equal 'Test', User.where(User.arel_table[:age].isnull('default').eq('default')).select(User.arel_table[:name]).first.name.to_s
150
150
  end
151
151
  end
152
152
 
153
153
  def test_floor
154
154
  if !$sqlite || !$load_extension_disabled
155
- assert_equal 0, User.where(User.arel_table[:name].eq('Negatif')).select((User.arel_table[:score].floor).as('res')).first.res
156
- assert_equal 65, User.where(User.arel_table[:name].eq('Arthur')).select((User.arel_table[:score].floor).as('res')).first.res
155
+ assert_equal 0, User.where(User.arel_table[:name].eq('Negatif')).select(User.arel_table[:score].floor.as('res')).first.res
156
+ assert_equal 65, User.where(User.arel_table[:name].eq('Arthur')).select(User.arel_table[:score].floor.as('res')).first.res
157
157
  end
158
158
  end
159
159
 
@@ -210,19 +210,19 @@ class ListTest < Minitest::Test
210
210
  end
211
211
 
212
212
  def test_replace
213
- assert_equal 'LucaX', User.where(User.arel_table[:name].eq('Lucas')).select((User.arel_table[:name].replace('s', 'X')).as('res')).first.res
214
- assert_equal 'replace', User.where(User.arel_table[:name].eq('Lucas')).select((User.arel_table[:name].replace(User.arel_table[:name], 'replace')).as('res')).first.res
213
+ assert_equal 'LucaX', User.where(User.arel_table[:name].eq('Lucas')).select(User.arel_table[:name].replace('s', 'X').as('res')).first.res
214
+ assert_equal 'replace', User.where(User.arel_table[:name].eq('Lucas')).select(User.arel_table[:name].replace(User.arel_table[:name], 'replace').as('res')).first.res
215
215
  end
216
216
 
217
217
  def test_round
218
- assert_equal 1, User.where(((User.arel_table[:age]).round(0)).eq(5.0)).count
219
- assert_equal 0, User.where(((User.arel_table[:age]).round(-1)).eq(6.0)).count
218
+ assert_equal 1, User.where(User.arel_table[:age].round(0).eq(5.0)).count
219
+ assert_equal 0, User.where(User.arel_table[:age].round(-1).eq(6.0)).count
220
220
  end
221
221
 
222
222
  def test_Soundex
223
223
  if !$sqlite || !$load_extension_disabled
224
- assert_equal 'C540', User.where(User.arel_table[:name].eq('Camille')).select((User.arel_table[:name].soundex).as('res')).first.res.to_s
225
- assert_equal 8, User.where((User.arel_table[:name].soundex).eq(User.arel_table[:name].soundex)).count
224
+ assert_equal 'C540', User.where(User.arel_table[:name].eq('Camille')).select(User.arel_table[:name].soundex.as('res')).first.res.to_s
225
+ assert_equal 8, User.where(User.arel_table[:name].soundex.eq(User.arel_table[:name].soundex)).count
226
226
  end
227
227
  end
228
228
 
@@ -230,8 +230,8 @@ class ListTest < Minitest::Test
230
230
  # .take(50) because of limit by ORDER BY
231
231
  assert_equal 110, User.select((User.arel_table[:age].sum + 1).as('res')).take(50).first.res
232
232
  assert_equal 218, User.select((User.arel_table[:age].sum + User.arel_table[:age].sum).as('res')).take(50).first.res
233
- assert_equal 327, User.select(((User.arel_table[:age] * 3).sum).as('res')).take(50).first.res
234
- assert_equal 2245, User.select(((User.arel_table[:age] * User.arel_table[:age]).sum).as('res')).take(50).first.res
233
+ assert_equal 327, User.select((User.arel_table[:age] * 3).sum.as('res')).take(50).first.res
234
+ assert_equal 2245, User.select((User.arel_table[:age] * User.arel_table[:age]).sum.as('res')).take(50).first.res
235
235
  end
236
236
 
237
237
  def test_trim
@@ -244,7 +244,7 @@ class ListTest < Minitest::Test
244
244
 
245
245
  def test_wday
246
246
  d = Date.new(2016, 06, 26)
247
- assert_equal 1, User.where(User.arel_table[:name].eq('Myung')).select((User.arel_table[:created_at].wday).as('res')).first.res.to_i
247
+ assert_equal 1, User.where(User.arel_table[:name].eq('Myung')).select(User.arel_table[:created_at].wday.as('res')).first.res.to_i
248
248
  assert_equal 0, User.select(d.wday).as('res').first.to_i
249
249
  end
250
250
  end
@@ -277,11 +277,11 @@ module ArelExtensions
277
277
  .must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
278
278
  _(c.union_all(c.union_all(c)).to_sql)
279
279
  .must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
280
- _((c.union_all(c)).union_all(c).to_sql)
280
+ _(c.union_all(c).union_all(c).to_sql)
281
281
  .must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
282
282
  _(c.union_all(c).union_all(c).to_sql)
283
283
  .must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
284
- _((c.union_all(c)).as('union_table').to_sql)
284
+ _(c.union_all(c).as('union_table').to_sql)
285
285
  .must_be_like %{((SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")) union_table}
286
286
  end
287
287
 
@@ -52,6 +52,7 @@ module ArelExtensions
52
52
  t.column :duration, :time
53
53
  t.column :other, :string
54
54
  t.column :score, :decimal, precision: 20, scale: 10
55
+ t.column :enabled, :boolean
55
56
  end
56
57
  @cnx.drop_table(:product_tests) rescue nil
57
58
  @cnx.create_table :product_tests do |t|
@@ -74,9 +75,9 @@ module ArelExtensions
74
75
  @lucas = User.where(id: u.id)
75
76
  u = User.create age: 15, name: 'Sophie', created_at: d, score: 20.16
76
77
  @sophie = User.where(id: u.id)
77
- u = User.create age: 20, name: 'Camille', created_at: d, score: -20.16, comments: ''
78
+ u = User.create age: 20, name: 'Camille', created_at: d, score: -20.16, comments: '', enabled: false
78
79
  @camille = User.where(id: u.id)
79
- u = User.create age: 21, name: 'Arthur', created_at: d, score: 65.62, comments: 'arrêté'
80
+ u = User.create age: 21, name: 'Arthur', created_at: d, score: 65.62, comments: 'arrêté', enabled: true
80
81
  @arthur = User.where(id: u.id)
81
82
  u = User.create age: 23, name: 'Myung', created_at: d, score: 20.16, comments: ' '
82
83
  @myung = User.where(id: u.id)
@@ -107,6 +108,7 @@ module ArelExtensions
107
108
  @duration = User.arel_table[:duration]
108
109
  @price = Product.arel_table[:price]
109
110
  @other = User.arel_table[:other]
111
+ @enabled = User.arel_table[:enabled]
110
112
  @not_in_table = User.arel_table[:not_in_table]
111
113
 
112
114
  @ut = User.arel_table
@@ -190,13 +192,13 @@ module ArelExtensions
190
192
  skip 'SQL Server forces order?' # TODO
191
193
  assert_equal 83, User.select((@age.sum + 1).as('res'), User.arel_table[:id].sum).take(50).reorder(@age).first.res
192
194
  assert_equal 164, User.reorder(nil).select((@age.sum + @age.sum).as('res'), User.arel_table[:id].sum).take(50).first.res
193
- assert_equal 246, User.reorder(nil).select(((@age * 3).sum).as('res'), User.arel_table[:id].sum).take(50).first.res
194
- assert_equal 4234, User.reorder(nil).select(((@age * @age).sum).as('res'), User.arel_table[:id].sum).take(50).first.res
195
+ assert_equal 246, User.reorder(nil).select((@age * 3).sum.as('res'), User.arel_table[:id].sum).take(50).first.res
196
+ assert_equal 4234, User.reorder(nil).select((@age * @age).sum.as('res'), User.arel_table[:id].sum).take(50).first.res
195
197
  else
196
198
  assert_equal 83, User.select((@age.sum + 1).as('res')).take(50).first.res
197
199
  assert_equal 164, User.select((@age.sum + @age.sum).as('res')).take(50).first.res
198
200
  assert_equal 246, User.select((@age * 3).sum.as('res')).take(50).first.res
199
- assert_equal 4234, User.select(((@age * @age).sum).as('res')).take(50).first.res
201
+ assert_equal 4234, User.select((@age * @age).sum.as('res')).take(50).first.res
200
202
  end
201
203
  end
202
204
 
@@ -213,7 +215,7 @@ module ArelExtensions
213
215
  end
214
216
 
215
217
  def test_rollup
216
- skip "sqlite not supported" if $sqlite
218
+ skip 'sqlite not supported' if $sqlite
217
219
  at = User.arel_table
218
220
  # single
219
221
  q = User.select(at[:name], at[:age].sum).group(Arel::Nodes::RollUp.new([at[:name]]))
@@ -260,8 +262,9 @@ module ArelExtensions
260
262
  assert_equal 'Camille Camille', t(@camille, @name + ' ' + @name)
261
263
  assert_equal 'Laure 2', t(@laure, @name + ' ' + 2)
262
264
  assert_equal 'Test Laure', t(@laure, Arel.quoted('Test ') + @name)
265
+ end
263
266
 
264
- skip 'No group_concat in SqlServer before 2017' if @env_db == 'mssql'
267
+ def test_group_concat
265
268
  assert_equal 'Lucas Sophie', t(User.where(name: %w[Lucas Sophie]), @name.group_concat(' '))
266
269
  assert_equal 'Lucas,Sophie', t(User.where(name: %w[Lucas Sophie]), @name.group_concat(','))
267
270
  assert_equal 'Lucas,Sophie', t(User.where(name: %w[Lucas Sophie]), @name.group_concat)
@@ -298,6 +301,7 @@ module ArelExtensions
298
301
  skip "Sqlite can't do md5" if $sqlite
299
302
  assert_equal 'e2cf99ca82a7e829d2a4ac85c48154d0', t(@camille, @name.md5)
300
303
  assert_equal 'c3d41bf5efb468a1bcce53bd53726c85', t(@lucas, @name.md5)
304
+ assert_equal '6eb6a030bcce166534b95bc2ab45d9cf', t(@lucas, Arel.quoted('a' * 50).md5)
301
305
  end
302
306
 
303
307
  def test_locate
@@ -323,14 +327,96 @@ module ArelExtensions
323
327
  else
324
328
  assert_equal('', t(@lucas, @name[42]))
325
329
  end
326
- assert_equal 'Lu', t(@lucas, @name[0, 2])
327
- assert_equal 'Lu', t(@lucas, @name[0..1])
328
330
 
329
331
  # substring should accept string function
330
332
  assert_equal 'Ce', t(@camille, @name.substring(1, 1).concat('e'))
331
333
  assert_equal 'Ce', t(@camille, @name.substring(1, 1) + 'e')
332
334
  end
333
335
 
336
+ def assert_substring(expected, scope, node)
337
+ expected = nil if expected == '' && @env_db == 'oracle'
338
+ actual = t(scope, node)
339
+ # Ruby 3.0+ requires assert_nil
340
+ expected.nil? ? assert_nil(actual) : assert_equal(expected, actual)
341
+ end
342
+
343
+ def test_substring_range
344
+ # exclusive end
345
+ assert_substring 'uc', @lucas, @name[1...3]
346
+ assert_substring 'uca', @lucas, @name[1...-1]
347
+
348
+ # boundary: begin exactly at length is valid (empty result),
349
+ # begin one past length is out of range (nil)
350
+ assert_substring '', @lucas, @name[5..6]
351
+ assert_substring nil, @lucas, @name[6..7]
352
+ assert_substring nil, @lucas, @name[100..200]
353
+ assert_substring nil, @lucas, @name[-100..-1]
354
+
355
+ # reversed / empty-result ranges, same-sign and mixed-sign
356
+ assert_substring '', @lucas, @name[3..1]
357
+ assert_substring '', @lucas, @name[-3..1]
358
+
359
+ # singleton slices
360
+ assert_substring 'L', @lucas, @name[0..0]
361
+ assert_substring 's', @lucas, @name[4..4]
362
+
363
+ # a second fixture, sanity-checking against a longer string
364
+ assert_substring 'mil', @camille, @name[2..4]
365
+ assert_substring 'lle', @camille, @name[-3..-1]
366
+
367
+ # NULL column
368
+ assert_nil t(@all_nil, @name[0..2])
369
+
370
+ # trailing whitespace
371
+ assert_substring ' ', @esme2, @name[4..4]
372
+ assert_substring 'esmé ', @esme2, @name[0..-1]
373
+
374
+ # passing a Range and an explicit length together is contradictory.
375
+ assert_raises(ArgumentError) { @name.substring(1..2, 5) }
376
+
377
+ end
378
+
379
+ def endless_range_supported?
380
+ Range.new(0, nil)
381
+ true
382
+ rescue ArgumentError
383
+ false
384
+ end
385
+
386
+ def beginless_range_supported?
387
+ Range.new(nil, 0)
388
+ true
389
+ rescue ArgumentError
390
+ false
391
+ end
392
+
393
+ def test_substring_range_endless
394
+ skip 'endless ranges not supported on this interpreter' if !endless_range_supported?
395
+
396
+ assert_substring 'as', @lucas, @name[Range.new(-2, nil)]
397
+ assert_substring 'Lucas', @lucas, @name[Range.new(-5, nil)] # begin == -length: still in range
398
+ assert_substring nil, @lucas, @name[Range.new(-6, nil)] # begin == -length - 1: out of range
399
+ assert_substring nil, @lucas, @name[Range.new(-100, nil)]
400
+
401
+ assert_nil t(@all_nil, @name[Range.new(-2, nil)])
402
+ assert_substring ' ', @esme2, @name[Range.new(-1, nil)]
403
+
404
+ assert_substring 'as', @lucas, @name.substring(Range.new(-2, nil))
405
+ end
406
+
407
+ def test_substring_range_beginless
408
+ skip 'beginless ranges not supported on this interpreter' if !beginless_range_supported?
409
+
410
+ assert_substring 'Luc', @lucas, @name[Range.new(nil, 2)]
411
+ assert_substring 'Lucas', @lucas, @name[Range.new(nil, -1)]
412
+ assert_substring 'Luca', @lucas, @name[Range.new(nil, -2)]
413
+
414
+ assert_nil t(@all_nil, @name[Range.new(nil, 3)])
415
+ assert_substring 'esmé', @esme2, @name[Range.new(nil, 3)]
416
+
417
+ assert_substring 'Luc', @lucas, @name.substring(Range.new(nil, 2))
418
+ end
419
+
334
420
  def test_find_in_set
335
421
  skip "Sqlite version can't load extension for find_in_set" if $sqlite && $load_extension_disabled
336
422
  skip 'SQL Server does not know about FIND_IN_SET' if @env_db == 'mssql'
@@ -538,7 +624,7 @@ module ArelExtensions
538
624
  # TODO: Standarize timezone conversion across all databases.
539
625
  # This test case will be refactored and should work the same across all vendors.
540
626
  if ENV['DB'] == 'mssql' && /Microsoft SQL Server (\d+)/.match(ActiveRecord::Base.connection.select_value('SELECT @@version'))[1].to_i < 2016
541
- skip "SQL Server < 2016 is not currently supported"
627
+ skip 'SQL Server < 2016 is not currently supported'
542
628
  end
543
629
 
544
630
  tz = ENV['DB'] == 'mssql' ? time_zones['mssql'] : time_zones['posix']
@@ -822,6 +908,7 @@ module ArelExtensions
822
908
  # TODO; cast types
823
909
  def test_cast_types
824
910
  assert_equal '5', t(@lucas, @age.cast(:string))
911
+ assert_equal 'a' * 50, t(@lucas, Arel.quoted('a' * 50).cast(:string))
825
912
  skip 'jdbc adapters does not work properly here (v52 works fine)' if RUBY_PLATFORM.match?(/java/i)
826
913
  if @env_db == 'mysql' || @env_db == 'postgresql' || @env_db == 'oracle' || @env_db == 'mssql'
827
914
  assert_equal 1, t(@laure, Arel.when(@duration.cast(:time).cast(:string).eq('12:42:21')).then(1).else(0)) unless @env_db == 'oracle' || @env_db == 'mssql'
@@ -937,9 +1024,9 @@ module ArelExtensions
937
1024
 
938
1025
  assert_equal 3, User.find_by_sql(@ut.project(@age).where(@age.gt(22)).union_all(@ut.project(@age).where(@age.lt(0))).to_sql).length
939
1026
  assert_equal 3, User.find_by_sql(@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(20))).union_all(@ut.project(@age).where(@age.eq(21))).to_sql).length
940
- assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.gt(22)).union_all(@ut.project(@age).where(@age.lt(0)))).as('my_union')).length
941
- assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(23))).union_all(@ut.project(@age).where(@age.eq(21)))).as('my_union')).length
942
- assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(20))).union_all(@ut.project(@age).where(@age.eq(21)))).as('my_union')).length
1027
+ assert_equal 3, User.select('*').from(@ut.project(@age).where(@age.gt(22)).union_all(@ut.project(@age).where(@age.lt(0))).as('my_union')).length
1028
+ assert_equal 3, User.select('*').from(@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(23))).union_all(@ut.project(@age).where(@age.eq(21))).as('my_union')).length
1029
+ assert_equal 3, User.select('*').from(@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(20))).union_all(@ut.project(@age).where(@age.eq(21))).as('my_union')).length
943
1030
 
944
1031
  assert (@ut.project(@age) + @ut.project(@age)).as('toto').table_name # as on union should answer to table_name (TableAlias)
945
1032
  end
@@ -1145,15 +1232,16 @@ module ArelExtensions
1145
1232
  assert_equal 1, t(@arthur, @name.levenshtein_distance('Artehur'))
1146
1233
  end
1147
1234
 
1148
- def test_json
1149
- skip "Can't be tested on travis"
1235
+ def test_json_create
1150
1236
  # creation
1151
1237
  assert_equal 'Arthur', t(@arthur, Arel.json(@name))
1152
1238
  assert_equal %w[Arthur Arthur], parse_json(t(@arthur, Arel.json(@name, @name)))
1153
1239
  assert_equal ({'Arthur' => 'Arthur', 'Arthur2' => 'ArthurArthur'}), parse_json(t(@arthur, Arel.json({@name => @name, @name + '2' => @name + @name})))
1154
1240
  assert_equal ({'Arthur' => 'Arthur', 'Arthur2' => 1}), parse_json(t(@arthur, Arel.json({@name => @name, @name + '2' => 1})))
1155
1241
  assert_equal [{'age' => 21}, {'name' => 'Arthur', 'score' => 65.62}], parse_json(t(@arthur, Arel.json([{age: @age}, {name: @name, score: @score}])))
1242
+ end
1156
1243
 
1244
+ def test_json_aggregate
1157
1245
  # aggregate
1158
1246
  assert_equal ({'5' => 'Lucas', '15' => 'Sophie', '23' => 'Myung', '25' => 'Laure'}),
1159
1247
  parse_json(t(User.group(:score).where(@age.is_not_null).where(@score == 20.16), Arel.json({@age => @name}).group(false)))
@@ -1164,7 +1252,10 @@ module ArelExtensions
1164
1252
 
1165
1253
  # puts User.group(:score).where(@age.is_not_null).where(@score == 20.16).select(@score, Arel.json({@age => @name}).group(true,[@age])).to_sql
1166
1254
  # puts User.group(:score).where(@age.is_not_null).where(@score == 20.16).select(@score, Arel.json({@age => @name}).group(true,[@age])).to_a
1255
+ end
1167
1256
 
1257
+ def test_json_get
1258
+ skip 'Known failure: postgres ->> returns text, not parsed JSON' if @env_db == 'postgresql'
1168
1259
  skip 'Not Yet Implemented' if $sqlite || %w[oracle mssql].include?(@env_db)
1169
1260
  # get
1170
1261
  h1 = Arel.json({@name => @name + @name, @name + '2' => 1})
@@ -1173,17 +1264,60 @@ module ArelExtensions
1173
1264
  assert_equal ({'age' => 21}), parse_json(t(@arthur, h2.get(0)))
1174
1265
  assert_equal 21, parse_json(t(@arthur, h2.get(0).get('age')))
1175
1266
  assert_nil t(@arthur, h2.get('age'))
1267
+ end
1268
+
1269
+ def test_json_set
1270
+ skip 'Not Yet Implemented' if $sqlite || %w[oracle mssql].include?(@env_db)
1176
1271
  # set
1272
+ h1 = Arel.json({@name => @name + @name, @name + '2' => 1})
1177
1273
  assert_equal ({'Arthur' => %w[toto tata], 'Arthur2' => 1}), parse_json(t(@arthur, h1.set(@name, %w[toto tata])))
1178
1274
  assert_equal ({'Arthur' => 'ArthurArthur', 'Arthur2' => 1, 'Arthur3' => 2}), parse_json(t(@arthur, h1.set(@name + '3', 2)))
1179
1275
  assert_equal ({'Arthur' => 'ArthurArthur', 'Arthur2' => 1, 'Arthur3' => nil}), parse_json(t(@arthur, h1.set(@name + '3', nil)))
1180
1276
  assert_equal ({'Arthur' => 'ArthurArthur', 'Arthur2' => 1, 'Arthur3' => {'a' => 2}}), parse_json(t(@arthur, h1.set(@name + '3', {a: 2})))
1277
+ end
1278
+
1279
+ def test_json_merge
1280
+ skip 'Not Yet Implemented' if $sqlite || %w[oracle mssql].include?(@env_db)
1181
1281
  # merge
1282
+ h1 = Arel.json({@name => @name + @name, @name + '2' => 1})
1182
1283
  assert_equal ({'Arthur' => %w[toto tata], 'Arthur2' => 1, 'Arthur3' => 2}), parse_json(t(@arthur, h1.merge({@name => %w[toto tata]}, {@name + '3' => 2})))
1183
1284
  assert_equal ({'Arthur' => %w[toto tata], 'Arthur2' => 1, 'Arthur3' => 2}), parse_json(t(@arthur, h1.merge({@name => %w[toto tata], @name + '3' => 2})))
1184
1285
  assert_equal ({'Arthur' => 'ArthurArthur', 'Arthur2' => 1}), parse_json(t(@arthur, h1.merge({})))
1185
1286
  end
1186
1287
 
1288
+ def test_json_scalar_values
1289
+ assert_nil t(@arthur, Arel.json(nil))
1290
+
1291
+ assert_equal 42, parse_json(t(@arthur, Arel.json(42)))
1292
+ assert_equal({}, parse_json(t(@arthur, Arel.json({}))))
1293
+ assert_equal [], parse_json(t(@arthur, Arel.json([])))
1294
+
1295
+ assert_equal true, parse_json(t(@arthur, Arel.json(true)))
1296
+ assert_equal ({'Arthur' => false}), parse_json(t(@arthur, Arel.json({@name => false})))
1297
+
1298
+ assert_equal ({'Arthur' => true}), parse_json(t(@arthur, Arel.json({@name => @enabled})))
1299
+ assert_equal ({'Camille' => false}), parse_json(t(@camille, Arel.json({@name => @enabled})))
1300
+ assert_equal ({'Lucas' => nil}), parse_json(t(@lucas, Arel.json({@name => @enabled})))
1301
+ end
1302
+
1303
+ def test_json_special_chars
1304
+ assert_equal ({'Arthur' => 'say "hi"'}), parse_json(t(@arthur, Arel.json({@name => 'say "hi"'})))
1305
+ assert_equal ({'Arthur' => 'a\\b'}), parse_json(t(@arthur, Arel.json({@name => 'a\\b'})))
1306
+ assert_equal ({'Arthur' => "line1\nline2"}), parse_json(t(@arthur, Arel.json({@name => "line1\nline2"})))
1307
+ assert_equal ({'we"ird' => 'Arthur'}), parse_json(t(@arthur, Arel.json({'we"ird' => @name})))
1308
+ end
1309
+
1310
+ def test_json_date_values
1311
+ assert_equal ({'Arthur' => '2016-05-23'}), parse_json(t(@arthur, Arel.json({@name => @created_at})))
1312
+ res = parse_json(t(@laure, Arel.json({@name => @duration})))['Laure']
1313
+ assert_equal '12:42:21', res.sub(/\.\d+\z/, '') # MySQL (unlinke MariaDB) adds `.\d+` to a datetime column inside a json.
1314
+
1315
+ # the generic builder's `:datetime` branch is only used by sqlite and mssql.
1316
+ if $sqlite || @env_db == 'mssql'
1317
+ assert_equal ({'Lucas' => '2014-03-03T12:42:00'}), parse_json(t(@lucas, Arel.json({@name => @updated_at})))
1318
+ end
1319
+ end
1320
+
1187
1321
  def test_as_on_everything
1188
1322
  name = @arthur.select(@name.as('NaMe')).first.attributes
1189
1323
  assert_equal 'Arthur', name['NaMe'] || name['name'] # because of Oracle
data/version_v1.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ArelExtensions
2
- VERSION = '1.6.0'.freeze
4
+ VERSION = '1.7.0'
3
5
  end
data/version_v2.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ArelExtensions
2
- VERSION = '2.4.0'.freeze
4
+ VERSION = '2.5.0'
3
5
  end