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
@@ -23,6 +23,5 @@ module ArelExtensions
23
23
  def !=(other)
24
24
  Arel::Nodes::NotEqual.new self, Arel::Nodes.build_quoted(other, self)
25
25
  end
26
-
27
26
  end
28
27
  end
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  require 'arel_extensions/nodes/then'
2
3
 
3
4
  module ArelExtensions
@@ -8,7 +9,7 @@ module ArelExtensions
8
9
  end
9
10
 
10
11
  def and *others
11
- Arel::Nodes::And.new([self]+ others.flatten)
12
+ Arel::Nodes::And.new self, others
12
13
  end
13
14
 
14
15
  def ⋁(other)
@@ -16,30 +17,54 @@ module ArelExtensions
16
17
  end
17
18
 
18
19
  def or *others
19
- args = others.flatten
20
- if args.length == 1
21
- Arel::Nodes::Or.new(self, args.first)
22
- else
23
- ArelExtensions::Nodes::Or.new([self]+ args)
24
- end
20
+ Arel::Nodes::Or.new self, others
25
21
  end
26
22
 
27
23
  def then(t, f = nil)
28
24
  ArelExtensions::Nodes::Then.new [self, t, f]
29
25
  end
26
+
30
27
  end
31
28
  end
32
29
 
33
- Arel::Nodes::And.class_eval do
30
+ class Arel::Nodes::And
34
31
  include ArelExtensions::BooleanFunctions
35
- end
36
32
 
37
- Arel::Nodes::Or.class_eval do
38
- include ArelExtensions::BooleanFunctions
33
+ def self.new *children
34
+ children =
35
+ children.flatten.map { |c|
36
+ c.is_a?(self) ? c.children : c
37
+ }.flatten
38
+ super(children)
39
+ end
40
+
39
41
  end
40
42
 
41
- ArelExtensions::Nodes.const_set('Or',Class.new(Arel::Nodes::And)).class_eval do
43
+ # For some reason, Arel's And is properly defined as variadic (it
44
+ # stores @children, and hashes it all). However Arel's Or is defined
45
+ # as binary, with only @left and @right, and hashing only @left and @right.
46
+ #
47
+ # So reimplement its ctor and accessors.
48
+
49
+ class Arel::Nodes::Or
42
50
  include ArelExtensions::BooleanFunctions
43
- end
44
51
 
52
+ attr_reader :children
45
53
 
54
+ def self.new *children
55
+ children =
56
+ children.flatten.map { |c|
57
+ c.is_a?(self) ? c.children : c
58
+ }.flatten
59
+ super(*children)
60
+ end
61
+
62
+ def initialize *children
63
+ @children = children
64
+ end
65
+
66
+ def hash
67
+ children.hash
68
+ end
69
+
70
+ end
@@ -39,6 +39,7 @@ module ArelExtensions
39
39
 
40
40
  def add_sql_functions(env_db = nil)
41
41
  env_db ||= @cnx.adapter_name
42
+ env_db = 'mysql' if env_db =~ /mysql/i
42
43
  if env_db =~ /sqlite/i
43
44
  begin
44
45
  add_sqlite_functions
@@ -52,10 +53,10 @@ module ArelExtensions
52
53
  sql.split(/^GO\s*$/).each {|str|
53
54
  @cnx.execute(str.strip) unless str.blank?
54
55
  }
55
- elsif env_db == 'mysql'
56
- sql.split("$$")[1..-2].each { |str|
57
- @cnx.execute(str.strip) unless str.strip.blank?
58
- }
56
+ elsif env_db =='mysql'
57
+ sql.split("$$")[1..-2].each { |str|
58
+ @cnx.execute(str.strip) unless str.strip.blank?
59
+ }
59
60
  else
60
61
  @cnx.execute(sql) unless sql.blank?
61
62
  end
@@ -1,6 +1,7 @@
1
1
  module ArelExtensions
2
2
  module Comparators
3
3
 
4
+
4
5
  def >(other)
5
6
  Arel::Nodes::GreaterThan.new self, Arel::Nodes.build_quoted(other, self)
6
7
  end
@@ -37,13 +38,14 @@ module ArelExtensions
37
38
  end
38
39
 
39
40
  private
40
- #Function use for not_regexp
41
+ # Function used for not_regexp.
41
42
  def convert_regexp(other)
42
43
  case other
43
44
  when String
44
- #Do nothing
45
+ # Do nothing.
45
46
  when Regexp
46
47
  other = other.source.gsub('\A','^')
48
+ other.gsub!('\z','$')
47
49
  other.gsub!('\Z','$')
48
50
  other.gsub!('\d','[0-9]')
49
51
  other.gsub!('\D','[^0-9]')
@@ -4,21 +4,23 @@ module ArelExtensions
4
4
  module InsertManager
5
5
 
6
6
  def bulk_insert(cols, data)
7
+ res_columns = []
7
8
  case cols.first
8
- when String, Symbol
9
- cols.each { |c|
10
- @ast.columns << @ast.relation[c]
11
- }
12
- when Array
13
- if String === cols.first.first
14
- @ast.columns = cols.map {|c| [@ast.relation[c.first]] }
15
- elsif Arel::Attributes::Attribute == cols.first.first
16
- @ast.columns = cols
17
- end
18
- when NilClass
19
- @ast.columns = @ast.relation.columns
9
+ when String, Symbol
10
+ cols.each { |c|
11
+ res_columns << @ast.relation[c]
12
+ }
13
+ when Array
14
+ if String === cols.first.first
15
+ res_columns = cols.map {|c| [@ast.relation[c.first]] }
16
+ elsif Arel::Attributes::Attribute == cols.first.first
17
+ res_columns = cols
18
+ end
19
+ when NilClass
20
+ res_columns = @ast.relation.columns
20
21
  end
21
- self.values = BulkValues.new(@ast.columns, data)
22
+ self.values = BulkValues.new(res_columns, data)
23
+ @ast.columns = res_columns
22
24
  end
23
25
 
24
26
  class BulkValues < Arel::Nodes::Node
@@ -24,7 +24,7 @@ module ArelExtensions
24
24
  else
25
25
  return Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
26
26
  end
27
- when ArelExtensions::Nodes::Function,ArelExtensions::Nodes::Case
27
+ when ArelExtensions::Nodes::Function,ArelExtensions::Nodes::Case
28
28
  return case self.return_type
29
29
  when :string, :text
30
30
  self.concat(other)
@@ -35,7 +35,7 @@ module ArelExtensions
35
35
  else
36
36
  self.concat(other)
37
37
  end
38
- when Arel::Nodes::Function
38
+ when Arel::Nodes::Function
39
39
  Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
40
40
  else
41
41
  begin
@@ -115,7 +115,7 @@ module ArelExtensions
115
115
  end
116
116
  when Arel::Nodes::Node, DateTime, Time, String, Date
117
117
  ArelExtensions::Nodes::DateDiff.new [self, other]
118
- when ArelExtensions::Nodes::Duration, Integer
118
+ when ArelExtensions::Nodes::Duration, Integer
119
119
  ArelExtensions::Nodes::DateSub.new [self, other]
120
120
  else # ActiveSupport::Duration
121
121
  ArelExtensions::Nodes::DateAdd.new [self, -other]
@@ -7,6 +7,7 @@ require 'arel_extensions/nodes/formatted_number'
7
7
  require 'arel_extensions/nodes/log10'
8
8
  require 'arel_extensions/nodes/power'
9
9
  require 'arel_extensions/nodes/std'
10
+ require 'arel_extensions/nodes/sum'
10
11
 
11
12
  module ArelExtensions
12
13
  module MathFunctions
@@ -62,12 +63,16 @@ module ArelExtensions
62
63
  end
63
64
 
64
65
  # Aggregate Functions
65
- def std unbiased = true
66
- ArelExtensions::Nodes::Std.new [self,unbiased]
66
+ def std opts = {unbiased: true}
67
+ ArelExtensions::Nodes::Std.new self, opts
67
68
  end
68
69
 
69
- def variance unbiased = true
70
- ArelExtensions::Nodes::Variance.new [self,unbiased]
70
+ def variance opts = {unbiased: true}
71
+ ArelExtensions::Nodes::Variance.new self, opts
72
+ end
73
+
74
+ def sum opts = {unbiased: true}
75
+ ArelExtensions::Nodes::Sum.new self, opts
71
76
  end
72
77
 
73
78
  #function that can be invoked to produce random numbers between 0 and 1
@@ -86,7 +91,7 @@ module ArelExtensions
86
91
  end
87
92
 
88
93
  # function returning a number at a specific format
89
- def format_number format_string, locale=nil
94
+ def format_number format_string, locale = nil
90
95
  begin
91
96
  sprintf(format_string,0) # this line is to get the right error message if the format_string is not correct
92
97
  m = /^(.*)%([ #+\-0]*)([1-9][0-9]+|[1-9]?)[.]?([0-9]*)([a-zA-Z])(.*)$/.match(format_string)
@@ -1,2 +1,2 @@
1
1
  require 'arel_extensions/nodes/function'
2
-
2
+ require 'arel_extensions/nodes/aggregate_function'
File without changes
@@ -0,0 +1,14 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class AggregateFunction < Function
4
+ attr_accessor :order, :group
5
+
6
+ def initialize node, **opts
7
+ @order = Array(opts[:order]).map{|e| convert_to_node(e)}
8
+ @group = Array(opts[:group]).map{|e| convert_to_node(e)}
9
+ super [node]
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -1,7 +1,11 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
- if Arel::VERSION < "7.1.0"
3
+ if Gem::Version.new(Arel::VERSION) < Gem::Version.new("7.1.0")
4
4
  class Case < Arel::Nodes::Node
5
+ include Arel::Expressions
6
+ include Arel::Math
7
+ include Arel::Predications
8
+ include Arel::OrderPredications
5
9
  attr_accessor :case, :conditions, :default
6
10
 
7
11
  def initialize expression = nil, default = nil
@@ -26,8 +30,10 @@ module ArelExtensions
26
30
  end
27
31
  end
28
32
 
29
- ArelExtensions::Nodes::Case.class_eval do
33
+ class ArelExtensions::Nodes::Case
30
34
  include Arel::Expressions
35
+ include Arel::Math
36
+ include Arel::Predications
31
37
  include Arel::OrderPredications
32
38
  include ArelExtensions::Math
33
39
  include ArelExtensions::Comparators
@@ -41,8 +47,6 @@ module ArelExtensions
41
47
  @conditions.last.right
42
48
  elsif @default
43
49
  @default.expr
44
- else
45
- nil
46
50
  end
47
51
  if obj.respond_to?(:return_type)
48
52
  obj.return_type
File without changes
@@ -2,7 +2,7 @@ module ArelExtensions
2
2
  module Nodes
3
3
  class Coalesce < Function
4
4
  RETURN_TYPE = :string
5
-
5
+
6
6
  attr_accessor :left_node_type
7
7
 
8
8
  def return_type
@@ -20,7 +20,7 @@ module ArelExtensions
20
20
  @left_node_type = :number
21
21
  when ArelExtensions::Nodes::Coalesce, ArelExtensions::Nodes::Function
22
22
  @left_node_type = expr.first.respond_to?(:left_node_type) ? expr.first.left_node_type : nil
23
- when Arel::Nodes::Node, Arel::Attributes::Attribute
23
+ when Arel::Nodes::Node, Arel::Attributes::Attribute
24
24
  @left_node_type = type_of_attribute(expr.first)
25
25
  when Date
26
26
  @left_node_type = :ruby_date
@@ -5,7 +5,7 @@ module ArelExtensions
5
5
 
6
6
  attr_accessor :ai, :ci, :option
7
7
 
8
- def initialize left, option=nil, ai=false, ci=false
8
+ def initialize left, option = nil, ai = false, ci = false
9
9
  @ai = ai
10
10
  @ci = ci
11
11
  @option = option
@@ -40,21 +40,14 @@ module ArelExtensions::Nodes
40
40
 
41
41
  end
42
42
 
43
- class GroupConcat < Function
43
+ class GroupConcat < AggregateFunction
44
44
  RETURN_TYPE = :string
45
45
 
46
- attr_accessor :orders
47
-
48
- def initialize expr
49
- tab = expr.map { |arg|
50
- if arg.is_a?(Array)
51
- @orders = arg
52
- nil
53
- else
54
- convert_to_node(arg)
55
- end
56
- }.compact
57
- super(tab)
46
+ attr_accessor :separator
47
+
48
+ def initialize node, separator = ', ', **opts
49
+ @separator = convert_to_node(separator)
50
+ super node, opts
58
51
  end
59
52
 
60
53
  end
@@ -12,7 +12,7 @@ module ArelExtensions
12
12
  res = []
13
13
  col = expr.first
14
14
  case col
15
- when Arel::Nodes::Node, Arel::Attributes::Attribute
15
+ when Arel::Nodes::Node, Arel::Attributes::Attribute
16
16
  @left_node_type = type_of_attribute(col)
17
17
  when Date
18
18
  @left_node_type = :ruby_date
@@ -21,7 +21,7 @@ module ArelExtensions
21
21
  end
22
22
  res << ([:date, :ruby_date].include?(@left_node_type) ? convert_to_date_node(col) : convert_to_datetime_node(col))
23
23
  case expr[1]
24
- when Arel::Nodes::Node, Arel::Attributes::Attribute
24
+ when Arel::Nodes::Node, Arel::Attributes::Attribute
25
25
  @right_node_type = type_of_attribute(expr[1])
26
26
  when Date
27
27
  @right_node_type = :ruby_date
@@ -136,7 +136,7 @@ module ArelExtensions
136
136
  if ArelExtensions::Nodes::Duration === v
137
137
  v.with_interval = true
138
138
  case v.left
139
- when 'd','m','y'
139
+ when 'd','m','y'
140
140
  Arel.sql('day')
141
141
  when 'h','mn','s'
142
142
  Arel.sql('second')
@@ -145,8 +145,6 @@ module ArelExtensions
145
145
  else
146
146
  Arel.sql(Arel::Visitors::MSSQL::DATE_MAPPING[v.left])
147
147
  end
148
- else
149
- nil
150
148
  end
151
149
  end
152
150
  end
@@ -13,12 +13,10 @@ module ArelExtensions
13
13
  super(tab, aliaz)
14
14
  end
15
15
 
16
-
17
16
  def left
18
17
  @expressions.first
19
18
  end
20
19
 
21
-
22
20
  def right
23
21
  @expressions[1]
24
22
  end
File without changes
File without changes
@@ -1,15 +1,15 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
- class Format < Function
3
+ class Format < Function
4
4
  RETURN_TYPE = :string
5
5
 
6
- attr_accessor :col_type, :iso_format
7
- def initialize expr
8
- col = expr.first
9
- @iso_format = expr[1]
10
- @col_type = type_of_attribute(col)
11
- super [col, convert_to_string_node(@iso_format)]
12
- end
6
+ attr_accessor :col_type, :iso_format
7
+ def initialize expr
8
+ col = expr.first
9
+ @iso_format = expr[1]
10
+ @col_type = type_of_attribute(col)
11
+ super [col, convert_to_string_node(@iso_format)]
12
+ end
13
13
  end
14
14
  end
15
15
  end
@@ -1,25 +1,25 @@
1
1
  module ArelExtensions
2
- module Nodes
3
- class FormattedNumber < Function
4
- RETURN_TYPE = :string
5
-
6
- attr_accessor :locale, :prefix, :suffix, :flags, :scientific_notation, :width,:precision, :type, :original_string
7
-
8
- def initialize expr
9
- # expr[1] = {:locale => 'fr_FR', :type => "e"/"f"/"d", :prefix => "$ ", :suffix => " %", :flags => " +-#0", :width => 5, :precision => 6}
10
- col = expr.first
11
- @locale = expr[1][:locale]
12
- @prefix = expr[1][:prefix]
13
- @suffix = expr[1][:suffix]
14
- @width = expr[1][:width]
15
- @precision = expr[1][:precision]
16
- @type = expr[1][:type]
17
- @flags = expr[1][:flags]
18
- @scientific_notation = /[eE]/.match(expr[1][:type]) || false
19
- @original_string = expr[1][:original_string]
20
- super [col]
21
- end
22
-
23
- end
24
- end
2
+ module Nodes
3
+ class FormattedNumber < Function
4
+ RETURN_TYPE = :string
5
+
6
+ attr_accessor :locale, :prefix, :suffix, :flags, :scientific_notation, :width,:precision, :type, :original_string
7
+
8
+ def initialize expr
9
+ # expr[1] = {:locale => 'fr_FR', :type => "e"/"f"/"d", :prefix => "$ ", :suffix => " %", :flags => " +-#0", :width => 5, :precision => 6}
10
+ col = expr.first
11
+ @locale = expr[1][:locale]
12
+ @prefix = expr[1][:prefix]
13
+ @suffix = expr[1][:suffix]
14
+ @width = expr[1][:width]
15
+ @precision = expr[1][:precision]
16
+ @type = expr[1][:type]
17
+ @flags = expr[1][:flags]
18
+ @scientific_notation = /[eE]/.match(expr[1][:type]) || false
19
+ @original_string = expr[1][:original_string]
20
+ super [col]
21
+ end
22
+
23
+ end
24
+ end
25
25
  end