arel 6.0.4 → 7.1.4
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 +4 -4
- data/History.txt +31 -12
- data/MIT-LICENSE.txt +2 -1
- data/{README.markdown → README.md} +88 -31
- data/lib/arel.rb +1 -1
- data/lib/arel/attributes/attribute.rb +8 -0
- data/lib/arel/crud.rb +4 -3
- data/lib/arel/delete_manager.rb +6 -1
- data/lib/arel/insert_manager.rb +1 -1
- data/lib/arel/math.rb +24 -0
- data/lib/arel/nodes.rb +6 -38
- data/lib/arel/nodes/binary.rb +0 -2
- data/lib/arel/nodes/bind_param.rb +3 -0
- data/lib/arel/nodes/case.rb +57 -0
- data/lib/arel/nodes/casted.rb +44 -0
- data/lib/arel/nodes/delete_statement.rb +2 -0
- data/lib/arel/nodes/infix_operation.rb +36 -1
- data/lib/arel/nodes/matches.rb +3 -1
- data/lib/arel/nodes/regexp.rb +14 -0
- data/lib/arel/nodes/select_core.rb +5 -5
- data/lib/arel/nodes/table_alias.rb +6 -2
- data/lib/arel/nodes/unary.rb +6 -3
- data/lib/arel/nodes/unary_operation.rb +25 -0
- data/lib/arel/predications.rb +38 -14
- data/lib/arel/select_manager.rb +6 -6
- data/lib/arel/table.rb +34 -59
- data/lib/arel/tree_manager.rb +3 -8
- data/lib/arel/update_manager.rb +1 -1
- data/lib/arel/visitors.rb +1 -23
- data/lib/arel/visitors/depth_first.rb +14 -2
- data/lib/arel/visitors/dot.rb +12 -1
- data/lib/arel/visitors/informix.rb +6 -1
- data/lib/arel/visitors/mssql.rb +35 -3
- data/lib/arel/visitors/mysql.rb +8 -0
- data/lib/arel/visitors/oracle.rb +13 -2
- data/lib/arel/visitors/oracle12.rb +59 -0
- data/lib/arel/visitors/postgresql.rb +56 -4
- data/lib/arel/visitors/sqlite.rb +9 -0
- data/lib/arel/visitors/to_sql.rb +94 -52
- data/lib/arel/visitors/where_sql.rb +10 -1
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1986f950d2ecce6990d5a420c7963c874bcf22b
|
4
|
+
data.tar.gz: e21fbc532cdf9feceed6883a9ed1a151c0c1ebb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0c67c17600ee4a7ce861bc688abf8d409d8b8c1c65969f26a9cfe8d7263eacd3c50f3e9548b80f1e071cd14e176e765addba6ec84de579c4797a123c960e2b1
|
7
|
+
data.tar.gz: a9f11f32f1868a9df3b27c88828513bf4106fcce946defc2d383488ab9fe9cb814408c51071ce6bbfe5aa8777d1a1fba7afd4fea89d4931282a2d7447343588e
|
data/History.txt
CHANGED
@@ -1,26 +1,45 @@
|
|
1
|
-
===
|
1
|
+
=== 7.1.4 / 2016-10-10
|
2
2
|
|
3
|
-
*
|
3
|
+
* bug fixes
|
4
4
|
|
5
|
-
*
|
5
|
+
* Remove deprecated usage inside arel.
|
6
6
|
|
7
|
-
===
|
7
|
+
=== 7.1.3 / 2016-10-07
|
8
8
|
|
9
|
-
*
|
9
|
+
* bug fixes
|
10
10
|
|
11
|
-
*
|
11
|
+
* Remove union mapping as :binary node when performing DepthFirst enumeration
|
12
|
+
* Fix invalid BindParam output in Dot visitor
|
13
|
+
* Add Arel::Nodes::Casted to dot visitor
|
14
|
+
* Use Arel::Nodes::BindParam in Oracle visitor for queries using both LIMIT and OFFSET
|
12
15
|
|
13
|
-
===
|
16
|
+
=== 7.1.2 / 2016-09-13
|
14
17
|
|
15
|
-
*
|
18
|
+
* bug fixes
|
16
19
|
|
17
|
-
*
|
20
|
+
* Don't store all aliases to a table
|
18
21
|
|
19
|
-
===
|
22
|
+
=== 7.1.1 / 2016-07-27
|
20
23
|
|
21
|
-
*
|
24
|
+
* bug fixes
|
25
|
+
|
26
|
+
* fix warning in `casted#hash`
|
27
|
+
|
28
|
+
=== 7.1.0 / 2016-07-19
|
29
|
+
|
30
|
+
* Enhancements
|
31
|
+
|
32
|
+
* Support Ruby 2.4 unified Integer class
|
33
|
+
* Implement `CASE` conditional expression
|
34
|
+
* Support for Bitwise Operations as `InfixOperations`
|
35
|
+
|
36
|
+
=== 7.0.0 / 2015-12-17
|
37
|
+
|
38
|
+
* Enhancements
|
22
39
|
|
23
|
-
*
|
40
|
+
* Remove deprecated method `Table#primary_key`
|
41
|
+
* Remove engine from the constructor arguments `Arel::Table`
|
42
|
+
* Deprecate automatic type casting within Arel
|
24
43
|
|
25
44
|
=== 6.0.0 / 2014-11-25
|
26
45
|
|
data/MIT-LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2007-
|
1
|
+
Copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -18,3 +18,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Arel
|
1
|
+
# Arel
|
2
2
|
|
3
3
|
* http://github.com/rails/arel
|
4
4
|
|
@@ -8,8 +8,8 @@ Arel Really Exasperates Logicians
|
|
8
8
|
|
9
9
|
Arel is a SQL AST manager for Ruby. It
|
10
10
|
|
11
|
-
1.
|
12
|
-
2.
|
11
|
+
1. simplifies the generation of complex SQL queries, and
|
12
|
+
2. adapts to various RDBMSes.
|
13
13
|
|
14
14
|
It is intended to be a framework framework; that is, you can build your own ORM
|
15
15
|
with it, focusing on innovative object and collection modeling as opposed to
|
@@ -17,7 +17,7 @@ database compatibility and query generation.
|
|
17
17
|
|
18
18
|
## Status
|
19
19
|
|
20
|
-
For the moment, Arel uses Active Record's connection adapters to connect to the various engines
|
20
|
+
For the moment, Arel uses Active Record's connection adapters to connect to the various engines and perform connection pooling, quoting, and type conversion.
|
21
21
|
|
22
22
|
## A Gentle Introduction
|
23
23
|
|
@@ -27,7 +27,7 @@ Generating a query with Arel is simple. For example, in order to produce
|
|
27
27
|
SELECT * FROM users
|
28
28
|
```
|
29
29
|
|
30
|
-
you construct a table relation and convert it to
|
30
|
+
you construct a table relation and convert it to SQL:
|
31
31
|
|
32
32
|
```ruby
|
33
33
|
users = Arel::Table.new(:users)
|
@@ -56,13 +56,48 @@ users.project(users[:id])
|
|
56
56
|
Comparison operators `=`, `!=`, `<`, `>`, `<=`, `>=`, `IN`:
|
57
57
|
|
58
58
|
```ruby
|
59
|
-
users.where(users[:age].eq(10)).project(Arel.sql('*'))
|
60
|
-
|
61
|
-
|
62
|
-
users.where(users[:age].
|
63
|
-
|
64
|
-
|
65
|
-
users.where(users[:age].
|
59
|
+
users.where(users[:age].eq(10)).project(Arel.sql('*'))
|
60
|
+
# => SELECT * FROM "users" WHERE "users"."age" = 10
|
61
|
+
|
62
|
+
users.where(users[:age].not_eq(10)).project(Arel.sql('*'))
|
63
|
+
# => SELECT * FROM "users" WHERE "users"."age" != 10
|
64
|
+
|
65
|
+
users.where(users[:age].lt(10)).project(Arel.sql('*'))
|
66
|
+
# => SELECT * FROM "users" WHERE "users"."age" < 10
|
67
|
+
|
68
|
+
users.where(users[:age].gt(10)).project(Arel.sql('*'))
|
69
|
+
# => SELECT * FROM "users" WHERE "users"."age" > 10
|
70
|
+
|
71
|
+
users.where(users[:age].lteq(10)).project(Arel.sql('*'))
|
72
|
+
# => SELECT * FROM "users" WHERE "users"."age" <= 10
|
73
|
+
|
74
|
+
users.where(users[:age].gteq(10)).project(Arel.sql('*'))
|
75
|
+
# => SELECT * FROM "users" WHERE "users"."age" >= 10
|
76
|
+
|
77
|
+
users.where(users[:age].in([20, 16, 17])).project(Arel.sql('*'))
|
78
|
+
# => SELECT * FROM "users" WHERE "users"."age" IN (20, 16, 17)
|
79
|
+
```
|
80
|
+
|
81
|
+
Bitwise operators `&`, `|`, `^`, `<<`, `>>`:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
users.where((users[:bitmap] & 16).gt(0)).project(Arel.sql('*'))
|
85
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" & 16) > 0
|
86
|
+
|
87
|
+
users.where((users[:bitmap] | 16).gt(0)).project(Arel.sql('*'))
|
88
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" | 16) > 0
|
89
|
+
|
90
|
+
users.where((users[:bitmap] ^ 16).gt(0)).project(Arel.sql('*'))
|
91
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" ^ 16) > 0
|
92
|
+
|
93
|
+
users.where((users[:bitmap] << 1).gt(0)).project(Arel.sql('*'))
|
94
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" << 1) > 0
|
95
|
+
|
96
|
+
users.where((users[:bitmap] >> 1).gt(0)).project(Arel.sql('*'))
|
97
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" >> 1) > 0
|
98
|
+
|
99
|
+
users.where((~ users[:bitmap]).gt(0)).project(Arel.sql('*'))
|
100
|
+
# => SELECT FROM "users" WHERE ~ "users"."bitmap" > 0
|
66
101
|
```
|
67
102
|
|
68
103
|
Joins resemble SQL strongly:
|
@@ -72,7 +107,7 @@ users.join(photos).on(users[:id].eq(photos[:user_id]))
|
|
72
107
|
# => SELECT * FROM users INNER JOIN photos ON users.id = photos.user_id
|
73
108
|
```
|
74
109
|
|
75
|
-
Left
|
110
|
+
Left joins:
|
76
111
|
|
77
112
|
```ruby
|
78
113
|
users.join(photos, Arel::Nodes::OuterJoin).on(users[:id].eq(photos[:user_id]))
|
@@ -93,7 +128,7 @@ users.project(users[:name]).group(users[:name])
|
|
93
128
|
# => SELECT users.name FROM users GROUP BY users.name
|
94
129
|
```
|
95
130
|
|
96
|
-
The best property of
|
131
|
+
The best property of Arel is its "composability," or closure under all operations. For example, to restrict AND project, just "chain" the method invocations:
|
97
132
|
|
98
133
|
```ruby
|
99
134
|
users \
|
@@ -119,23 +154,35 @@ The `AND` operator behaves similarly.
|
|
119
154
|
Aggregate functions `AVG`, `SUM`, `COUNT`, `MIN`, `MAX`, `HAVING`:
|
120
155
|
|
121
156
|
```ruby
|
122
|
-
photos.group(photos[:user_id]).having(photos[:id].count.gt(5))
|
123
|
-
|
124
|
-
|
125
|
-
users.project(users[:age].
|
126
|
-
|
127
|
-
|
157
|
+
photos.group(photos[:user_id]).having(photos[:id].count.gt(5))
|
158
|
+
# => SELECT FROM photos GROUP BY photos.user_id HAVING COUNT(photos.id) > 5
|
159
|
+
|
160
|
+
users.project(users[:age].sum)
|
161
|
+
# => SELECT SUM(users.age) FROM users
|
162
|
+
|
163
|
+
users.project(users[:age].average)
|
164
|
+
# => SELECT AVG(users.age) FROM users
|
165
|
+
|
166
|
+
users.project(users[:age].maximum)
|
167
|
+
# => SELECT MAX(users.age) FROM users
|
168
|
+
|
169
|
+
users.project(users[:age].minimum)
|
170
|
+
# => SELECT MIN(users.age) FROM users
|
171
|
+
|
172
|
+
users.project(users[:age].count)
|
173
|
+
# => SELECT COUNT(users.age) FROM users
|
128
174
|
```
|
129
175
|
|
130
176
|
Aliasing Aggregate Functions:
|
131
177
|
|
132
178
|
```ruby
|
133
|
-
users.project(users[:age].average.as("mean_age"))
|
179
|
+
users.project(users[:age].average.as("mean_age"))
|
180
|
+
# => SELECT AVG(users.age) AS mean_age FROM users
|
134
181
|
```
|
135
182
|
|
136
183
|
### The Crazy Features
|
137
184
|
|
138
|
-
The examples above are fairly simple and other libraries match or come close to matching the expressiveness of Arel (e.g
|
185
|
+
The examples above are fairly simple and other libraries match or come close to matching the expressiveness of Arel (e.g. `Sequel` in Ruby).
|
139
186
|
|
140
187
|
#### Inline math operations
|
141
188
|
|
@@ -179,12 +226,13 @@ Joining a table to itself requires aliasing in SQL. This aliasing can be handled
|
|
179
226
|
replies = comments.alias
|
180
227
|
comments_with_replies = \
|
181
228
|
comments.join(replies).on(replies[:parent_id].eq(comments[:id])).where(comments[:id].eq(1))
|
182
|
-
# => SELECT * FROM comments INNER JOIN comments AS comments_2
|
229
|
+
# => SELECT * FROM comments INNER JOIN comments AS comments_2
|
230
|
+
# WHERE comments_2.parent_id = comments.id AND comments.id = 1
|
183
231
|
```
|
184
232
|
|
185
233
|
This will return the reply for the first comment.
|
186
234
|
|
187
|
-
[Common Table Expressions(CTE)](https://en.wikipedia.org/wiki/Common_table_expressions#Common_table_expression) support via:
|
235
|
+
[Common Table Expressions (CTE)](https://en.wikipedia.org/wiki/Common_table_expressions#Common_table_expression) support via:
|
188
236
|
|
189
237
|
Create a `CTE`
|
190
238
|
|
@@ -201,7 +249,9 @@ users.
|
|
201
249
|
project(users[:id], cte_table[:click].sum).
|
202
250
|
with(composed_cte)
|
203
251
|
|
204
|
-
# => WITH cte_table AS (SELECT FROM photos WHERE photos.created_at > '2014-05-02')
|
252
|
+
# => WITH cte_table AS (SELECT FROM photos WHERE photos.created_at > '2014-05-02')
|
253
|
+
# SELECT users.id, SUM(cte_table.click)
|
254
|
+
# FROM users INNER JOIN cte_table ON users.id = cte_table.user_id
|
205
255
|
```
|
206
256
|
|
207
257
|
When your query is too complex for `Arel`, you can use `Arel::SqlLiteral`:
|
@@ -214,14 +264,21 @@ photo_clicks = Arel::Nodes::SqlLiteral.new(<<-SQL
|
|
214
264
|
ELSE default_calculation END
|
215
265
|
SQL
|
216
266
|
)
|
267
|
+
|
217
268
|
photos.project(photo_clicks.as("photo_clicks"))
|
218
269
|
# => SELECT CASE WHEN condition1 THEN calculation1
|
219
|
-
WHEN condition2 THEN calculation2
|
220
|
-
WHEN condition3 THEN calculation3
|
221
|
-
ELSE default_calculation END
|
222
|
-
|
270
|
+
# WHEN condition2 THEN calculation2
|
271
|
+
# WHEN condition3 THEN calculation3
|
272
|
+
# ELSE default_calculation END
|
273
|
+
# FROM "photos"
|
223
274
|
```
|
224
275
|
|
225
|
-
|
276
|
+
## Contributing to Arel
|
277
|
+
|
278
|
+
Arel is the work of many contributors. You're encouraged to submit pull requests, propose
|
279
|
+
features and discuss issues.
|
280
|
+
|
281
|
+
See [CONTRIBUTING](CONTRIBUTING.md).
|
226
282
|
|
227
|
-
|
283
|
+
## License
|
284
|
+
Arel is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
data/lib/arel.rb
CHANGED
@@ -12,6 +12,14 @@ module Arel
|
|
12
12
|
def lower
|
13
13
|
relation.lower self
|
14
14
|
end
|
15
|
+
|
16
|
+
def type_cast_for_database(value)
|
17
|
+
relation.type_cast_for_database(name, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def able_to_type_cast?
|
21
|
+
relation.able_to_type_cast?
|
22
|
+
end
|
15
23
|
end
|
16
24
|
|
17
25
|
class String < Attribute; end
|
data/lib/arel/crud.rb
CHANGED
@@ -3,7 +3,7 @@ module Arel
|
|
3
3
|
# FIXME hopefully we can remove this
|
4
4
|
module Crud
|
5
5
|
def compile_update values, pk
|
6
|
-
um = UpdateManager.new
|
6
|
+
um = UpdateManager.new
|
7
7
|
|
8
8
|
if Nodes::SqlLiteral === values
|
9
9
|
relation = @ctx.from
|
@@ -26,11 +26,12 @@ module Arel
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def create_insert
|
29
|
-
InsertManager.new
|
29
|
+
InsertManager.new
|
30
30
|
end
|
31
31
|
|
32
32
|
def compile_delete
|
33
|
-
dm = DeleteManager.new
|
33
|
+
dm = DeleteManager.new
|
34
|
+
dm.take @ast.limit.expr if @ast.limit
|
34
35
|
dm.wheres = @ctx.wheres
|
35
36
|
dm.from @ctx.froms
|
36
37
|
dm
|
data/lib/arel/delete_manager.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Arel
|
2
2
|
class DeleteManager < Arel::TreeManager
|
3
|
-
def initialize
|
3
|
+
def initialize
|
4
4
|
super
|
5
5
|
@ast = Nodes::DeleteStatement.new
|
6
6
|
@ctx = @ast
|
@@ -11,6 +11,11 @@ module Arel
|
|
11
11
|
self
|
12
12
|
end
|
13
13
|
|
14
|
+
def take limit
|
15
|
+
@ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) if limit
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
14
19
|
def wheres= list
|
15
20
|
@ast.wheres = list
|
16
21
|
end
|
data/lib/arel/insert_manager.rb
CHANGED
data/lib/arel/math.rb
CHANGED
@@ -15,5 +15,29 @@ module Arel
|
|
15
15
|
def /(other)
|
16
16
|
Arel::Nodes::Division.new(self, other)
|
17
17
|
end
|
18
|
+
|
19
|
+
def &(other)
|
20
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::BitwiseAnd.new(self, other))
|
21
|
+
end
|
22
|
+
|
23
|
+
def |(other)
|
24
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::BitwiseOr.new(self, other))
|
25
|
+
end
|
26
|
+
|
27
|
+
def ^(other)
|
28
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::BitwiseXor.new(self, other))
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(other)
|
32
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::BitwiseShiftLeft.new(self, other))
|
33
|
+
end
|
34
|
+
|
35
|
+
def >>(other)
|
36
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::BitwiseShiftRight.new(self, other))
|
37
|
+
end
|
38
|
+
|
39
|
+
def ~@
|
40
|
+
Arel::Nodes::BitwiseNot.new(self)
|
41
|
+
end
|
18
42
|
end
|
19
43
|
end
|
data/lib/arel/nodes.rb
CHANGED
@@ -28,8 +28,10 @@ require 'arel/nodes/join_source'
|
|
28
28
|
require 'arel/nodes/delete_statement'
|
29
29
|
require 'arel/nodes/table_alias'
|
30
30
|
require 'arel/nodes/infix_operation'
|
31
|
+
require 'arel/nodes/unary_operation'
|
31
32
|
require 'arel/nodes/over'
|
32
33
|
require 'arel/nodes/matches'
|
34
|
+
require 'arel/nodes/regexp'
|
33
35
|
|
34
36
|
# nary
|
35
37
|
require 'arel/nodes/and'
|
@@ -46,6 +48,9 @@ require 'arel/nodes/named_function'
|
|
46
48
|
# windows
|
47
49
|
require 'arel/nodes/window'
|
48
50
|
|
51
|
+
# conditional expressions
|
52
|
+
require 'arel/nodes/case'
|
53
|
+
|
49
54
|
# joins
|
50
55
|
require 'arel/nodes/full_outer_join'
|
51
56
|
require 'arel/nodes/inner_join'
|
@@ -55,41 +60,4 @@ require 'arel/nodes/string_join'
|
|
55
60
|
|
56
61
|
require 'arel/nodes/sql_literal'
|
57
62
|
|
58
|
-
|
59
|
-
module Nodes
|
60
|
-
class Casted < Arel::Nodes::Node # :nodoc:
|
61
|
-
attr_reader :val, :attribute
|
62
|
-
def initialize val, attribute
|
63
|
-
@val = val
|
64
|
-
@attribute = attribute
|
65
|
-
super()
|
66
|
-
end
|
67
|
-
|
68
|
-
def nil?; @val.nil?; end
|
69
|
-
|
70
|
-
def eql? other
|
71
|
-
self.class == other.class &&
|
72
|
-
self.val == other.val &&
|
73
|
-
self.attribute == other.attribute
|
74
|
-
end
|
75
|
-
alias :== :eql?
|
76
|
-
end
|
77
|
-
|
78
|
-
class Quoted < Arel::Nodes::Unary # :nodoc:
|
79
|
-
end
|
80
|
-
|
81
|
-
def self.build_quoted other, attribute = nil
|
82
|
-
case other
|
83
|
-
when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::Nodes::BindParam, Arel::SelectManager
|
84
|
-
other
|
85
|
-
else
|
86
|
-
case attribute
|
87
|
-
when Arel::Attributes::Attribute
|
88
|
-
Casted.new other, attribute
|
89
|
-
else
|
90
|
-
Quoted.new other
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
63
|
+
require 'arel/nodes/casted'
|