arel_extensions 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6b227ca895990c123d78d688d01f00a27a563b70
4
- data.tar.gz: 21bc450f1776d263294b6d56523adb59843825bb
3
+ metadata.gz: 8aa9a662954d3fc3157f1275991e4bf04e13ea63
4
+ data.tar.gz: ff96c79a38ce3a7f9f675ac3af7596caf689f7f0
5
5
  SHA512:
6
- metadata.gz: 42e41791d3c25cb5c3a5900b992ee7e22e813bdb278406b3dc3eeaa02ec2477d76696c4abb50a93956f1b9bf55be5ef8234e96720bbcfc47de53afb8b5fb3ab3
7
- data.tar.gz: 07fd67e1b8fe666bdff49c2baeae98d828cacfc0ae6a6b049e98850df6db4c0b24c370350feb973fb0b9f04e633955fb4375f33e020910a0b896f9629bfde300
6
+ metadata.gz: 741e88fb943d9482ff4e7966747f56bf94d0aed909ed6f838e084eef53f89b74a55a90ad22daeaed1a53ee7d05192b52a7c03ca3c9988d32e2e511052821411e
7
+ data.tar.gz: 75dcbf19327872a9d6b7eab78c334f9875e56ba3ef5f6c2d571c10c6acc2d2eb91254cd757ef97590ea4770689db59e9a9775869f6d7345e8a4eb00572c388d0
data/.travis.yml CHANGED
@@ -54,6 +54,7 @@ rvm:
54
54
  - 2.2.5
55
55
  - 2.3.1
56
56
  - 2.4.0
57
+ - 2.5.0
57
58
  - rbx-2
58
59
  - jruby-9.0.5.0
59
60
  - jruby-head
@@ -77,6 +78,8 @@ matrix:
77
78
  jdk: openjdk7
78
79
  - rvm: 2.4.0
79
80
  jdk: openjdk7
81
+ - rvm: 2.5.0
82
+ jdk: openjdk7
80
83
  - rvm: ruby-head
81
84
  jdk: openjdk7
82
85
  - rvm: rbx-2
@@ -91,6 +94,8 @@ matrix:
91
94
  jdk: oraclejdk8
92
95
  - rvm: 2.4.0
93
96
  jdk: oraclejdk8
97
+ - rvm: 2.5.0
98
+ jdk: oraclejdk8
94
99
  - rvm: ruby-head
95
100
  jdk: oraclejdk8
96
101
  - rvm: jruby-head
data/README.md CHANGED
@@ -138,10 +138,19 @@ t[:birthdate].format('%Y-%m-%d').to_sql
138
138
  Arel-extensions allows to use functions on case clause
139
139
 
140
140
  ```ruby
141
- (t[:name].when("smith").then(1).when("doe").then(2).else(0).sum.to_sql
141
+ t[:name].when("smith").then(1).when("doe").then(2).else(0).sum.to_sql
142
142
  # => SUM(CASE "my_table"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END)
143
143
  ```
144
144
 
145
+ ## Cast Function
146
+
147
+ Arel-extensions allows to cast type on constants and attributes
148
+
149
+ ```ruby
150
+ t[:id].cast('char').to_sql
151
+ # => CAST("my_table"."id" AS char)
152
+ ```
153
+
145
154
 
146
155
  ## Stored Procedures and User-defined functions
147
156
 
@@ -1,12 +1,12 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'rails', '~> 5.1.4'
3
+ gem 'rails', '5.1.4'
4
4
  gem 'arel', '~> 8'
5
5
 
6
6
  group :development, :test do
7
- gem 'activesupport', '~> 5.1.4'
8
- gem 'activemodel', '~> 5.1.4'
9
- gem 'activerecord', '~> 5.1.4'
7
+ gem 'activesupport', '5.1.4'
8
+ gem 'activemodel', '5.1.4'
9
+ gem 'activerecord', '5.1.4'
10
10
 
11
11
  gem "sqlite3", :platforms => [:mri, :mswin, :mingw]
12
12
  gem "mysql2", :platforms => [:mri, :mswin, :mingw]
@@ -57,6 +57,8 @@ require 'arel_extensions/nodes/union'
57
57
  require 'arel_extensions/nodes/union_all'
58
58
  require 'arel_extensions/nodes/as'
59
59
  require 'arel_extensions/nodes/case'
60
+ require 'arel_extensions/nodes/soundex'
61
+ require 'arel_extensions/nodes/cast'
60
62
  require 'arel_extensions/predications'
61
63
 
62
64
 
@@ -116,18 +118,15 @@ Arel::SelectManager.class_eval do
116
118
  include ArelExtensions::Nodes
117
119
  end
118
120
 
119
- Arel::Nodes::Union.class_eval do
120
- include ArelExtensions::SetFunctions
121
+ Arel::Nodes::As.class_eval do
121
122
  include ArelExtensions::Nodes
122
123
  end
123
124
 
124
- Arel::Nodes::UnionAll.class_eval do
125
- include ArelExtensions::SetFunctions
126
- include ArelExtensions::Nodes
127
- end
128
125
 
129
- Arel::Nodes::As.class_eval do
130
- include ArelExtensions::Nodes
126
+ if Arel::VERSION.to_i >= 7
127
+ Arel::Nodes::Case.class_eval do
128
+ include ArelExtensions::Predications
129
+ end
131
130
  end
132
131
 
133
132
 
@@ -1,5 +1,6 @@
1
1
  require 'arel_extensions/nodes'
2
2
  require 'arel_extensions/nodes/concat'
3
+ require 'arel_extensions/nodes/cast'
3
4
 
4
5
  require 'arel_extensions/nodes/date_diff'
5
6
  require 'arel_extensions/nodes/duration'
@@ -14,6 +15,7 @@ module ArelExtensions
14
15
  #Date and integer adds or subtracts a specified time interval from a date.
15
16
  def +(other)
16
17
  return ArelExtensions::Nodes::Concat.new [self, other] if self.is_a?(Arel::Nodes::Quoted)
18
+
17
19
  if self.is_a?(Arel::Nodes::Grouping)
18
20
  if self.expr.left.is_a?(String) || self.expr.right.is_a?(String)
19
21
  return ArelExtensions::Nodes::Concat.new [self, other]
@@ -21,6 +23,7 @@ module ArelExtensions
21
23
  return Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
22
24
  end
23
25
  end
26
+
24
27
  return case self.class.return_type
25
28
  when :string, :text
26
29
  ArelExtensions::Nodes::Concat.new [self, other]
@@ -3,6 +3,9 @@ require 'arel_extensions/nodes/ceil'
3
3
  require 'arel_extensions/nodes/floor'
4
4
  require 'arel_extensions/nodes/round'
5
5
  require 'arel_extensions/nodes/rand'
6
+ require 'arel_extensions/nodes/formatted_number'
7
+ require 'arel_extensions/nodes/log10'
8
+ require 'arel_extensions/nodes/power'
6
9
 
7
10
  module ArelExtensions
8
11
  module MathFunctions
@@ -21,6 +24,22 @@ module ArelExtensions
21
24
  def floor
22
25
  ArelExtensions::Nodes::Floor.new [self]
23
26
  end
27
+
28
+ # function gives the base 10 log
29
+ def log10
30
+ ArelExtensions::Nodes::Log10.new [self]
31
+ end
32
+
33
+ # function gives the power of a number
34
+ def pow exposant = 0
35
+ ArelExtensions::Nodes::Power.new [self,exposant]
36
+ end
37
+
38
+ # function gives the power of a number
39
+ def power exposant = 0
40
+ ArelExtensions::Nodes::Power.new [self,exposant]
41
+ end
42
+
24
43
 
25
44
  #function that can be invoked to produce random numbers between 0 and 1
26
45
  # def rand seed = nil
@@ -36,6 +55,27 @@ module ArelExtensions
36
55
  ArelExtensions::Nodes::Round.new [self]
37
56
  end
38
57
  end
58
+
59
+ # function returning a number at a specific format
60
+ def format_number format_string, locale=nil
61
+ begin
62
+ sprintf(format_string,0) # this line is to get the right error message if the format_string is not correct
63
+ m = /^(.*)%([ #+\-0]*)([1-9][0-9]+|[1-9]?)[.]?([0-9]*)([a-zA-Z])(.*)$/.match(format_string)
64
+ opts = {
65
+ :prefix => m[1],
66
+ :flags => m[2].split(//).uniq.join,
67
+ :width => m[3].to_i,
68
+ :precision => m[4] != '' ? m[4].to_i : 6,
69
+ :type => m[5],
70
+ :suffix => m[6],
71
+ :locale => locale
72
+ }
73
+ # opts = {:locale => 'fr_FR', :type => "e"/"f"/"d", :prefix => "$ ", :suffix => " %", :flags => " +-#0", :width => 5, :precision => 6}
74
+ ArelExtensions::Nodes::FormattedNumber.new [self,opts]
75
+ rescue
76
+ Arel::Nodes.build_quoted('Wrong Format')
77
+ end
78
+ end
39
79
 
40
80
  end
41
81
  end
@@ -2,8 +2,9 @@ module ArelExtensions
2
2
  module Nodes
3
3
  if Arel::VERSION.to_i < 7
4
4
  class Case < Arel::Nodes::Node
5
- include ArelExtensions::Predications
6
5
  include Arel::Expressions
6
+ include ArelExtensions::Comparators
7
+ include ArelExtensions::Predications
7
8
 
8
9
  attr_accessor :case, :conditions, :default
9
10
 
@@ -56,11 +57,15 @@ module ArelExtensions
56
57
  end
57
58
 
58
59
  class Else < Arel::Nodes::Unary # :nodoc:
59
- end
60
+ end
60
61
 
61
62
  else
62
63
 
63
- class Case < Arel::Nodes::Case
64
+ class Case < Arel::Nodes::Case
65
+ include Arel::Expressions
66
+ include ArelExtensions::Comparators
67
+ include ArelExtensions::Predications
68
+
64
69
  end
65
70
 
66
71
  end
@@ -0,0 +1,52 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class Cast < Function
4
+ @@return_type= :string
5
+
6
+ def initialize expr
7
+ as_attr = expr[1]
8
+ case expr[1]
9
+ when 'bigint', 'int', 'smallint', 'tinyint', 'bit', 'decimal', 'numeric', 'money', 'smallmoney', 'float', 'real'
10
+ @@return_type= :number
11
+ when 'datetime', 'smalldatetime'
12
+ @@return_type= :ruby_time
13
+ when 'char', 'varchar', 'text', 'nchar', 'nvarchar', 'ntext'
14
+ @@return_type= :string
15
+ when 'binary', 'varbinary', 'image'
16
+ @@return_type= :binary
17
+ when :number
18
+ @@return_type= :number
19
+ as_attr = 'int'
20
+ when :datetime
21
+ @@return_type= :ruby_time
22
+ as_attr = 'datetime'
23
+ when :string
24
+ @@return_type= :string
25
+ as_attr = 'char'
26
+ when :binary
27
+ @@return_type= :binary
28
+ as_attr = 'binary'
29
+ else
30
+ @@return_type= :string
31
+ as_attr = 'char'
32
+ end
33
+ tab = [convert_to_node(expr.first), Arel::Nodes::SqlLiteral.new(as_attr)]
34
+ return super(tab)
35
+ end
36
+
37
+ def +(other)
38
+ case @@return_type
39
+ when :string
40
+ return ArelExtensions::Nodes::Concat.new [self, other]
41
+ when :ruby_time
42
+ ArelExtensions::Nodes::DateAdd.new [self, other]
43
+ else
44
+ Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
45
+ end
46
+ end
47
+
48
+
49
+
50
+ end
51
+ end
52
+ end
@@ -2,9 +2,10 @@ module ArelExtensions
2
2
  module Nodes
3
3
  class Coalesce < Function
4
4
  include ArelExtensions::Math
5
+ include ArelExtensions::Comparators
6
+
5
7
  attr_accessor :left_node_type
6
8
 
7
-
8
9
  def initialize expr
9
10
  tab = expr.map { |arg|
10
11
  convert_to_node(arg)
@@ -15,7 +16,7 @@ module ArelExtensions
15
16
  when Integer, Float
16
17
  @left_node_type = :number
17
18
  when ArelExtensions::Nodes::Coalesce, ArelExtensions::Nodes::Function
18
- @left_node_type = expr.first.try(:left_node_type)
19
+ @left_node_type = expr.first.respond_to?(:left_node_type) ? expr.first.left_node_type : nil
19
20
  when Arel::Nodes::Node, Arel::Attributes::Attribute
20
21
  @left_node_type = type_of_attribute(expr.first)
21
22
  when Date
@@ -25,7 +26,7 @@ module ArelExtensions
25
26
  end
26
27
  return super(tab)
27
28
  end
28
-
29
+
29
30
  end
30
31
  end
31
32
  end
@@ -13,6 +13,10 @@ module ArelExtensions
13
13
  def +(other)
14
14
  return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
15
15
  end
16
+
17
+ def concat(other)
18
+ return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
19
+ end
16
20
 
17
21
  end
18
22
 
@@ -6,7 +6,7 @@ module ArelExtensions
6
6
  attr_accessor :col_type, :iso_format
7
7
  def initialize expr
8
8
  col = expr.first
9
- @iso_format = expr[1]
9
+ @iso_format = expr[1]
10
10
  @col_type = type_of_attribute(col)
11
11
  super [col, convert_to_string_node(@iso_format)]
12
12
  end
@@ -0,0 +1,56 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class FormattedNumber < Function
4
+ @@return_type = :string
5
+
6
+ attr_accessor :locale, :prefix, :suffix, :flags, :scientific_notation, :width,:precision, :type
7
+
8
+ def initialize expr
9
+ # expr[1] = {:locale => 'fr_FR', :type => "e"/"f"/"d", :prefix => "$ ", :suffix => " %", :flags => " +-#0", :width => 5, :precision => 6}
10
+ col = expr.first
11
+ @locale = expr[1][:locale]
12
+ @prefix = expr[1][:prefix]
13
+ @suffix = expr[1][:suffix]
14
+ @width = expr[1][:width]
15
+ @precision = expr[1][:precision]
16
+ @type = expr[1][:type]
17
+ @flags = expr[1][:flags]
18
+ @scientific_notation = /[eE]/.match(expr[1][:type]) || false
19
+ super [col]
20
+ end
21
+
22
+ def locale
23
+ @locale
24
+ end
25
+
26
+ def prefix
27
+ @prefix
28
+ end
29
+
30
+ def suffix
31
+ @suffix
32
+ end
33
+
34
+ def width
35
+ @width
36
+ end
37
+
38
+ def precision
39
+ @precision
40
+ end
41
+
42
+ def type
43
+ @type
44
+ end
45
+
46
+ def flags
47
+ @flags
48
+ end
49
+
50
+ def scientific_notation
51
+ @scientific_notation
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -1,8 +1,11 @@
1
+ require 'arel_extensions/predications'
2
+
1
3
  module ArelExtensions
2
4
  module Nodes
3
5
  class Function < Arel::Nodes::Function
4
6
  include Arel::Math
5
7
  include Arel::Expressions
8
+ include ArelExtensions::Predications
6
9
 
7
10
  cattr_accessor :return_type
8
11
 
@@ -25,10 +28,14 @@ module ArelExtensions
25
28
  @expressions[1]
26
29
  end
27
30
 
28
- def type_of_attribute(att)
31
+ def type_of_attribute(att)
29
32
  case att
30
33
  when Arel::Attributes::Attribute
31
- Arel::Table.engine.connection.schema_cache.columns_hash(att.relation.table_name)[att.name.to_s].type
34
+ begin
35
+ Arel::Table.engine.connection.schema_cache.columns_hash(att.relation.table_name)[att.name.to_s].type
36
+ rescue
37
+ att
38
+ end
32
39
  when ArelExtensions::Nodes::Function
33
40
  att.class.return_type
34
41
  # else
@@ -0,0 +1,8 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class Log10 < Function
4
+ @@return_type = :number
5
+
6
+ end
7
+ end
8
+ end
@@ -12,7 +12,6 @@ module ArelExtensions
12
12
  super(left, r, escape, false)
13
13
  end
14
14
  end
15
-
16
15
  end
17
16
 
18
17
  class IDoesNotMatch < IMatches
@@ -0,0 +1,11 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class Power < Function
4
+ @@return_type = :number
5
+
6
+ def initialize expr
7
+ super [convert_to_node(expr.first), convert_to_number(expr[1])]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class Repeat < Function
4
+ @@return_type = :string
5
+
6
+ def initialize expr
7
+ tab = expr.map { |arg|
8
+ convert_to_node(arg)
9
+ }
10
+ return super(tab)
11
+ end
12
+
13
+ def +(other)
14
+ return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
@@ -1,7 +1,18 @@
1
1
  module ArelExtensions
2
- module Nodes
3
- class Soundex < Function
4
- @@return_type = :string
5
- end
6
- end
2
+ module Nodes
3
+ class Soundex < Function
4
+ include Arel::Expressions
5
+ include ArelExtensions::Comparators
6
+
7
+ @@return_type = :string
8
+
9
+ def ==(other)
10
+ Arel::Nodes::Equality.new self, Arel::Nodes.build_quoted(other, self)
11
+ end
12
+
13
+ def !=(other)
14
+ Arel::Nodes::NotEqual.new self, Arel::Nodes.build_quoted(other, self)
15
+ end
16
+ end
17
+ end
7
18
  end
@@ -5,5 +5,23 @@ module ArelExtensions
5
5
  ArelExtensions::Nodes::Case.new(self).when(right)
6
6
  end
7
7
  end
8
+
9
+ def matches(other, escape=nil)
10
+ Arel::Nodes::Matches.new(self, Arel::Nodes.build_quoted(other), escape)
11
+ end
12
+
13
+ def imatches(other, escape=nil)
14
+ ArelExtensions::Nodes::IMatches.new(self, other, escape)
15
+ end
16
+
17
+ def cast_as_char
18
+ ArelExtensions::Nodes::Cast.new([self,'char'])
19
+ end
20
+
21
+ def cast right
22
+ ArelExtensions::Nodes::Cast.new([self,right])
23
+ end
24
+
25
+
8
26
  end
9
27
  end
@@ -16,6 +16,9 @@ module ArelExtensions
16
16
  ArelExtensions::Nodes::UnionAll.new(self,other)
17
17
  end
18
18
 
19
+ def uniq
20
+ self
21
+ end
19
22
 
20
23
  end
21
24
  end
@@ -10,6 +10,9 @@ require 'arel_extensions/nodes/trim'
10
10
  require 'arel_extensions/nodes/change_case'
11
11
  require 'arel_extensions/nodes/blank'
12
12
  require 'arel_extensions/nodes/format'
13
+ require 'arel_extensions/nodes/repeat'
14
+ require 'arel_extensions/nodes/cast'
15
+
13
16
 
14
17
  module ArelExtensions
15
18
  module StringFunctions
@@ -110,6 +113,10 @@ module ArelExtensions
110
113
  def not_blank
111
114
  ArelExtensions::Nodes::NotBlank.new [self]
112
115
  end
116
+
117
+ def repeat other = 1
118
+ ArelExtensions::Nodes::Repeat.new [self, other]
119
+ end
113
120
 
114
121
  end
115
122
  end
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "1.0.2".freeze
2
+ VERSION = "1.0.3".freeze
3
3
  end
@@ -28,6 +28,26 @@ module ArelExtensions
28
28
  collector << ")"
29
29
  collector
30
30
  end
31
+
32
+ def visit_ArelExtensions_Nodes_Log10 o, collector
33
+ collector << "LOG10("
34
+ o.expressions.each_with_index { |arg, i|
35
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
36
+ collector = visit arg, collector
37
+ }
38
+ collector << ")"
39
+ collector
40
+ end
41
+
42
+ def visit_ArelExtensions_Nodes_Power o, collector
43
+ collector << "POWER("
44
+ o.expressions.each_with_index { |arg, i|
45
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
46
+ collector = visit arg, collector
47
+ }
48
+ collector << ")"
49
+ collector
50
+ end
31
51
 
32
52
  def visit_ArelExtensions_Nodes_IsNull o, collector
33
53
  collector << "("
@@ -69,6 +89,17 @@ module ArelExtensions
69
89
  collector << ")"
70
90
  collector
71
91
  end
92
+
93
+ def visit_ArelExtensions_Nodes_Repeat o, collector
94
+ collector << "REPLICATE("
95
+ o.expressions.each_with_index { |arg, i|
96
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
97
+ collector = visit arg, collector
98
+ }
99
+ collector << ")"
100
+ collector
101
+ end
102
+
72
103
 
73
104
  def visit_ArelExtensions_Nodes_DateDiff o, collector
74
105
  collector << if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
@@ -9,6 +9,28 @@ module ArelExtensions
9
9
  '%M' => '%i', '%S' => '%S', '%L' => '', '%N' => '%f', '%z' => ''
10
10
  }
11
11
 
12
+
13
+ #Math functions
14
+ def visit_ArelExtensions_Nodes_Log10 o, collector
15
+ collector << "LOG10("
16
+ o.expressions.each_with_index { |arg, i|
17
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
18
+ collector = visit arg, collector
19
+ }
20
+ collector << ")"
21
+ collector
22
+ end
23
+
24
+ def visit_ArelExtensions_Nodes_Power o, collector
25
+ collector << "POW("
26
+ o.expressions.each_with_index { |arg, i|
27
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
28
+ collector = visit arg, collector
29
+ }
30
+ collector << ")"
31
+ collector
32
+ end
33
+
12
34
  #String functions
13
35
  def visit_ArelExtensions_Nodes_IMatches o, collector # insensitive on ASCII
14
36
  collector = visit o.left, collector
@@ -38,7 +60,13 @@ module ArelExtensions
38
60
  collector << "CONCAT("
39
61
  o.expressions.each_with_index { |arg, i|
40
62
  collector << Arel::Visitors::MySQL::COMMA unless i == 0
41
- collector = visit arg, collector
63
+ if (arg.is_a?(Numeric)) || (arg.is_a?(Arel::Attributes::Attribute))
64
+ collector << "CAST("
65
+ collector = visit arg, collector
66
+ collector << " AS char)"
67
+ else
68
+ collector = visit arg, collector
69
+ end
42
70
  }
43
71
  collector << ")"
44
72
  collector
@@ -81,6 +109,16 @@ module ArelExtensions
81
109
  collector << ")"
82
110
  collector
83
111
  end
112
+
113
+ def visit_ArelExtensions_Nodes_Repeat o, collector
114
+ collector << "REPEAT("
115
+ o.expressions.each_with_index { |arg, i|
116
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
117
+ collector = visit arg, collector
118
+ }
119
+ collector << ")"
120
+ collector
121
+ end
84
122
 
85
123
  def visit_ArelExtensions_Nodes_Format o, collector
86
124
  case o.col_type
@@ -95,6 +133,8 @@ module ArelExtensions
95
133
  when :integer, :float, :decimal
96
134
  collector << "FORMAT("
97
135
  collector = visit o.left, collector
136
+ collector << Arel::Visitors::ToSql::COMMA
137
+ collector << '2'
98
138
  collector << Arel::Visitors::ToSql::COMMA
99
139
  collector = visit o.right, collector
100
140
  collector << ")"
@@ -9,6 +9,28 @@ module ArelExtensions
9
9
  '%H' => 'HH24', '%k' => '', '%I' => 'HH', '%l' => '', '%P' => 'am', '%p' => 'AM', # hours
10
10
  '%M' => 'MI', '%S' => 'SS', '%L' => 'MS', '%N' => 'US', '%z' => 'tz' # seconds, subseconds
11
11
  }
12
+
13
+ def visit_ArelExtensions_Nodes_Log10 o, collector
14
+ collector << "LOG("
15
+ o.expressions.each_with_index { |arg, i|
16
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
17
+ collector = visit arg, collector
18
+ }
19
+ collector << ",10)"
20
+ collector
21
+ end
22
+
23
+ def visit_ArelExtensions_Nodes_Power o, collector
24
+ collector << "POWER("
25
+ o.expressions.each_with_index { |arg, i|
26
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
27
+ collector = visit arg, collector
28
+ }
29
+ collector << ")"
30
+ collector
31
+ end
32
+
33
+
12
34
 
13
35
  def visit_ArelExtensions_Nodes_Concat o, collector
14
36
  collector << '('
@@ -265,6 +287,15 @@ module ArelExtensions
265
287
  collector << ")"
266
288
  collector
267
289
  end
290
+
291
+ def visit_ArelExtensions_Nodes_Repeat o, collector
292
+ collector << "LPAD('',"
293
+ collector = visit o.expressions[1], collector
294
+ collector << Arel::Visitors::ToSql::COMMA
295
+ collector = visit o.expressions[0], collector
296
+ collector << ")"
297
+ collector
298
+ end
268
299
 
269
300
  # add primary_key if not present, avoid zip
270
301
  if Arel::VERSION.to_i < 7
@@ -19,6 +19,27 @@ module ArelExtensions
19
19
  collector << ")"
20
20
  collector
21
21
  end
22
+
23
+ def visit_ArelExtensions_Nodes_Power o, collector
24
+ collector << "POWER("
25
+ o.expressions.each_with_index { |arg, i|
26
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
27
+ collector = visit arg, collector
28
+ }
29
+ collector << ")"
30
+ collector
31
+ end
32
+
33
+ def visit_ArelExtensions_Nodes_Log10 o, collector
34
+ collector << "LOG("
35
+ o.expressions.each_with_index { |arg, i|
36
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
37
+ collector = visit arg, collector
38
+ }
39
+ collector << ")"
40
+ collector
41
+ end
42
+
22
43
 
23
44
  remove_method(:visit_Arel_Nodes_Regexp) rescue nil
24
45
  def visit_Arel_Nodes_Regexp o, collector
@@ -100,6 +121,16 @@ module ArelExtensions
100
121
  collector << ")"
101
122
  collector
102
123
  end
124
+
125
+ def visit_ArelExtensions_Nodes_Repeat o, collector
126
+ collector << "REPEAT("
127
+ o.expressions.each_with_index { |arg, i|
128
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
129
+ collector = visit arg, collector
130
+ }
131
+ collector << ")"
132
+ collector
133
+ end
103
134
 
104
135
  def visit_ArelExtensions_Nodes_DateAdd o, collector
105
136
  collector = visit o.left, collector
@@ -1,6 +1,7 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::ToSql.class_eval do
3
+ Arel::Visitors::ToSql.class_eval do
4
+
4
5
 
5
6
  # Math Functions
6
7
  def visit_ArelExtensions_Nodes_Abs o, collector
@@ -52,6 +53,23 @@ module ArelExtensions
52
53
  collector << ")"
53
54
  collector
54
55
  end
56
+
57
+ def visit_ArelExtensions_Nodes_Log10 o, collector
58
+ collector << "LOG10("
59
+ collector = visit o.left, collector
60
+ collector << ")"
61
+ collector
62
+ end
63
+
64
+ def visit_ArelExtensions_Nodes_Power o, collector
65
+ collector << "POW("
66
+ o.expressions.each_with_index { |arg, i|
67
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
68
+ collector = visit arg, collector
69
+ }
70
+ collector << ")"
71
+ collector
72
+ end
55
73
 
56
74
  # String functions
57
75
  def visit_ArelExtensions_Nodes_Concat o, collector
@@ -110,6 +128,16 @@ module ArelExtensions
110
128
  collector << ")"
111
129
  collector
112
130
  end
131
+
132
+ def visit_ArelExtensions_Nodes_Repeat o, collector
133
+ collector << "REPEAT("
134
+ o.expressions.each_with_index { |arg, i|
135
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
136
+ collector = visit arg, collector
137
+ }
138
+ collector << ")"
139
+ collector
140
+ end
113
141
 
114
142
  def visit_ArelExtensions_Nodes_FindInSet o, collector
115
143
  collector << "FIND_IN_SET("
@@ -223,6 +251,15 @@ module ArelExtensions
223
251
 
224
252
  #comparators
225
253
 
254
+ def visit_ArelExtensions_Nodes_Cast o, collector
255
+ collector << "CAST("
256
+ collector = visit o.left, collector
257
+ collector << " AS "
258
+ collector = visit o.right, collector
259
+ collector << ")"
260
+ collector
261
+ end
262
+
226
263
  def visit_ArelExtensions_Nodes_Coalesce o, collector
227
264
  collector << "COALESCE("
228
265
  o.expressions.each_with_index { |arg, i|
@@ -434,7 +471,56 @@ module ArelExtensions
434
471
  collector << "ELSE "
435
472
  visit o.expr, collector
436
473
  end
437
-
474
+
475
+
476
+
477
+ def visit_ArelExtensions_Nodes_FormattedNumber o, collector
478
+ col = o.left
479
+ params = o.locale ? [o.precision,Arel::Nodes.build_quoted(o.locale)] : [o.precision]
480
+ sign = ArelExtensions::Nodes::Case.new.when(col<0).
481
+ then('-').
482
+ else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
483
+ sign_length = ArelExtensions::Nodes::Length.new([sign])
484
+
485
+ if o.scientific_notation
486
+ number = ArelExtensions::Nodes::Concat.new([
487
+ Arel::Nodes::NamedFunction.new('FORMAT',[
488
+ col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)
489
+ ]+params),
490
+ o.type,
491
+ Arel::Nodes::NamedFunction.new('FORMAT',[
492
+ col.abs.log10.floor,
493
+ 0
494
+ ])
495
+ ])
496
+ else
497
+ number = Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
498
+ end
499
+
500
+ repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
501
+ when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
502
+ then(Arel::Nodes.build_quoted(
503
+ o.flags.include?('-') ? ' ' : (o.flags.include?('0') ? '0' : ' ')
504
+ ).repeat(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length))
505
+ ).
506
+ else('')
507
+ before = (!o.flags.include?('0'))&&(!o.flags.include?('-')) ? repeated_char : ''
508
+ middle = (o.flags.include?('0'))&&(!o.flags.include?('-')) ? repeated_char : ''
509
+ after = o.flags.include?('-') ? repeated_char : ''
510
+ full_number = col.when(0).then(0).else(
511
+ ArelExtensions::Nodes::Concat.new([
512
+ before,
513
+ sign,
514
+ middle,
515
+ number,
516
+ after
517
+ ])
518
+ )
519
+ collector = visit ArelExtensions::Nodes::Concat.new([Arel::Nodes.build_quoted(o.prefix),full_number,Arel::Nodes.build_quoted(o.suffix)]), collector
520
+
521
+ collector
522
+ end
523
+
438
524
  end
439
525
  end
440
526
  end
@@ -43,6 +43,9 @@ module ArelExtensions
43
43
  compile(@price.abs + 42).must_be_like %{(ABS("products"."price") + 42)}
44
44
  compile(@price.ceil + 42).must_be_like %{(CEIL("products"."price") + 42)}
45
45
  compile(@price.floor + 42).must_be_like %{(FLOOR("products"."price") + 42)}
46
+ compile(@price.log10 + 42).must_be_like %{(LOG10("products"."price") + 42)}
47
+ compile(@price.power(42) + 42).must_be_like %{(POW("products"."price", 42) + 42)}
48
+ compile(@price.pow(42) + 42).must_be_like %{(POW("products"."price", 42) + 42)}
46
49
  compile(@price.ceil + @price.floor).must_be_like %{(CEIL("products"."price") + FLOOR("products"."price"))}
47
50
  compile((@price.ceil + @price.floor).abs).must_be_like %{ABS((CEIL("products"."price") + FLOOR("products"."price")))}
48
51
  compile(@price.round + 42).must_be_like %{(ROUND("products"."price") + 42)}
@@ -110,11 +113,6 @@ module ArelExtensions
110
113
  compile(c.idoes_not_match('%test%')).must_be_like %{"users"."name" NOT ILIKE '%test%'}
111
114
  end
112
115
 
113
- it "should accept comparators on functions" do
114
- c = @table[:name]
115
- #compile(c.soundex == 'test').must_be_like %{SOUNDEX("users"."name") = 'test'}
116
- end
117
-
118
116
  # Maths
119
117
  # DateDiff
120
118
  it "should diff date col and date" do
@@ -219,9 +217,6 @@ module ArelExtensions
219
217
 
220
218
  end
221
219
 
222
-
223
- puts "AREL VERSION : " + Arel::VERSION.to_s
224
-
225
220
  # Case
226
221
  it "should accept case clause" do
227
222
  @table[:name].when("smith").then("cool").when("doe").then("fine").else("uncool").to_sql
@@ -233,9 +228,71 @@ module ArelExtensions
233
228
  ArelExtensions::Nodes::Case.new(@table[:name]).when("smith").then(1).when("doe").then(2).else(0).to_sql
234
229
  .must_be_like %{CASE "users"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END}
235
230
  @table[:name].when("smith").then(1).when("doe").then(2).else(0).sum.to_sql
236
- .must_be_like %{SUM(CASE "users"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END)}
231
+ .must_be_like %{SUM(CASE "users"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END)}
232
+ @table[:name].when("smith").then("cool").else("uncool").matches('value',false).to_sql
233
+ .must_be_like %{CASE "users"."name" WHEN 'smith' THEN 'cool' ELSE 'uncool' END LIKE 'value'}
234
+ @table[:name].when("smith").then("cool").else("uncool").imatches('value',false).to_sql
235
+ .must_be_like %{CASE "users"."name" WHEN 'smith' THEN 'cool' ELSE 'uncool' END ILIKE 'value'}
237
236
  end
238
-
237
+
238
+
239
+ it "should accept comparators on functions" do
240
+ c = @table[:name]
241
+ compile(c.soundex == 'test').must_be_like %{SOUNDEX("users"."name") = 'test'}
242
+ end
243
+
244
+
245
+ it "should accept in on select statement" do
246
+ c = @table[:name]
247
+ compile(c.in(@table.project(@table[:name])))
248
+ .must_be_like %{"users"."name" IN (SELECT "users"."name" FROM "users")}
249
+ end
250
+
251
+ it "should accept coalesce function properly even on none actual tables and attributes" do
252
+ fake_at = Arel::Table.new('fake_table')
253
+ compile(fake_at['fake_attribute'].coalesce('other_value'))
254
+ .must_be_like %{COALESCE("fake_table"."fake_attribute", 'other_value')}
255
+ compile(fake_at['fake_attribute'].coalesce('other_value1','other_value2'))
256
+ .must_be_like %{COALESCE("fake_table"."fake_attribute", 'other_value1', 'other_value2')}
257
+ compile(fake_at['fake_attribute'].coalesce('other_value1').coalesce('other_value2'))
258
+ .must_be_like %{COALESCE(COALESCE("fake_table"."fake_attribute", 'other_value1'), 'other_value2')}
259
+ compile(fake_at['fake_attribute'].coalesce('other_value').matches('truc'))
260
+ .must_be_like %{COALESCE("fake_table"."fake_attribute", 'other_value') LIKE 'truc'}
261
+ compile(fake_at['fake_attribute'].coalesce('other_value').imatches('truc'))
262
+ .must_be_like %{COALESCE("fake_table"."fake_attribute", 'other_value') ILIKE 'truc'}
263
+ end
264
+
265
+ it "should be possible to cast nodes types" do
266
+
267
+
268
+ compile(@table[:id].cast_as_char)
269
+ .must_be_like %{CAST("users"."id" AS char)}
270
+
271
+ compile(@table[:id].cast('char'))
272
+ .must_be_like %{CAST("users"."id" AS char)}
273
+
274
+ compile(@table[:id].coalesce(' ').cast('char'))
275
+ .must_be_like %{CAST(COALESCE("users"."id", ' ') AS char)}
276
+
277
+ compile(@table[:id].coalesce(' ').cast(:string))
278
+ .must_be_like %{CAST(COALESCE("users"."id", ' ') AS char)}
279
+
280
+ compile(@table[:id].cast('char') + ' ')
281
+ .must_be_like %{CONCAT(CAST("users"."id" AS char), ' ')}
282
+
283
+ compile(@table[:id].cast('int') + 2)
284
+ .must_be_like %{(CAST("users"."id" AS int) + 2)}
285
+
286
+ end
287
+
288
+
289
+ it "should be possible to specify a cool format on number" do
290
+ #puts @price.format_number("$$ %+030.2e €€","fr_FR").to_sql
291
+ # compile(@price.format_number("$$ %+030.2e €€","fr_FR"))
292
+ # .must_be_like %{CONCAT('$$ ', CASE \"products\".\"price\" WHEN 0 THEN 0 ELSE CONCAT('', CASE WHEN \"products\".\"price\" < 0 THEN '-' ELSE '+' END, CASE WHEN (ABS(30) - (LENGTH(CONCAT(FORMAT(ABS(\"products\".\"price\") / POW(10, FLOOR(LOG10(ABS(\"products\".\"price\")))), 2, 'fr_FR'), 'e', FORMAT(FLOOR(LOG10(ABS(\"products\".\"price\"))), 0))) + LENGTH(CASE WHEN \"products\".\"price\" < 0 THEN '-' ELSE '+' END))) > 0 THEN REPEAT('0', (ABS(30) - (LENGTH(CONCAT(FORMAT(ABS(\"products\".\"price\") / POW(10, FLOOR(LOG10(ABS(\"products\".\"price\")))), 2, 'fr_FR'), 'e', FORMAT(FLOOR(LOG10(ABS(\"products\".\"price\"))), 0))) + LENGTH(CASE WHEN \"products\".\"price\" < 0 THEN '-' ELSE '+' END)))) ELSE '' END, CONCAT(FORMAT(ABS(\"products\".\"price\") / POW(10, FLOOR(LOG10(ABS(\"products\".\"price\")))), 2, 'fr_FR'), 'e', FORMAT(FLOOR(LOG10(ABS(\"products\".\"price\"))), 0)), '') END, ' €€')}
293
+ end
294
+
295
+ puts "AREL VERSION : " + Arel::VERSION.to_s
239
296
  end
240
297
  end
241
298
  end
@@ -249,6 +249,7 @@ module ArelExtensions
249
249
  skip "PostgreSql version can't load extension for soundex" if @env_db == 'postgresql'
250
250
  assert_equal "C540", t(@camille, @name.soundex)
251
251
  assert_equal 8, User.where(@name.soundex.eq(@name.soundex)).count
252
+ assert_equal 8, User.where(@name.soundex == @name.soundex).count
252
253
  end
253
254
 
254
255
  def test_change_case
@@ -458,6 +459,25 @@ module ArelExtensions
458
459
  def test_case
459
460
  assert_equal 4, User.find_by_sql(@ut.project(@score.when(20.16).then(1).else(0).as('score_bin')).to_sql).sum(&:score_bin)
460
461
  end
462
+
463
+ def test_format_numbers
464
+ #score of Arthur = 65.62
465
+ if @env_db == 'mysql'
466
+ assert_equal "AZERTY65,62" , t(@arthur, @score.format_number("AZERTY%.2f","fr_FR"))
467
+ assert_equal "65,62AZERTY" , t(@arthur, @score.format_number("%.2fAZERTY","fr_FR"))
468
+ assert_equal "$ 65.62 €" , t(@arthur, @score.format_number("$ %.2f €","en_EN"))
469
+ assert_equal "$ 0065,62 €" , t(@arthur, @score.format_number("$ %07.2f €","fr_FR"))
470
+ assert_equal "$ 65,62 €" , t(@arthur, @score.format_number("$ %-07.2f €","fr_FR"))
471
+ assert_equal "$ 65,62 €" , t(@arthur, @score.format_number("$ %-7.2f €","fr_FR"))
472
+ assert_equal "$ 65,62 €" , t(@arthur, @score.format_number("$ % 7.2f €","fr_FR"))
473
+ assert_equal "$ +65,62 €" , t(@arthur, @score.format_number("$ % +7.2f €","fr_FR"))
474
+ assert_equal "$ +065,62 €" , t(@arthur, @score.format_number("$ %0+7.2f €","fr_FR"))
475
+ assert_equal "$ 6,56e1 €" , t(@arthur, @score.format_number("$ %.2e €","fr_FR"))
476
+ assert_equal "$ 6,56E1 €" , t(@arthur, @score.format_number("$ %.2E €","fr_FR"))
477
+ assert_equal "Wrong Format" , t(@arthur, @score.format_number("$ %...234.6F €","fr_FR"))
478
+ end
479
+ end
480
+
461
481
 
462
482
  end
463
483
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arel_extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yann Azoury
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-01-16 00:00:00.000000000 Z
13
+ date: 2018-03-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: arel
@@ -123,6 +123,7 @@ files:
123
123
  - lib/arel_extensions/nodes/as.rb
124
124
  - lib/arel_extensions/nodes/blank.rb
125
125
  - lib/arel_extensions/nodes/case.rb
126
+ - lib/arel_extensions/nodes/cast.rb
126
127
  - lib/arel_extensions/nodes/ceil.rb
127
128
  - lib/arel_extensions/nodes/change_case.rb
128
129
  - lib/arel_extensions/nodes/coalesce.rb
@@ -132,12 +133,16 @@ files:
132
133
  - lib/arel_extensions/nodes/find_in_set.rb
133
134
  - lib/arel_extensions/nodes/floor.rb
134
135
  - lib/arel_extensions/nodes/format.rb
136
+ - lib/arel_extensions/nodes/formatted_number.rb
135
137
  - lib/arel_extensions/nodes/function.rb
136
138
  - lib/arel_extensions/nodes/is_null.rb
137
139
  - lib/arel_extensions/nodes/length.rb
138
140
  - lib/arel_extensions/nodes/locate.rb
141
+ - lib/arel_extensions/nodes/log10.rb
139
142
  - lib/arel_extensions/nodes/matches.rb
143
+ - lib/arel_extensions/nodes/power.rb
140
144
  - lib/arel_extensions/nodes/rand.rb
145
+ - lib/arel_extensions/nodes/repeat.rb
141
146
  - lib/arel_extensions/nodes/replace.rb
142
147
  - lib/arel_extensions/nodes/round.rb
143
148
  - lib/arel_extensions/nodes/soundex.rb