== Sequel: Concise ORM for Ruby

Sequel is an ORM framework for Ruby. Sequel provides thread safety, connection pooling, and a concise DSL for constructing queries and table schemas.

Sequel makes it easy to deal with multiple records without having to break your teeth on SQL.

== Resources

* {Project page}[http://code.google.com/p/ruby-sequel/]
* {Source code}[http://ruby-sequel.googlecode.com/svn/]
* {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
* {RubyForge page}[http://rubyforge.org/projects/sequel/]

To check out the source code:
  
  svn co http://ruby-sequel.googlecode.com/svn/trunk
  
=== Contact

If you have any comments or suggestions please send an email to ciconia at gmail.com and I'll get back to you.

== Installation

  sudo gem install sequel
  
== Supported Databases

Sequel currently supports:

* Postgresql
* SQLite 3
* MySQL (preliminary support)

== The Sequel Console

Sequel includes an IRB console for quick'n'dirty access to databases. You can use it like this:

  sequel sqlite:///test.db

You get an IRB session with the database object stored in DB.

== A Short Tutorial

=== Connecting to a database

Before connecting to a database, you should require the corresponding adaptor, for example:

  require 'sequel/sqlite'

Note: you don't need to require 'sequel' separately before that, as each adapter requires 'sequel' if it hasn't yet been required.

There are two ways to create a connection to a database. The easier way is to provide a connection URL:

  DB = Sequel.open("sqlite:///blog.db")

You can also specify optional parameters, such as the connection pool size:

  DB = Sequel.open("postgres://postgres:postgres@localhost/my_db",
    :max_connections => 10)

The second, more verbose, way is to create an instance of a database class:

  DB = Sequel::Postgres::Database.new(:database => 'my_db', :host => 'localhost')

=== Arbitrary SQL queries

  DB.execute("create table t (a text, b text)")
  DB.execute("insert into t values ('a', 'b')")

Or more succinctly:

  DB << "create table t (a text, b text)"
  DB << "insert into t values ('a', 'b')"

=== Creating Datasets

Dataset is the primary means through which records are retrieved and manipulated. You can create an blank dataset by using the dataset method:

  dataset = DB.dataset

Or by using the from methods:

  posts = DB.from(:posts)

You can also use the equivalent shorthand:

  posts = DB[:posts]

Note: the dataset will only fetch records when you explicitly ask for them, as will be shown below. Datasets can be manipulated to filter through records, change record order and even join tables, as will also be shown below.

=== Retrieving Records

You can retrieve records by using the all method:

  posts.all

The all method returns an array of hashes, where each hash corresponds to a record.

You can also iterate through records one at a time:

  posts.each {|row| p row}

Or perform more advanced stuff:

  posts.map(:id)
  posts.inject({}) {|h, r| h[r[:id]] = r[:name]}
  
You can also retrieve the first record in a dataset:

  posts.first
  
Or retrieve a single record with a specific value:

  posts[:id => 1]
  
If the dataset is ordered, you can also ask for the last record:

  posts.order(:stamp).last
  
=== Filtering Records

The simplest way to filter records is to provide a hash of values to match:

  my_posts = posts.filter(:category => 'ruby', :author => 'david')
  
You can also specify ranges:

  my_posts = posts.filter(:stamp => (2.weeks.ago)..(1.week.ago))
  
Or lists of values:

  my_posts = posts.filter(:category => ['ruby', 'postgres', 'linux'])
  
Some adapters (like postgresql) will also let you specify Regexps:

  my_posts = posts.filter(:category => /ruby/i)
  
You can also use an inverse filter:

  my_posts = posts.exclude(:category => /ruby/i)

You can then retrieve the records by using any of the retrieval methods:

  my_posts.each {|row| p row}
  
You can also specify a custom WHERE clause:

  posts.filter('(stamp < ?) AND (author <> ?)', 3.days.ago, author_name)
  
=== Summarizing Records

Counting records is easy:
  posts.filter(:category => /ruby/i).count

And you can also query maximum/minimum values:
  max_value = DB[:history].max(:value)
  
Or calculate a sum:
  total = DB[:items].sum(:price)
  
=== Ordering Records

  posts.order(:stamp)
  
You can also specify descending order

  posts.order(:stamp.DESC)

=== Deleting Records

  posts.filter('stamp < ?', 3.days.ago).delete
  
=== Inserting Records

  posts.insert(:category => 'ruby', :author => 'david')
  
Or alternatively:

  posts << {:category => 'ruby', :author => 'david'}
  
=== Updating Records

  posts.filter('stamp < ?', 3.days.ago).update(:state => 'archived')

=== Joining Tables

Joining is very useful in a variety of scenarios, for example many-to-many relationships. With Sequel it's really easy:

  order_items = DB[:items].join(:order_items, :item_id => :id).
    filter(:order_items__order_id => 1234)
  
This is equivalent to the SQL:

  SELECT * FROM items LEFT OUTER JOIN order_items
  ON order_items.item_id = items.id 
  WHERE order_items.order_id = 1234

You can then do anything you like with the dataset:

  order_total = order_items.sum(:price)
  
Which is equivalent to the SQL:

  SELECT sum(price) FROM items LEFT OUTER JOIN order_items
  ON order_items.item_id = items.id
  WHERE order_items.order_id = 1234