arel_extensions 1.2.2 → 1.2.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +7 -4
  3. data/.travis.yml +59 -91
  4. data/Gemfile +2 -2
  5. data/README.md +17 -12
  6. data/Rakefile +38 -27
  7. data/appveyor.yml +1 -1
  8. data/arel_extensions.gemspec +1 -1
  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 +26 -24
  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/blank.rb +14 -11
  29. data/lib/arel_extensions/nodes/case.rb +8 -4
  30. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  31. data/lib/arel_extensions/nodes/change_case.rb +2 -2
  32. data/lib/arel_extensions/nodes/coalesce.rb +2 -2
  33. data/lib/arel_extensions/nodes/collate.rb +12 -12
  34. data/lib/arel_extensions/nodes/concat.rb +6 -13
  35. data/lib/arel_extensions/nodes/date_diff.rb +3 -5
  36. data/lib/arel_extensions/nodes/duration.rb +0 -2
  37. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  38. data/lib/arel_extensions/nodes/floor.rb +0 -0
  39. data/lib/arel_extensions/nodes/format.rb +8 -8
  40. data/lib/arel_extensions/nodes/formatted_number.rb +23 -23
  41. data/lib/arel_extensions/nodes/function.rb +10 -0
  42. data/lib/arel_extensions/nodes/is_null.rb +10 -8
  43. data/lib/arel_extensions/nodes/json.rb +28 -30
  44. data/lib/arel_extensions/nodes/length.rb +0 -0
  45. data/lib/arel_extensions/nodes/levenshtein_distance.rb +5 -5
  46. data/lib/arel_extensions/nodes/locate.rb +7 -7
  47. data/lib/arel_extensions/nodes/matches.rb +4 -4
  48. data/lib/arel_extensions/nodes/power.rb +6 -5
  49. data/lib/arel_extensions/nodes/rand.rb +0 -0
  50. data/lib/arel_extensions/nodes/repeat.rb +2 -2
  51. data/lib/arel_extensions/nodes/replace.rb +24 -6
  52. data/lib/arel_extensions/nodes/round.rb +5 -5
  53. data/lib/arel_extensions/nodes/soundex.rb +16 -15
  54. data/lib/arel_extensions/nodes/std.rb +19 -21
  55. data/lib/arel_extensions/nodes/substring.rb +8 -15
  56. data/lib/arel_extensions/nodes/sum.rb +7 -0
  57. data/lib/arel_extensions/nodes/trim.rb +3 -3
  58. data/lib/arel_extensions/nodes/union.rb +2 -3
  59. data/lib/arel_extensions/nodes/union_all.rb +1 -2
  60. data/lib/arel_extensions/nodes/wday.rb +0 -0
  61. data/lib/arel_extensions/null_functions.rb +2 -2
  62. data/lib/arel_extensions/predications.rb +35 -33
  63. data/lib/arel_extensions/set_functions.rb +16 -16
  64. data/lib/arel_extensions/string_functions.rb +34 -12
  65. data/lib/arel_extensions/tasks.rb +5 -5
  66. data/lib/arel_extensions/version.rb +1 -1
  67. data/lib/arel_extensions/visitors.rb +1 -1
  68. data/lib/arel_extensions/visitors/ibm_db.rb +1 -1
  69. data/lib/arel_extensions/visitors/mssql.rb +18 -17
  70. data/lib/arel_extensions/visitors/mysql.rb +92 -46
  71. data/lib/arel_extensions/visitors/oracle.rb +40 -28
  72. data/lib/arel_extensions/visitors/oracle12.rb +1 -1
  73. data/lib/arel_extensions/visitors/postgresql.rb +80 -34
  74. data/lib/arel_extensions/visitors/sqlite.rb +54 -46
  75. data/lib/arel_extensions/visitors/to_sql.rb +75 -62
  76. data/test/arelx_test_helper.rb +28 -0
  77. data/test/real_db_test.rb +1 -1
  78. data/test/support/fake_record.rb +4 -0
  79. data/test/test_comparators.rb +9 -8
  80. data/test/visitors/test_bulk_insert_oracle.rb +8 -7
  81. data/test/visitors/test_bulk_insert_sqlite.rb +9 -8
  82. data/test/visitors/test_bulk_insert_to_sql.rb +8 -10
  83. data/test/visitors/test_oracle.rb +41 -40
  84. data/test/visitors/test_to_sql.rb +367 -193
  85. data/test/with_ar/all_agnostic_test.rb +85 -39
  86. data/test/with_ar/insert_agnostic_test.rb +3 -2
  87. data/test/with_ar/test_bulk_sqlite.rb +6 -5
  88. data/test/with_ar/test_math_sqlite.rb +4 -4
  89. data/test/with_ar/test_string_mysql.rb +4 -6
  90. data/test/with_ar/test_string_sqlite.rb +3 -7
  91. data/version_v1.rb +3 -0
  92. data/version_v2.rb +3 -0
  93. metadata +13 -7
  94. 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]')
@@ -3,31 +3,33 @@ require 'arel'
3
3
  module ArelExtensions
4
4
  module InsertManager
5
5
 
6
- def bulk_insert(cols, data)
7
- 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
20
- end
21
- self.values = BulkValues.new(@ast.columns, data)
22
- end
6
+ def bulk_insert(cols, data)
7
+ res_columns = []
8
+ case cols.first
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
21
+ end
22
+ self.values = BulkValues.new(res_columns, data)
23
+ @ast.columns = res_columns
24
+ end
23
25
 
24
- class BulkValues < Arel::Nodes::Node
25
- attr_accessor :left, :cols
26
- def initialize(cols, values)
27
- @left = values
28
- @cols = cols
29
- end
30
- end
26
+ class BulkValues < Arel::Nodes::Node
27
+ attr_accessor :left, :cols
28
+ def initialize(cols, values)
29
+ @left = values
30
+ @cols = cols
31
+ end
32
+ end
31
33
 
32
34
  end
33
35
  end
@@ -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,20 +1,23 @@
1
+ require 'arel_extensions/boolean_functions'
2
+
1
3
  module ArelExtensions
2
4
  module Nodes
3
- class Blank < Function
4
- RETURN_TYPE = :boolean
5
+ class Blank < Arel::Nodes::Unary
6
+ include ArelExtensions::BooleanFunctions
7
+ RETURN_TYPE = :boolean
5
8
 
6
- def initialize expr
7
- # super [expr.first.coalesce('').trim.trim("\t").trim("\n")]
8
- super expr
9
- end
9
+ def initialize expr
10
+ super expr.first
11
+ end
10
12
  end
11
13
 
12
- class NotBlank < Function
13
- RETURN_TYPE = :boolean
14
+ class NotBlank < Arel::Nodes::Unary
15
+ include ArelExtensions::BooleanFunctions
16
+ RETURN_TYPE = :boolean
14
17
 
15
- def initialize expr
16
- super expr
17
- end
18
+ def initialize expr
19
+ super expr.first
20
+ end
18
21
  end
19
22
 
20
23
  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
@@ -1,11 +1,11 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
3
  class Downcase < Function
4
- RETURN_TYPE = :string
4
+ RETURN_TYPE = :string
5
5
  end
6
6
 
7
7
  class Upcase < Function
8
- RETURN_TYPE = :string
8
+ RETURN_TYPE = :string
9
9
  end
10
10
  end
11
11
  end
@@ -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
@@ -1,18 +1,18 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
3
  class Collate < Function
4
- RETURN_TYPE = :string
5
-
6
- attr_accessor :ai, :ci, :option
7
-
8
- def initialize left, option=nil, ai=false, ci=false
9
- @ai = ai
10
- @ci = ci
11
- @option = option
12
- tab = [convert_to_node(left)]
13
- return super(tab)
14
- end
15
-
4
+ RETURN_TYPE = :string
5
+
6
+ attr_accessor :ai, :ci, :option
7
+
8
+ def initialize left, option = nil, ai = false, ci = false
9
+ @ai = ai
10
+ @ci = ci
11
+ @option = option
12
+ tab = [convert_to_node(left)]
13
+ return super(tab)
14
+ end
15
+
16
16
  end
17
17
  end
18
18
  end
@@ -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