arel_extensions 1.0.2 → 1.0.3

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.
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