arel_extensions 2.0.20 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +153 -72
  3. data/README.md +42 -0
  4. data/arel_extensions.gemspec +1 -1
  5. data/gemfiles/rails5_2.gemfile +6 -5
  6. data/gemfiles/rails6.gemfile +6 -6
  7. data/gemfiles/rails6_1.gemfile +5 -5
  8. data/gemfiles/rails7.gemfile +30 -0
  9. data/gemspecs/arel_extensions-v1.gemspec +1 -1
  10. data/gemspecs/arel_extensions-v2.gemspec +1 -1
  11. data/lib/arel_extensions/aliases.rb +14 -0
  12. data/lib/arel_extensions/attributes.rb +2 -0
  13. data/lib/arel_extensions/insert_manager.rb +19 -17
  14. data/lib/arel_extensions/math.rb +16 -16
  15. data/lib/arel_extensions/nodes/case.rb +2 -1
  16. data/lib/arel_extensions/nodes/cast.rb +1 -1
  17. data/lib/arel_extensions/nodes/function.rb +4 -2
  18. data/lib/arel_extensions/nodes/json.rb +3 -1
  19. data/lib/arel_extensions/nodes/length.rb +6 -0
  20. data/lib/arel_extensions/nodes/replace.rb +0 -8
  21. data/lib/arel_extensions/nodes/union.rb +1 -1
  22. data/lib/arel_extensions/nodes/union_all.rb +1 -1
  23. data/lib/arel_extensions/string_functions.rb +10 -2
  24. data/lib/arel_extensions/version.rb +1 -1
  25. data/lib/arel_extensions/visitors/mssql.rb +1 -1
  26. data/lib/arel_extensions/visitors/mysql.rb +9 -2
  27. data/lib/arel_extensions/visitors/oracle.rb +2 -2
  28. data/lib/arel_extensions/visitors/postgresql.rb +14 -8
  29. data/lib/arel_extensions/visitors/to_sql.rb +14 -9
  30. data/lib/arel_extensions/visitors.rb +9 -1
  31. data/lib/arel_extensions.rb +55 -11
  32. data/test/real_db_test.rb +5 -1
  33. data/test/visitors/test_to_sql.rb +18 -11
  34. data/test/with_ar/all_agnostic_test.rb +14 -4
  35. data/test/with_ar/insert_agnostic_test.rb +6 -2
  36. data/test/with_ar/test_bulk_sqlite.rb +6 -2
  37. data/test/with_ar/test_math_sqlite.rb +6 -2
  38. data/test/with_ar/test_string_mysql.rb +6 -2
  39. data/test/with_ar/test_string_sqlite.rb +6 -2
  40. data/version_v1.rb +1 -1
  41. data/version_v2.rb +1 -1
  42. metadata +9 -7
@@ -46,7 +46,9 @@ module ArelExtensions
46
46
  when DateTime, Time
47
47
  convert_to_node(n.strftime("%Y-%m-%dT%H:%M:%S.%L%:z"))
48
48
  when NilClass
49
- Arel.sql('null')
49
+ Arel.null
50
+ when Arel::SelectManager
51
+ Arel.grouping(n)
50
52
  else
51
53
  convert_to_node(n)
52
54
  end
@@ -2,6 +2,12 @@ module ArelExtensions
2
2
  module Nodes
3
3
  class Length < Function
4
4
  RETURN_TYPE = :integer
5
+ attr_accessor :bytewise
6
+
7
+ def initialize(node, bytewise = true)
8
+ @bytewise = bytewise
9
+ super([node])
10
+ end
5
11
  end
6
12
  end
7
13
  end
@@ -10,10 +10,6 @@ module ArelExtensions
10
10
  @substitute = convert_to_node(substitute)
11
11
  super([@left,@pattern,@substitute])
12
12
  end
13
-
14
- def +(other)
15
- return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
16
- end
17
13
  end
18
14
 
19
15
  class RegexpReplace < Function
@@ -26,10 +22,6 @@ module ArelExtensions
26
22
  @substitute = convert_to_node(substitute)
27
23
  super([@left,@pattern,@substitute])
28
24
  end
29
-
30
- def +(other)
31
- return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
32
- end
33
25
  end
34
26
  end
35
27
  end
@@ -14,7 +14,7 @@ module ArelExtensions
14
14
  end
15
15
 
16
16
  def as other
17
- Arel::Nodes::TableAlias.new Arel::Nodes::Grouping.new(self), Arel::Nodes::SqlLiteral.new(other.to_s)
17
+ Arel::Nodes::TableAlias.new Arel.grouping(self), Arel::Nodes::SqlLiteral.new(other.to_s)
18
18
  end
19
19
  end
20
20
  end
@@ -10,7 +10,7 @@ module ArelExtensions
10
10
  end
11
11
 
12
12
  def as other
13
- Arel::Nodes::TableAlias.new Arel::Nodes::Grouping.new(self), Arel::Nodes::SqlLiteral.new(other.to_s)
13
+ Arel::Nodes::TableAlias.new Arel.grouping(self), Arel::Nodes::SqlLiteral.new(other.to_s)
14
14
  end
15
15
  end
16
16
  end
@@ -24,9 +24,17 @@ module ArelExtensions
24
24
  ArelExtensions::Nodes::FindInSet.new [other, self]
25
25
  end
26
26
 
27
- # LENGTH function returns the length of the value in a text field.
27
+ # LENGTH function returns the length (bytewise) of the value in a text field.
28
28
  def length
29
- ArelExtensions::Nodes::Length.new [self]
29
+ ArelExtensions::Nodes::Length.new self, true
30
+ end
31
+
32
+ def byte_length
33
+ ArelExtensions::Nodes::Length.new self, true
34
+ end
35
+
36
+ def char_length
37
+ ArelExtensions::Nodes::Length.new self, false
30
38
  end
31
39
 
32
40
  # LOCATE function returns the first starting position of a string in another string.
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "2.0.20".freeze
2
+ VERSION = "2.1.0".freeze
3
3
  end
@@ -155,7 +155,7 @@ module ArelExtensions
155
155
  end
156
156
 
157
157
  def visit_ArelExtensions_Nodes_Length o, collector
158
- collector << "LEN("
158
+ collector << "#{o.bytewise ? 'DATALENGTH' : 'LEN'}("
159
159
  collector = visit o.expr, collector
160
160
  collector << ")"
161
161
  collector
@@ -354,9 +354,16 @@ module ArelExtensions
354
354
  else
355
355
  collector = visit o.left, collector
356
356
  end
357
- collector << " AS `"
357
+ collector << " AS "
358
+
359
+ # sometimes these values are already quoted, if they are, don't double quote it
360
+ quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '`' && o.right[-1] != '`'
361
+
362
+ collector << '`' if quote
358
363
  collector = visit o.right, collector
359
- collector << "`"
364
+ collector << '`' if quote
365
+
366
+ collector
360
367
  collector
361
368
  end
362
369
 
@@ -5,7 +5,7 @@ module ArelExtensions
5
5
  SPECIAL_CHARS = {"\t" => 'CHR(9)', "\n" => 'CHR(10)', "\r" => 'CHR(13)'}
6
6
  DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'IW', 'y' => 'YEAR', 'wd' => 'D', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
7
7
  DATE_FORMAT_DIRECTIVES = {
8
- '%Y' => 'IYYY', '%C' => 'CC', '%y' => 'YY', '%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
8
+ '%Y' => 'YYYY', '%C' => 'CC', '%y' => 'YY', '%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
9
9
  '%d' => 'DD', '%e' => 'FMDD', '%j' => 'DDD', '%w' => '', '%A' => 'Day', # day, weekday
10
10
  '%H' => 'HH24', '%k' => '', '%I' => 'HH', '%l' => '', '%P' => 'am', '%p' => 'AM', # hours
11
11
  '%M' => 'MI', '%S' => 'SS', '%L' => 'MS', '%N' => 'US', '%z' => 'tz' # seconds, subseconds
@@ -308,7 +308,7 @@ module ArelExtensions
308
308
  end
309
309
 
310
310
  def visit_ArelExtensions_Nodes_Length o, collector
311
- collector << "LENGTH("
311
+ collector << "LENGTH#{o.bytewise ? 'B' : ''}("
312
312
  collector = visit o.expr, collector
313
313
  collector << ")"
314
314
  collector
@@ -7,7 +7,7 @@ module ArelExtensions
7
7
  }.freeze
8
8
 
9
9
  DATE_FORMAT_DIRECTIVES = {
10
- '%Y' => 'IYYY', '%C' => 'CC', '%y' => 'YY',
10
+ '%Y' => 'YYYY', '%C' => 'CC', '%y' => 'YY',
11
11
  '%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
12
12
  '%d' => 'DD', '%e' => 'FMDD', '%j' => 'DDD', '%w' => '', '%A' => 'Day', # day, weekday
13
13
  '%H' => 'HH24', '%k' => '', '%I' => 'HH', '%l' => '', '%P' => 'am', '%p' => 'AM', # hours
@@ -86,9 +86,15 @@ module ArelExtensions
86
86
  else
87
87
  collector = visit o.left, collector
88
88
  end
89
- collector << " AS \""
89
+ collector << " AS "
90
+
91
+ # sometimes these values are already quoted, if they are, don't double quote it
92
+ quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
93
+
94
+ collector << '"' if quote
90
95
  collector = visit o.right, collector
91
- collector << "\""
96
+ collector << '"' if quote
97
+
92
98
  collector
93
99
  end
94
100
 
@@ -373,6 +379,8 @@ module ArelExtensions
373
379
  Arel::Nodes::SqlLiteral.new('date')
374
380
  when :binary
375
381
  Arel::Nodes::SqlLiteral.new('binary')
382
+ when :jsonb
383
+ Arel::Nodes::SqlLiteral.new('jsonb')
376
384
  else
377
385
  Arel::Nodes::SqlLiteral.new(o.as_attr.to_s)
378
386
  end
@@ -401,13 +409,13 @@ module ArelExtensions
401
409
  ArelExtensions::Nodes::Concat.new([
402
410
  Arel::Nodes::NamedFunction.new('TRIM',[
403
411
  Arel::Nodes::NamedFunction.new('TO_CHAR',[
404
- col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
412
+ Arel.when(col.not_eq 0).then(col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)).else(1),
405
413
  Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
406
414
  ])]),
407
415
  o.type,
408
416
  Arel::Nodes::NamedFunction.new('TRIM',[
409
417
  Arel::Nodes::NamedFunction.new('TO_CHAR',[
410
- col.abs.log10.floor,
418
+ Arel.when(col.not_eq 0).then(col.abs.log10.floor).else(0),
411
419
  Arel::Nodes.build_quoted('FM'+nines_before)
412
420
  ])])
413
421
  ])
@@ -511,8 +519,6 @@ module ArelExtensions
511
519
  collector << '::jsonb'
512
520
  when NilClass
513
521
  collector << %Q['null'::jsonb]
514
- when Arel::Attributes::Attribute
515
- collector = visit o.dict.cast(:jsonb), collector
516
522
  else
517
523
  collector = visit o.dict, collector
518
524
  collector << '::jsonb'
@@ -532,7 +538,7 @@ module ArelExtensions
532
538
 
533
539
  def visit_ArelExtensions_Nodes_JsonGet o,collector
534
540
  collector = visit o.dict, collector
535
- collector << ' -> '
541
+ collector << ' ->> '
536
542
  collector = visit o.key, collector
537
543
  collector
538
544
  end
@@ -8,10 +8,15 @@ module ArelExtensions
8
8
  def make_json_string expr
9
9
  Arel::Nodes.build_quoted('"') \
10
10
  + expr
11
+ .coalesce('')
11
12
  .replace('\\','\\\\').replace('"','\"').replace("\n", '\n') \
12
13
  + '"'
13
14
  end
14
15
 
16
+ def make_json_null
17
+ Arel::Nodes.build_quoted("null")
18
+ end
19
+
15
20
  # Math Functions
16
21
  def visit_ArelExtensions_Nodes_Abs o, collector
17
22
  collector << "ABS("
@@ -117,7 +122,7 @@ module ArelExtensions
117
122
  end
118
123
 
119
124
  def visit_ArelExtensions_Nodes_Length o, collector
120
- collector << "LENGTH("
125
+ collector << "#{o.bytewise ? '' : 'CHAR_'}LENGTH("
121
126
  collector = visit o.left, collector
122
127
  collector << ")"
123
128
  collector
@@ -601,20 +606,20 @@ module ArelExtensions
601
606
  def json_value(o,v)
602
607
  case o.type_of_node(v)
603
608
  when :string
604
- Arel.when(v.is_null).then(Arel::Nodes.build_quoted("null")).else(make_json_string(v))
609
+ Arel.when(v.is_null).then(make_json_null).else(make_json_string(v))
605
610
  when :date
606
611
  s = v.format('%Y-%m-%d')
607
- Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
612
+ Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
608
613
  when :datetime
609
614
  s = v.format('%Y-%m-%dT%H:%M:%S')
610
- Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
615
+ Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
611
616
  when :time
612
617
  s = v.format('%H:%M:%S')
613
- Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
618
+ Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
614
619
  when :nil
615
- Arel::Nodes.build_quoted("null")
620
+ make_json_null
616
621
  else
617
- ArelExtensions::Nodes::Cast.new([v, :string]).coalesce("null")
622
+ ArelExtensions::Nodes::Cast.new([v, :string]).coalesce(make_json_null)
618
623
  end
619
624
  end
620
625
 
@@ -636,7 +641,7 @@ module ArelExtensions
636
641
  if i != 0
637
642
  res += ', '
638
643
  end
639
- res += make_json_string(ArelExtensions::Nodes::Cast.new([k, :string]).coalesce("")) + ': '
644
+ res += make_json_string(ArelExtensions::Nodes::Cast.new([k, :string])) + ': '
640
645
  res += json_value(o,v)
641
646
  end
642
647
  res += '}'
@@ -658,7 +663,7 @@ module ArelExtensions
658
663
  if i != 0
659
664
  res = res + ', '
660
665
  end
661
- kv = make_json_string(ArelExtensions::Nodes::Cast.new([k, :string]).coalesce("")) + ': '
666
+ kv = make_json_string(ArelExtensions::Nodes::Cast.new([k, :string])) + ': '
662
667
  kv += json_value(o,v)
663
668
  res = res + kv.group_concat(', ', order: Array(orders)).coalesce('')
664
669
  end
@@ -9,9 +9,17 @@ if defined?(Arel::Visitors::Oracle)
9
9
  require 'arel_extensions/visitors/oracle12'
10
10
  end
11
11
 
12
- if defined?(Arel::Visitors::MSSQL)
12
+ if defined?(Arel::Visitors::SQLServer) || defined?(Arel::Visitors::MSSQL)
13
13
  require 'arel_extensions/visitors/mssql'
14
+ end
14
15
 
16
+ if defined?(Arel::Visitors::SQLServer)
17
+ class Arel::Visitors::SQLServer
18
+ include ArelExtensions::Visitors::MSSQL
19
+ end
20
+ end
21
+
22
+ if defined?(Arel::Visitors::MSSQL)
15
23
  class Arel::Visitors::MSSQL
16
24
  include ArelExtensions::Visitors::MSSQL
17
25
 
@@ -33,6 +33,12 @@ class Arel::Nodes::Grouping
33
33
  include Arel::OrderPredications
34
34
  end
35
35
 
36
+ class Arel::Nodes::Ordering
37
+ def eql? other
38
+ self.hash.eql? other.hash
39
+ end
40
+ end
41
+
36
42
  class Arel::Nodes::Function
37
43
  include Arel::Math
38
44
  include Arel::Expressions
@@ -46,6 +52,7 @@ if Gem::Version.new(Arel::VERSION) >= Gem::Version.new("7.1.0")
46
52
  end
47
53
 
48
54
  require 'arel_extensions/version'
55
+ require 'arel_extensions/aliases'
49
56
  require 'arel_extensions/attributes'
50
57
  require 'arel_extensions/visitors'
51
58
  require 'arel_extensions/nodes'
@@ -82,11 +89,13 @@ module Arel
82
89
  end
83
90
 
84
91
  def self.json *expr
85
- if expr.length == 1
86
- ArelExtensions::Nodes::Json.new(expr.first)
87
- else
88
- ArelExtensions::Nodes::Json.new(expr)
89
- end
92
+ ArelExtensions::Nodes::Json.new(
93
+ if expr.length == 1
94
+ expr.first
95
+ else
96
+ expr
97
+ end
98
+ )
90
99
  end
91
100
 
92
101
  def self.when condition
@@ -94,20 +103,31 @@ module Arel
94
103
  end
95
104
 
96
105
  def self.duration s, expr
97
- ArelExtensions::Nodes::Duration.new(s.to_s+'i',expr)
106
+ ArelExtensions::Nodes::Duration.new("#{s}i", expr)
98
107
  end
99
108
 
109
+ def self.grouping *v
110
+ Arel::Nodes::Grouping.new(*v)
111
+ end
112
+
113
+ # The TRUE pseudo literal.
100
114
  def self.true
101
- Arel::Nodes::Equality.new(1,1)
115
+ Arel::Nodes::Equality.new(1, 1)
102
116
  end
103
117
 
118
+ # The FALSE pseudo literal.
104
119
  def self.false
105
- Arel::Nodes::Equality.new(1,0)
120
+ Arel::Nodes::Equality.new(1, 0)
121
+ end
122
+
123
+ # The NULL literal.
124
+ def self.null
125
+ Arel::Nodes.build_quoted(nil)
106
126
  end
107
127
 
108
128
  def self.tuple *v
109
- tmp = Arel::Nodes::Grouping.new(nil)
110
- Arel::Nodes::Grouping.new(v.map{|e| tmp.convert_to_node(e)})
129
+ tmp = Arel.grouping(nil)
130
+ Arel.grouping v.map{|e| tmp.convert_to_node(e)}
111
131
  end
112
132
  end
113
133
 
@@ -117,6 +137,7 @@ class Arel::Attributes::Attribute
117
137
  end
118
138
 
119
139
  class Arel::Nodes::Function
140
+ include ArelExtensions::Aliases
120
141
  include ArelExtensions::Math
121
142
  include ArelExtensions::Comparators
122
143
  include ArelExtensions::DateDuration
@@ -152,6 +173,9 @@ class Arel::Nodes::Unary
152
173
  include ArelExtensions::MathFunctions
153
174
  include ArelExtensions::Comparators
154
175
  include ArelExtensions::Predications
176
+ def eql? other
177
+ hash == other.hash
178
+ end
155
179
  end
156
180
 
157
181
  class Arel::Nodes::Binary
@@ -161,6 +185,9 @@ class Arel::Nodes::Binary
161
185
  include ArelExtensions::Comparators
162
186
  include ArelExtensions::BooleanFunctions
163
187
  include ArelExtensions::Predications
188
+ def eql? other
189
+ hash == other.hash
190
+ end
164
191
  end
165
192
 
166
193
  class Arel::Nodes::Equality
@@ -177,6 +204,19 @@ end
177
204
  class Arel::SelectManager
178
205
  include ArelExtensions::SetFunctions
179
206
  include ArelExtensions::Nodes
207
+
208
+ def as table_name
209
+ Arel::Nodes::TableAlias.new(self, table_name)
210
+ end
211
+
212
+ # Install an alias, if present.
213
+ def xas table_name
214
+ if table_name.present?
215
+ as table_name
216
+ else
217
+ self
218
+ end
219
+ end
180
220
  end
181
221
 
182
222
  class Arel::Nodes::As
@@ -186,7 +226,11 @@ end
186
226
  class Arel::Table
187
227
  alias_method(:old_alias, :alias) rescue nil
188
228
  def alias(name = "#{self.name}_2")
189
- name.blank? ? self : Arel::Nodes::TableAlias.new(self,name)
229
+ if name.present?
230
+ Arel::Nodes::TableAlias.new(self, name)
231
+ else
232
+ self
233
+ end
190
234
  end
191
235
  end
192
236
 
data/test/real_db_test.rb CHANGED
@@ -8,7 +8,11 @@ require 'arel_extensions'
8
8
  def setup_db
9
9
  ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
10
10
  ActiveRecord::Base.establish_connection(ENV['DB'].try(:to_sym) || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
11
- ActiveRecord::Base.default_timezone = :utc
11
+ if ActiveRecord::VERSION::MAJOR >= 7
12
+ ActiveRecord.default_timezone = :utc
13
+ else
14
+ ActiveRecord::Base.default_timezone = :utc
15
+ end
12
16
  @cnx = ActiveRecord::Base.connection
13
17
  if ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
14
18
  $sqlite = true
@@ -282,16 +282,23 @@ module ArelExtensions
282
282
  .must_be_like %{CASE "users"."name" WHEN 'smith' THEN 'cool' ELSE 'uncool' END ILIKE 'value'}
283
283
  end
284
284
 
285
- it "should be possible to use as on anything" do
286
- _(compile(@table[:name].as('alias'))).must_be_like %{"users"."name" AS alias}
287
- _(compile(@table[:name].concat(' test').as('alias'))).must_be_like %{CONCAT("users"."name", ' test') AS alias}
288
- _(compile((@table[:name] + ' test').as('alias'))).must_be_like %{CONCAT("users"."name", ' test') AS alias}
289
- _(compile((@table[:age] + 42).as('alias'))).must_be_like %{("users"."age" + 42) AS alias}
290
- _(compile(@table[:name].coalesce('').as('alias'))).must_be_like %{COALESCE("users"."name", '') AS alias}
291
- _(compile(Arel::Nodes.build_quoted('test').as('alias'))).must_be_like %{'test' AS alias}
292
- _(compile(@table.project(@table[:name]).as('alias'))).must_be_like %{(SELECT "users"."name" FROM "users") alias}
293
- _(compile(@table[:name].when("smith").then("cool").else("uncool").as('alias')))
294
- .must_be_like %{CASE "users"."name" WHEN 'smith' THEN 'cool' ELSE 'uncool' END AS alias}
285
+ it "should be possible to use as/xas on anything" do
286
+ {
287
+ @table[:name] => %{"users"."name" AS alias},
288
+ @table[:name].concat(' test') => %{CONCAT("users"."name", ' test') AS alias},
289
+ (@table[:name] + ' test') => %{CONCAT("users"."name", ' test') AS alias},
290
+ (@table[:age] + 42) => %{("users"."age" + 42) AS alias},
291
+ @table[:name].coalesce('') => %{COALESCE("users"."name", '') AS alias},
292
+ Arel::Nodes.build_quoted('test') => %{'test' AS alias},
293
+ @table.project(@table[:name]) => %{(SELECT "users"."name" FROM "users") "alias"},
294
+ @table[:name].when("smith").then("cool").else("uncool") => %{CASE "users"."name" WHEN 'smith' THEN 'cool' ELSE 'uncool' END AS alias},
295
+ }.each do |exp, res|
296
+ _(compile(exp.as('alias'))).must_be_like res
297
+ _(compile(exp.xas('alias'))).must_be_like res
298
+
299
+ res_no_alias = res.gsub(/\s*(?:AS alias|"alias")\s*\z/, '')
300
+ _(compile(exp.xas(nil))).must_be_like res_no_alias
301
+ end
295
302
  end
296
303
 
297
304
  it "should accept comparators on functions" do
@@ -373,7 +380,7 @@ module ArelExtensions
373
380
  end
374
381
 
375
382
  it "should respecting Grouping" do
376
- g = ->(*v) { Arel::Nodes::Grouping.new(v) }
383
+ g = ->(*v) { Arel.grouping(v) }
377
384
  _(compile(g[@table[:id], @table[:age]].in [g[1, 42]]))
378
385
  .must_be_like %{("users"."id", "users"."age") IN ((1, 42))}
379
386
  _(compile(g[@table[:id], @table[:age]].in [g[1, 42], g[2, 51]]))
@@ -2,7 +2,7 @@ require 'arelx_test_helper'
2
2
  require 'date'
3
3
 
4
4
  module ArelExtensions
5
- module WthAr
5
+ module WithAr
6
6
  class ListTest < Minitest::Test
7
7
  require 'minitest/pride'
8
8
  def connect_db
@@ -14,7 +14,11 @@ module ArelExtensions
14
14
  @env_db = ENV['DB']
15
15
  end
16
16
  ActiveRecord::Base.establish_connection(@env_db.try(:to_sym) || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
17
- ActiveRecord::Base.default_timezone = :utc
17
+ if ActiveRecord::VERSION::MAJOR >= 7
18
+ ActiveRecord.default_timezone = :utc
19
+ else
20
+ ActiveRecord::Base.default_timezone = :utc
21
+ end
18
22
  @cnx = ActiveRecord::Base.connection
19
23
  $sqlite = @cnx.adapter_name =~ /sqlite/i
20
24
  $load_extension_disabled ||= false
@@ -166,6 +170,12 @@ module ArelExtensions
166
170
  # Since Arel10 (Rails6.1), some unwanted behaviors on aggregated calculation were present.
167
171
  # This should works no matter which version of rails is used
168
172
  assert User.group(:score).average(:id).values.all?{|e| !e.nil?}
173
+
174
+ # Since Rails 7, a patch to calculations.rb has tirggered a double
175
+ # quoting of the alias name. See https://github.com/rails/rails/commit/7e6e9091e55c3357b0162d44b6ab955ed0c718d5
176
+ # Before the patch that fixed this the following error would occur:
177
+ # ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: zero-length delimited identifier at or near """"
178
+ assert User.group(:score).count(:id).values.all?{|e| !e.nil?}
169
179
  end
170
180
 
171
181
  # String Functions
@@ -358,7 +368,6 @@ module ArelExtensions
358
368
 
359
369
  def test_format
360
370
  assert_equal '2016-05-23', t(@lucas, @created_at.format('%Y-%m-%d'))
361
- skip "SQL Server does not accept any format" if @env_db == 'mssql'
362
371
  assert_equal '2014/03/03 12:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S'))
363
372
  assert_equal '12:42%', t(@lucas, @updated_at.format('%R%%'))
364
373
  end
@@ -500,7 +509,8 @@ module ArelExtensions
500
509
  assert_equal Time, t(@lucas,@updated_at.cast(:string).cast(:datetime)).class
501
510
  assert_equal Time, t(@lucas,@updated_at.cast(:time)).class
502
511
 
503
- assert_equal "2014-03-03 12:42:00", t(@lucas,@updated_at.cast(:string)) unless @env_db == 'mssql' # locale dependent
512
+ # mysql adapter in rails7 adds some infos we just squeeze here
513
+ assert_equal "2014-03-03 12:42:00", t(@lucas,@updated_at.cast(:string)).split('.').first unless @env_db == 'mssql' # locale dependent
504
514
  assert_equal Date.parse("2014-03-03"), t(@lucas,Arel::Nodes.build_quoted('2014-03-03').cast(:date))
505
515
  assert_equal Date.parse("5014-03-03"), t(@lucas,(@age.cast(:string) + '014-03-03').cast(:date))
506
516
  assert_equal Time.parse("2014-03-03 12:42:00 UTC"), t(@lucas,@updated_at.cast(:string).cast(:datetime))
@@ -2,7 +2,7 @@ require 'arelx_test_helper'
2
2
  require 'date'
3
3
 
4
4
  module ArelExtensions
5
- module WthAr
5
+ module WithAr
6
6
  class InsertManagerTest < Minitest::Test
7
7
  def setup_db
8
8
  ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
@@ -13,7 +13,11 @@ module ArelExtensions
13
13
  @env_db = ENV['DB']
14
14
  end
15
15
  ActiveRecord::Base.establish_connection(@env_db.try(:to_sym) || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
16
- ActiveRecord::Base.default_timezone = :utc
16
+ if ActiveRecord::VERSION::MAJOR >= 7
17
+ ActiveRecord.default_timezone = :utc
18
+ else
19
+ ActiveRecord::Base.default_timezone = :utc
20
+ end
17
21
  @cnx = ActiveRecord::Base.connection
18
22
  Arel::Table.engine = ActiveRecord::Base
19
23
  if File.exist?("init/#{@env_db}.sql")
@@ -1,12 +1,16 @@
1
1
  require 'arelx_test_helper'
2
2
 
3
3
  module ArelExtensions
4
- module WthAr
4
+ module WithAr
5
5
  describe 'the sqlite visitor' do
6
6
  before do
7
7
  ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
8
8
  ActiveRecord::Base.establish_connection(ENV['DB'] || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
9
- ActiveRecord::Base.default_timezone = :utc
9
+ if ActiveRecord::VERSION::MAJOR >= 7
10
+ ActiveRecord.default_timezone = :utc
11
+ else
12
+ ActiveRecord::Base.default_timezone = :utc
13
+ end
10
14
  @cnx = ActiveRecord::Base.connection
11
15
  Arel::Table.engine = ActiveRecord::Base
12
16
  @cnx.drop_table(:users) rescue nil
@@ -1,12 +1,16 @@
1
1
  require 'arelx_test_helper'
2
2
 
3
3
  module ArelExtensions
4
- module WthAr
4
+ module WithAr
5
5
  describe 'the sqlite visitor can do maths' do
6
6
  before do
7
7
  ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
8
8
  ActiveRecord::Base.establish_connection(ENV['DB'] || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
9
- ActiveRecord::Base.default_timezone = :utc
9
+ if ActiveRecord::VERSION::MAJOR >= 7
10
+ ActiveRecord.default_timezone = :utc
11
+ else
12
+ ActiveRecord::Base.default_timezone = :utc
13
+ end
10
14
  Arel::Table.engine = ActiveRecord::Base
11
15
  @cnx = ActiveRecord::Base.connection
12
16
  @cnx.drop_table(:users) rescue nil
@@ -2,12 +2,16 @@ require 'arelx_test_helper'
2
2
  require 'date'
3
3
 
4
4
  module ArelExtensions
5
- module WthAr
5
+ module WithAr
6
6
  describe 'the mysql visitor can do string operations' do
7
7
  before do
8
8
  ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
9
9
  ActiveRecord::Base.establish_connection(ENV['DB'] || (RUBY_PLATFORM == 'java' ? :"jdbc-mysql" : :mysql))
10
- ActiveRecord::Base.default_timezone = :utc
10
+ if ActiveRecord::VERSION::MAJOR >= 7
11
+ ActiveRecord.default_timezone = :utc
12
+ else
13
+ ActiveRecord::Base.default_timezone = :utc
14
+ end
11
15
  begin
12
16
  @cnx = ActiveRecord::Base.connection
13
17
  rescue => e
@@ -2,12 +2,16 @@ require 'arelx_test_helper'
2
2
  require 'date'
3
3
 
4
4
  module ArelExtensions
5
- module WthAr
5
+ module WithAr
6
6
  describe 'the sqlite visitor can do string operations' do
7
7
  before do
8
8
  ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
9
9
  ActiveRecord::Base.establish_connection(ENV['DB'] || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
10
- ActiveRecord::Base.default_timezone = :utc
10
+ if ActiveRecord::VERSION::MAJOR >= 7
11
+ ActiveRecord.default_timezone = :utc
12
+ else
13
+ ActiveRecord::Base.default_timezone = :utc
14
+ end
11
15
  @cnx = ActiveRecord::Base.connection
12
16
  Arel::Table.engine = ActiveRecord::Base
13
17
  @cnx.drop_table(:users) rescue nil
data/version_v1.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "1.2.23".freeze
2
+ VERSION = "1.3.0".freeze
3
3
  end