arel_extensions 1.2.23 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +358 -71
  3. data/Gemfile +8 -8
  4. data/README.md +86 -0
  5. data/arel_extensions.gemspec +1 -1
  6. data/gemfiles/rails5_2.gemfile +8 -7
  7. data/gemfiles/rails6.gemfile +7 -8
  8. data/gemfiles/rails6_1.gemfile +6 -7
  9. data/gemfiles/rails7.gemfile +22 -0
  10. data/gemspecs/arel_extensions-v1.gemspec +1 -1
  11. data/gemspecs/arel_extensions-v2.gemspec +1 -1
  12. data/lib/arel_extensions/aliases.rb +14 -0
  13. data/lib/arel_extensions/attributes.rb +2 -0
  14. data/lib/arel_extensions/date_duration.rb +2 -2
  15. data/lib/arel_extensions/helpers.rb +48 -0
  16. data/lib/arel_extensions/insert_manager.rb +19 -17
  17. data/lib/arel_extensions/math.rb +22 -32
  18. data/lib/arel_extensions/nodes/case.rb +5 -6
  19. data/lib/arel_extensions/nodes/cast.rb +1 -1
  20. data/lib/arel_extensions/nodes/date_diff.rb +23 -4
  21. data/lib/arel_extensions/nodes/format.rb +3 -2
  22. data/lib/arel_extensions/nodes/function.rb +2 -6
  23. data/lib/arel_extensions/nodes/json.rb +3 -1
  24. data/lib/arel_extensions/nodes/length.rb +6 -0
  25. data/lib/arel_extensions/nodes/replace.rb +0 -8
  26. data/lib/arel_extensions/nodes/union.rb +1 -1
  27. data/lib/arel_extensions/nodes/union_all.rb +1 -1
  28. data/lib/arel_extensions/string_functions.rb +10 -2
  29. data/lib/arel_extensions/version.rb +1 -1
  30. data/lib/arel_extensions/visitors/mssql.rb +109 -51
  31. data/lib/arel_extensions/visitors/mysql.rb +15 -2
  32. data/lib/arel_extensions/visitors/oracle.rb +8 -3
  33. data/lib/arel_extensions/visitors/postgresql.rb +21 -11
  34. data/lib/arel_extensions/visitors/sqlite.rb +6 -3
  35. data/lib/arel_extensions/visitors/to_sql.rb +14 -9
  36. data/lib/arel_extensions/visitors.rb +9 -1
  37. data/lib/arel_extensions.rb +66 -12
  38. data/test/arelx_test_helper.rb +45 -0
  39. data/test/database.yml +8 -2
  40. data/test/real_db_test.rb +5 -1
  41. data/test/support/fake_record.rb +1 -1
  42. data/test/visitors/test_to_sql.rb +39 -11
  43. data/test/with_ar/all_agnostic_test.rb +79 -6
  44. data/test/with_ar/insert_agnostic_test.rb +6 -2
  45. data/test/with_ar/test_bulk_sqlite.rb +6 -2
  46. data/test/with_ar/test_math_sqlite.rb +6 -2
  47. data/test/with_ar/test_string_mysql.rb +6 -2
  48. data/test/with_ar/test_string_sqlite.rb +6 -2
  49. data/version_v1.rb +1 -1
  50. data/version_v2.rb +1 -1
  51. metadata +10 -8
  52. data/appveyor.yml +0 -44
@@ -8,23 +8,22 @@ group :development, :test do
8
8
  gem 'activemodel', '~> 6.0.0'
9
9
  gem 'activerecord', '~> 6.0.0'
10
10
 
11
- gem "sqlite3", '~> 1.4', platforms: [:mri, :mswin, :mingw]
12
- gem "mysql2", '0.5.2', platforms: [:mri, :mswin, :mingw]
13
- gem "pg",'< 1.0.0', platforms: [:mri, :mingw]
11
+ gem "sqlite3", '~> 1.4', platforms: [:mri]
12
+ gem "mysql2", '0.5.2', platforms: [:mri]
13
+ gem "pg",'< 1.0.0', platforms: [:mri]
14
14
 
15
- #gem "tiny_tds", platforms: [:mri, :mingw] if RUBY_PLATFORM =~ /windows/
16
- #gem "activerecord-sqlserver-adapter", platforms: [:mri, :mingw]
15
+ gem "tiny_tds", platforms: [:mri, :mingw, :x64_mingw, :mswin]
16
+ gem "activerecord-sqlserver-adapter", '~> 6.0', platforms: [:mri, :mingw, :x64_mingw, :mswin]
17
17
 
18
18
  gem 'ruby-oci8', platforms: [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
19
19
  gem 'activerecord-oracle_enhanced-adapter', '~> 6.0.0' if ENV.has_key? 'ORACLE_HOME'
20
20
 
21
21
  # for JRuby
22
- gem 'activerecord-jdbc-adapter', platforms: :jruby
22
+ gem 'activerecord-jdbc-adapter', github: 'jruby/activerecord-jdbc-adapter', tag: 'v60.4', platforms: :jruby
23
23
  gem "jdbc-sqlite3", platforms: :jruby
24
24
  gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
25
25
  gem "activerecord-jdbcmysql-adapter", platforms: :jruby
26
26
  gem "activerecord-jdbcpostgresql-adapter", platforms: :jruby
27
- gem "activerecord-jdbcmssql-adapter", platforms: :jruby
28
27
  end
29
28
 
30
- gemspec path: "../"
29
+ gemspec path: "../"
@@ -8,23 +8,22 @@ group :development, :test do
8
8
  gem 'activemodel', '~> 6.1.0'
9
9
  gem 'activerecord', '~> 6.1.0'
10
10
 
11
- gem "sqlite3", '~> 1.4', platforms: [:mri, :mswin, :mingw]
12
- gem "mysql2", '0.5.2', platforms: [:mri, :mswin, :mingw]
13
- gem "pg",'~> 1.1', platforms: [:mri, :mingw]
11
+ gem "sqlite3", '~> 1.4', platforms: [:mri]
12
+ gem "mysql2", '0.5.2', platforms: [:mri]
13
+ gem "pg",'~> 1.1', platforms: [:mri]
14
14
 
15
- #gem "tiny_tds", platforms: [:mri, :mingw] if RUBY_PLATFORM =~ /windows/
16
- #gem "activerecord-sqlserver-adapter", platforms: [:mri, :mingw]
15
+ gem "tiny_tds", platforms: [:mri, :mingw, :x64_mingw, :mswin]
16
+ gem "activerecord-sqlserver-adapter", '~> 6.1.0', platforms: [:mri, :mingw, :x64_mingw, :mswin]
17
17
 
18
18
  gem 'ruby-oci8', platforms: [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
19
19
  gem 'activerecord-oracle_enhanced-adapter', '~> 6.0.0' if ENV.has_key? 'ORACLE_HOME'
20
20
 
21
21
  # for JRuby
22
- gem 'activerecord-jdbc-adapter', platforms: :jruby
22
+ gem 'activerecord-jdbc-adapter', github: 'jruby/activerecord-jdbc-adapter', tag: 'v61.1', platforms: :jruby
23
23
  gem "jdbc-sqlite3", platforms: :jruby
24
24
  gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
25
25
  gem "activerecord-jdbcmysql-adapter", platforms: :jruby
26
26
  gem "activerecord-jdbcpostgresql-adapter", platforms: :jruby
27
- gem "activerecord-jdbcmssql-adapter", platforms: :jruby
28
27
  end
29
28
 
30
29
  gemspec path: "../"
@@ -0,0 +1,22 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'rails', '~> 7.0.1'
4
+
5
+
6
+ group :development, :test do
7
+ gem 'activesupport', '~> 7.0.1'
8
+ gem 'activemodel', '~> 7.0.1'
9
+ gem 'activerecord', '~> 7.0.1'
10
+
11
+ gem "sqlite3", '~> 1.4', platforms: [:mri]
12
+ gem "mysql2", '0.5.2', platforms: [:mri]
13
+ gem "pg",'~> 1.1', platforms: [:mri]
14
+
15
+ gem "tiny_tds", platforms: [:mri, :mingw, :x64_mingw, :mswin]
16
+ gem "activerecord-sqlserver-adapter", '~> 7.0.0.0', platforms: [:mri, :mingw, :x64_mingw, :mswin]
17
+
18
+ gem 'ruby-oci8', platforms: [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
19
+ gem 'activerecord-oracle_enhanced-adapter', '~> 6.0.0' if ENV.has_key? 'ORACLE_HOME'
20
+ end
21
+
22
+ gemspec path: "../"
@@ -23,6 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency('arel', '>= 6.0')
24
24
 
25
25
  s.add_development_dependency('minitest', '~> 5.9')
26
- s.add_development_dependency('rdoc', '~> 4.0')
26
+ s.add_development_dependency('rdoc', '>= 6.3.1')
27
27
  s.add_development_dependency('rake', '~> 12.3.3')
28
28
  end
@@ -23,6 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency('activerecord', '>= 6.0')
24
24
 
25
25
  s.add_development_dependency('minitest', '~> 5.9')
26
- s.add_development_dependency('rdoc', '~> 4.0')
26
+ s.add_development_dependency('rdoc', '>= 6.3.1')
27
27
  s.add_development_dependency('rake', '~> 12.3.3')
28
28
  end
@@ -0,0 +1,14 @@
1
+ module ArelExtensions
2
+ module Aliases
3
+
4
+ # Install an alias, if present.
5
+ def xas other
6
+ if other.present?
7
+ Arel::Nodes::As.new(self, Arel.sql(other))
8
+ else
9
+ self
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -1,3 +1,4 @@
1
+ require 'arel_extensions/aliases'
1
2
  require 'arel_extensions/math'
2
3
  require 'arel_extensions/comparators'
3
4
  require 'arel_extensions/date_duration'
@@ -8,6 +9,7 @@ require 'arel_extensions/predications'
8
9
 
9
10
  module ArelExtensions
10
11
  module Attributes
12
+ include ArelExtensions::Aliases
11
13
  include ArelExtensions::Math
12
14
  include ArelExtensions::Comparators
13
15
  include ArelExtensions::DateDuration
@@ -40,8 +40,8 @@ module ArelExtensions
40
40
  ArelExtensions::Nodes::Duration.new "s", self
41
41
  end
42
42
 
43
- def format(tpl)
44
- ArelExtensions::Nodes::Format.new [self, tpl]
43
+ def format(tpl, time_zone = nil)
44
+ ArelExtensions::Nodes::Format.new [self, tpl, time_zone]
45
45
  end
46
46
  end
47
47
  end
@@ -0,0 +1,48 @@
1
+ module ArelExtensions
2
+
3
+ #
4
+ # column_of
5
+ #
6
+ # Before the creation of these methods, getting the column name was done
7
+ # uniquely through the code found in `column_of_via_arel_table`.
8
+ #
9
+ # This turned out to be unreliable, most notably when using adapters that do
10
+ # not come with activerecord standard batteries. SQL Server is the most
11
+ # notorious example.
12
+ #
13
+ # Currently, we're using a needlessly complicated way to address this issue.
14
+ # Different versions of activerecord are behaving differently; the public APIs
15
+ # do not seem to come with any guarantees, so we need to be sure that we're
16
+ # coveing all these cases.
17
+
18
+ def self.column_of_via_arel_table(table_name, column_name)
19
+ begin
20
+ Arel::Table.engine.connection.schema_cache.columns_hash(table_name)[column_name]
21
+ rescue NoMethodError
22
+ nil
23
+ rescue Exception => e
24
+ puts "Failed to fetch column info for #{table_name}.#{column_name} ."
25
+ puts "This should never be reached."
26
+ puts "#{e.class}: #{e}"
27
+ nil
28
+ end
29
+ end
30
+
31
+ def self.column_of(table_name, column_name)
32
+ use_arel_table = !ActiveRecord::Base.connected? || \
33
+ (ActiveRecord::Base.connection.pool.respond_to?(:schema_cache) && ActiveRecord::Base.connection.pool.schema_cache.nil?)
34
+
35
+ if use_arel_table
36
+ column_of_via_arel_table(table_name, column_name)
37
+ else
38
+ if ActiveRecord::Base.connection.pool.respond_to?(:pool_config)
39
+ ActiveRecord::Base.connection.pool.pool_config.schema_cache.columns_hash(table_name)[column_name]
40
+ elsif ActiveRecord::Base.connection.pool.respond_to?(:schema_cache)
41
+ ActiveRecord::Base.connection.pool.schema_cache.columns_hash(table_name)[column_name]
42
+ else
43
+ puts ">>> We really shouldn't be here #{table_name}.#{column_name}"
44
+ column_of_via_arel_table(table_name, column_name)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -3,23 +3,25 @@ require 'arel'
3
3
  module ArelExtensions
4
4
  module InsertManager
5
5
  def bulk_insert(cols, data)
6
- res_columns = []
7
- case cols.first
8
- when String, Symbol
9
- cols.each { |c|
10
- res_columns << @ast.relation[c]
11
- }
12
- when Array
13
- if String === cols.first.first
14
- res_columns = cols.map {|c| [@ast.relation[c.first]] }
15
- elsif Arel::Attributes::Attribute == cols.first.first
16
- res_columns = cols
17
- end
18
- when NilClass
19
- res_columns = @ast.relation.columns
20
- end
21
- self.values = BulkValues.new(res_columns, data)
22
- @ast.columns = res_columns
6
+ raise ArgumentError, "cols must be present" if cols.blank?
7
+ columns =
8
+ case cols.first
9
+ when Array
10
+ case cols.first.first
11
+ when Arel::Attributes::Attribute
12
+ cols
13
+ when String, Symbol
14
+ cols.map {|c| [@ast.relation[c.first]] }
15
+ else
16
+ raise ArgumentError, "cols has an invalid type: #{cols.first.first.class}"
17
+ end
18
+ when String, Symbol
19
+ cols.map { |c| @ast.relation[c] }
20
+ else
21
+ raise ArgumentError, "cols has an invalid type: #{cols.first.class}"
22
+ end
23
+ self.values = BulkValues.new(columns, data)
24
+ @ast.columns = columns
23
25
  end
24
26
 
25
27
  class BulkValues < Arel::Nodes::Node
@@ -1,3 +1,5 @@
1
+ require 'arel_extensions/helpers'
2
+
1
3
  require 'arel_extensions/nodes'
2
4
  require 'arel_extensions/nodes/function'
3
5
  require 'arel_extensions/nodes/concat'
@@ -22,37 +24,33 @@ module ArelExtensions
22
24
  if self.expr.left.is_a?(String) || self.expr.right.is_a?(String)
23
25
  return self.concat(other)
24
26
  else
25
- return Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
27
+ return Arel.grouping(Arel::Nodes::Addition.new self, other)
26
28
  end
27
29
  when ArelExtensions::Nodes::Function,ArelExtensions::Nodes::Case
28
30
  return case self.return_type
29
31
  when :string, :text
30
32
  self.concat(other)
31
33
  when :integer, :decimal, :float, :number, :int
32
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
34
+ Arel.grouping(Arel::Nodes::Addition.new self, other)
33
35
  when :date, :datetime
34
36
  ArelExtensions::Nodes::DateAdd.new [self, other]
35
37
  else
36
38
  self.concat(other)
37
39
  end
38
40
  when Arel::Nodes::Function
39
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
41
+ Arel.grouping(Arel::Nodes::Addition.new self, other)
40
42
  else
41
- begin
42
- col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
43
- rescue Exception
44
- col = nil
45
- end
43
+ col = self.respond_to?(:relation)? ArelExtensions::column_of(self.relation.table_name, self.name.to_s) : nil
46
44
  if (!col) # if the column doesn't exist in the database
47
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new(self, other))
45
+ Arel.grouping(Arel::Nodes::Addition.new(self, Arel::Nodes.build_quoted(other)))
48
46
  else
49
47
  arg = col.type
50
48
  if arg == :integer || (!arg)
51
49
  other = other.to_i if other.is_a?(String)
52
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
50
+ Arel.grouping(Arel::Nodes::Addition.new self, Arel::Nodes.build_quoted(other))
53
51
  elsif arg == :decimal || arg == :float
54
52
  other = Arel.sql(other) if other.is_a?(String) # Arel should accept Float & BigDecimal!
55
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
53
+ Arel.grouping(Arel::Nodes::Addition.new self, Arel::Nodes.build_quoted(other))
56
54
  elsif arg == :datetime || arg == :date
57
55
  ArelExtensions::Nodes::DateAdd.new [self, other]
58
56
  elsif arg == :string || arg == :text
@@ -68,41 +66,33 @@ module ArelExtensions
68
66
  case self
69
67
  when Arel::Nodes::Grouping
70
68
  if self.expr.left.is_a?(Date) || self.expr.left.is_a?(DateTime)
71
- Arel::Nodes::Grouping.new(ArelExtensions::Nodes::DateSub.new [self, other])
69
+ Arel.grouping(ArelExtensions::Nodes::DateSub.new [self, Arel::Nodes.build_quoted(other)])
72
70
  else
73
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
71
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
74
72
  end
75
73
  when ArelExtensions::Nodes::Function, ArelExtensions::Nodes::Case
76
74
  case self.return_type
77
75
  when :string, :text # ???
78
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other)) # ??
76
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other))) # ??
79
77
  when :integer, :decimal, :float, :number
80
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
78
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
81
79
  when :date, :datetime
82
- ArelExtensions::Nodes::DateSub.new [self, other]
80
+ ArelExtensions::Nodes::DateSub.new [self, Arel::Nodes.build_quoted(other)]
83
81
  else
84
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
82
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
85
83
  end
86
84
  when Arel::Nodes::Function
87
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
85
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
88
86
  else
89
- begin
90
- col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
91
- rescue Exception
92
- col = nil
93
- end
87
+ col = ArelExtensions::column_of(self.relation.table_name, self.name.to_s)
94
88
  if (!col) # if the column doesn't exist in the database
95
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
89
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
96
90
  else
97
91
  arg = col.type
98
92
  if (arg == :date || arg == :datetime)
99
93
  case other
100
94
  when Arel::Attributes::Attribute
101
- begin
102
- col2 = Arel::Table.engine.connection.schema_cache.columns_hash(other.relation.table_name)[other.name.to_s]
103
- rescue Exception
104
- col2 = nil
105
- end
95
+ col2 = ArelExtensions::column_of(other.relation.table_name, other.name.to_s)
106
96
  if (!col2) # if the column doesn't exist in the database
107
97
  ArelExtensions::Nodes::DateSub.new [self, other]
108
98
  else
@@ -123,11 +113,11 @@ module ArelExtensions
123
113
  else
124
114
  case other
125
115
  when Integer, Float, BigDecimal
126
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other.to_s)))
116
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel.sql(other.to_s)))
127
117
  when String
128
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
118
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
129
119
  else
130
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
120
+ Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
131
121
  end
132
122
  end
133
123
  end
@@ -1,3 +1,5 @@
1
+ require 'arel_extensions/helpers'
2
+
1
3
  module ArelExtensions
2
4
  module Nodes
3
5
  if Gem::Version.new(Arel::VERSION) < Gem::Version.new("7.1.0")
@@ -34,6 +36,7 @@ module ArelExtensions
34
36
  include Arel::Math
35
37
  include Arel::Predications
36
38
  include Arel::OrderPredications
39
+ include ArelExtensions::Aliases
37
40
  include ArelExtensions::Math
38
41
  include ArelExtensions::Comparators
39
42
  include ArelExtensions::Predications
@@ -56,11 +59,7 @@ module ArelExtensions
56
59
  when Date, DateTime,Time
57
60
  :datetime
58
61
  when Arel::Attributes::Attribute
59
- begin
60
- Arel::Table.engine.connection.schema_cache.columns_hash(obj.relation.table_name)[obj.name.to_s].type
61
- rescue Exception
62
- :string
63
- end
62
+ ArelExtensions::column_of(obj.relation.table_name, obj.name.to_s)&.type || :string
64
63
  else
65
64
  :string
66
65
  end
@@ -102,7 +101,7 @@ module ArelExtensions
102
101
  alias :== :eql?
103
102
 
104
103
  def as other
105
- Arel::Nodes::As.new self, Arel::Nodes::SqlLiteral.new(other)
104
+ Arel::Nodes::As.new self, Arel.sql(other)
106
105
  end
107
106
  end
108
107
  end
@@ -42,7 +42,7 @@ module ArelExtensions
42
42
  when :ruby_time
43
43
  ArelExtensions::Nodes::DateAdd.new [self, other]
44
44
  else
45
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
45
+ Arel.grouping(Arel::Nodes::Addition.new self, other)
46
46
  end
47
47
  end
48
48
 
@@ -117,7 +117,16 @@ module ArelExtensions
117
117
  if @date_type == :date
118
118
  v.to_i / (24*3600)
119
119
  elsif @date_type == :datetime
120
- v.to_i
120
+ if v.parts.size == 1
121
+ # first entry in the dict v.parts; one of [:years, :months, :weeks, :days, :hours, :minutes, :seconds]
122
+ # | the value
123
+ # | |
124
+ # | |
125
+ # v v
126
+ v.parts.first.second
127
+ else
128
+ v.to_i
129
+ end
121
130
  end
122
131
  else
123
132
  v
@@ -130,7 +139,17 @@ module ArelExtensions
130
139
  if @date_type == :date
131
140
  Arel.sql('day')
132
141
  elsif @date_type == :datetime
133
- Arel.sql('second')
142
+ res = if v.parts.size == 1
143
+ # first entry in the dict v.parts; one of [:years, :months, :weeks, :days, :hours, :minutes, :seconds]
144
+ # | the key
145
+ # | | convert symbol to string
146
+ # | | | remove the plural suffix `s`
147
+ # v v v v
148
+ v.parts.first.first.to_s[0..-2]
149
+ else
150
+ 'second'
151
+ end
152
+ Arel.sql(res)
134
153
  end
135
154
  else
136
155
  if ArelExtensions::Nodes::Duration === v
@@ -141,9 +160,9 @@ module ArelExtensions
141
160
  when 'h','mn','s'
142
161
  Arel.sql('second')
143
162
  when /i\z/
144
- Arel.sql(Arel::Visitors::MSSQL::DATE_MAPPING[v.left[0..-2]])
163
+ Arel.sql(ArelExtensions::Visitors::MSSQL::LOADED_VISITOR::DATE_MAPPING[v.left[0..-2]])
145
164
  else
146
- Arel.sql(Arel::Visitors::MSSQL::DATE_MAPPING[v.left])
165
+ Arel.sql(ArelExtensions::Visitors::MSSQL::LOADED_VISITOR::DATE_MAPPING[v.left])
147
166
  end
148
167
  end
149
168
  end
@@ -5,11 +5,12 @@ module ArelExtensions
5
5
  class Format < Function
6
6
  RETURN_TYPE = :string
7
7
 
8
- attr_accessor :col_type, :iso_format
8
+ attr_accessor :col_type, :iso_format, :time_zone
9
9
 
10
10
  def initialize expr
11
- col = expr.first
11
+ col = expr[0]
12
12
  @iso_format = convert_format(expr[1])
13
+ @time_zone = Arel::Nodes.build_quoted(expr[2]) if expr[2]
13
14
  @col_type = type_of_attribute(col)
14
15
  super [col, convert_to_string_node(@iso_format)]
15
16
  end
@@ -51,11 +51,7 @@ module ArelExtensions
51
51
  def type_of_attribute(att)
52
52
  case att
53
53
  when Arel::Attributes::Attribute
54
- begin
55
- Arel::Table.engine.connection.schema_cache.columns_hash(att.relation.table_name)[att.name.to_s].type
56
- rescue
57
- att
58
- end
54
+ ArelExtensions::column_of(att.relation.table_name, att.name.to_s)&.type || att
59
55
  when ArelExtensions::Nodes::Function
60
56
  att.return_type
61
57
  # else
@@ -80,7 +76,7 @@ module ArelExtensions
80
76
  when ActiveSupport::Duration
81
77
  Arel.sql(object.to_i)
82
78
  when Array
83
- Arel::Nodes::Grouping.new(object.map{|e| convert_to_node(e)})
79
+ Arel.grouping(object.map{|e| convert_to_node(e)})
84
80
  else
85
81
  raise(ArgumentError, "#{object.class} cannot be converted to CONCAT arg")
86
82
  end
@@ -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 = "1.2.23".freeze
2
+ VERSION = "1.3.1".freeze
3
3
  end