arel 5.0.1.20140414130214 → 9.0.0
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 +44 -4
- data/MIT-LICENSE.txt +2 -1
- data/README.md +295 -0
- data/lib/arel/alias_predication.rb +1 -0
- data/lib/arel/attributes/attribute.rb +9 -0
- data/lib/arel/attributes.rb +1 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +32 -0
- data/lib/arel/collectors/plain_string.rb +19 -0
- data/lib/arel/collectors/sql_string.rb +24 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/compatibility/wheres.rb +1 -0
- data/lib/arel/crud.rb +5 -3
- data/lib/arel/delete_manager.rb +7 -1
- data/lib/arel/errors.rb +8 -0
- data/lib/arel/expressions.rb +6 -4
- data/lib/arel/factory_methods.rb +2 -1
- data/lib/arel/insert_manager.rb +12 -2
- data/lib/arel/math.rb +25 -0
- data/lib/arel/nodes/and.rb +2 -5
- data/lib/arel/nodes/ascending.rb +1 -0
- data/lib/arel/nodes/binary.rb +2 -3
- data/lib/arel/nodes/bind_param.rb +27 -0
- data/lib/arel/nodes/case.rb +58 -0
- data/lib/arel/nodes/casted.rb +45 -0
- data/lib/arel/nodes/count.rb +3 -0
- data/lib/arel/nodes/delete_statement.rb +3 -0
- data/lib/arel/nodes/descending.rb +1 -0
- data/lib/arel/nodes/equality.rb +1 -0
- data/lib/arel/nodes/extract.rb +5 -13
- data/lib/arel/nodes/false.rb +2 -0
- data/lib/arel/nodes/full_outer_join.rb +7 -0
- data/lib/arel/nodes/function.rb +4 -1
- data/lib/arel/nodes/grouping.rb +1 -0
- data/lib/arel/nodes/in.rb +1 -0
- data/lib/arel/nodes/infix_operation.rb +37 -1
- data/lib/arel/nodes/inner_join.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +6 -2
- data/lib/arel/nodes/join_source.rb +1 -0
- data/lib/arel/nodes/matches.rb +17 -0
- data/lib/arel/nodes/named_function.rb +1 -0
- data/lib/arel/nodes/node.rb +6 -1
- data/lib/arel/nodes/outer_join.rb +1 -0
- data/lib/arel/nodes/over.rb +1 -0
- data/lib/arel/nodes/regexp.rb +15 -0
- data/lib/arel/nodes/right_outer_join.rb +7 -0
- data/lib/arel/nodes/select_core.rb +6 -5
- data/lib/arel/nodes/select_statement.rb +1 -0
- data/lib/arel/nodes/sql_literal.rb +1 -3
- data/lib/arel/nodes/string_join.rb +1 -0
- data/lib/arel/nodes/table_alias.rb +7 -2
- data/lib/arel/nodes/terminal.rb +2 -0
- data/lib/arel/nodes/true.rb +2 -0
- data/lib/arel/nodes/unary.rb +7 -3
- data/lib/arel/nodes/unary_operation.rb +26 -0
- data/lib/arel/nodes/unqualified_column.rb +1 -0
- data/lib/arel/nodes/update_statement.rb +1 -0
- data/lib/arel/nodes/values.rb +1 -0
- data/lib/arel/nodes/values_list.rb +23 -0
- data/lib/arel/nodes/window.rb +25 -5
- data/lib/arel/nodes/with.rb +1 -0
- data/lib/arel/nodes.rb +13 -0
- data/lib/arel/order_predications.rb +1 -0
- data/lib/arel/predications.rb +115 -55
- data/lib/arel/select_manager.rb +26 -40
- data/lib/arel/table.rb +36 -68
- data/lib/arel/tree_manager.rb +13 -14
- data/lib/arel/update_manager.rb +4 -3
- data/lib/arel/visitors/depth_first.rb +85 -57
- data/lib/arel/visitors/dot.rb +101 -82
- data/lib/arel/visitors/ibm_db.rb +5 -2
- data/lib/arel/visitors/informix.rb +43 -21
- data/lib/arel/visitors/mssql.rb +75 -24
- data/lib/arel/visitors/mysql.rb +52 -22
- data/lib/arel/visitors/oracle.rb +51 -26
- data/lib/arel/visitors/oracle12.rb +60 -0
- data/lib/arel/visitors/postgresql.rb +72 -8
- data/lib/arel/visitors/reduce.rb +27 -0
- data/lib/arel/visitors/sqlite.rb +13 -2
- data/lib/arel/visitors/to_sql.rb +533 -287
- data/lib/arel/visitors/visitor.rb +18 -9
- data/lib/arel/visitors/where_sql.rb +15 -2
- data/lib/arel/visitors.rb +2 -25
- data/lib/arel/window_predications.rb +1 -0
- data/lib/arel.rb +4 -12
- metadata +50 -133
- data/.autotest +0 -26
- data/.gemtest +0 -0
- data/.travis.yml +0 -14
- data/Gemfile +0 -12
- data/Manifest.txt +0 -133
- data/README.markdown +0 -150
- data/Rakefile +0 -20
- data/arel.gemspec +0 -41
- data/lib/arel/deprecated.rb +0 -4
- data/lib/arel/expression.rb +0 -5
- data/lib/arel/sql/engine.rb +0 -10
- data/lib/arel/sql_literal.rb +0 -4
- data/lib/arel/visitors/bind_visitor.rb +0 -34
- data/lib/arel/visitors/join_sql.rb +0 -19
- data/lib/arel/visitors/order_clauses.rb +0 -11
- data/test/attributes/test_attribute.rb +0 -664
- data/test/helper.rb +0 -13
- data/test/nodes/test_and.rb +0 -20
- data/test/nodes/test_as.rb +0 -34
- data/test/nodes/test_ascending.rb +0 -44
- data/test/nodes/test_bin.rb +0 -33
- data/test/nodes/test_count.rb +0 -39
- data/test/nodes/test_delete_statement.rb +0 -34
- data/test/nodes/test_descending.rb +0 -44
- data/test/nodes/test_distinct.rb +0 -20
- data/test/nodes/test_equality.rb +0 -84
- data/test/nodes/test_extract.rb +0 -33
- data/test/nodes/test_false.rb +0 -20
- data/test/nodes/test_grouping.rb +0 -25
- data/test/nodes/test_infix_operation.rb +0 -40
- data/test/nodes/test_insert_statement.rb +0 -42
- data/test/nodes/test_named_function.rb +0 -46
- data/test/nodes/test_node.rb +0 -39
- data/test/nodes/test_not.rb +0 -29
- data/test/nodes/test_or.rb +0 -34
- data/test/nodes/test_over.rb +0 -67
- data/test/nodes/test_select_core.rb +0 -69
- data/test/nodes/test_select_statement.rb +0 -49
- data/test/nodes/test_sql_literal.rb +0 -69
- data/test/nodes/test_sum.rb +0 -24
- data/test/nodes/test_table_alias.rb +0 -36
- data/test/nodes/test_true.rb +0 -21
- data/test/nodes/test_update_statement.rb +0 -58
- data/test/nodes/test_window.rb +0 -73
- data/test/support/fake_record.rb +0 -123
- data/test/test_attributes.rb +0 -66
- data/test/test_crud.rb +0 -63
- data/test/test_delete_manager.rb +0 -42
- data/test/test_factory_methods.rb +0 -44
- data/test/test_insert_manager.rb +0 -142
- data/test/test_select_manager.rb +0 -1090
- data/test/test_table.rb +0 -208
- data/test/test_update_manager.rb +0 -124
- data/test/visitors/test_bind_visitor.rb +0 -58
- data/test/visitors/test_depth_first.rb +0 -238
- data/test/visitors/test_dispatch_contamination.rb +0 -22
- data/test/visitors/test_dot.rb +0 -76
- data/test/visitors/test_ibm_db.rb +0 -27
- data/test/visitors/test_informix.rb +0 -52
- data/test/visitors/test_join_sql.rb +0 -42
- data/test/visitors/test_mssql.rb +0 -66
- data/test/visitors/test_mysql.rb +0 -55
- data/test/visitors/test_oracle.rb +0 -156
- data/test/visitors/test_postgres.rb +0 -84
- data/test/visitors/test_sqlite.rb +0 -23
- data/test/visitors/test_to_sql.rb +0 -476
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc3719172683caa3fb2bd15ce6ef3ed477a8b2ef
|
|
4
|
+
data.tar.gz: 74b4f1d7d44871db56c62907ed1c4ba51040bf6e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bedb8b1701ee99fca067c4a52f92d096648d25d67d4809408ede3eeb40298b3fc83a9fdccf1f4195c32f871ee430eba29f11dedd19b12032639a8b03bc7ec54c
|
|
7
|
+
data.tar.gz: 97a3b3155db8b0c57ca4616598d45d8aad7592c56a6882e0e7f5369eeac221802cae5cd60b4d72a92d755eb13be4b39aedc9ea967e1c6faac28953a3414d1f39
|
data/History.txt
CHANGED
|
@@ -1,9 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
=== 9.0.0 / 2017-11-14
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
* Enhancements
|
|
4
|
+
* `InsertManager#insert` is now chainable
|
|
5
|
+
* Support multiple inserts
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
=== 8.0.0 / 2017-02-21
|
|
8
|
+
|
|
9
|
+
* Enhancements
|
|
10
|
+
|
|
11
|
+
* Remove deprecated type casting support in Arel
|
|
12
|
+
* Frozen all string literals in Arel
|
|
13
|
+
|
|
14
|
+
=== 7.1.1 / 2016-07-27
|
|
15
|
+
|
|
16
|
+
* Bug Fixes
|
|
17
|
+
|
|
18
|
+
* Fix warning in `Casted#hash`
|
|
19
|
+
|
|
20
|
+
=== 7.1.0 / 2016-07-19
|
|
21
|
+
|
|
22
|
+
* Enhancements
|
|
23
|
+
|
|
24
|
+
* Support Ruby 2.4 unified Integer class
|
|
25
|
+
* Implement `CASE` conditional expression
|
|
26
|
+
* Support for Bitwise Operations as `InfixOperations`
|
|
27
|
+
|
|
28
|
+
=== 7.0.0 / 2015-12-17
|
|
29
|
+
|
|
30
|
+
* Enhancements
|
|
31
|
+
|
|
32
|
+
* Remove deprecated method `Table#primary_key`
|
|
33
|
+
* Remove engine from the constructor arguments `Arel::Table`
|
|
34
|
+
* Deprecate automatic type casting within Arel
|
|
35
|
+
|
|
36
|
+
=== 6.0.0 / 2014-11-25
|
|
37
|
+
|
|
38
|
+
* Enhancements
|
|
39
|
+
|
|
40
|
+
* Remove deprecated `Arel::Expression`
|
|
41
|
+
* Remove deprecated `Arel::SqlLiteral`
|
|
42
|
+
* Remove deprecated `SelectManager#joins`
|
|
43
|
+
* Remove deprecated `SelectManager#to_a`
|
|
44
|
+
* Remove deprecated `Arel::Sql::Engine`
|
|
45
|
+
* Remove deprecated `Arel::InnerJoin` constant
|
|
46
|
+
* Remove deprecated `Arel::OuterJoin` constant
|
|
7
47
|
|
|
8
48
|
== 5.0.0 / 2013-12-04
|
|
9
49
|
|
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
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Arel
|
|
2
|
+
|
|
3
|
+
* http://github.com/rails/arel
|
|
4
|
+
* [API Documentation](http://www.rubydoc.info/github/rails/arel)
|
|
5
|
+
|
|
6
|
+
## DESCRIPTION
|
|
7
|
+
|
|
8
|
+
Arel Really Exasperates Logicians
|
|
9
|
+
|
|
10
|
+
Arel is a SQL AST manager for Ruby. It
|
|
11
|
+
|
|
12
|
+
1. simplifies the generation of complex SQL queries, and
|
|
13
|
+
2. adapts to various RDBMSes.
|
|
14
|
+
|
|
15
|
+
It is intended to be a framework framework; that is, you can build your own ORM
|
|
16
|
+
with it, focusing on innovative object and collection modeling as opposed to
|
|
17
|
+
database compatibility and query generation.
|
|
18
|
+
|
|
19
|
+
## Status
|
|
20
|
+
|
|
21
|
+
For the moment, Arel uses Active Record's connection adapters to connect to the various engines and perform connection pooling, quoting, and type conversion.
|
|
22
|
+
|
|
23
|
+
## A Gentle Introduction
|
|
24
|
+
|
|
25
|
+
Generating a query with Arel is simple. For example, in order to produce
|
|
26
|
+
|
|
27
|
+
```sql
|
|
28
|
+
SELECT * FROM users
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
you construct a table relation and convert it to SQL:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
users = Arel::Table.new(:users)
|
|
35
|
+
query = users.project(Arel.sql('*'))
|
|
36
|
+
query.to_sql
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### More Sophisticated Queries
|
|
40
|
+
|
|
41
|
+
Here is a whirlwind tour through the most common SQL operators. These will probably cover 80% of all interaction with the database.
|
|
42
|
+
|
|
43
|
+
First is the 'restriction' operator, `where`:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
users.where(users[:name].eq('amy'))
|
|
47
|
+
# => SELECT * FROM users WHERE users.name = 'amy'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
What would, in SQL, be part of the `SELECT` clause is called in Arel a `projection`:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
users.project(users[:id])
|
|
54
|
+
# => SELECT users.id FROM users
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Comparison operators `=`, `!=`, `<`, `>`, `<=`, `>=`, `IN`:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
users.where(users[:age].eq(10)).project(Arel.sql('*'))
|
|
61
|
+
# => SELECT * FROM "users" WHERE "users"."age" = 10
|
|
62
|
+
|
|
63
|
+
users.where(users[:age].not_eq(10)).project(Arel.sql('*'))
|
|
64
|
+
# => SELECT * FROM "users" WHERE "users"."age" != 10
|
|
65
|
+
|
|
66
|
+
users.where(users[:age].lt(10)).project(Arel.sql('*'))
|
|
67
|
+
# => SELECT * FROM "users" WHERE "users"."age" < 10
|
|
68
|
+
|
|
69
|
+
users.where(users[:age].gt(10)).project(Arel.sql('*'))
|
|
70
|
+
# => SELECT * FROM "users" WHERE "users"."age" > 10
|
|
71
|
+
|
|
72
|
+
users.where(users[:age].lteq(10)).project(Arel.sql('*'))
|
|
73
|
+
# => SELECT * FROM "users" WHERE "users"."age" <= 10
|
|
74
|
+
|
|
75
|
+
users.where(users[:age].gteq(10)).project(Arel.sql('*'))
|
|
76
|
+
# => SELECT * FROM "users" WHERE "users"."age" >= 10
|
|
77
|
+
|
|
78
|
+
users.where(users[:age].in([20, 16, 17])).project(Arel.sql('*'))
|
|
79
|
+
# => SELECT * FROM "users" WHERE "users"."age" IN (20, 16, 17)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Bitwise operators `&`, `|`, `^`, `<<`, `>>`:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
users.where((users[:bitmap] & 16).gt(0)).project(Arel.sql('*'))
|
|
86
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" & 16) > 0
|
|
87
|
+
|
|
88
|
+
users.where((users[:bitmap] | 16).gt(0)).project(Arel.sql('*'))
|
|
89
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" | 16) > 0
|
|
90
|
+
|
|
91
|
+
users.where((users[:bitmap] ^ 16).gt(0)).project(Arel.sql('*'))
|
|
92
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" ^ 16) > 0
|
|
93
|
+
|
|
94
|
+
users.where((users[:bitmap] << 1).gt(0)).project(Arel.sql('*'))
|
|
95
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" << 1) > 0
|
|
96
|
+
|
|
97
|
+
users.where((users[:bitmap] >> 1).gt(0)).project(Arel.sql('*'))
|
|
98
|
+
# => SELECT * FROM "users" WHERE ("users"."bitmap" >> 1) > 0
|
|
99
|
+
|
|
100
|
+
users.where((~ users[:bitmap]).gt(0)).project(Arel.sql('*'))
|
|
101
|
+
# => SELECT * FROM "users" WHERE ~ "users"."bitmap" > 0
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Joins resemble SQL strongly:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
users.join(photos).on(users[:id].eq(photos[:user_id]))
|
|
108
|
+
# => SELECT * FROM users INNER JOIN photos ON users.id = photos.user_id
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Left joins:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
users.join(photos, Arel::Nodes::OuterJoin).on(users[:id].eq(photos[:user_id]))
|
|
115
|
+
# => SELECT FROM users LEFT OUTER JOIN photos ON users.id = photos.user_id
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
What are called `LIMIT` and `OFFSET` in SQL are called `take` and `skip` in Arel:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
users.take(5) # => SELECT * FROM users LIMIT 5
|
|
122
|
+
users.skip(4) # => SELECT * FROM users OFFSET 4
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`GROUP BY` is called `group`:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
users.project(users[:name]).group(users[:name])
|
|
129
|
+
# => SELECT users.name FROM users GROUP BY users.name
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The best property of Arel is its "composability," or closure under all operations. For example, to restrict AND project, just "chain" the method invocations:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
users \
|
|
136
|
+
.where(users[:name].eq('amy')) \
|
|
137
|
+
.project(users[:id]) \
|
|
138
|
+
# => SELECT users.id FROM users WHERE users.name = 'amy'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
All operators are chainable in this way, and they are chainable any number of times, in any order.
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
users.where(users[:name].eq('bob')).where(users[:age].lt(25))
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The `OR` operator works like this:
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
users.where(users[:name].eq('bob').or(users[:age].lt(25)))
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The `AND` operator behaves similarly. Here is an example of the `DISTINCT` operator:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
posts = Arel::Table.new(:posts)
|
|
157
|
+
posts.project(posts[:title])
|
|
158
|
+
posts.distinct
|
|
159
|
+
posts.to_sql # => 'SELECT DISTINCT "posts"."title" FROM "posts"'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Aggregate functions `AVG`, `SUM`, `COUNT`, `MIN`, `MAX`, `HAVING`:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
photos.group(photos[:user_id]).having(photos[:id].count.gt(5))
|
|
166
|
+
# => SELECT FROM photos GROUP BY photos.user_id HAVING COUNT(photos.id) > 5
|
|
167
|
+
|
|
168
|
+
users.project(users[:age].sum)
|
|
169
|
+
# => SELECT SUM(users.age) FROM users
|
|
170
|
+
|
|
171
|
+
users.project(users[:age].average)
|
|
172
|
+
# => SELECT AVG(users.age) FROM users
|
|
173
|
+
|
|
174
|
+
users.project(users[:age].maximum)
|
|
175
|
+
# => SELECT MAX(users.age) FROM users
|
|
176
|
+
|
|
177
|
+
users.project(users[:age].minimum)
|
|
178
|
+
# => SELECT MIN(users.age) FROM users
|
|
179
|
+
|
|
180
|
+
users.project(users[:age].count)
|
|
181
|
+
# => SELECT COUNT(users.age) FROM users
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Aliasing Aggregate Functions:
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
users.project(users[:age].average.as("mean_age"))
|
|
188
|
+
# => SELECT AVG(users.age) AS mean_age FROM users
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### The Advanced Features
|
|
192
|
+
|
|
193
|
+
The examples above are fairly simple and other libraries match or come close to matching the expressiveness of Arel (e.g. `Sequel` in Ruby).
|
|
194
|
+
|
|
195
|
+
#### Inline math operations
|
|
196
|
+
|
|
197
|
+
Suppose we have a table `products` with prices in different currencies. And we have a table `currency_rates`, of constantly changing currency rates. In Arel:
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
products = Arel::Table.new(:products)
|
|
201
|
+
# Attributes: [:id, :name, :price, :currency_id]
|
|
202
|
+
|
|
203
|
+
currency_rates = Arel::Table.new(:currency_rates)
|
|
204
|
+
# Attributes: [:from_id, :to_id, :date, :rate]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Now, to order products by price in user preferred currency simply call:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
products.
|
|
211
|
+
join(:currency_rates).on(products[:currency_id].eq(currency_rates[:from_id])).
|
|
212
|
+
where(currency_rates[:to_id].eq(user_preferred_currency), currency_rates[:date].eq(Date.today)).
|
|
213
|
+
order(products[:price] * currency_rates[:rate])
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Complex Joins
|
|
217
|
+
|
|
218
|
+
##### Alias
|
|
219
|
+
Where Arel really shines is in its ability to handle complex joins and aggregations. As a first example, let's consider an "adjacency list", a tree represented in a table. Suppose we have a table `comments`, representing a threaded discussion:
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
comments = Arel::Table.new(:comments)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
And this table has the following attributes:
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
# [:id, :body, :parent_id]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The `parent_id` column is a foreign key from the `comments` table to itself.
|
|
232
|
+
Joining a table to itself requires aliasing in SQL. This aliasing can be handled from Arel as below:
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
replies = comments.alias
|
|
236
|
+
comments_with_replies = \
|
|
237
|
+
comments.join(replies).on(replies[:parent_id].eq(comments[:id])).where(comments[:id].eq(1))
|
|
238
|
+
# => SELECT * FROM comments INNER JOIN comments AS comments_2
|
|
239
|
+
# WHERE comments_2.parent_id = comments.id AND comments.id = 1
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
This will return the reply for the first comment.
|
|
243
|
+
|
|
244
|
+
##### CTE
|
|
245
|
+
[Common Table Expressions (CTE)](https://en.wikipedia.org/wiki/Common_table_expressions#Common_table_expression) support via:
|
|
246
|
+
|
|
247
|
+
Create a `CTE`
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
cte_table = Arel::Table.new(:cte_table)
|
|
251
|
+
composed_cte = Arel::Nodes::As.new(cte_table, photos.where(photos[:created_at].gt(Date.current)))
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Use the created `CTE`:
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
users.
|
|
258
|
+
join(cte_table).on(users[:id].eq(cte_table[:user_id])).
|
|
259
|
+
project(users[:id], cte_table[:click].sum).
|
|
260
|
+
with(composed_cte)
|
|
261
|
+
|
|
262
|
+
# => WITH cte_table AS (SELECT FROM photos WHERE photos.created_at > '2014-05-02')
|
|
263
|
+
# SELECT users.id, SUM(cte_table.click)
|
|
264
|
+
# FROM users INNER JOIN cte_table ON users.id = cte_table.user_id
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
#### Write SQL strings
|
|
268
|
+
When your query is too complex for `Arel`, you can use `Arel::SqlLiteral`:
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
photo_clicks = Arel::Nodes::SqlLiteral.new(<<-SQL
|
|
272
|
+
CASE WHEN condition1 THEN calculation1
|
|
273
|
+
WHEN condition2 THEN calculation2
|
|
274
|
+
WHEN condition3 THEN calculation3
|
|
275
|
+
ELSE default_calculation END
|
|
276
|
+
SQL
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
photos.project(photo_clicks.as("photo_clicks"))
|
|
280
|
+
# => SELECT CASE WHEN condition1 THEN calculation1
|
|
281
|
+
# WHEN condition2 THEN calculation2
|
|
282
|
+
# WHEN condition3 THEN calculation3
|
|
283
|
+
# ELSE default_calculation END
|
|
284
|
+
# FROM "photos"
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Contributing to Arel
|
|
288
|
+
|
|
289
|
+
Arel is the work of many contributors. You're encouraged to submit pull requests, propose
|
|
290
|
+
features and discuss issues.
|
|
291
|
+
|
|
292
|
+
See [CONTRIBUTING](CONTRIBUTING.md).
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
Arel is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Arel
|
|
2
3
|
module Attributes
|
|
3
4
|
class Attribute < Struct.new :relation, :name
|
|
@@ -12,6 +13,14 @@ module Arel
|
|
|
12
13
|
def lower
|
|
13
14
|
relation.lower self
|
|
14
15
|
end
|
|
16
|
+
|
|
17
|
+
def type_cast_for_database(value)
|
|
18
|
+
relation.type_cast_for_database(name, value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def able_to_type_cast?
|
|
22
|
+
relation.able_to_type_cast?
|
|
23
|
+
end
|
|
15
24
|
end
|
|
16
25
|
|
|
17
26
|
class String < Attribute; end
|
data/lib/arel/attributes.rb
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arel
|
|
4
|
+
module Collectors
|
|
5
|
+
class Bind
|
|
6
|
+
def initialize
|
|
7
|
+
@binds = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def << str
|
|
11
|
+
self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add_bind bind
|
|
15
|
+
@binds << bind
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def value
|
|
20
|
+
@binds
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arel
|
|
4
|
+
module Collectors
|
|
5
|
+
class Composite
|
|
6
|
+
def initialize(left, right)
|
|
7
|
+
@left = left
|
|
8
|
+
@right = right
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def << str
|
|
12
|
+
left << str
|
|
13
|
+
right << str
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_bind bind, &block
|
|
18
|
+
left.add_bind bind, &block
|
|
19
|
+
right.add_bind bind, &block
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def value
|
|
24
|
+
[left.value, right.value]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
attr_reader :left, :right
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'arel/collectors/plain_string'
|
|
4
|
+
|
|
5
|
+
module Arel
|
|
6
|
+
module Collectors
|
|
7
|
+
class SQLString < PlainString
|
|
8
|
+
def initialize(*)
|
|
9
|
+
super
|
|
10
|
+
@bind_index = 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add_bind bind
|
|
14
|
+
self << yield(@bind_index)
|
|
15
|
+
@bind_index += 1
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def compile bvs
|
|
20
|
+
value
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Arel
|
|
3
|
+
module Collectors
|
|
4
|
+
class SubstituteBinds
|
|
5
|
+
def initialize(quoter, delegate_collector)
|
|
6
|
+
@quoter = quoter
|
|
7
|
+
@delegate = delegate_collector
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def << str
|
|
11
|
+
delegate << str
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_bind bind
|
|
16
|
+
self << quoter.quote(bind)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def value
|
|
20
|
+
delegate.value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
attr_reader :quoter, :delegate
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/arel/crud.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Arel
|
|
2
3
|
###
|
|
3
4
|
# FIXME hopefully we can remove this
|
|
4
5
|
module Crud
|
|
5
6
|
def compile_update values, pk
|
|
6
|
-
um = UpdateManager.new
|
|
7
|
+
um = UpdateManager.new
|
|
7
8
|
|
|
8
9
|
if Nodes::SqlLiteral === values
|
|
9
10
|
relation = @ctx.from
|
|
@@ -26,11 +27,12 @@ module Arel
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def create_insert
|
|
29
|
-
InsertManager.new
|
|
30
|
+
InsertManager.new
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def compile_delete
|
|
33
|
-
dm = DeleteManager.new
|
|
34
|
+
dm = DeleteManager.new
|
|
35
|
+
dm.take @ast.limit.expr if @ast.limit
|
|
34
36
|
dm.wheres = @ctx.wheres
|
|
35
37
|
dm.from @ctx.froms
|
|
36
38
|
dm
|
data/lib/arel/delete_manager.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Arel
|
|
2
3
|
class DeleteManager < Arel::TreeManager
|
|
3
|
-
def initialize
|
|
4
|
+
def initialize
|
|
4
5
|
super
|
|
5
6
|
@ast = Nodes::DeleteStatement.new
|
|
6
7
|
@ctx = @ast
|
|
@@ -11,6 +12,11 @@ module Arel
|
|
|
11
12
|
self
|
|
12
13
|
end
|
|
13
14
|
|
|
15
|
+
def take limit
|
|
16
|
+
@ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) if limit
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
14
20
|
def wheres= list
|
|
15
21
|
@ast.wheres = list
|
|
16
22
|
end
|
data/lib/arel/errors.rb
ADDED
data/lib/arel/expressions.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Arel
|
|
2
3
|
module Expressions
|
|
3
4
|
def count distinct = false
|
|
@@ -5,23 +6,24 @@ module Arel
|
|
|
5
6
|
end
|
|
6
7
|
|
|
7
8
|
def sum
|
|
8
|
-
Nodes::Sum.new [self]
|
|
9
|
+
Nodes::Sum.new [self]
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def maximum
|
|
12
|
-
Nodes::Max.new [self]
|
|
13
|
+
Nodes::Max.new [self]
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def minimum
|
|
16
|
-
Nodes::Min.new [self]
|
|
17
|
+
Nodes::Min.new [self]
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def average
|
|
20
|
-
Nodes::Avg.new [self]
|
|
21
|
+
Nodes::Avg.new [self]
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def extract field
|
|
24
25
|
Nodes::Extract.new [self], field
|
|
25
26
|
end
|
|
27
|
+
|
|
26
28
|
end
|
|
27
29
|
end
|
data/lib/arel/factory_methods.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Arel
|
|
2
3
|
###
|
|
3
4
|
# Methods for creating various nodes
|
|
@@ -37,7 +38,7 @@ module Arel
|
|
|
37
38
|
###
|
|
38
39
|
# Create a LOWER() function
|
|
39
40
|
def lower column
|
|
40
|
-
Nodes::NamedFunction.new 'LOWER', [column]
|
|
41
|
+
Nodes::NamedFunction.new 'LOWER', [Nodes.build_quoted(column)]
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
end
|
data/lib/arel/insert_manager.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Arel
|
|
2
3
|
class InsertManager < Arel::TreeManager
|
|
3
|
-
def initialize
|
|
4
|
+
def initialize
|
|
4
5
|
super
|
|
5
6
|
@ast = Nodes::InsertStatement.new
|
|
6
7
|
end
|
|
@@ -13,11 +14,15 @@ module Arel
|
|
|
13
14
|
def columns; @ast.columns end
|
|
14
15
|
def values= val; @ast.values = val; end
|
|
15
16
|
|
|
17
|
+
def select select
|
|
18
|
+
@ast.select = select
|
|
19
|
+
end
|
|
20
|
+
|
|
16
21
|
def insert fields
|
|
17
22
|
return if fields.empty?
|
|
18
23
|
|
|
19
24
|
if String === fields
|
|
20
|
-
@ast.values = SqlLiteral.new(fields)
|
|
25
|
+
@ast.values = Nodes::SqlLiteral.new(fields)
|
|
21
26
|
else
|
|
22
27
|
@ast.relation ||= fields.first.first.relation
|
|
23
28
|
|
|
@@ -29,10 +34,15 @@ module Arel
|
|
|
29
34
|
end
|
|
30
35
|
@ast.values = create_values values, @ast.columns
|
|
31
36
|
end
|
|
37
|
+
self
|
|
32
38
|
end
|
|
33
39
|
|
|
34
40
|
def create_values values, columns
|
|
35
41
|
Nodes::Values.new values, columns
|
|
36
42
|
end
|
|
43
|
+
|
|
44
|
+
def create_values_list(rows)
|
|
45
|
+
Nodes::ValuesList.new(rows)
|
|
46
|
+
end
|
|
37
47
|
end
|
|
38
48
|
end
|