arel_extensions 2.0.24 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
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