arel_extensions 1.2.8 → 1.2.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +54 -86
  3. data/README.md +1 -1
  4. data/Rakefile +13 -2
  5. data/gemfiles/rails4.gemfile +1 -1
  6. data/gemfiles/rails6.gemfile +1 -1
  7. data/generate_gems.sh +4 -3
  8. data/lib/arel_extensions.rb +45 -20
  9. data/lib/arel_extensions/attributes.rb +0 -1
  10. data/lib/arel_extensions/boolean_functions.rb +39 -12
  11. data/lib/arel_extensions/insert_manager.rb +7 -5
  12. data/lib/arel_extensions/nodes/abs.rb +0 -0
  13. data/lib/arel_extensions/nodes/case.rb +8 -2
  14. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  15. data/lib/arel_extensions/nodes/coalesce.rb +0 -0
  16. data/lib/arel_extensions/nodes/concat.rb +0 -0
  17. data/lib/arel_extensions/nodes/duration.rb +0 -0
  18. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  19. data/lib/arel_extensions/nodes/floor.rb +0 -0
  20. data/lib/arel_extensions/nodes/function.rb +0 -0
  21. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  22. data/lib/arel_extensions/nodes/json.rb +40 -25
  23. data/lib/arel_extensions/nodes/length.rb +0 -0
  24. data/lib/arel_extensions/nodes/locate.rb +0 -0
  25. data/lib/arel_extensions/nodes/rand.rb +0 -0
  26. data/lib/arel_extensions/nodes/replace.rb +0 -0
  27. data/lib/arel_extensions/nodes/round.rb +0 -0
  28. data/lib/arel_extensions/nodes/soundex.rb +0 -0
  29. data/lib/arel_extensions/nodes/substring.rb +0 -0
  30. data/lib/arel_extensions/nodes/trim.rb +0 -0
  31. data/lib/arel_extensions/nodes/union.rb +0 -0
  32. data/lib/arel_extensions/nodes/wday.rb +0 -0
  33. data/lib/arel_extensions/predications.rb +22 -19
  34. data/lib/arel_extensions/set_functions.rb +2 -2
  35. data/lib/arel_extensions/string_functions.rb +13 -0
  36. data/lib/arel_extensions/version.rb +1 -1
  37. data/lib/arel_extensions/visitors.rb +1 -1
  38. data/lib/arel_extensions/visitors/ibm_db.rb +1 -1
  39. data/lib/arel_extensions/visitors/mysql.rb +24 -9
  40. data/lib/arel_extensions/visitors/oracle.rb +7 -8
  41. data/lib/arel_extensions/visitors/postgresql.rb +1 -1
  42. data/lib/arel_extensions/visitors/sqlite.rb +11 -17
  43. data/lib/arel_extensions/visitors/to_sql.rb +56 -55
  44. data/test/arelx_test_helper.rb +28 -0
  45. data/test/support/fake_record.rb +4 -0
  46. data/test/test_comparators.rb +8 -7
  47. data/test/visitors/test_bulk_insert_oracle.rb +4 -3
  48. data/test/visitors/test_bulk_insert_sqlite.rb +4 -3
  49. data/test/visitors/test_bulk_insert_to_sql.rb +3 -3
  50. data/test/visitors/test_oracle.rb +41 -41
  51. data/test/visitors/test_to_sql.rb +366 -206
  52. data/test/with_ar/all_agnostic_test.rb +19 -8
  53. data/test/with_ar/insert_agnostic_test.rb +1 -1
  54. data/test/with_ar/test_bulk_sqlite.rb +4 -3
  55. data/test/with_ar/test_math_sqlite.rb +1 -1
  56. data/test/with_ar/test_string_mysql.rb +1 -1
  57. data/test/with_ar/test_string_sqlite.rb +1 -1
  58. data/version_v1.rb +1 -1
  59. data/version_v2.rb +1 -1
  60. metadata +3 -3
  61. 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,28 +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
51
+
52
+ attr_reader :children
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
+
43
70
  end
@@ -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
9
  when String, Symbol
9
10
  cols.each { |c|
10
- @ast.columns << @ast.relation[c]
11
+ res_columns << @ast.relation[c]
11
12
  }
12
13
  when Array
13
14
  if String === cols.first.first
14
- @ast.columns = cols.map {|c| [@ast.relation[c.first]] }
15
+ res_columns = cols.map {|c| [@ast.relation[c.first]] }
15
16
  elsif Arel::Attributes::Attribute == cols.first.first
16
- @ast.columns = cols
17
+ res_columns = cols
17
18
  end
18
19
  when NilClass
19
- @ast.columns = @ast.relation.columns
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
File without changes
@@ -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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -26,6 +26,44 @@ module ArelExtensions
26
26
  [@dict].hash
27
27
  end
28
28
 
29
+ def convert_to_json_node(n)
30
+ case n
31
+ when JsonNode
32
+ n.dict
33
+ when Array
34
+ n.map{|e|
35
+ (e.is_a?(Array) || e.is_a?(Hash)) ? Json.new(e) : convert_to_json_node(e)
36
+ }
37
+ when Hash
38
+ n.reduce({}){|acc,v|
39
+ acc[convert_to_json_node(v[0])] = (v[1].is_a?(Array) || v[1].is_a?(Hash)) ? Json.new(v[1]) : convert_to_json_node(v[1])
40
+ acc
41
+ }
42
+ when String, Numeric, TrueClass, FalseClass
43
+ convert_to_node(n)
44
+ when Date
45
+ convert_to_node(n.strftime("%Y-%m-%d"))
46
+ when DateTime, Time
47
+ convert_to_node(n.strftime("%Y-%m-%dT%H:%M:%S.%L%:z"))
48
+ when NilClass
49
+ Arel.sql('null')
50
+ else
51
+ convert_to_node(n)
52
+ end
53
+ end
54
+
55
+ def type_of_node(v)
56
+ if v.is_a?(Arel::Attributes::Attribute)
57
+ self.type_of_attribute(v)
58
+ elsif v.respond_to?(:return_type)
59
+ v.return_type
60
+ elsif v.nil?
61
+ :nil
62
+ else
63
+ :string
64
+ end
65
+ end
66
+
29
67
  end
30
68
 
31
69
  class Json < JsonNode
@@ -33,32 +71,9 @@ module ArelExtensions
33
71
  def initialize *expr
34
72
  @dict =
35
73
  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')
52
- else
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
59
- end
74
+ convert_to_json_node(expr.first)
60
75
  else
61
- expr.map{|e| (e.is_a?(Array) || e.is_a?(Hash)) ? Json.new(e) : convert_to_node(e) }
76
+ expr.map{|e| convert_to_json_node(e) }
62
77
  end
63
78
  super
64
79
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,5 +1,6 @@
1
1
  module ArelExtensions
2
2
  module Predications
3
+
3
4
  def when right, expression = nil
4
5
  ArelExtensions::Nodes::Case.new(self).when(right,expression)
5
6
  end
@@ -20,23 +21,25 @@ 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
32
  nils, values = other.partition{ |v| v.nil? }
29
33
  ranges, values = values.partition{ |v| v.is_a?(Range) || v.is_a?(Arel::SelectManager)}
30
-
31
34
  # In order of (imagined) decreasing efficiency: nil, values, and then more complex.
32
35
  clauses =
33
36
  nils.uniq.map { |r| self.in(r) } \
34
37
  + (case values.uniq.size
35
38
  when 0 then []
36
- when 1 then [self == values[0]]
39
+ when 1 then [values[0].is_a?(Arel::Nodes::Grouping) ? self.in(values[0]) : self.eq(values[0])]
37
40
  else [Arel::Nodes::In.new(self, quoted_array(values))] end) \
38
41
  + ranges.uniq.map { |r| self.in(r) }
39
- clauses.empty? ? self.is_null : clauses.reduce(&:or)
42
+ clauses.empty? ? Arel.false : clauses.reduce(&:or)
40
43
  when nil
41
44
  self.is_null
42
45
  when Arel::SelectManager
@@ -46,24 +49,25 @@ module ArelExtensions
46
49
  end
47
50
  end
48
51
 
49
- 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
50
54
  case other
51
55
  when Range
52
56
  Arel::Nodes::Not.new(self.between(other))
57
+ when Arel::Nodes::Grouping
58
+ Arel::Nodes::NotIn.new(self, quoted_node(other))
53
59
  when Enumerable
54
- if other.include?(nil)
55
- other.delete(nil)
56
- case other.length
57
- when 0
58
- self.is_not_null
59
- when 1
60
- self.is_not_null.and(self!=other[0])
61
- else
62
- self.is_not_null.and(Arel::Nodes::NotIn.new(self,quoted_array(other)))
63
- end
64
- else
65
- Arel::Nodes::NotIn.new(self,quoted_array(other))
66
- 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
67
71
  when nil
68
72
  self.is_not_null
69
73
  when Arel::SelectManager
@@ -93,6 +97,5 @@ module ArelExtensions
93
97
  raise(ArgumentError, "#{object.class} can not be converted to CONCAT arg")
94
98
  end
95
99
  end
96
-
97
100
  end
98
101
  end
@@ -23,10 +23,10 @@ module ArelExtensions
23
23
  end
24
24
  end
25
25
 
26
- Arel::Nodes::Union.class_eval do
26
+ class Arel::Nodes::Union
27
27
  include ArelExtensions::SetFunctions
28
28
  end
29
29
 
30
- Arel::Nodes::UnionAll.class_eval do
30
+ class Arel::Nodes::UnionAll
31
31
  include ArelExtensions::SetFunctions
32
32
  end
@@ -64,6 +64,19 @@ module ArelExtensions
64
64
  grouping_any :imatches, others, escape
65
65
  end
66
66
 
67
+ # def grouping_any method, others, *extra
68
+ # puts "*******************"
69
+ # puts method
70
+ # puts others.inspect
71
+ # puts extra.inspect
72
+ # puts "-------------------"
73
+ # res = super(method,others,*extra)
74
+ # puts res.to_sql
75
+ # puts res.inspect
76
+ # puts "*******************"
77
+ # res
78
+ # end
79
+
67
80
  def imatches_all others, escape = nil
68
81
  grouping_all :imatches, others, escape, escape
69
82
  end
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "1.2.8".freeze
2
+ VERSION = "1.2.16".freeze
3
3
  end
@@ -6,7 +6,7 @@ require 'arel_extensions/visitors/postgresql'
6
6
  require 'arel_extensions/visitors/sqlite'
7
7
  require 'arel_extensions/visitors/mssql'
8
8
 
9
- Arel::Visitors::MSSQL.class_eval do
9
+ class Arel::Visitors::MSSQL
10
10
  include ArelExtensions::Visitors::MSSQL
11
11
 
12
12
  alias_method :old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As
@@ -1,6 +1,6 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::IBM_DB.class_eval do
3
+ class Arel::Visitors::IBM_DB
4
4
 
5
5
  def visit_ArelExtensions_Nodes_Ceil o, collector
6
6
  collector << "CEILING("
@@ -1,6 +1,6 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::MySQL.class_eval do
3
+ class Arel::Visitors::MySQL
4
4
  Arel::Visitors::MySQL::DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'WEEK', 'y' => 'YEAR', 'wd' => 'WEEKDAY', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
5
5
  Arel::Visitors::MySQL::DATE_FORMAT_DIRECTIVES = { # ISO C / POSIX
6
6
  '%Y' => '%Y', '%C' => '', '%y' => '%y', '%m' => '%m', '%B' => '%M', '%b' => '%b', '%^b' => '%b', # year, month
@@ -193,6 +193,13 @@ module ArelExtensions
193
193
  collector
194
194
  end
195
195
 
196
+ def visit_ArelExtensions_Nodes_RegexpReplace o, collector
197
+ if !regexp_replace_supported?
198
+ warn("Warning : ArelExtensions: REGEXP_REPLACE does not seem to be available in the current version of the DBMS, it might crash")
199
+ end
200
+ super(o,collector)
201
+ end
202
+
196
203
  def visit_ArelExtensions_Nodes_Format o, collector
197
204
  case o.col_type
198
205
  when :date, :datetime
@@ -403,7 +410,7 @@ module ArelExtensions
403
410
 
404
411
  def visit_Aggregate_For_AggregateFunction o, collector
405
412
  if !window_supported?
406
- warn("Warning : ArelExtensions: Window Functions are not available in the current version on the DBMS.")
413
+ warn("Warning : ArelExtensions: Window Functions are not available in the current version of the DBMS.")
407
414
  return collector
408
415
  end
409
416
 
@@ -449,14 +456,22 @@ module ArelExtensions
449
456
  version_supported?('10.2.3', '8.0')
450
457
  end
451
458
 
452
- def version_supported?(mysql_v = '10.2.3',mariadb_v = '5.7.0')
459
+ def regexp_replace_supported?
460
+ version_supported?('10.0.5', '8.0')
461
+ end
462
+
463
+ def version_supported?(mariadb_v = '10.2.3', mysql_v = '5.7.0')
453
464
  conn = Arel::Table.engine.connection
454
- conn.send(:mariadb?) &&
455
- (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mysql_v ||
456
- conn.respond_to?(:version) && conn.send(:version) >= mysql_v) ||
457
- !Arel::Table.engine.connection.send(:mariadb?) &&
458
- (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mariadb_v ||
459
- conn.respond_to?(:version) && conn.send(:version) >= mariadb_v)
465
+ conn.send(:mariadb?) && \
466
+ (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mariadb_v || \
467
+ conn.respond_to?(:version) && conn.send(:version) >= mariadb_v || \
468
+ conn.instance_variable_get(:"@version") && conn.instance_variable_get(:"@version") >= mariadb_v) || \
469
+ !conn.send(:mariadb?) && \
470
+ (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mysql_v || \
471
+ conn.respond_to?(:version) && conn.send(:version) >= mysql_v || \
472
+ conn.instance_variable_get(:"@version") && conn.instance_variable_get(:"@version") >= mysql_v)
473
+ # ideally we should parse the instance_variable @full_version because @version contains only the supposedly
474
+ # corresponding mysql version of the current mariadb version (which is not very helpful most of the time)
460
475
  end
461
476
 
462
477
  def visit_ArelExtensions_Nodes_Json o,collector