arel_extensions 2.0.0 → 2.0.10

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -4
  3. data/.travis.yml +59 -91
  4. data/Gemfile +14 -19
  5. data/README.md +17 -12
  6. data/Rakefile +38 -27
  7. data/appveyor.yml +1 -1
  8. data/arel_extensions.gemspec +2 -2
  9. data/functions.html +3 -3
  10. data/gemfiles/rails4.gemfile +1 -1
  11. data/gemfiles/rails6.gemfile +30 -0
  12. data/gemspec_v2/arel_extensions-v2.gemspec +28 -0
  13. data/generate_gems.sh +14 -0
  14. data/init/mssql.sql +4 -4
  15. data/init/mysql.sql +38 -38
  16. data/init/postgresql.sql +21 -21
  17. data/lib/arel_extensions.rb +63 -19
  18. data/lib/arel_extensions/attributes.rb +0 -1
  19. data/lib/arel_extensions/boolean_functions.rb +38 -13
  20. data/lib/arel_extensions/common_sql_functions.rb +5 -4
  21. data/lib/arel_extensions/comparators.rb +4 -2
  22. data/lib/arel_extensions/insert_manager.rb +15 -13
  23. data/lib/arel_extensions/math.rb +3 -3
  24. data/lib/arel_extensions/math_functions.rb +10 -5
  25. data/lib/arel_extensions/nodes.rb +1 -1
  26. data/lib/arel_extensions/nodes/abs.rb +0 -0
  27. data/lib/arel_extensions/nodes/aggregate_function.rb +14 -0
  28. data/lib/arel_extensions/nodes/case.rb +8 -4
  29. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  30. data/lib/arel_extensions/nodes/coalesce.rb +2 -2
  31. data/lib/arel_extensions/nodes/collate.rb +1 -1
  32. data/lib/arel_extensions/nodes/concat.rb +6 -13
  33. data/lib/arel_extensions/nodes/date_diff.rb +3 -5
  34. data/lib/arel_extensions/nodes/duration.rb +0 -2
  35. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  36. data/lib/arel_extensions/nodes/floor.rb +0 -0
  37. data/lib/arel_extensions/nodes/format.rb +8 -8
  38. data/lib/arel_extensions/nodes/formatted_number.rb +23 -23
  39. data/lib/arel_extensions/nodes/function.rb +2 -0
  40. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  41. data/lib/arel_extensions/nodes/json.rb +28 -30
  42. data/lib/arel_extensions/nodes/length.rb +0 -0
  43. data/lib/arel_extensions/nodes/locate.rb +0 -0
  44. data/lib/arel_extensions/nodes/matches.rb +4 -4
  45. data/lib/arel_extensions/nodes/power.rb +6 -5
  46. data/lib/arel_extensions/nodes/rand.rb +0 -0
  47. data/lib/arel_extensions/nodes/repeat.rb +2 -2
  48. data/lib/arel_extensions/nodes/replace.rb +24 -6
  49. data/lib/arel_extensions/nodes/round.rb +5 -5
  50. data/lib/arel_extensions/nodes/soundex.rb +16 -15
  51. data/lib/arel_extensions/nodes/std.rb +19 -21
  52. data/lib/arel_extensions/nodes/substring.rb +8 -15
  53. data/lib/arel_extensions/nodes/sum.rb +7 -0
  54. data/lib/arel_extensions/nodes/trim.rb +3 -3
  55. data/lib/arel_extensions/nodes/union.rb +2 -3
  56. data/lib/arel_extensions/nodes/union_all.rb +0 -1
  57. data/lib/arel_extensions/nodes/wday.rb +0 -0
  58. data/lib/arel_extensions/null_functions.rb +2 -2
  59. data/lib/arel_extensions/predications.rb +35 -33
  60. data/lib/arel_extensions/set_functions.rb +2 -2
  61. data/lib/arel_extensions/string_functions.rb +34 -12
  62. data/lib/arel_extensions/tasks.rb +5 -5
  63. data/lib/arel_extensions/version.rb +1 -1
  64. data/lib/arel_extensions/visitors.rb +1 -1
  65. data/lib/arel_extensions/visitors/ibm_db.rb +1 -1
  66. data/lib/arel_extensions/visitors/mssql.rb +14 -13
  67. data/lib/arel_extensions/visitors/mysql.rb +90 -37
  68. data/lib/arel_extensions/visitors/oracle.rb +15 -15
  69. data/lib/arel_extensions/visitors/oracle12.rb +1 -1
  70. data/lib/arel_extensions/visitors/postgresql.rb +78 -32
  71. data/lib/arel_extensions/visitors/sqlite.rb +61 -53
  72. data/lib/arel_extensions/visitors/to_sql.rb +70 -58
  73. data/test/arelx_test_helper.rb +28 -0
  74. data/test/real_db_test.rb +1 -1
  75. data/test/support/fake_record.rb +1 -1
  76. data/test/test_comparators.rb +9 -8
  77. data/test/visitors/test_bulk_insert_oracle.rb +8 -7
  78. data/test/visitors/test_bulk_insert_sqlite.rb +9 -8
  79. data/test/visitors/test_bulk_insert_to_sql.rb +8 -10
  80. data/test/visitors/test_oracle.rb +41 -40
  81. data/test/visitors/test_to_sql.rb +367 -193
  82. data/test/with_ar/all_agnostic_test.rb +68 -35
  83. data/test/with_ar/insert_agnostic_test.rb +3 -2
  84. data/test/with_ar/test_bulk_sqlite.rb +6 -5
  85. data/test/with_ar/test_math_sqlite.rb +4 -4
  86. data/test/with_ar/test_string_mysql.rb +4 -6
  87. data/test/with_ar/test_string_sqlite.rb +3 -7
  88. data/version_v1.rb +3 -0
  89. data/version_v2.rb +3 -0
  90. metadata +14 -7
  91. data/test/helper.rb +0 -18
@@ -71,6 +71,8 @@ module ArelExtensions
71
71
  Arel.sql('NULL')
72
72
  when ActiveSupport::Duration
73
73
  Arel.sql(object.to_i)
74
+ when Array
75
+ Arel::Nodes::Grouping.new(object.map{|r| convert_to_node(e)})
74
76
  else
75
77
  raise(ArgumentError, "#{object.class} can not be converted to CONCAT arg")
76
78
  end
File without changes
@@ -18,7 +18,7 @@ module ArelExtensions
18
18
  JsonSet.new(self,key,value)
19
19
  end
20
20
 
21
- def group as_array = true, orders= nil
21
+ def group as_array = true, orders = nil
22
22
  JsonGroup.new(self,as_array, orders)
23
23
  end
24
24
 
@@ -31,33 +31,35 @@ module ArelExtensions
31
31
  class Json < JsonNode
32
32
 
33
33
  def initialize *expr
34
- if expr.length == 1
35
- case expr.first
36
- when JsonNode
37
- @dict = expr.first.dict
38
- when Array
39
- @dict = expr.first.map{|e|
40
- (e.is_a?(Array) || e.is_a?(Hash)) ? Json.new(e) : convert_to_node(e)
41
- }
42
- when Hash
43
- @dict = expr.first.inject({}){|acc,v|
44
- acc[convert_to_node(v[0])] = (v[1].is_a?(Array) || v[1].is_a?(Hash)) ? Json.new(v[1]) : convert_to_node(v[1])
45
- acc
46
- }
47
- when String, Numeric, TrueClass, FalseClass
48
- @dict = convert_to_node(expr.first)
49
- when NilClass
50
- @dict = Arel.sql('null')
51
- else
52
- if expr.first.is_a?(String) || (expr.first.is_a?(Arel::Attributes::Attribute) && type_of_attribute(expr.first) == :string) || (expr.first.return_type == :string)
53
- @dict = convert_to_node(expr.first)
34
+ @dict =
35
+ if expr.length == 1
36
+ case exp = expr.first
37
+ when JsonNode
38
+ exp.dict
39
+ when Array
40
+ exp.map{|e|
41
+ (e.is_a?(Array) || e.is_a?(Hash)) ? Json.new(e) : convert_to_node(e)
42
+ }
43
+ when Hash
44
+ exp.reduce({}){|acc,v|
45
+ acc[convert_to_node(v[0])] = (v[1].is_a?(Array) || v[1].is_a?(Hash)) ? Json.new(v[1]) : convert_to_node(v[1])
46
+ acc
47
+ }
48
+ when String, Numeric, TrueClass, FalseClass
49
+ convert_to_node(exp)
50
+ when NilClass
51
+ Arel.sql('null')
54
52
  else
55
- @dict = [convert_to_node(expr.first)]
53
+ if (exp.is_a?(Arel::Attributes::Attribute) && type_of_attribute(exp) == :string) \
54
+ || (exp.return_type == :string)
55
+ convert_to_node(exp)
56
+ else
57
+ [convert_to_node(exp)]
58
+ end
56
59
  end
60
+ else
61
+ expr.map{|e| (e.is_a?(Array) || e.is_a?(Hash)) ? Json.new(e) : convert_to_node(e) }
57
62
  end
58
- else
59
- @dict = expr.map{|e| (e.is_a?(Array) || e.is_a?(Hash)) ? Json.new(e) : convert_to_node(e) }
60
- end
61
63
  super
62
64
  end
63
65
 
@@ -73,11 +75,7 @@ module ArelExtensions
73
75
  @dict = as_array ? json : json.dict
74
76
  @as_array = as_array
75
77
  if orders
76
- if orders.is_a?(Array)
77
- @orders = orders
78
- else
79
- @orders = [orders]
80
- end
78
+ @orders = Array(orders)
81
79
  end
82
80
  end
83
81
  end
File without changes
File without changes
@@ -3,7 +3,7 @@ module ArelExtensions
3
3
  class IMatches < Arel::Nodes::Matches
4
4
 
5
5
  attr_accessor :case_sensitive if Arel::VERSION.to_i < 7
6
-
6
+
7
7
  def initialize(left, right, escape = nil)
8
8
  r = Arel::Nodes.build_quoted(right)
9
9
  if Arel::VERSION.to_i < 7 # managed by default in version 7+ (rails 5), so useful for rails 3 & 4
@@ -17,13 +17,13 @@ module ArelExtensions
17
17
 
18
18
  class IDoesNotMatch < IMatches
19
19
  end
20
-
20
+
21
21
  class AiMatches < IMatches
22
22
  end
23
-
23
+
24
24
  class AiIMatches < IMatches
25
25
  end
26
-
26
+
27
27
  class SMatches < IMatches
28
28
  end
29
29
 
@@ -1,11 +1,12 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
3
  class Power < Function
4
- RETURN_TYPE = :number
5
-
6
- def initialize expr
7
- super [convert_to_node(expr.first), convert_to_number(expr[1])]
8
- end
4
+ RETURN_TYPE = :number
5
+
6
+ def initialize expr
7
+ super [convert_to_node(expr.first), convert_to_number(expr[1])]
8
+ end
9
+
9
10
  end
10
11
  end
11
12
  end
File without changes
@@ -2,7 +2,7 @@ module ArelExtensions
2
2
  module Nodes
3
3
  class Repeat < Function
4
4
  RETURN_TYPE = :string
5
-
5
+
6
6
  def initialize expr
7
7
  tab = expr.map { |arg|
8
8
  convert_to_node(arg)
@@ -11,7 +11,7 @@ module ArelExtensions
11
11
  end
12
12
 
13
13
  def +(other)
14
- return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
14
+ return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
15
15
  end
16
16
 
17
17
  end
@@ -2,18 +2,36 @@ module ArelExtensions
2
2
  module Nodes
3
3
  class Replace < Function
4
4
  RETURN_TYPE = :string
5
+ attr_accessor :left, :pattern, :substitute
5
6
 
6
- def initialize expr
7
- tab = expr.map { |arg|
8
- convert_to_node(arg)
9
- }
10
- return super(tab)
7
+ def initialize left, pattern, substitute
8
+ @left = convert_to_node(left)
9
+ @pattern = convert_to_node(pattern)
10
+ @substitute = convert_to_node(substitute)
11
+ super([@left,@pattern,@substitute])
11
12
  end
12
13
 
13
14
  def +(other)
14
- return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
15
+ return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
15
16
  end
16
17
 
17
18
  end
19
+
20
+ class RegexpReplace < Function
21
+ RETURN_TYPE = :string
22
+ attr_accessor :left, :pattern, :substitute
23
+
24
+ def initialize left, pattern, substitute
25
+ @left = convert_to_node(left)
26
+ @pattern = (pattern.is_a?(Regexp) ? pattern : %r[#{pattern}])
27
+ @substitute = convert_to_node(substitute)
28
+ super([@left,@pattern,@substitute])
29
+ end
30
+
31
+ def +(other)
32
+ return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
33
+ end
34
+ end
35
+
18
36
  end
19
37
  end
@@ -4,11 +4,11 @@ module ArelExtensions
4
4
  RETURN_TYPE = :number
5
5
 
6
6
  def initialize expr
7
- if expr && expr.length == 1
8
- super [convert_to_node(expr.first)]
9
- else
10
- super [convert_to_node(expr.first), convert_to_number(expr[1])]
11
- end
7
+ if expr && expr.length == 1
8
+ super [convert_to_node(expr.first)]
9
+ else
10
+ super [convert_to_node(expr.first), convert_to_number(expr[1])]
11
+ end
12
12
  end
13
13
 
14
14
  end
@@ -1,18 +1,19 @@
1
1
  module ArelExtensions
2
- module Nodes
3
- class Soundex < Function
4
- include Arel::Expressions
5
- include ArelExtensions::Comparators
6
-
7
- RETURN_TYPE = :string
8
-
9
- def ==(other)
10
- Arel::Nodes::Equality.new self, Arel::Nodes.build_quoted(other, self)
11
- end
2
+ module Nodes
3
+ class Soundex < Function
4
+ include Arel::Expressions
5
+ include ArelExtensions::Comparators
12
6
 
13
- def !=(other)
14
- Arel::Nodes::NotEqual.new self, Arel::Nodes.build_quoted(other, self)
15
- end
16
- end
17
- end
7
+ RETURN_TYPE = :string
8
+
9
+ def ==(other)
10
+ Arel::Nodes::Equality.new self, Arel::Nodes.build_quoted(other, self)
11
+ end
12
+
13
+ def !=(other)
14
+ Arel::Nodes::NotEqual.new self, Arel::Nodes.build_quoted(other, self)
15
+ end
16
+
17
+ end
18
+ end
18
19
  end
@@ -1,26 +1,24 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
- class Std < Function
4
- RETURN_TYPE = :number
5
-
6
- attr_accessor :unbiased_estimator
7
- def initialize expr
8
- col = expr.first
9
- @unbiased_estimator = expr[1]
10
- super [col]
11
- end
3
+ class Std < AggregateFunction
4
+ RETURN_TYPE = :number
5
+ attr_accessor :unbiased_estimator
6
+
7
+ def initialize node, opts = {}
8
+ @unbiased_estimator = opts[:unbiased] ? true : false
9
+ super node, opts
10
+ end
12
11
  end
13
-
14
- class Variance < Function
15
- RETURN_TYPE = :number
16
-
17
- attr_accessor :unbiased_estimator
18
- def initialize expr
19
- col = expr.first
20
- @unbiased_estimator = expr[1]
21
- super [col]
22
- end
23
- end
24
-
12
+
13
+ class Variance < AggregateFunction
14
+ RETURN_TYPE = :number
15
+ attr_accessor :unbiased_estimator
16
+
17
+ def initialize node, opts = {}
18
+ @unbiased_estimator = opts[:unbiased] ? true : false
19
+ super node, opts
20
+ end
21
+ end
22
+
25
23
  end
26
24
  end
@@ -1,22 +1,15 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
3
  class Substring < Function
4
- RETURN_TYPE = :string
4
+ RETURN_TYPE = :string
5
5
 
6
- def initialize expr
7
- tab = [convert_to_node(expr[0]), convert_to_node(expr[1])]
8
- if expr[2]
9
- tab << convert_to_node(expr[2])
10
- # else
11
- # tab << expr[0].length
12
- end
13
- return super(tab)
14
- end
15
-
16
- # def +(other)
17
- # puts "[Substring] : #{other.inspect} (#{self.expressions.inspect})"
18
- # return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
19
- # end
6
+ def initialize expr
7
+ tab = [convert_to_node(expr[0]), convert_to_node(expr[1])]
8
+ if expr[2]
9
+ tab << convert_to_node(expr[2])
10
+ end
11
+ return super(tab)
12
+ end
20
13
 
21
14
  end
22
15
  end
@@ -0,0 +1,7 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class Sum < AggregateFunction
4
+ RETURN_TYPE = :number
5
+ end
6
+ end
7
+ end
@@ -2,7 +2,7 @@ module ArelExtensions
2
2
  module Nodes
3
3
  class Trim < Function
4
4
  RETURN_TYPE = :string
5
-
5
+
6
6
  def initialize expr
7
7
  tab = expr.map { |arg|
8
8
  convert_to_node(arg)
@@ -11,13 +11,13 @@ module ArelExtensions
11
11
  end
12
12
 
13
13
  def +(other)
14
- return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
14
+ return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
15
15
  end
16
16
 
17
17
  end
18
18
 
19
19
  class Ltrim < Trim
20
- RETURN_TYPE = :string
20
+ RETURN_TYPE = :string
21
21
  end
22
22
 
23
23
  class Rtrim < Trim
@@ -7,11 +7,11 @@ module ArelExtensions
7
7
  end
8
8
 
9
9
  def +(other)
10
- return ArelExtensions::Nodes::Union.new(self,other)
10
+ return ArelExtensions::Nodes::Union.new(self,other)
11
11
  end
12
12
 
13
13
  def union(other)
14
- return ArelExtensions::Nodes::UnionAll.new(self,other)
14
+ return ArelExtensions::Nodes::UnionAll.new(self,other)
15
15
  end
16
16
 
17
17
  def as other
@@ -21,4 +21,3 @@ module ArelExtensions
21
21
 
22
22
  end
23
23
  end
24
-
@@ -17,4 +17,3 @@ module ArelExtensions
17
17
 
18
18
  end
19
19
  end
20
-
File without changes
@@ -8,12 +8,12 @@ module ArelExtensions
8
8
  def is_null
9
9
  ArelExtensions::Nodes::IsNull.new [self]
10
10
  end
11
-
11
+
12
12
  #ISNOTNULL function lets you return an alternative value when an expression is NOT NULL.
13
13
  def is_not_null
14
14
  ArelExtensions::Nodes::IsNotNull.new [self]
15
15
  end
16
-
16
+
17
17
  # returns the first non-null expr in the expression list. You must specify at least two expressions.
18
18
  #If all occurrences of expr evaluate to null, then the function returns null.
19
19
  def coalesce *args
@@ -1,10 +1,11 @@
1
1
  module ArelExtensions
2
2
  module Predications
3
- def when right, expression=nil
3
+
4
+ def when right, expression = nil
4
5
  ArelExtensions::Nodes::Case.new(self).when(right,expression)
5
6
  end
6
7
 
7
- def matches(other, escape=nil,case_sensitive= nil)
8
+ def matches(other, escape = nil,case_sensitive = nil)
8
9
  if Arel::VERSION.to_i < 7
9
10
  Arel::Nodes::Matches.new(self, Arel::Nodes.build_quoted(other), escape)
10
11
  else
@@ -12,7 +13,7 @@ module ArelExtensions
12
13
  end
13
14
  end
14
15
 
15
- def imatches(other, escape=nil)
16
+ def imatches(other, escape = nil)
16
17
  ArelExtensions::Nodes::IMatches.new(self, other, escape)
17
18
  end
18
19
 
@@ -20,51 +21,53 @@ module ArelExtensions
20
21
  ArelExtensions::Nodes::Cast.new([self,right])
21
22
  end
22
23
 
23
- def in(other) #In should handle nil element in the Array
24
+ def in(*other) #In should handle nil element in the Array
25
+ other = other.first if other.length <= 1
24
26
  case other
25
27
  when Range
26
28
  self.between(other)
29
+ when Arel::Nodes::Grouping
30
+ Arel::Nodes::In.new(self, quoted_node(other))
27
31
  when Enumerable
28
- if other.include?(nil)
29
- other.delete(nil)
30
- case other.length
31
- when 0
32
- self.is_null
33
- when 1
34
- self.is_null.or(self==other[0])
35
- else
36
- self.is_null.or(Arel::Nodes::In.new(self,quoted_array(other)))
37
- end
38
- else
39
- Arel::Nodes::In.new(self,quoted_array(other))
40
- end
32
+ nils, values = other.partition{ |v| v.nil? }
33
+ ranges, values = values.partition{ |v| v.is_a?(Range) || v.is_a?(Arel::SelectManager)}
34
+ # In order of (imagined) decreasing efficiency: nil, values, and then more complex.
35
+ clauses =
36
+ nils.uniq.map { |r| self.in(r) } \
37
+ + (case values.uniq.size
38
+ when 0 then []
39
+ when 1 then [values[0].is_a?(Arel::Nodes::Grouping) ? self.in(values[0]) : self.eq(values[0])]
40
+ else [Arel::Nodes::In.new(self, quoted_array(values))] end) \
41
+ + ranges.uniq.map { |r| self.in(r) }
42
+ clauses.empty? ? Arel.false : clauses.reduce(&:or)
41
43
  when nil
42
44
  self.is_null
43
45
  when Arel::SelectManager
44
46
  Arel::Nodes::In.new(self, other.ast)
45
47
  else
46
- Arel::Nodes::In.new(self,quoted_node(other))
48
+ Arel::Nodes::In.new(self, quoted_node(other))
47
49
  end
48
50
  end
49
51
 
50
- def not_in(other) #In should handle nil element in the Array
52
+ def not_in(*other) #In should handle nil element in the Array
53
+ other = other.first if other.length <= 1
51
54
  case other
52
55
  when Range
53
56
  Arel::Nodes::Not.new(self.between(other))
57
+ when Arel::Nodes::Grouping
58
+ Arel::Nodes::NotIn.new(self, quoted_node(other))
54
59
  when Enumerable
55
- if other.include?(nil)
56
- other.delete(nil)
57
- case other.length
58
- when 0
59
- self.is_not_null
60
- when 1
61
- self.is_not_null.and(self!=other[0])
62
- else
63
- self.is_not_null.and(Arel::Nodes::NotIn.new(self,quoted_array(other)))
64
- end
65
- else
66
- Arel::Nodes::NotIn.new(self,quoted_array(other))
67
- end
60
+ nils, values = other.partition{ |v| v.nil? }
61
+ ranges, values = values.partition{ |v| v.is_a?(Range) || v.is_a?(Arel::SelectManager)}
62
+ # In order of (imagined) decreasing efficiency: nil, values, and then more complex.
63
+ clauses =
64
+ nils.uniq.map { |r| self.not_in(r) } \
65
+ + (case values.uniq.size
66
+ when 0 then []
67
+ when 1 then [values[0].is_a?(Arel::Nodes::Grouping) ? self.not_in(values[0]) : self.not_eq(values[0])]
68
+ else [Arel::Nodes::NotIn.new(self, quoted_array(values))] end) \
69
+ + ranges.uniq.map { |r| self.not_in(r) }
70
+ Arel::Nodes::And.new clauses
68
71
  when nil
69
72
  self.is_not_null
70
73
  when Arel::SelectManager
@@ -94,6 +97,5 @@ module ArelExtensions
94
97
  raise(ArgumentError, "#{object.class} can not be converted to CONCAT arg")
95
98
  end
96
99
  end
97
-
98
100
  end
99
101
  end