arel_extensions 0.9.5 → 0.9.6

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.
@@ -7,12 +7,12 @@ group :development, :test do
7
7
  gem 'activemodel', '~> 4.0'
8
8
  gem 'activerecord', '~> 4.0'
9
9
 
10
- gem "sqlite3", :platforms => [:mri, :mswin, :mingw, :x64_mingw]
11
- gem "mysql2", :platforms => [:mri, :mswin, :mingw, :x64_mingw]
12
- gem "pg", :platforms => [:mri, :mingw, :x64_mingw]
10
+ gem "sqlite3", :platforms => [:mri, :mswin, :mingw]
11
+ gem "mysql2", :platforms => [:mri, :mswin, :mingw]
12
+ gem "pg", :platforms => [:mri, :mingw]
13
13
 
14
- gem "tiny_tds", :platforms => [:mri, :mingw, :x64_mingw]
15
- gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :x64_mingw]
14
+ gem "tiny_tds", :platforms => [:mri, :mingw, :mswin]
15
+ gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :mswin]
16
16
 
17
17
  gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
18
18
  gem 'activerecord-oracle_enhanced-adapter', '~> 1.6.0' if ENV.has_key? 'ORACLE_HOME'
@@ -7,12 +7,12 @@ group :development, :test do
7
7
  gem 'activemodel', '~> 5'
8
8
  gem 'activerecord', '~> 5'
9
9
 
10
- gem "sqlite3", :platforms => [:mri, :mswin, :mingw, :x64_mingw]
11
- gem "mysql2", :platforms => [:mri, :mswin, :mingw, :x64_mingw]
12
- gem "pg", :platforms => [:mri, :mingw, :x64_mingw]
10
+ gem "sqlite3", :platforms => [:mri, :mswin, :mingw]
11
+ gem "mysql2", :platforms => [:mri, :mswin, :mingw]
12
+ gem "pg", :platforms => [:mri, :mingw]
13
13
 
14
- gem "tiny_tds", :platforms => [:mri, :mingw, :x64_mingw]
15
- # gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw]
14
+ gem "tiny_tds", :platforms => [:mri, :mingw]
15
+ # gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
16
16
 
17
17
  gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
18
18
  gem 'activerecord-oracle_enhanced-adapter', '~> 1.7.0' if ENV.has_key? 'ORACLE_HOME'
data/init/sqlite.sql CHANGED
@@ -1 +0,0 @@
1
-
@@ -1,5 +1,7 @@
1
1
  require 'arel'
2
2
 
3
+ require 'arel_extensions/railtie' if defined?(Rails::Railtie)
4
+
3
5
  # UnaryOperation|Grouping|Extract < Unary < Arel::Nodes::Node
4
6
  # Equality|Regexp|Matches < Binary < Arel::Nodes::Node
5
7
  # Count|NamedFunction < Function < Arel::Nodes::Node
@@ -12,6 +12,16 @@ module ArelExtensions
12
12
  #Date and integer adds or subtracts a specified time interval from a date.
13
13
  def +(other)
14
14
  return ArelExtensions::Nodes::Concat.new [self, other] if self.is_a?(Arel::Nodes::Quoted)
15
+ return case self.class.return_type
16
+ when :string, :text
17
+ ArelExtensions::Nodes::Concat.new [self, other]
18
+ when :integer, :decimal, :float, :number
19
+ Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
20
+ when :date, :datetime
21
+ ArelExtensions::Nodes::DateAdd.new [self, other]
22
+ else
23
+ ArelExtensions::Nodes::Concat.new [self, other]
24
+ end if self.is_a?(ArelExtensions::Nodes::Function)
15
25
  arg = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s].type
16
26
  if arg == :integer
17
27
  other = other.to_i if other.is_a?(String)
@@ -29,6 +39,16 @@ module ArelExtensions
29
39
  #function returns the time between two dates
30
40
  #function returns the susbration between two int
31
41
  def -(other)
42
+ return case self.class.return_type
43
+ when :string, :text # ???
44
+ Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other)) # ??
45
+ when :integer, :decimal, :float, :number
46
+ Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
47
+ when :date, :datetime
48
+ ArelExtensions::Nodes::DateSub.new [self, other]
49
+ else
50
+ Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
51
+ end if self.is_a?(ArelExtensions::Nodes::Function)
32
52
  arg = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s].type
33
53
  if (arg == :date || arg == :datetime)
34
54
  case other
@@ -45,8 +65,14 @@ module ArelExtensions
45
65
  ArelExtensions::Nodes::DateSub.new [self, other]
46
66
  end
47
67
  else
48
- other = Arel.sql(other) if other.is_a?(String)# Arel should accept Float & BigDecimal!
49
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
68
+ case other
69
+ when Fixnum, Float, BigDecimal
70
+ Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other.to_s)))
71
+ when String
72
+ Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
73
+ else
74
+ Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
75
+ end
50
76
  end
51
77
  end
52
78
 
@@ -1,11 +1,28 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
3
  class Coalesce < Function
4
+ include ArelExtensions::Math
5
+ attr_accessor :left_node_type
6
+
4
7
 
5
8
  def initialize expr
6
9
  tab = expr.map { |arg|
7
10
  convert_to_node(arg)
8
11
  }
12
+ case expr.first
13
+ when String
14
+ @left_node_type = :string
15
+ when Integer, Fixnum, Float
16
+ @left_node_type = :number
17
+ when ArelExtensions::Nodes::Coalesce, ArelExtensions::Nodes::Function
18
+ @left_node_type = expr.first.try(:left_node_type)
19
+ when Arel::Nodes::Node, Arel::Attributes::Attribute
20
+ @left_node_type = type_of_attribute(expr.first)
21
+ when Date
22
+ @left_node_type = :ruby_date
23
+ when DateTime, Time
24
+ @left_node_type = :ruby_time
25
+ end
9
26
  return super(tab)
10
27
  end
11
28
 
@@ -0,0 +1,21 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class Substring < Function
4
+
5
+ def initialize expr
6
+ tab = [convert_to_node(expr[0]), convert_to_node(expr[1])]
7
+ if expr[2]
8
+ tab << convert_to_node(expr[2])
9
+ # else
10
+ # tab << expr[0].length
11
+ end
12
+ return super(tab)
13
+ end
14
+
15
+ def +(other)
16
+ return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ require 'rails'
2
+
3
+ module ArelExtensions
4
+ class Railtie < Rails::Railtie
5
+ rake_tasks do
6
+ load 'arel_extensions/tasks.rb'
7
+ end
8
+ end
9
+ end
@@ -1,6 +1,7 @@
1
1
  require 'arel_extensions/nodes/concat' if Arel::VERSION.to_i < 7
2
2
  require 'arel_extensions/nodes/length'
3
3
  require 'arel_extensions/nodes/locate'
4
+ require 'arel_extensions/nodes/substring'
4
5
  require 'arel_extensions/nodes/matches'
5
6
  require 'arel_extensions/nodes/find_in_set'
6
7
  require 'arel_extensions/nodes/replace'
@@ -28,6 +29,20 @@ module ArelExtensions
28
29
  ArelExtensions::Nodes::Locate.new [self, val]
29
30
  end
30
31
 
32
+ def substring start, len = nil
33
+ ArelExtensions::Nodes::Substring.new [self, start, len]
34
+ end
35
+ def [](start, ind = nil)
36
+ start += 1 if start.is_a?(Integer)
37
+ if start.is_a?(Range)
38
+ ArelExtensions::Nodes::Substring.new [self, start.begin + 1, start.end - start.begin + 1]
39
+ elsif start.is_a?(Integer) && !ind
40
+ ArelExtensions::Nodes::Substring.new [self, start, 1]
41
+ else
42
+ ArelExtensions::Nodes::Substring.new [self, start, ind - start + 1]
43
+ end
44
+ end
45
+
31
46
  #SOUNDEX function returns a character string containing the phonetic representation of char.
32
47
  def soundex
33
48
  ArelExtensions::Nodes::Soundex.new [self]
@@ -0,0 +1,18 @@
1
+ namespace :arelextensions do
2
+ desc 'Install DB functions'
3
+ task :install_functions => :environment do
4
+ if ENV['DB'] == 'oracle' && ((defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx") || (RUBY_PLATFORM == 'java')) # not supported
5
+ @env_db = (RUBY_PLATFORM == 'java' ? "jdbc-sqlite" : 'sqlite')
6
+ else
7
+ @env_db = ENV['DB']
8
+ end
9
+ ActiveRecord::Base.establish_connection(@env_db.try(:to_sym) || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
10
+ @cnx = ActiveRecord::Base.connection
11
+ if File.exist?("init/#{@env_db}.sql")
12
+ sql = File.read("init/#{@env_db}.sql")
13
+ unless sql.blank?
14
+ @cnx.execute(sql) rescue $stderr << "can't create functions\n"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module ArelExtensions
3
- VERSION = "0.9.5".freeze
3
+ VERSION = "0.9.6".freeze
4
4
  end
@@ -23,11 +23,11 @@ module ArelExtensions
23
23
  def visit_ArelExtensions_Nodes_DateDiff o, collector
24
24
  collector << "DAY("
25
25
  collector = visit o.left, collector
26
- collector<< ","
27
- if(o.right.is_a?(Arel::Attributes::Attribute))
26
+ collector << ","
27
+ if o.right.is_a?(Arel::Attributes::Attribute)
28
28
  collector = visit o.right, collector
29
29
  else
30
- collector<< "'#{o.right}'"
30
+ collector << "'#{o.right}'"
31
31
  end
32
32
  collector << ")"
33
33
  collector
@@ -36,17 +36,17 @@ module ArelExtensions
36
36
 
37
37
  def visit_ArelExtensions_Nodes_Duration o , collector
38
38
  #visit left for period
39
- if(o.left == "d")
39
+ if o.left == "d"
40
40
  collector << "DAY("
41
- elsif(o.left == "m")
41
+ elsif o.left == "m"
42
42
  collector << "MONTH("
43
- elsif (o.left == "w")
43
+ elsif o.left == "w"
44
44
  collector << "WEEK"
45
- elsif (o.left == "y")
45
+ elsif o.left == "y"
46
46
  collector << "YEAR("
47
47
  end
48
48
  #visit right
49
- if(o.right.is_a?(Arel::Attributes::Attribute))
49
+ if o.right.is_a?(Arel::Attributes::Attribute)
50
50
  collector = visit o.right, collector
51
51
  else
52
52
  collector << "'#{o.right}'"
@@ -56,7 +56,6 @@ module ArelExtensions
56
56
  end
57
57
 
58
58
 
59
-
60
59
  def visit_ArelExtensions_Nodes_IsNull o, collector
61
60
  collector << "COALESCE("
62
61
  collector = visit o.left, collector
@@ -66,10 +65,10 @@ module ArelExtensions
66
65
  else
67
66
  collector << "'#{o.right}'"
68
67
  end
69
- collector <<")"
68
+ collector << ")"
70
69
  collector
71
70
  end
72
71
 
73
72
  end
74
73
  end
75
- end
74
+ end
@@ -137,6 +137,21 @@ module ArelExtensions
137
137
  collector
138
138
  end
139
139
 
140
+ def visit_ArelExtensions_Nodes_Substring o, collector
141
+ collector << "SUBSTRING("
142
+ collector = visit o.expressions[0], collector
143
+ collector << Arel::Visitors::MSSQL::COMMA
144
+ collector = visit o.expressions[1], collector
145
+ collector << Arel::Visitors::MSSQL::COMMA
146
+ if !o.expressions[2]
147
+ collector = visit o.expressions[0].length, collector
148
+ else
149
+ collector = visit o.expressions[2], collector
150
+ end
151
+ collector << ")"
152
+ collector
153
+ end
154
+
140
155
  def visit_ArelExtensions_Nodes_Trim o, collector
141
156
  if o.right
142
157
  collector << "REPLACE(REPLACE(LTRIM(RTRIM(REPLACE(REPLACE("
@@ -61,7 +61,17 @@ module ArelExtensions
61
61
  collector << "COALESCE("
62
62
  o.expressions.each_with_index { |arg, i|
63
63
  collector << Arel::Visitors::Oracle::COMMA unless i == 0
64
- collector = visit arg, collector
64
+ if i > 0 && o.left_node_type == :text
65
+ if arg == ''
66
+ collector << 'empty_clob()'
67
+ else
68
+ collector << 'TO_CLOB('
69
+ collector = visit arg, collector
70
+ collector << ')'
71
+ end
72
+ else
73
+ collector = visit arg, collector
74
+ end
65
75
  }
66
76
  collector << ")"
67
77
  collector
@@ -164,6 +174,16 @@ module ArelExtensions
164
174
  collector
165
175
  end
166
176
 
177
+ def visit_ArelExtensions_Nodes_Substring o, collector
178
+ collector << "SUBSTR("
179
+ o.expressions.each_with_index { |arg, i|
180
+ collector << Arel::Visitors::Oracle::COMMA unless i == 0
181
+ collector = visit arg, collector
182
+ }
183
+ collector << ")"
184
+ collector
185
+ end
186
+
167
187
  def visit_ArelExtensions_Nodes_Trim o, collector
168
188
  collector << 'TRIM(' # BOTH
169
189
  if o.right.expr && SPECIAL_CHARS[o.right.expr]
@@ -129,6 +129,16 @@ module ArelExtensions
129
129
  collector
130
130
  end
131
131
 
132
+ def visit_ArelExtensions_Nodes_Substring o, collector
133
+ collector << "SUBSTR("
134
+ o.expressions.each_with_index { |arg, i|
135
+ collector << Arel::Visitors::PostgreSQL::COMMA unless i == 0
136
+ collector = visit arg, collector
137
+ }
138
+ collector << ")"
139
+ collector
140
+ end
141
+
132
142
  def visit_ArelExtensions_Nodes_IsNull o, collector
133
143
  collector = visit o.left, collector
134
144
  collector << ' IS NULL'
@@ -77,6 +77,16 @@ module ArelExtensions
77
77
  collector
78
78
  end
79
79
 
80
+ def visit_ArelExtensions_Nodes_Substring o, collector
81
+ collector << "SUBSTR("
82
+ o.expressions.each_with_index { |arg, i|
83
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
84
+ collector = visit arg, collector
85
+ }
86
+ collector << ")"
87
+ collector
88
+ end
89
+
80
90
  def visit_ArelExtensions_Nodes_IsNull o, collector
81
91
  collector = visit o.left, collector
82
92
  collector << ' IS NULL'
@@ -109,12 +119,40 @@ module ArelExtensions
109
119
  end
110
120
 
111
121
  def visit_ArelExtensions_Nodes_Wday o, collector
112
- collector << "strftime('%w',"
122
+ collector << "STRFTIME('%w',"
113
123
  collector = visit o.date, collector
114
124
  collector << ")"
115
125
  collector
116
126
  end
117
127
 
128
+ # CASE WHEN ROUND(3.42,1) > round(3.42) THEN round(3.42) ELSE round(3.42)-1 END
129
+ # OR CAST(3.14 AS INTEGER)
130
+ def visit_ArelExtensions_Nodes_Floor o, collector
131
+ collector << "CASE WHEN ROUND("
132
+ collector = visit o.left, collector
133
+ collector << ", 1) > ROUND("
134
+ collector = visit o.left, collector
135
+ collector << ") THEN ROUND("
136
+ collector = visit o.left, collector
137
+ collector << ") ELSE ROUND("
138
+ collector = visit o.left, collector
139
+ collector << ") - 1 END"
140
+ collector
141
+ end
142
+
143
+ def visit_ArelExtensions_Nodes_Ceil o, collector
144
+ collector << "CASE WHEN ROUND("
145
+ collector = visit o.left, collector
146
+ collector << ", 1) > ROUND("
147
+ collector = visit o.left, collector
148
+ collector << ") THEN ROUND("
149
+ collector = visit o.left, collector
150
+ collector << ") + 1 ELSE ROUND("
151
+ collector = visit o.left, collector
152
+ collector << ") END"
153
+ collector
154
+ end
155
+
118
156
  def visit_ArelExtensions_InsertManager_BulkValues o, collector
119
157
  o.left.each_with_index do |row, idx|
120
158
  collector << 'SELECT '
@@ -91,6 +91,16 @@ module ArelExtensions
91
91
  collector
92
92
  end
93
93
 
94
+ def visit_ArelExtensions_Nodes_Substring o, collector
95
+ collector << "SUBSTRING("
96
+ o.expressions.each_with_index { |arg, i|
97
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
98
+ collector = visit arg, collector
99
+ }
100
+ collector << ")"
101
+ collector
102
+ end
103
+
94
104
  def visit_ArelExtensions_Nodes_Replace o, collector
95
105
  collector << "REPLACE("
96
106
  o.expressions.each_with_index { |arg, i|
@@ -77,7 +77,7 @@ module ArelExtensions
77
77
  @lucas = User.where(:id => u.id)
78
78
  u = User.create :age => 15, :name => "Sophie", :created_at => d, :score => 20.16
79
79
  @sophie = User.where(:id => u.id)
80
- u = User.create :age => 20, :name => "Camille", :created_at => d, :score => 20.16
80
+ u = User.create :age => 20, :name => "Camille", :created_at => d, :score => -20.16
81
81
  @camille = User.where(:id => u.id)
82
82
  u = User.create :age => 21, :name => "Arthur", :created_at => d, :score => 65.62
83
83
  @arthur = User.where(:id => u.id)
@@ -115,19 +115,24 @@ module ArelExtensions
115
115
 
116
116
  def test_abs
117
117
  assert_equal 42, t(@neg, @age.abs)
118
+ assert_equal 20.16, t(@camille, @score.abs)
118
119
  assert_equal 14, t(@laure, (@age - 39).abs)
119
120
  assert_equal 28, t(@laure, (@age - 39).abs + (@age - 39).abs)
120
121
  end
121
122
 
122
123
  def test_ceil
123
- skip "Sqlite version can't load extension for ceil" if $sqlite && $load_extension_disabled
124
- assert_equal 1, t(@neg, @score.ceil)
124
+ # skip "Sqlite version can't load extension for ceil" if $sqlite && $load_extension_disabled
125
+ assert_equal 2, t(@test, @score.ceil) # 1.62
126
+ assert_equal (-20), t(@camille, @score.ceil) # -20.16
127
+ assert_equal (-20), t(@camille, (@score - 0.5).ceil) # -20.16
125
128
  assert_equal 63, t(@arthur, @age.ceil + 42)
126
129
  end
127
130
 
128
131
  def test_floor
129
- skip "Sqlite version can't load extension for floor" if $sqlite && $load_extension_disabled
132
+ # skip "Sqlite version can't load extension for floor" if $sqlite && $load_extension_disabled
130
133
  assert_equal 0, t(@neg, @score.floor)
134
+ assert_equal 1, t(@test, @score.floor) # 1.62
135
+ assert_equal (-9), t(@test, (@score - 10).floor) # 1.62
131
136
  assert_equal 42, t(@arthur, @score.floor - 23)
132
137
  end
133
138
 
@@ -185,6 +190,18 @@ module ArelExtensions
185
190
  assert_equal 5, t(@lucas, @name.locate("s"))
186
191
  end
187
192
 
193
+ def test_substring
194
+ assert_equal 'C', t(@camille, @name.substring(1, 1))
195
+ assert_equal (@env_db == 'oracle' ? nil : ''), t(@lucas, @name.substring(42))
196
+ assert_equal 'Lu', t(@lucas, @name.substring(1,2))
197
+
198
+ assert_equal 'C', t(@camille, @name[0, 1])
199
+ assert_equal 'C', t(@camille, @name[0])
200
+ assert_equal (@env_db == 'oracle' ? nil : ''), t(@lucas, @name[42])
201
+ assert_equal 'Lu', t(@lucas, @name[0,2])
202
+ assert_equal 'Lu', t(@lucas, @name[0..1])
203
+ end
204
+
188
205
  def test_find_in_set
189
206
  skip "Sqlite version can't load extension for find_in_set" if $sqlite && $load_extension_disabled
190
207
  skip "SQL Server does not know about FIND_IN_SET" if @env_db == 'mssql'
@@ -269,6 +286,12 @@ module ArelExtensions
269
286
  end
270
287
 
271
288
  def test_coalesce
289
+ assert_equal 'Camille concat', t(@camille, @name.coalesce(nil, "default") + ' concat')
290
+
291
+ assert_equal ' ', t(@myung, @comments.coalesce("Myung").coalesce('ignored'))
292
+ assert_equal 'Laure', t(@laure, @comments.coalesce("Laure"))
293
+ assert_equal '', t(@laure, @comments.coalesce(""))
294
+
272
295
  if @env_db == 'postgresql'
273
296
  assert_equal 100, t(@test, @age.coalesce(100))
274
297
  assert_equal "Camille", t(@camille, @name.coalesce(nil, "default"))