aboutsummaryrefslogtreecommitdiffstats
path: root/README
blob: efd379d7840c286170fddc7b3cc8e76fbc0e5c58 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
== Abstract ==

Arel is a Relational Algebra for Ruby. It 1) simplifies the generation complex of SQL queries and it 2) transparently adapts to various RDBMS systems. It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.

== A Gentle Introduction ==

Generating a query with ARel is simple. For example, in order to produce

    SELECT * FROM users
   
you construct a table relation and convert it to sql:

    Arel::Table.new(:users).to_sql
   
In fact, you will probably never call `#to_sql`. Let `users = Arel::Table.new(:users)`. Rather, you'll work with data from the table directly. You can iterate through all rows in the `users` table like this:

    users.each { |user| ... }
    
In other words, Arel relations behave implement Ruby's Eunmerable interface. Let's have a look at a concrete example:

    users.first # => {'id' => 10, 'name' => 'bob'}
    
As you can see, Arel converts the rows from the database into a hash, the values of which are sublimated to the appropriate Ruby primitive (integers, strings, and so forth).

== Relational Algebra ==

Arel is based on the Relational Algebra, a mathematical model that is also the inspiration for relational databases. Arel::Relation objects do not represent queries per se (i.e., they are not object-representations of `SELECT`, `INSERT`, `UPDATE`, or `DELETE` statements), rather they represent a collection of data that you can select from, insert into, update, and delete. For example, to insert a row into the users table, do the following:

    users.insert({users[:name] => 'amy'}) # => INSERT INTO users (users.name) VALUES ('amy')
    
To delete all users:

    users.delete # => DELETE FROM users
    
To update:

    users.update({users[:name] => 'carl'}) # => UPDATE users SET name = 'carl'
    
As you can see, the `relation` named `users` does not represent an individual query; rather it is an abstraction on a collection of data and it can produce appropriate SQL queries to do the various CRUD operations.

=== More Sophisticated <strike>Queries</strike> Relations ===

Following the Relational Algebra, Arel's interface uses some jargon that differs from standard SQL. For example, in order to add a `WHERE` clause to your relations, you use the `select` operation:

    users.select(users[:name].eq('amy')) # => SELECT * FROM users WHERE users.name = 'amy'

What would, in SQL, be part of the `SELECT` clause is called here a `projection`:

    users.project(users[:id]) # => SELECT users.id FROM users
    
Joins are fairly straightforward:

    users.join(photos).on(users[:id].eq(photos[:user_id])) => SELECT * FROM users INNER JOIN photos ON users.id = photos.user_id

The best property of the Relational is compositionality, or closure under all operations. For example, to select and project:

    users                                 \
      .select(users[:name].eq('amy')) \
      .project(users[:id])                \
    # => SELECT users.id FROM users WHERE users.name = 'amy'


== Contributions ==

I appreciate all contributions to Arel. There is only one "unusual" requirement I have concerning code style: all specs should be written without mocks, using concrete examples to elicit testable behavior. This has two benefits: it 1) ensures the tests serve as concrete documentation and 2) suits the functional nature of this library, which emphasizes algebraic transformation rather than decoupled components.