arel_extensions 2.0.24 → 2.1.2

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +358 -71
  3. data/Gemfile +8 -8
  4. data/README.md +107 -0
  5. data/gemfiles/rails5_2.gemfile +8 -7
  6. data/gemfiles/rails6.gemfile +7 -8
  7. data/gemfiles/rails6_1.gemfile +6 -7
  8. data/gemfiles/rails7.gemfile +22 -0
  9. data/lib/arel_extensions/aliases.rb +14 -0
  10. data/lib/arel_extensions/attributes.rb +2 -0
  11. data/lib/arel_extensions/date_duration.rb +2 -2
  12. data/lib/arel_extensions/helpers.rb +48 -0
  13. data/lib/arel_extensions/math.rb +17 -27
  14. data/lib/arel_extensions/nodes/case.rb +5 -10
  15. data/lib/arel_extensions/nodes/date_diff.rb +23 -4
  16. data/lib/arel_extensions/nodes/format.rb +3 -2
  17. data/lib/arel_extensions/nodes/function.rb +1 -7
  18. data/lib/arel_extensions/version.rb +1 -1
  19. data/lib/arel_extensions/visitors/mssql.rb +123 -51
  20. data/lib/arel_extensions/visitors/mysql.rb +23 -2
  21. data/lib/arel_extensions/visitors/oracle.rb +13 -1
  22. data/lib/arel_extensions/visitors/postgresql.rb +23 -5
  23. data/lib/arel_extensions/visitors/sqlite.rb +6 -3
  24. data/lib/arel_extensions/visitors/to_sql.rb +13 -8
  25. data/lib/arel_extensions.rb +27 -7
  26. data/test/arelx_test_helper.rb +45 -1
  27. data/test/database.yml +8 -2
  28. data/test/real_db_test.rb +5 -1
  29. data/test/support/fake_record.rb +1 -1
  30. data/test/visitors/test_to_sql.rb +38 -10
  31. data/test/with_ar/all_agnostic_test.rb +83 -5
  32. data/test/with_ar/insert_agnostic_test.rb +6 -2
  33. data/test/with_ar/test_bulk_sqlite.rb +6 -2
  34. data/test/with_ar/test_math_sqlite.rb +6 -2
  35. data/test/with_ar/test_string_mysql.rb +6 -2
  36. data/test/with_ar/test_string_sqlite.rb +6 -2
  37. data/version_v1.rb +1 -1
  38. data/version_v2.rb +1 -1
  39. metadata +6 -4
  40. 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: "../"
@@ -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
@@ -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'
@@ -38,21 +40,17 @@ module ArelExtensions
38
40
  when Arel::Nodes::Function
39
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.grouping(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.grouping(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.grouping(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.grouping(ArelExtensions::Nodes::DateSub.new [self, other])
69
+ Arel.grouping(ArelExtensions::Nodes::DateSub.new [self, Arel::Nodes.build_quoted(other)])
72
70
  else
73
- Arel.grouping(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.grouping(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.grouping(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.grouping(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.grouping(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.grouping(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
@@ -127,7 +117,7 @@ module ArelExtensions
127
117
  when String
128
118
  Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
129
119
  else
130
- Arel.grouping(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,11 +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)
106
- end
107
-
108
- def xas other
109
- Arel::Nodes::As.new self, Arel::Nodes::SqlLiteral.new(other)
104
+ Arel::Nodes::As.new self, Arel.sql(other)
110
105
  end
111
106
  end
112
107
  end
@@ -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 = expr[2]
13
14
  @col_type = type_of_attribute(col)
14
15
  super [col, convert_to_string_node(@iso_format)]
15
16
  end
@@ -51,13 +51,7 @@ module ArelExtensions
51
51
  def type_of_attribute(att)
52
52
  case att
53
53
  when Arel::Attributes::Attribute
54
- begin
55
- if Arel::Table.engine.connection.tables.include? att.relation.table_name
56
- Arel::Table.engine.connection.schema_cache.columns_hash(att.relation.table_name)[att.name.to_s].type
57
- end
58
- rescue
59
- att
60
- end
54
+ ArelExtensions::column_of(att.relation.table_name, att.name.to_s)&.type || att
61
55
  when ArelExtensions::Nodes::Function
62
56
  att.return_type
63
57
  # else
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "2.0.24".freeze
2
+ VERSION = "2.1.2".freeze
3
3
  end