<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>vendor/ruby-sequel/lib/sequel/array_keys.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/lib/sequel/model/caching.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/spec/array_keys_spec.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/stress/access_perf.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/stress/hashes_vs_array_keys.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/stress/mem_array.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/stress/mem_array_fields.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/stress/mem_array_keys.rb</filename>
    </added>
    <added>
      <filename>vendor/ruby-sequel/stress/mem_hashes.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,137 @@
 === SVN
 
+* Implemented custom row fetchers in SQLite adapter.
+
+=== 0.3.0.1 (2007-10-20)
+
+* Changed Database#fetch to return a modified dataset.
+
+=== 0.3 (2007-10-20)
+
+* Added stock transforms to Dataset#transform. Refactored Model.serialize.
+
+* Added Database#logger= method for setting the database logger object.
+
+* Fixed Model.[] to act as shortcut to Model.find when a hash is given (#71).
+
+* Added support for old and new decimal types in MySQL adapter, and updated MYSQL_TYPES with MySQL 5.0 constants (#72).
+
+* Implemented Database#disconnect method for all adapters.
+
+* Fixed small bug in ArrayKeys module.
+
+* Implemented model caching by primary key.
+
+* Separated Model.find and Model.[] functionality. Model.find takes a filter. Model.[] is strictly for finding by primary keys.
+
+* Enhanced Dataset#first to accept a filter block. Model#find can also now accept a filter block.
+
+* Changed Database#[] to act as shortcut to #fetch if a string is given.
+
+* Renamed Database#each to #fetch. If no block is given, the method returns an enumerator.
+
+* Changed Dataset#join methods to correctly literalize values in join conditions (#70).
+
+* Fixed #filter with ranges to correctly literalize field names (#69).
+
+* Implemented Database#each method for quickly retrieving records with arbitrary SQL (thanks Aman Gupta).
+
+* Fixed bug in postgres adapter where a LiteralString would be literalized as a regular String.
+
+* Fixed SQLite insert with subquery (#68).
+
+* Reverted back to hashes as default mode. Added Sequel.use_array_tuples and Sequel.use_hash_tuples methods.
+
+* Fixed problem with arrays with keys when using #delete.
+
+* Implemented ArrayKeys as substitute for ArrayFields.
+
+* Added Dataset#each_hash method.
+
+* Rewrote SQLite::Database#transaction to use sqlite3-ruby library implementation of transactions.
+
+* Fixed Model.destroy_all to work correctly in cases where no before_destroy hook is defined and an after_destroy hook is defined.
+
+* Restored Model.has_hooks? implementation.
+
+* Changed Database#&lt;&lt; to strip comments and whitespace only when an array is given.
+
+* Changed Schema::Generator#primary_key to accept calls with the type argument omitted.
+
+* Hooks can now be prepended or appended by choice.
+
+* Changed Model.subset to define filter method on the underlying dataset instead of the model class.
+
+* Fixed Dataset#transform to work with array fields.
+
+* Added Dataset#to_csv method.
+
+* PrettyTable can now extract column names from arrayfields.
+
+* Converted ado, dbi, odbc adapters to use arrayfields instead of hashes.
+
+* Fixed composite key support.
+
+* Fixed Dataset#insert_sql, update_sql to support array fields.
+
+* Converted sqlite, mysql, postgres adapters to use arrayfields instead of hashes.
+
+* Extended Dataset#from to auto alias sub-queries.
+
+* Extended Dataset#from to accept hash for aliasing tables.
+
+* Added before_update, after_update hooks.
+
+=== 0.2.1.1 (2007-10-07)
+
+* Added Date literalization to sqlite adapter (#60).
+
+* Changed Model.serialize to allow calling it after the class is defined (#59).
+
+* Fixed after_create hooks to allow calling save inside the hook (#58).
+
+* Fixed MySQL quoting of sql functions (#57).
+
+* Implemented rollback! global method for cancelling transactions in progress.
+
+* Fixed =~ operator in Sequelizer.
+
+* Fixed ODBC::Dataset#fetch_rows (thanks Dusty).
+
+* Renamed Model.recreate_table to create_table!. recreate_table is deprecated and will issue a warning (#56).
+
+=== 0.2.1 (2007-09-24)
+
+* Added default implementation of Model.primary_key_hash.
+
+* Fixed Sequel::Model() to set dataset for inherited classes.
+
+* Rewrote Model.serialize to use Dataset#transform.
+
+* Implemented Dataset#transform.
+
+* Added gem spec for Windows (without ParseTree dependency).
+
+* Added support for dynamic strings in Sequelizer (#49).
+
+* Query branch merged into trunk.
+
+* Implemented self-changing methods.
+
+* Add support for ternary operator to Sequelizer.
+
+* Fixed sequelizer to evaluate expressions if they don't involve symbols or literal strings.
+
+* Added protection against using #each, #delete, #insert, #update inside query blocks.
+
+* Improved Model#method_missing to deal with invalid attributes.
+
+* Implemented Dataset#query.
+
+* Added Dataset#group_by as alias for Dataset#group.
+
+* Added Dataset#order_by as alias for Dataset#order.
+
 * More model refactoring. Added support for composite keys.
 
 * Added Dataset#empty? method (#46).</diff>
      <filename>vendor/ruby-sequel/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,3 @@
-=== 0.1.9
-
-* Rewrite schema code to allow adapter-specific behavior.
-
 === 0.2.0
 
 * Overhaul of Model code.</diff>
      <filename>vendor/ruby-sequel/ROADMAP</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@ require 'fileutils'
 include FileUtils
 
 NAME = &quot;sequel&quot;
-VERS = &quot;0.2.0.2&quot;
+VERS = &quot;0.3.0.1&quot;
 CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
 RDOC_OPTS = ['--quiet', '--title', &quot;Sequel: Concise ORM for Ruby&quot;,
   &quot;--opname&quot;, &quot;index.html&quot;,
@@ -55,11 +55,41 @@ spec = Gem::Specification.new do |s|
   s.bindir = &quot;bin&quot;
 end
 
+win_spec = Gem::Specification.new do |s|
+  s.name = NAME
+  s.version = VERS
+  s.platform = Gem::Platform::WIN32
+  s.has_rdoc = true
+  s.extra_rdoc_files = [&quot;README&quot;, &quot;CHANGELOG&quot;, &quot;COPYING&quot;]
+  s.rdoc_options += RDOC_OPTS + 
+    ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/sequel.rb']
+  s.summary = &quot;Lightweight ORM library for Ruby&quot;
+  s.description = s.summary
+  s.author = &quot;Sharon Rosner&quot;
+  s.email = 'ciconia@gmail.com'
+  s.homepage = 'http://sequel.rubyforge.org'
+  s.executables = ['sequel']
+
+  s.add_dependency('metaid')
+  
+  s.required_ruby_version = '&gt;= 1.8.4'
+
+  s.files = %w(COPYING README Rakefile) + Dir.glob(&quot;{bin,doc,spec,lib}/**/*&quot;)
+      
+  s.require_path = &quot;lib&quot;
+  s.bindir = &quot;bin&quot;
+end
+
 Rake::GemPackageTask.new(spec) do |p|
   p.need_tar = true
   p.gem_spec = spec
 end
 
+Rake::GemPackageTask.new(win_spec) do |p|
+  p.need_tar = true
+  p.gem_spec = win_spec
+end
+
 task :install do
   sh %{rake package}
   sh %{sudo gem install pkg/#{NAME}-#{VERS}}
@@ -88,6 +118,11 @@ Spec::Rake::SpecTask.new('spec') do |t|
   t.rcov = true
 end
 
+desc &quot;Run specs without coverage&quot;
+Spec::Rake::SpecTask.new('spec_no_cov') do |t|
+  t.spec_files = FileList['spec/*_spec.rb']
+end
+
 desc &quot;Run adapter specs without coverage&quot;
 Spec::Rake::SpecTask.new('spec_adapters') do |t|
   t.spec_files = FileList['spec/adapters/*_spec.rb']</diff>
      <filename>vendor/ruby-sequel/Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,37 +1,37 @@
-require 'metaid'
-
-files = %w[
-  core_ext error connection_pool pretty_table
-  dataset migration model schema database 
-]
-dir = File.join(File.dirname(__FILE__), 'sequel')
-files.each {|f| require(File.join(dir, f))}
-
-module Sequel #:nodoc:
-  class &lt;&lt; self
-    # call-seq:
-    #   Sequel::Database.connect(conn_string)
-    #   Sequel.connect(conn_string)
-    #   Sequel.open(conn_string)
-    #
-    # Creates a new database object based on the supplied connection string.
-    # The specified scheme determines the database class used, and the rest
-    # of the string specifies the connection options. For example:
-    #   DB = Sequel.open 'sqlite:///blog.db'
-    def connect(*args)
-      Database.connect(*args)
-    end
-    
-    alias_method :open, :connect
-    
-    def single_threaded=(value)
-      Database.single_threaded = value
-    end
-  end
-end
-
-class Object
-  def Sequel(*args)
-    Sequel.connect(*args)
-  end
-end
\ No newline at end of file
+require 'metaid'
+
+files = %w[
+  core_ext array_keys error connection_pool pretty_table
+  dataset migration model schema database 
+]
+dir = File.join(File.dirname(__FILE__), 'sequel')
+files.each {|f| require(File.join(dir, f))}
+
+module Sequel #:nodoc:
+  class &lt;&lt; self
+    # call-seq:
+    #   Sequel::Database.connect(conn_string)
+    #   Sequel.connect(conn_string)
+    #   Sequel.open(conn_string)
+    #
+    # Creates a new database object based on the supplied connection string.
+    # The specified scheme determines the database class used, and the rest
+    # of the string specifies the connection options. For example:
+    #   DB = Sequel.open 'sqlite:///blog.db'
+    def connect(*args)
+      Database.connect(*args)
+    end
+    
+    alias_method :open, :connect
+    
+    def single_threaded=(value)
+      Database.single_threaded = value
+    end
+  end
+end
+
+class Object
+  def Sequel(*args)
+    Sequel.connect(*args)
+  end
+end</diff>
      <filename>vendor/ruby-sequel/lib/sequel.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,6 +21,10 @@ module Sequel
         handle.Open(dbname)
         handle
       end
+      
+      def disconnect
+        # how do we disconnect? couldn't find anything in the docs
+      end
     
       def dataset(opts = nil)
         ADO::Dataset.new(self, opts)
@@ -63,6 +67,19 @@ module Sequel
         end
       end
     
+      def array_tuples_fetch_rows(sql, &amp;block)
+        @db.synchronize do
+          s = @db.execute sql
+          
+          fields = s.Fields.extend(Enumerable)
+          @columns = fields.map {|x| x.Name.to_sym}
+          
+          s.moveFirst
+          s.getRows.transpose.each {|r| r.fields = @columns; yield r}
+        end
+        self
+      end
+      
       def insert(*values)
         @db.do insert_sql(*values)
       end</diff>
      <filename>vendor/ruby-sequel/lib/sequel/ado.rb</filename>
    </modified>
    <modified>
      <diff>@@ -68,6 +68,14 @@ module Sequel
       raise e.is_a?(StandardError) ? e : e.message
     end
     
+    def disconnect(&amp;block)
+      @mutex.synchronize do
+        @available_connections.each {|c| block[c]} if block
+        @available_connections = []
+        @created_count = @allocated.size
+      end
+    end
+    
     private
       # Returns the connection owned by the supplied thread, if any.
       def owned_connection(thread)
@@ -112,6 +120,7 @@ module Sequel
   # in single-threaded applications. ConnectionPool imposes a substantial
   # performance penalty, so SingleThreadedPool is used to gain some speed.
   class SingleThreadedPool
+    attr_reader :conn
     attr_writer :connection_proc
     
     # Initializes the instance with the supplied block as the connection_proc.
@@ -128,5 +137,10 @@ module Sequel
       # if the error is not a StandardError it is converted into RuntimeError.
       raise e.is_a?(StandardError) ? e : e.message
     end
+    
+    def disconnect(&amp;block)
+      block[@conn] if block &amp;&amp; @conn
+      @conn = nil
+    end
   end
 end</diff>
      <filename>vendor/ruby-sequel/lib/sequel/connection_pool.rb</filename>
    </modified>
    <modified>
      <diff>@@ -70,17 +70,17 @@ end
 module FieldCompositionMethods
   # Constructs a DESC clause for use in an ORDER BY clause.
   def DESC
-    &quot;#{to_field_name} DESC&quot;
+    &quot;#{to_field_name} DESC&quot;.lit
   end
   
   # Constructs an AS clause for field aliasing.
   def AS(target)
-    &quot;#{to_field_name} AS #{target}&quot;
+    &quot;#{to_field_name} AS #{target}&quot;.lit
   end
 
   # Constructs a qualified wildcard (*) clause.
   def ALL
-    &quot;#{to_s}.*&quot;
+    &quot;#{to_s}.*&quot;.lit
   end
   
   FIELD_TITLE_RE1 = /^(.*)\sAS\s(.+)$/i.freeze</diff>
      <filename>vendor/ruby-sequel/lib/sequel/core_ext.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,8 @@ module Sequel
   # The Database class is meant to be subclassed by database adapters in order
   # to provide the functionality needed for executing queries.
   class Database
-    attr_reader :opts, :pool, :logger
+    attr_reader :opts, :pool
+    attr_accessor :logger
     
     # Constructs a new instance of a database connection with the specified
     # options hash.
@@ -32,6 +33,10 @@ module Sequel
       raise NotImplementedError, &quot;#connect should be overriden by adapters&quot;
     end
     
+    def disconnect
+      raise NotImplementedError, &quot;#disconnect should be overriden by adapters&quot;
+    end
+    
     def multi_threaded?
       !@single_threaded
     end
@@ -60,9 +65,28 @@ module Sequel
     
     # Returns a blank dataset
     def dataset
-      Sequel::Dataset.new(self)
+      ds = Sequel::Dataset.new(self)
     end
-
+    
+    def fetch(sql, *args, &amp;block)
+      ds = dataset
+      sql = sql.gsub('?') {|m|  ds.literal(args.shift)}
+      if block
+        ds.fetch_rows(sql, &amp;block)
+      else
+        ds.meta_def(:select_sql) {|*args| sql}
+        ds.meta_def(:sql) {|*args| sql}
+        ds
+      end
+    end
+    alias_method :&gt;&gt;, :fetch
+    
+    # Converts a query block into a dataset. For more information see 
+    # Dataset#query.
+    def query(&amp;block)
+      dataset.query(&amp;block)
+    end
+    
     # Returns a new dataset with the from method invoked. If a block is given,
     # it is used as a filter on the dataset.
     def from(*args, &amp;block)
@@ -73,16 +97,18 @@ module Sequel
     # Returns a new dataset with the select method invoked.
     def select(*args); dataset.select(*args); end
     
-    alias_method :[], :from
-
+    def [](*args)
+      (String === args.first) ? fetch(*args) : from(*args)
+    end
+    
     def execute(sql)
       raise NotImplementedError
     end
     
-    # Executes the supplied SQL. The SQL can be supplied as a string or as an
-    # array of strings. Comments and excessive white space are removed. See
-    # also Array#to_sql.
-    def &lt;&lt;(sql); execute(sql.to_sql); end
+    # Executes the supplied SQL statement. The SQL can be supplied as a string
+    # or as an array of strings. If an array is give, comments and excessive 
+    # white space are removed. See also Array#to_sql.
+    def &lt;&lt;(sql); execute((Array === sql) ? sql.to_sql : sql); end
     
     # Acquires a database connection, yielding it to the passed block.
     def synchronize(&amp;block)
@@ -155,7 +181,7 @@ module Sequel
           result
         rescue =&gt; e
           conn.execute(SQL_ROLLBACK)
-          raise e
+          raise e unless SequelRollbackError === e
         ensure
           @transactions.delete(Thread.current)
         end</diff>
      <filename>vendor/ruby-sequel/lib/sequel/database.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,8 @@
 require 'time'
 require 'date'
 
-require File.join(File.dirname(__FILE__), 'dataset/sequelizer')
 require File.join(File.dirname(__FILE__), 'dataset/sql')
+require File.join(File.dirname(__FILE__), 'dataset/sequelizer')
 require File.join(File.dirname(__FILE__), 'dataset/convenience')
 
 module Sequel
@@ -86,6 +86,7 @@ module Sequel
     def initialize(db, opts = nil)
       @db = db
       @opts = opts || {}
+      @row_proc = nil
     end
     
     # Returns a new instance of the dataset with with the give options merged.
@@ -146,17 +147,14 @@ module Sequel
       @columns || []
     end
     
+    # Inserts the supplied values into the associated table.
     def &lt;&lt;(*args)
       insert(*args)
     end
     
     # Iterates over the records in the dataset
     def each(opts = nil, &amp;block)
-      if (opts &amp;&amp; opts[:naked]) || !@row_filter
-        fetch_rows(select_sql(opts), &amp;block)
-      else
-        fetch_rows(select_sql(opts)) {|r| block[@row_filter[r]]}
-      end
+      fetch_rows(select_sql(opts), &amp;block)
     end
 
     # Returns the the model classes associated with the dataset as a hash.
@@ -224,18 +222,18 @@ module Sequel
       when nil: # set_model(nil) =&gt; no
         # no argument provided, so the dataset is denuded
         @opts.merge!(:naked =&gt; true, :models =&gt; nil, :polymorphic_key =&gt; nil)
-        remove_row_filter
+        remove_row_proc
         # extend_with_stock_each
       when Class:
         # isomorphic model
         @opts.merge!(:naked =&gt; nil, :models =&gt; {nil =&gt; key}, :polymorphic_key =&gt; nil)
-        set_row_filter {|h| key.new(h, *args)}
+        set_row_proc {|h| key.new(h, *args)}
         extend_with_destroy
       when Symbol:
         # polymorphic model
         hash = args.shift || raise(SequelError, &quot;No class hash supplied for polymorphic model&quot;)
         @opts.merge!(:naked =&gt; true, :models =&gt; hash, :polymorphic_key =&gt; key)
-        set_row_filter do |h|
+        set_row_proc do |h|
           c = hash[h[key]] || hash[nil] || \
             raise(SequelError, &quot;No matching model class for record (#{polymorphic_key} =&gt; #{h[polymorphic_key].inspect})&quot;)
           c.new(h, *args)
@@ -252,18 +250,123 @@ module Sequel
     # record. The filter should return a value which is then passed to the 
     # iterating block. In order to elucidate, here's a contrived example:
     #
-    #   dataset.set_row_filter {|h| h.merge(:xxx =&gt; 'yyy')}
+    #   dataset.set_row_proc {|h| h.merge(:xxx =&gt; 'yyy')}
     #   dataset.first[:xxx] #=&gt; &quot;yyy&quot; # always!
     #
-    def set_row_filter(&amp;filter)
-      @row_filter = filter
+    def set_row_proc(&amp;filter)
+      @row_proc = filter
+      update_each_method
+    end
+    
+    # Removes the row making proc.
+    def remove_row_proc
+      @row_proc = nil
+      update_each_method
+    end
+    
+    STOCK_TRANSFORMS = {
+      :marshal =&gt; [proc {|v| Marshal.load(v)}, proc {|v| Marshal.dump(v)}],
+      :yaml =&gt; [proc {|v| YAML.load v if v}, proc {|v| v.to_yaml}]
+    }
+    
+    # Sets a value transform which is used to convert values loaded and saved
+    # to/from the database. The transform should be supplied as a hash. Each
+    # value in the hash should be an array containing two proc objects - one
+    # for transforming loaded values, and one for transforming saved values.
+    # The following example demonstrates how to store Ruby objects in a dataset
+    # using Marshal serialization:
+    #
+    #   dataset.transform(:obj =&gt; [
+    #     proc {|v| Marshal.load(v)},
+    #     proc {|v| Marshal.dump(v)}
+    #   ])
+    #
+    #   dataset.insert_sql(:obj =&gt; 1234) #=&gt;
+    #   &quot;INSERT INTO items (obj) VALUES ('\004\bi\002\322\004')&quot;
+    #
+    # Another form of using transform is by specifying stock transforms:
+    # 
+    #   dataset.transform(:obj =&gt; :marshal)
+    #
+    # The currently supported stock transforms are :marshal and :yaml.
+    def transform(t)
+      @transform = t
+      t.each do |k, v|
+        case v
+        when Array:
+          if (v.size != 2) || !v.first.is_a?(Proc) &amp;&amp; !v.last.is_a?(Proc)
+            raise SequelError, &quot;Invalid transform specified&quot;
+          end
+        else
+          unless v = STOCK_TRANSFORMS[v]
+            raise SequelError, &quot;Invalid transform specified&quot;
+          else
+            t[k] = v
+          end
+        end
+      end
+      update_each_method
     end
     
-    def remove_row_filter
-      @row_filter = nil
+    # Applies the value transform for data loaded from the database.
+    def transform_load(r)
+      @transform.each do |k, tt|
+        if r.has_key?(k)
+          r[k] = tt[0][r[k]]
+        end
+      end
+      r
+    end
+    
+    # Applies the value transform for data saved to the database.
+    def transform_save(r)
+      @transform.each do |k, tt|
+        if r.has_key?(k)
+          r[k] = tt[1][r[k]]
+        end
+      end
+      r
+    end
+    
+    # Updates the each method according to whether @row_proc and @transform are
+    # set or not.
+    def update_each_method
+      # warning: ugly code generation ahead
+      if @row_proc &amp;&amp; @transform
+        class &lt;&lt; self
+          def each(opts = nil, &amp;block)
+            if opts &amp;&amp; opts[:naked]
+              fetch_rows(select_sql(opts)) {|r| block[transform_load(r)]}
+            else
+              fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(r)]]}
+            end
+          end
+        end
+      elsif @row_proc
+        class &lt;&lt; self
+          def each(opts = nil, &amp;block)
+            if opts &amp;&amp; opts[:naked]
+              fetch_rows(select_sql(opts), &amp;block)
+            else
+              fetch_rows(select_sql(opts)) {|r| block[@row_proc[r]]}
+            end
+          end
+        end
+      elsif @transform
+        class &lt;&lt; self
+          def each(opts = nil, &amp;block)
+            fetch_rows(select_sql(opts)) {|r| block[transform_load(r)]}
+          end
+        end
+      else
+        class &lt;&lt; self
+          def each(opts = nil, &amp;block)
+            fetch_rows(select_sql(opts), &amp;block)
+          end
+        end
+      end
     end
     
-    private
     # Extends the dataset with a destroy method, that calls destroy for each
     # record in the dataset.
     def extend_with_destroy
@@ -276,6 +379,16 @@ module Sequel
         end
       end
     end
+
+    @@dataset_classes = []
+
+    def self.dataset_classes
+      @@dataset_classes
+    end
+
+    def self.inherited(c)
+      @@dataset_classes &lt;&lt; c
+    end
   end
 end
 </diff>
      <filename>vendor/ruby-sequel/lib/sequel/dataset.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,6 +3,12 @@ require 'enumerator'
 module Sequel
   class Dataset
     module Convenience
+      # Iterates through each record, converting it into a hash.
+      def each_hash(&amp;block)
+        each {|a| block[a.to_hash]}
+      end
+      
+      # Returns true if the record count is 0
       def empty?
         count == 0
       end
@@ -24,13 +30,16 @@ module Sequel
 
       # Returns the first record in the dataset. If the num argument is specified,
       # an array is returned with the first &lt;i&gt;num&lt;/i&gt; records.
-      def first(*args)
+      def first(*args, &amp;block)
+        if block
+          return filter(&amp;block).single_record(:limit =&gt; 1)
+        end
         args = args.empty? ? 1 : (args.size == 1) ? args.first : args
         case args
         when 1: single_record(:limit =&gt; 1)
         when Fixnum: limit(args).all
         else
-          filter(args).single_record(:limit =&gt; 1)
+          filter(args, &amp;block).single_record(:limit =&gt; 1)
         end
       end
 
@@ -168,6 +177,22 @@ module Sequel
         Sequel::PrettyTable.print(naked.all, cols.empty? ? columns : cols)
       end
       
+      COMMA_SEPARATOR = ', '.freeze
+      
+      # Returns a string in CSV format containing the dataset records. By 
+      # default the CSV representation includes the column titles in the
+      # first line. You can turn that off by passing false as the 
+      # include_column_titles argument.
+      def to_csv(include_column_titles = true)
+        records = naked.to_a
+        csv = ''
+        if include_column_titles
+          csv &lt;&lt; &quot;#{@columns.join(COMMA_SEPARATOR)}\r\n&quot;
+        end
+        records.each {|r| csv &lt;&lt; &quot;#{r.join(COMMA_SEPARATOR)}\r\n&quot;}
+        csv
+      end
+
       # Inserts multiple records into the associated table. This method can be
       # to efficiently insert a large amounts of records into a table. Inserts
       # are automatically wrapped in a transaction. If the :commit_every 
@@ -190,6 +215,48 @@ module Sequel
           end
         end
       end
+      
+      module QueryBlockCopy
+        def each(*args); raise SequelError, &quot;#each cannot be invoked inside a query block.&quot;; end
+        def insert(*args); raise SequelError, &quot;#insert cannot be invoked inside a query block.&quot;; end
+        def update(*args); raise SequelError, &quot;#update cannot be invoked inside a query block.&quot;; end
+        def delete(*args); raise SequelError, &quot;#delete cannot be invoked inside a query block.&quot;; end
+        
+        def clone_merge(opts)
+          @opts.merge!(opts)
+        end
+      end
+      
+      # Translates a query block into a dataset. Query blocks can be useful
+      # when expressing complex SELECT statements, e.g.:
+      #
+      #   dataset = DB[:items].query do
+      #     select :x, :y, :z
+      #     where {:x &gt; 1 &amp;&amp; :y &gt; 2}
+      #     order_by :z.DESC
+      #   end
+      #
+      def query(&amp;block)
+        copy = clone_merge({})
+        copy.extend(QueryBlockCopy)
+        copy.instance_eval(&amp;block)
+        clone_merge(copy.opts)
+      end
+      
+      MUTATION_RE = /^(.+)!$/.freeze
+      
+      def method_missing(m, *args, &amp;block)
+        if m.to_s =~ MUTATION_RE
+          m = $1.to_sym
+          super unless respond_to?(m)
+          copy = send(m, *args, &amp;block)
+          super if copy.class != self.class
+          @opts.merge!(copy.opts)
+          self
+        else
+          super
+        end
+      end
     end
   end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/lib/sequel/dataset/convenience.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,8 +40,8 @@ class Sequel::Dataset
       case r
       when Range:
         r.exclude_end? ? \
-          &quot;(#{l} &gt;= #{literal(r.begin)} AND #{l} &lt; #{literal(r.end)})&quot; : \
-          &quot;(#{l} &gt;= #{literal(r.begin)} AND #{l} &lt;= #{literal(r.end)})&quot;
+          &quot;(#{literal(l)} &gt;= #{literal(r.begin)} AND #{literal(l)} &lt; #{literal(r.end)})&quot; : \
+          &quot;(#{literal(l)} &gt;= #{literal(r.begin)} AND #{literal(l)} &lt;= #{literal(r.end)})&quot;
       when Array:
         &quot;(#{literal(l)} IN (#{literal(r)}))&quot;
       when Sequel::Dataset:
@@ -108,7 +108,11 @@ class Sequel::Dataset
       when :&gt;, :&lt;, :&gt;=, :&lt;=
         l = eval_expr(e[1], b)
         r = eval_expr(e[3][1], b)
-        &quot;(#{literal(l)} #{op} #{literal(r)})&quot;
+        if (Symbol === l) || (Sequel::LiteralString === l) || (Symbol === r) || (Sequel::LiteralString === r)
+          &quot;(#{literal(l)} #{op} #{literal(r)})&quot;
+        else
+          ext_expr(e, b)
+        end
       when :==
         l = eval_expr(e[1], b)
         r = eval_expr(e[3][1], b)
@@ -120,7 +124,11 @@ class Sequel::Dataset
       when :+, :-, :*, :/, :%
         l = eval_expr(e[1], b)
         r = eval_expr(e[3][1], b)
-        &quot;(#{literal(l)} #{op} #{literal(r)})&quot;.lit
+        if (Symbol === l) || (Sequel::LiteralString === l) || (Symbol === r) || (Sequel::LiteralString === r)
+          &quot;(#{literal(l)} #{op} #{literal(r)})&quot;.lit
+        else
+          ext_expr(e, b)
+        end
       when :in, :in?
         # in/in? operators are supported using two forms:
         #   :x.in([1, 2, 3])
@@ -146,12 +154,32 @@ class Sequel::Dataset
       end
     end
     
+    def fcall_expr(e, b)
+      ext_expr(e, b)
+    end
+    
+    def vcall_expr(e, b)
+      eval(e[1].to_s, b)
+    end
+    
+    def iter_expr(e, b)
+      if e[1] == [:fcall, :proc]
+        eval_expr(e[3], b) # inline proc
+      else
+        ext_expr(e, b) # method call with inline proc
+      end
+    end
+    
     # Evaluates a parse-tree into an SQL expression.
     def eval_expr(e, b)
       case e[0]
       when :call # method call
         call_expr(e, b)
-      when :ivar, :cvar, :dvar, :vcall, :const, :gvar # local ref
+      when :fcall
+        fcall_expr(e, b)
+      when :vcall
+        vcall_expr(e, b)
+      when :ivar, :cvar, :dvar, :const, :gvar # local ref
         eval(e[1].to_s, b)
       when :nth_ref:
         eval(&quot;$#{e[1]}&quot;, b)
@@ -182,16 +210,14 @@ class Sequel::Dataset
         r = eval_expr(e[1], b)
         compare_expr(l, r)
       when :iter
-        if e[1] == [:fcall, :proc]
-          eval_expr(e[3], b) # inline proc
-        else
-          ext_expr(e, b) # method call with inline proc
-        end
+        iter_expr(e, b)
       when :dasgn, :dasgn_curr
         # assignment
         l = e[1]
         r = eval_expr(e[2], b)
         raise SequelError, &quot;Invalid expression #{l} = #{r}. Did you mean :#{l} == #{r}?&quot;
+      when :if, :dstr
+        ext_expr(e, b)
       else
         raise SequelError, &quot;Invalid expression tree: #{e.inspect}&quot;
       end
@@ -210,7 +236,7 @@ class Sequel::Dataset
         &quot;(#{e[1..-1].map {|i| pt_expr(i, b)}.join(&quot; AND &quot;)})&quot;
       when :or # x || y
         &quot;(#{pt_expr(e[1], b)} OR #{pt_expr(e[2], b)})&quot;
-      when :call, :vcall, :iter # method calls, blocks
+      when :call, :vcall, :iter, :match3 # method calls, blocks
         eval_expr(e, b)
       else # literals
         if e == [:lvar, :block]</diff>
      <filename>vendor/ruby-sequel/lib/sequel/dataset/sequelizer.rb</filename>
    </modified>
    <modified>
      <diff>@@ -52,7 +52,9 @@ module Sequel
         if fields.empty?
           WILDCARD
         else
-          fields.map {|i| field_name(i)}.join(COMMA_SEPARATOR)
+          fields.map do |i|
+            i.is_a?(Hash) ? i.map {|kv| &quot;#{literal(kv[0])} AS #{kv[1]}&quot;} : literal(i)
+          end.join(COMMA_SEPARATOR)
         end
       end
 
@@ -61,8 +63,19 @@ module Sequel
         if source.nil? || source.empty?
           raise SequelError, 'No source specified for query'
         end
-        source.map {|i| i.is_a?(Dataset) ? i.to_table_reference : i}.
-          join(COMMA_SEPARATOR)
+        auto_alias_count = 0
+        source.map do |i|
+          case i
+          when Dataset:
+            auto_alias_count += 1
+            i.to_table_reference(auto_alias_count)
+          when Hash:
+            i.map {|k, v| &quot;#{k.is_a?(Dataset) ? k.to_table_reference : k} #{v}&quot;}.
+              join(COMMA_SEPARATOR)
+          else
+            i
+          end
+        end.join(COMMA_SEPARATOR)
       end
 
       NULL = &quot;NULL&quot;.freeze
@@ -92,7 +105,7 @@ module Sequel
         when NilClass: NULL
         when TrueClass: TRUE
         when FalseClass: FALSE
-        when Symbol: v.to_field_name
+        when Symbol: quoted_field_name(v.to_field_name)
         when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
         when Time: v.strftime(TIMESTAMP_FORMAT)
         when Date: v.strftime(DATE_FORMAT)
@@ -111,7 +124,10 @@ module Sequel
         case expr
         when Hash:
           parenthesize = false if expr.size == 1
-          fmt = expr.map {|i| compare_expr(i[0], i[1])}.join(AND_SEPARATOR)
+          # fmt = expr.map {|i| compare_expr(i[0], i[1])}.join(AND_SEPARATOR)
+          # N.B.: We convert this to an array and sort it in order to have a fixed order for testability.
+          # Hash in Ruby 1.8 has no order, so Hash#map is indeterminate, which makes it hard to test.
+          fmt = expr.to_a.sort_by { |k, v| k.to_s }.map {|i| compare_expr(i[0], i[1])}.join(AND_SEPARATOR)
         when Array:
           fmt = expr.shift.gsub(QUESTION_MARK) {literal(expr.shift)}
         when Proc:
@@ -145,6 +161,8 @@ module Sequel
       def order(*order)
         clone_merge(:order =&gt; order)
       end
+      
+      alias_method :order_by, :order
 
       # Returns a copy of the dataset with the order reversed. If no order is
       # given, the existing order is inverted.
@@ -152,7 +170,7 @@ module Sequel
         order(*invert_order(order.empty? ? @opts[:order] : order))
       end
 
-      DESC_ORDER_REGEXP = /(.*)\sDESC/.freeze
+      DESC_ORDER_REGEXP = /(.*)\sDESC/i.freeze
 
       # Inverts the given order by breaking it into a list of field references
       # and inverting them.
@@ -165,7 +183,7 @@ module Sequel
         order.each do |f|
           f.to_s.split(',').map do |p|
             p.strip!
-            new_order &lt;&lt; (p =~ DESC_ORDER_REGEXP ? $1 : p.to_sym.DESC)
+            new_order &lt;&lt; ((p =~ DESC_ORDER_REGEXP ? $1 : p.to_sym.DESC).lit)
           end
         end
         new_order
@@ -176,6 +194,8 @@ module Sequel
       def group(*fields)
         clone_merge(:group =&gt; fields)
       end
+      
+      alias_method :group_by, :group
 
       # Returns a copy of the dataset with the given conditions imposed upon it.  
       # If the query has been grouped, then the conditions are imposed in the 
@@ -308,13 +328,13 @@ module Sequel
           raise SequelError, &quot;Invalid join type: #{type}&quot;
         end
 
-        join_expr = expr.map do |k, v|
-          l = qualified_field_name(k, table)
-          r = qualified_field_name(v, @opts[:last_joined_table] || @opts[:from].first)
-          &quot;(#{l} = #{r})&quot;
-        end.join(AND_SEPARATOR)
-
-        &quot; #{join_type} #{table} ON #{join_expr}&quot;
+        join_conditions = {}
+        expr.each do |k, v|
+          k = qualified_field_name(k, table).intern if k.is_a?(Symbol)
+          v = qualified_field_name(v, @opts[:last_joined_table] || @opts[:from].first).intern if v.is_a?(Symbol)
+          join_conditions[k] = v
+        end
+        &quot; #{join_type} #{table} ON #{expression_list(join_conditions)}&quot;
       end
 
       # Returns a joined dataset with the specified join type and condition.
@@ -423,7 +443,20 @@ module Sequel
         else
           values = values[0] if values.size == 1
           case values
+          when Array
+            if values.fields
+              if values.empty?
+                &quot;INSERT INTO #{@opts[:from]} DEFAULT VALUES;&quot;
+              else
+                fl = values.fields
+                vl = transform_save(values.values).map {|v| literal(v)}
+                &quot;INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)});&quot;
+              end
+            else
+              &quot;INSERT INTO #{@opts[:from]} VALUES (#{literal(values)});&quot;
+            end
           when Hash
+            values = transform_save(values) if @transform
             if values.empty?
               &quot;INSERT INTO #{@opts[:from]} DEFAULT VALUES;&quot;
             else
@@ -432,7 +465,7 @@ module Sequel
               &quot;INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)});&quot;
             end
           when Dataset
-            &quot;INSERT INTO #{@opts[:from]} #{literal(values)}&quot;
+            &quot;INSERT INTO #{@opts[:from]} #{literal(values)};&quot;
           else
             &quot;INSERT INTO #{@opts[:from]} VALUES (#{literal(values)});&quot;
           end
@@ -452,6 +485,10 @@ module Sequel
           raise SequelError, &quot;Can't update a joined dataset&quot;
         end
 
+        if values.is_a?(Array) &amp;&amp; values.fields
+          values = values.to_hash
+        end
+        values = transform_save(values) if @transform
         set_list = values.map {|k, v| &quot;#{field_name(k)} = #{literal(v)}&quot;}.
           join(COMMA_SEPARATOR)
         sql = &quot;UPDATE #{@opts[:from]} SET #{set_list}&quot;
@@ -488,11 +525,11 @@ module Sequel
       # Returns a table reference for use in the FROM clause. If the dataset has
       # only a :from option refering to a single table, only the table name is 
       # returned. Otherwise a subquery is returned.
-      def to_table_reference
+      def to_table_reference(idx = nil)
         if opts.keys == [:from] &amp;&amp; opts[:from].size == 1
           opts[:from].first.to_s
         else
-          &quot;(#{sql})&quot;
+          idx ? &quot;(#{sql}) t#{idx}&quot; : &quot;(#{sql})&quot;
         end
       end
 
@@ -517,7 +554,7 @@ module Sequel
         end
       end
       
-      SELECT_COUNT = {:select =&gt; [&quot;COUNT(*)&quot;], :order =&gt; nil}.freeze
+      SELECT_COUNT = {:select =&gt; [&quot;COUNT(*)&quot;.lit], :order =&gt; nil}.freeze
 
       # Returns the number of records in the dataset.
       def count</diff>
      <filename>vendor/ruby-sequel/lib/sequel/dataset/sql.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,6 +14,10 @@ module Sequel
         dbname = 'DBI:' + dbname unless dbname =~ /^DBI:/
         ::DBI.connect(dbname, @opts[:user], @opts[:password])
       end
+      
+      def disconnect
+        @pool.disconnect {|c| c.disconnect}
+      end
     
       def dataset(opts = nil)
         DBI::Dataset.new(self, opts)
@@ -56,6 +60,19 @@ module Sequel
         self
       end
       
+      def array_tuples_fetch_rows(sql, &amp;block)
+        @db.synchronize do
+          s = @db.execute sql
+          begin
+            @columns = s.column_names.map {|c| c.to_sym}
+            s.fetch {|r| r.fields = @columns; yield r}
+          ensure
+            s.finish rescue nil
+          end
+        end
+        self
+      end
+      
       def hash_row(stmt, row)
         @columns.inject({}) do |m, c|
           m[c] = row.shift</diff>
      <filename>vendor/ruby-sequel/lib/sequel/dbi.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,2 +1,11 @@
 class SequelError &lt; StandardError
+end
+
+class SequelRollbackError &lt; StandardError
+end
+
+class Object
+  def rollback!
+    raise SequelRollbackError
+  end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/lib/sequel/error.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,104 +1,276 @@
-module Sequel
-  class Model
-  end
-end
-
-require File.join(File.dirname(__FILE__), 'model/base')
-require File.join(File.dirname(__FILE__), 'model/hooks')
-require File.join(File.dirname(__FILE__), 'model/record')
-require File.join(File.dirname(__FILE__), 'model/schema')
-require File.join(File.dirname(__FILE__), 'model/relations')
-
-module Sequel
-  class Model
-    def self.subset(name, *args, &amp;block)
-      meta_def(name) {filter(*args, &amp;block)}
-    end
-    
-    def self.find(cond)
-      dataset[cond.is_a?(Hash) ? cond : {primary_key =&gt; cond}]
-    end
-    
-    def self.find_or_create(cond)
-      find(cond) || create(cond)
-    end
-
-    class &lt;&lt; self; alias_method :[], :find; end
-    
-    ############################################################################
-    
-    def self.destroy_all
-      has_hooks?(:before_destroy) ? dataset.destroy : dataset.delete
-    end
-    def self.delete_all; dataset.delete; end
-    
-    FIND_BY_REGEXP = /^find_by_(.*)/.freeze
-    FILTER_BY_REGEXP = /^filter_by_(.*)/.freeze
-    ALL_BY_REGEXP = /^all_by_(.*)/.freeze
-    
-    def self.method_missing(m, *args, &amp;block)
-      Thread.exclusive do
-        method_name = m.to_s
-        if method_name =~ FIND_BY_REGEXP
-          c = $1.to_sym
-          meta_def(method_name) {|arg| find(c =&gt; arg)}
-        elsif method_name =~ FILTER_BY_REGEXP
-          c = $1.to_sym
-          meta_def(method_name) {|arg| filter(c =&gt; arg)}
-        elsif method_name =~ ALL_BY_REGEXP
-          c = $1.to_sym
-          meta_def(method_name) {|arg| filter(c =&gt; arg).all}
-        elsif dataset.respond_to?(m)
-          instance_eval(&quot;def #{m}(*args, &amp;block); dataset.#{m}(*args, &amp;block); end&quot;)
-        end
-      end
-      respond_to?(m) ? send(m, *args, &amp;block) : super(m, *args)
-    end
-    
-    def self.join(*args)
-      table_name = dataset.opts[:from].first
-      dataset.join(*args).select(table_name.to_sym.ALL)
-    end
-    
-    def [](field); @values[field]; end
-    
-    def []=(field, value); @values[field] = value; end
-    
-    WRITE_ATTR_REGEXP = /(.*)=$/.freeze
-
-    def method_missing(m, value = nil)
-      write = m.to_s =~ WRITE_ATTR_REGEXP
-      att = write ? $1.to_sym : m
-      # raise unless the att is recognized or this is a new unaved record
-      super unless @values.include?(att) || @new
-      
-      write ? (self[att] = value) : self[att]
-    end
-    
-    def each(&amp;block); @values.each(&amp;block); end
-    def keys; @values.keys; end
-    
-    def id; @values[:id]; end
-    
-    def ==(obj)
-      (obj.class == model) &amp;&amp; (obj.values == @values)
-    end
-    
-    SERIALIZE_GET_PROC = &quot;proc {@unserialized_%s ||= YAML.load(@values[:%s]) }&quot;.freeze
-    SERIALIZE_SET_PROC = &quot;proc {|v| @values[:%s] = v.to_yaml; @unserialized_%s = v }&quot;.freeze
-    
-    def self.serialize(*fields)
-      fields.each do |f|
-        # define getter
-        define_method f, &amp;eval(SERIALIZE_GET_PROC % [f, f])
-        # define setter
-        define_method &quot;#{f}=&quot;, &amp;eval(SERIALIZE_SET_PROC % [f, f])
-        # add before_create to serialize values before creation
-        before_create do
-          @values[f] = @values[f].to_yaml
-        end
-      end      
-    end
-  end
-  
-end
+module Sequel
+  # == Cheatsheet:
+  #   class Item &lt; Sequel::Model(:items)
+  #     set_schema do
+  #       primary_key :id
+  #       text :name, :unique =&gt; true, :null =&gt; false
+  #       boolean :active, :default =&gt; true
+  #       integer :grade
+  #
+  #       index :grade
+  #     end
+  #   end
+  #
+  #   Item.create_table unless Item.table_exists?
+  #   Item.create_table!
+  #
+  #   i = Item.create(:name =&gt; 'Shoes', :grade =&gt; 0)
+  #
+  #   Item[1].grade #=&gt; 0
+  #
+  #   i.set(:grade =&gt; 2)
+  #   i.grade # =&gt; 2
+  #
+  #   Item[:name =&gt; 'Shoes'].grade # =&gt; 2
+  #
+  #   i.grade = 4
+  #   Item[1].grade # =&gt; 2
+  #   i.save
+  #   Item[1].grade # =&gt; 4
+  #
+  # == Subsets
+  # Subsets are filter mapped to class methods:
+  #
+  #   class Ticket &lt; Sequel::Model(:tickets)
+  #
+  #     subset(:pending) { finished_at == nil }
+  #     subset(:closed)  { finished_at != nil }
+  #
+  #     # ...
+  #
+  #   end
+  #
+  # Now you can do:
+  #
+  #   Ticket.pending.each { |ticket| puts ticket.caption }
+  #
+  # == Advanced filtering methods (or dataset magic)
+  # One of the cool features of Sequel::Model is that it acts as a proxy to
+  # the underlying dataset, so you can invoke methods on the class instead of
+  # on the dataset:
+  #
+  #   Customer.filter(:name =~ 'Roberts')
+  #
+  # In the prevailing style of implementing models (which is actually very
+  # similar to ActiveRecord models) table-wide operations are defined as
+  # class methods:
+  #
+  #   class Node &lt; Sequel::Model(:nodes)
+  #     def self.subtree(path)
+  #       filter(:path =&gt; Regexp.new(&quot;^#{path}(/.+)?$&quot;))
+  #     end
+  #     def self.alarms
+  #       filter {:kind =&gt; ALARM}
+  #     end
+  #     def self.recalculate
+  #       exclude(:expression =&gt; nil).each {|n| n.calculate}
+  #     end
+  #   end
+  #
+  # The recalculate class method calls the exclude method. The exclude
+  # call is proxied to the underlying dataset, which lets you call each
+  # method separately:
+  #
+  #   Node.subtree('/test')
+  #   Node.alarms
+  #   Node.recalculate
+  #
+  # ... but this will raise a NoMethodError:
+  #
+  #   Node.subtree('/test').alarms.recalculate
+  #
+  # It turns out the solution is very simple - instead of defining class
+  # methods, define dataset methods:
+  #
+  #   class Node &lt; Sequel::Model(:nodes)
+  #     def dataset.subtree(path)
+  #       filter(:path =&gt; Regexp.new(&quot;^#{path}(/.+)?$&quot;))
+  #     end
+  #     def dataset.alarms
+  #       filter {:kind =&gt; ALARM}
+  #     end
+  #     def dataset.recalculate
+  #       exclude(:expression =&gt; nil).each {|n| n.calculate}
+  #     end
+  #   end
+  #
+  # Now you can mix all of these methods any way you like:
+  #
+  #   Node.filter {:stamp &lt; Time.now &lt; 3600}.alarms
+  #   Node.filter(:project_id =&gt; 123).subtree('/abc')
+  #   Node.subtree('/test').recalculate
+  #   # ...
+  #
+  # == Schemas
+  # You can define your schema in the Model class itself:
+  #
+  #   class Comment &lt; Sequel::Model(:comments)
+  #     set_schema do
+  #       primary_key :id
+  #       foreign_key :post_id, :table =&gt; :posts, :on_delete =&gt; :cascade
+  #
+  #       varchar :name
+  #       varchar :email
+  #       text :comment
+  #     end
+  #
+  #     # ...
+  #
+  #   end
+  #
+  # == Hooks
+  # You can setup hooks here:
+  # * before_save calls either
+  # * before_create with
+  # * after_create or if record already exists
+  # * before_update with
+  # * after_update and finally
+  # * after_save
+  # ... and here:
+  # * before_destroy with
+  # * after_destroy
+  #
+  # ...with:
+  #
+  #   class Example &lt; Sequel::Model(:hooks)
+  #     before_create { self.created_at = Time.now }
+  #
+  #     # ...
+  #   end
+  #
+  # == Serialization of complexe attributes
+  # Sometimes there are datatypes you can't natively map to your db. In this
+  # case you can just do serialize:
+  #
+  #   class Serialized &lt; Sequel::Model(:serialized)
+  #     serialize :column1, :format =&gt; :yaml    # YAML is the default serialization method
+  #     serialize :column2, :format =&gt; :marshal # serializes through marshalling
+  #
+  #     # ...
+  #
+  #   end
+  class Model
+    alias_method :model, :class
+  end
+end
+
+require File.join(File.dirname(__FILE__), 'model/base')
+require File.join(File.dirname(__FILE__), 'model/hooks')
+require File.join(File.dirname(__FILE__), 'model/record')
+require File.join(File.dirname(__FILE__), 'model/schema')
+require File.join(File.dirname(__FILE__), 'model/relations')
+require File.join(File.dirname(__FILE__), 'model/caching')
+
+module Sequel
+  class Model
+
+    # Defines a method that returns a filtered dataset.
+    def self.subset(name, *args, &amp;block)
+      dataset.meta_def(name) {filter(*args, &amp;block)}
+    end
+
+    # Comprehensive description goes here!
+    def primary_key_hash(value)
+      # stock implementation
+      {:id =&gt; value}
+    end
+
+    # Finds a single record according to the supplied filter, e.g.:
+    #
+    #   Ticket.find :author =&gt; 'Sharon' # =&gt; record
+    #   Ticket.find {:price}17 # =&gt; Dataset
+    #
+    def self.find(*args, &amp;block)
+      dataset.filter(*args, &amp;block).limit(1).first
+      # dataset[cond.is_a?(Hash) ? cond : primary_key_hash(cond)]
+    end
+    
+    def self.[](*args)
+      args = args.first if (args.size == 1)
+      dataset[(Hash === args) ? args : primary_key_hash(args)]
+    end
+    
+    # Like find but invokes create with given conditions when record does not
+    # exists.
+    def self.find_or_create(cond)
+      find(cond) || create(cond)
+    end
+
+    ############################################################################
+
+    # Like delete_all, but invokes before_destroy and after_destroy hooks if used.
+    def self.destroy_all
+      has_hooks?(:before_destroy) || has_hooks?(:after_destroy) ? \
+        dataset.destroy : dataset.delete
+    end
+    # Deletes all records.
+    def self.delete_all
+      dataset.delete
+    end
+
+    FIND_BY_REGEXP = /^find_by_(.*)/.freeze
+    FILTER_BY_REGEXP = /^filter_by_(.*)/.freeze
+    ALL_BY_REGEXP = /^all_by_(.*)/.freeze
+
+    def self.method_missing(m, *args, &amp;block) #:nodoc:
+      Thread.exclusive do
+        method_name = m.to_s
+        if method_name =~ FIND_BY_REGEXP
+          c = $1.to_sym
+          meta_def(method_name) {|arg| find(c =&gt; arg)}
+        elsif method_name =~ FILTER_BY_REGEXP
+          c = $1.to_sym
+          meta_def(method_name) {|arg| filter(c =&gt; arg)}
+        elsif method_name =~ ALL_BY_REGEXP
+          c = $1.to_sym
+          meta_def(method_name) {|arg| filter(c =&gt; arg).all}
+        elsif dataset.respond_to?(m)
+          instance_eval(&quot;def #{m}(*args, &amp;block); dataset.#{m}(*args, &amp;block); end&quot;)
+        end
+      end
+      respond_to?(m) ? send(m, *args, &amp;block) : super(m, *args)
+    end
+
+    # Comprehensive description goes here!
+    def self.join(*args)
+      table_name = dataset.opts[:from].first
+      dataset.join(*args).select(table_name.to_sym.ALL)
+    end
+
+    # Returns value of attribute.
+    def [](field)
+      @values[field]
+    end
+    # Sets value of attribute.
+    def []=(field, value)
+      @values[field] = value
+    end
+
+    # Enumerates through all attributes.
+    #
+    # === Example:
+    #   Ticket.find(7).each { |k, v| puts &quot;#{k} =&gt; #{v}&quot; }
+    def each(&amp;block)
+      @values.each(&amp;block)
+    end
+    # Returns attribute names.
+    def keys
+      @values.keys
+    end
+
+    # Returns value for &lt;tt&gt;:id&lt;/tt&gt; attribute.
+    def id
+      @values[:id]
+    end
+
+    # Compares models by values.
+    def ==(obj)
+      (obj.class == model) &amp;&amp; (obj.values == @values)
+    end
+    # Compares object by pkey.
+    def ===(obj)
+      (obj.class == model) &amp;&amp; (obj.pkey == pkey)
+    end
+
+  end
+end</diff>
      <filename>vendor/ruby-sequel/lib/sequel/model.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,49 +1,90 @@
 module Sequel
   class Model
-    # returns the database associated with the model class
+    # Returns the database associated with the Model class.
     def self.db
-      @db ||= ((superclass != Object) &amp;&amp; (superclass.db)) || \
-        raise(SequelError, &quot;No database associated with #{self}.&quot;)
+      @db ||= (superclass != Object) &amp;&amp; superclass.db or
+      raise SequelError, &quot;No database associated with #{self}&quot;
     end
     
-    # sets the database associated with the model class
-    def self.db=(db); @db = db; end
+    # Sets the database associated with the Model class.
+    def self.db=(db)
+      @db = db
+    end
     
-    # called when a database is opened in order to automatically associate the
+    # Called when a database is opened in order to automatically associate the
     # first opened database with model classes.
     def self.database_opened(db)
       @db = db if self == Model &amp;&amp; !@db
     end
 
-    # returns the dataset associated with the model class.
+    # Returns the dataset associated with the Model class.
     def self.dataset
-      @dataset || raise(SequelError, &quot;No dataset associated with #{self}.&quot;)
+      @dataset || super_dataset or
+      raise SequelError, &quot;No dataset associated with #{self}&quot;
+    end
+    
+    def self.super_dataset # :nodoc:
+      superclass.dataset if superclass and superclass.respond_to? :dataset
+    end
+    
+    # Returns the columns in the result set in their original order.
+    #
+    # See Dataset#columns for more information.
+    def self.columns
+      @columns ||= @dataset.columns or
+      raise SequelError, &quot;Could not fetch columns for #{self}&quot;
     end
 
-    # Sets the dataset associated with the model class.
+    # Sets the dataset associated with the Model class.
     def self.set_dataset(ds)
       @db = ds.db
       @dataset = ds
       @dataset.set_model(self)
+      @dataset.transform(@transform) if @transform
     end
     
-    # Returns the dataset assoiated with the object's model class.
+    # Returns the database assoiated with the object's Model class.
     def db
-      @db ||= self.class.db
+      @db ||= model.db
     end
 
-    # Returns the dataset assoiated with the object's model class.
+    # Returns the dataset assoiated with the object's Model class.
+    #
+    # See Dataset for more information.
     def dataset
-      @dataset ||= self.class.dataset
+      model.dataset
     end
-
-    attr_reader :values, :pkey
     
-    def model
-      @model ||= self.class
+    # Returns the columns associated with the object's Model class.
+    def columns
+      model.columns
+    end
+
+    # Serializes column with YAML or through marshalling.
+    def self.serialize(*columns)
+      format = columns.pop[:format] if Hash === columns.last
+      format ||= :yaml
+      
+      @transform = columns.inject({}) do |m, c|
+        m[c] = format
+        m
+      end
+      @dataset.transform(@transform) if @dataset
     end
   end
 
+  # Lets you create a Model class with its table name already set or reopen
+  # an existing Model.
+  #
+  # Makes given dataset inherited.
+  #
+  # === Example:
+  #   class Comment &lt; Sequel::Model(:comments)
+  #     table_name # =&gt; :comments
+  #
+  #     # ...
+  #
+  #   end
   def self.Model(source)
     @models ||= {}
     @models[source] ||= Class.new(Sequel::Model) do</diff>
      <filename>vendor/ruby-sequel/lib/sequel/model/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,40 +1,126 @@
 module Sequel
   class Model
-    def self.get_hooks(key)
-      @hooks ||= {}
-      @hooks[key] ||= []
+
+    class ChainBroken &lt; RuntimeError # :nodoc:
     end
-    
-    def self.has_hooks?(key)
-      !get_hooks(key).empty?
+
+    # This Hash translates verbs to methodnames used in chain manipulation
+    # methods.
+    VERB_TO_METHOD = {:prepend =&gt; :unshift, :append =&gt; :push}
+
+    # Returns @hooks which is an instance of Hash with its hook identifier
+    # (Symbol) as key and the chain of hooks (Array) as value.
+    #
+    # If it is not already set it'll be with an empty set of hooks.
+    # This behaviour will change in the future to allow inheritance.
+    #
+    # For the time being, you should be able to do:
+    #
+    #   class A &lt; Sequel::Model(:a)
+    #     before_save { 'Do something...' }
+    #   end
+    #
+    #   class B &lt; A
+    #     @hooks = superclass.hooks.clone
+    #     before_save # =&gt; [#&lt;Proc:0x0000c6e8@(example.rb):123&gt;]
+    #   end
+    #
+    # In this case you should remember that the clone doesn't create any new
+    # instances of your chains, so if you change the chain here it changes in
+    # its superclass, too.
+    def self.hooks
+      @hooks ||= Hash.new { |h, k| h[k] = [] }
     end
-    
-    def run_hooks(key)
-      self.class.get_hooks(key).each {|h| instance_eval(&amp;h)}
+
+    # Adds block to chain of Hooks for &lt;tt&gt;:before_save&lt;/tt&gt;.
+    # It can either be prepended (default) or appended.
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.before_save(verb = :prepend, &amp;block)
+      hooks[:before_save].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:before_save]
     end
-    
-    def self.before_save(&amp;block)
-      get_hooks(:before_save).unshift(block)
+    # Adds block to chain of Hooks for &lt;tt&gt;:before_create&lt;/tt&gt;.
+    # It can either be prepended (default) or appended.
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.before_create(verb = :prepend, &amp;block)
+      hooks[:before_create].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:before_create]
     end
-    
-    def self.before_create(&amp;block)
-      get_hooks(:before_create).unshift(block)
+    # Adds block to chain of Hooks for &lt;tt&gt;:before_update&lt;/tt&gt;.
+    # It can either be prepended (default) or appended.
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.before_update(verb = :prepend, &amp;block)
+      hooks[:before_update].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:before_update]
     end
-    
-    def self.before_destroy(&amp;block)
-      get_hooks(:before_destroy).unshift(block)
+    # Adds block to chain of Hooks for &lt;tt&gt;:before_destroy&lt;/tt&gt;.
+    # It can either be prepended (default) or appended.
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.before_destroy(verb = :prepend, &amp;block)
+      hooks[:before_destroy].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:before_destroy]
     end
-    
-    def self.after_save(&amp;block)
-      get_hooks(:after_save) &lt;&lt; block
+
+    # Adds block to chain of Hooks for &lt;tt&gt;:after_save&lt;/tt&gt;.
+    # It can either be prepended or appended (default).
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.after_save(verb = :append, &amp;block)
+      hooks[:after_save].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:after_save]
     end
-    
-    def self.after_create(&amp;block)
-      get_hooks(:after_create) &lt;&lt; block
+    # Adds block to chain of Hooks for &lt;tt&gt;:after_create&lt;/tt&gt;.
+    # It can either be prepended or appended (default).
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.after_create(verb = :append, &amp;block)
+      hooks[:after_create].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:after_create]
+    end
+    # Adds block to chain of Hooks for &lt;tt&gt;:after_update&lt;/tt&gt;.
+    # It can either be prepended or appended (default).
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.after_update(verb = :append, &amp;block)
+      hooks[:after_update].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:after_update]
+    end
+    # Adds block to chain of Hooks for &lt;tt&gt;:after_destroy&lt;/tt&gt;.
+    # It can either be prepended or appended (default).
+    #
+    # Returns the chain itself.
+    #
+    # Valid verbs are &lt;tt&gt;:prepend&lt;/tt&gt; and &lt;tt&gt;:append&lt;/tt&gt;.
+    def self.after_destroy(verb = :append, &amp;block)
+      hooks[:after_destroy].send VERB_TO_METHOD.fetch(verb), block if block
+      hooks[:after_destroy]
+    end
+
+    # Evaluates specified chain of Hooks through &lt;tt&gt;instance_eval&lt;/tt&gt;.
+    def run_hooks(key)
+      model.hooks[key].each { |h| instance_eval &amp;h }
     end
     
-    def self.after_destroy(&amp;block)
-      get_hooks(:after_destroy) &lt;&lt; block
+    def self.has_hooks?(key)
+      hooks[key] &amp;&amp; !hooks[key].empty?
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>vendor/ruby-sequel/lib/sequel/model/hooks.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,32 +1,76 @@
 module Sequel
   class Model
-    def self.primary_key; :id; end
+    attr_reader :values
+
+    # Returns key for primary key.
+    def self.primary_key
+      :id
+    end
     
-    def self.set_primary_key(key)
+    # Returns primary key attribute hash.
+    def self.primary_key_hash(value)
+      {:id =&gt; value}
+    end
+    
+    # Sets primary key, regular and composite are possible.
+    #
+    # == Example:
+    #   class Tagging &lt; Sequel::Model(:taggins)
+    #     # composite key
+    #     set_primary_key :taggable_id, :tag_id
+    #   end
+    #
+    #   class Person &lt; Sequel::Model(:person)
+    #     # regular key
+    #     set_primary_key :person_id
+    #   end
+    #
+    # &lt;i&gt;You can even set it to nil!&lt;/i&gt;
+    def self.set_primary_key(*key)
       # if k is nil, we go to no_primary_key
       return no_primary_key unless key
       
+      # backwards compat
+      key = (key.length == 1) ? key[0] : key.flatten
+
       # redefine primary_key
       meta_def(:primary_key) {key}
       
-      if key.is_a?(Array) # composite key
+      unless key.is_a? Array # regular primary key
         class_def(:this) do
-          @this ||= self.class.dataset.filter( \
-            @values.reject {|k, v| !key.include?(k)} \
-          ).naked
+          @this ||= dataset.filter(key =&gt; @values[key]).limit(1).naked
         end
-      else # regular key
-        class_def(:this) do
-          @this ||= self.class.dataset.filter(key =&gt; @values[key]).naked
+        class_def(:cache_key) do
+          pk = @values[key] || (raise SequelError, 'no primary key for this record')
+          @cache_key ||= &quot;#{self.class}:#{pk}&quot;
+        end
+        meta_def(:primary_key_hash) do |v|
+          {key =&gt; v}
+        end
+      else # composite key
+        exp_list = key.map {|k| &quot;#{k.inspect} =&gt; @values[#{k.inspect}]&quot;}
+        block = eval(&quot;proc {@this ||= self.class.dataset.filter(#{exp_list.join(',')}).limit(1).naked}&quot;)
+        class_def(:this, &amp;block)
+        
+        exp_list = key.map {|k| '#{@values[%s]}' % k.inspect}.join(',')
+        block = eval('proc {@cache_key ||= &quot;#{self.class}:%s&quot;}' % exp_list)
+        class_def(:cache_key, &amp;block)
+
+        meta_def(:primary_key_hash) do |v|
+          key.inject({}) {|m, i| m[i] = v.shift; m}
         end
       end
     end
     
-    def self.no_primary_key
+    def self.no_primary_key #:nodoc:
       meta_def(:primary_key) {nil}
+      meta_def(:primary_key_hash) {|v| raise SequelError, &quot;#{self} does not have a primary key&quot;}
       class_def(:this) {raise SequelError, &quot;No primary key is associated with this model&quot;}
+      class_def(:cache_key) {raise SequelError, &quot;No primary key is associated with this model&quot;}
     end
     
+    # Creates new instance with values set to passed-in Hash ensuring that
+    # new? returns true.
     def self.create(values = {})
       db.transaction do
         obj = new(values, true)
@@ -35,36 +79,55 @@ module Sequel
       end
     end
     
+    # Returns (naked) dataset bound to current instance.
     def this
-      @this ||= self.class.dataset.filter(:id =&gt; @values[:id]).naked
+      @this ||= self.class.dataset.filter(:id =&gt; @values[:id]).limit(1).naked
     end
     
-    # instance method
+    # Returns a key unique to the underlying record for caching
+    def cache_key
+      pk = @values[:id] || (raise SequelError, 'no primary key for this record')
+      @cache_key ||= &quot;#{self.class}:#{pk}&quot;
+    end
+
+    # Returns primary key column(s) for object's Model class.
     def primary_key
       @primary_key ||= self.class.primary_key
     end
     
+    # Returns value for primary key.
     def pkey
       @pkey ||= @values[primary_key]
     end
     
-    def initialize(values = {}, new = false)
+    # Creates new instance with values set to passed-in Hash.
+    #
+    # This method guesses whether the record exists when
+    # &lt;tt&gt;new_record&lt;/tt&gt; is set to false.
+    def initialize(values = {}, new_record = false)
       @values = values
-      @new = new
-      if !new # determine if it's a new record
+
+      @new = new_record
+      unless @new # determine if it's a new record
         pk = primary_key
+        # if there's no primary key for the model class, or
+        # @values doesn't contain a primary key value, then 
+        # we regard this instance as new.
         @new = (pk == nil) || (!(Array === pk) &amp;&amp; !@values[pk])
       end
     end
     
+    # Returns true if the current instance represents a new record.
     def new?
       @new
     end
     
+    # Returns true when current instance exists, false otherwise.
     def exists?
       this.count &gt; 0
     end
     
+    # Creates or updates dataset for Model and runs hooks.
     def save
       run_hooks(:before_save)
       if @new
@@ -79,6 +142,7 @@ module Sequel
           @this = nil # remove memoized this dataset
           refresh
         end
+        @new = false
         run_hooks(:after_create)
       else
         run_hooks(:before_update)
@@ -86,20 +150,22 @@ module Sequel
         run_hooks(:after_update)
       end
       run_hooks(:after_save)
-      @new = false
       self
     end
 
+    # Updates and saves values to database from the passed-in Hash.
     def set(values)
       this.update(values)
-      @values.merge!(values)
+      values.each {|k, v| @values[k] = v}
     end
     
+    # Reloads values from database and returns self.
     def refresh
       @values = this.first || raise(SequelError, &quot;Record not found&quot;)
       self
     end
 
+    # Like delete but runs hooks before and after delete.
     def destroy
       db.transaction do
         run_hooks(:before_destroy)
@@ -108,10 +174,38 @@ module Sequel
       end
     end
     
+    # Deletes and returns self.
     def delete
       this.delete
       self
     end
     
+    ATTR_RE = /^([a-zA-Z_]\w*)(=)?$/.freeze
+
+    def method_missing(m, *args) #:nodoc:
+      if m.to_s =~ ATTR_RE
+        att = $1.to_sym
+        write = $2 == '='
+        
+        # check wether the column is legal
+        unless columns.include?(att)
+          raise SequelError, &quot;Invalid column (#{att.inspect}) for #{self}&quot;
+        end
+
+        # define the column accessor
+        Thread.exclusive do
+          if write
+            model.class_def(m) {|v| @values[att] = v}
+          else
+            model.class_def(m) {@values[att]}
+          end
+        end
+        
+        # call the accessor
+        respond_to?(m) ? send(m, *args) : super(m, *args)
+      else
+        super(m, *args)
+      end
+    end
   end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/lib/sequel/model/record.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,12 @@
+# TODO: refactoring...
 module Sequel
   class Model
+
     ONE_TO_ONE_PROC = &quot;proc {i = @values[:%s]; %s[i] if i}&quot;.freeze
     ID_POSTFIX = &quot;_id&quot;.freeze
     FROM_DATASET = &quot;db[%s]&quot;.freeze
     
+    # Comprehensive description goes here!
     def self.one_to_one(name, opts)
       klass = opts[:class] ? opts[:class] : (FROM_DATASET % name.inspect)
       key = opts[:key] || (name.to_s + ID_POSTFIX)
@@ -12,6 +15,8 @@ module Sequel
   
     ONE_TO_MANY_PROC = &quot;proc {%s.filter(:%s =&gt; pkey)}&quot;.freeze
     ONE_TO_MANY_ORDER_PROC = &quot;proc {%s.filter(:%s =&gt; pkey).order(%s)}&quot;.freeze
+
+    # Comprehensive description goes here!
     def self.one_to_many(name, opts)
       klass = opts[:class] ? opts[:class] :
         (FROM_DATASET % (opts[:table] || name.inspect))</diff>
      <filename>vendor/ruby-sequel/lib/sequel/model/relations.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,9 @@
 module Sequel
   class Model
+    # Defines a table schema (see Schema::Generator for more information).
+    #
+    # This is only needed if you want to use the create_table or drop_table
+    # methods.
     def self.set_schema(name = nil, &amp;block)
       name ? set_dataset(db[name]) : name = table_name
       @schema = Schema::Generator.new(db, name, &amp;block)
@@ -8,29 +12,41 @@ module Sequel
       end
     end
     
+    # Returns table schema for direct descendant of Model.
     def self.schema
       @schema || ((superclass != Model) &amp;&amp; (superclass.schema))
     end
 
+    # Returns name of table.
     def self.table_name
       dataset.opts[:from].first
     end
     
+    # Returns true if table exists, false otherwise.
     def self.table_exists?
       db.table_exists?(table_name)
     end
     
+    # Creates table.
     def self.create_table
       db.create_table_sql_list(*schema.create_info).each {|s| db &lt;&lt; s} 
     end
     
+    # Drops table.
     def self.drop_table
       db.execute db.drop_table_sql(table_name)
     end
     
-    def self.recreate_table
+    # Like create_table but invokes drop_table when table_exists? is true.
+    def self.create_table!
       drop_table if table_exists?
       create_table
     end
+    
+    # Deprecated, use create_table! instead.
+    def self.recreate_table
+      warn &quot;Model.recreate_table is deprecated. Please use Model.create_table! instead.&quot;
+      create_table!
+    end
   end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/lib/sequel/model/schema.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,27 +2,40 @@ if !Object.const_defined?('Sequel')
   require File.join(File.dirname(__FILE__), '../sequel')
 end
 
+require &quot;bigdecimal&quot;
+require &quot;bigdecimal/util&quot;
 require 'mysql'
 
 # Monkey patch Mysql::Result to yield hashes with symbol keys
 class Mysql::Result
   MYSQL_TYPES = {
-    0 =&gt; :to_i,
-    1 =&gt; :to_i,
-    2 =&gt; :to_i,
-    3 =&gt; :to_i,
-    4 =&gt; :to_f,
-    5 =&gt; :to_f,
-    7 =&gt; :to_time,
-    8 =&gt; :to_i,
-    9 =&gt; :to_i,
-    10 =&gt; :to_time,
-    11 =&gt; :to_time,
-    12 =&gt; :to_time,
-    13 =&gt; :to_i,
-    14 =&gt; :to_time,
-    247 =&gt; :to_i,
-    248 =&gt; :to_i
+    0   =&gt; :to_d,     # MYSQL_TYPE_DECIMAL
+    1   =&gt; :to_i,     # MYSQL_TYPE_TINY
+    2   =&gt; :to_i,     # MYSQL_TYPE_SHORT
+    3   =&gt; :to_i,     # MYSQL_TYPE_LONG
+    4   =&gt; :to_f,     # MYSQL_TYPE_FLOAT
+    5   =&gt; :to_f,     # MYSQL_TYPE_DOUBLE
+    # 6   =&gt; ??,        # MYSQL_TYPE_NULL
+    7   =&gt; :to_time,  # MYSQL_TYPE_TIMESTAMP
+    8   =&gt; :to_i,     # MYSQL_TYPE_LONGLONG
+    9   =&gt; :to_i,     # MYSQL_TYPE_INT24
+    10  =&gt; :to_time,  # MYSQL_TYPE_DATE
+    11  =&gt; :to_time,  # MYSQL_TYPE_TIME
+    12  =&gt; :to_time,  # MYSQL_TYPE_DATETIME
+    13  =&gt; :to_i,     # MYSQL_TYPE_YEAR
+    14  =&gt; :to_time,  # MYSQL_TYPE_NEWDATE
+    # 15  =&gt; :to_s      # MYSQL_TYPE_VARCHAR
+    # 16  =&gt; :to_s,     # MYSQL_TYPE_BIT
+    246 =&gt; :to_d,     # MYSQL_TYPE_NEWDECIMAL
+    247 =&gt; :to_i,     # MYSQL_TYPE_ENUM
+    248 =&gt; :to_i      # MYSQL_TYPE_SET
+    # 249 =&gt; :to_s,     # MYSQL_TYPE_TINY_BLOB
+    # 250 =&gt; :to_s,     # MYSQL_TYPE_MEDIUM_BLOB
+    # 251 =&gt; :to_s,     # MYSQL_TYPE_LONG_BLOB
+    # 252 =&gt; :to_s,     # MYSQL_TYPE_BLOB
+    # 253 =&gt; :to_s,     # MYSQL_TYPE_VAR_STRING
+    # 254 =&gt; :to_s,     # MYSQL_TYPE_STRING
+    # 255 =&gt; :to_s      # MYSQL_TYPE_GEOMETRY
   }
   
   def convert_type(v, type)
@@ -40,7 +53,20 @@ class Mysql::Result
     @columns
   end
   
-  def each_hash(with_table=nil)
+  def each_array(with_table = nil)
+    c = columns
+    while row = fetch_row
+      c.each_with_index do |f, i|
+        if (t = MYSQL_TYPES[@column_types[i]]) &amp;&amp; (v = row[i])
+          row[i] = v.send(t)
+        end
+      end
+      row.fields = c
+      yield row
+    end
+  end
+  
+  def each_hash(with_table = nil)
     c = columns
     while row = fetch_row
       h = {}
@@ -78,6 +104,10 @@ module Sequel
         conn
       end
       
+      def disconnect
+        @pool.disconnect {|c| c.close}
+      end
+      
       def tables
         @pool.hold do |conn|
           conn.list_tables.map {|t| t.to_sym}
@@ -95,7 +125,7 @@ module Sequel
         end
       end
       
-      def query(sql)
+      def execute_select(sql)
         @logger.info(sql) if @logger
         @pool.hold do |conn|
           conn.query(sql)
@@ -133,7 +163,7 @@ module Sequel
             result
           rescue =&gt; e
             conn.query(SQL_ROLLBACK)
-            raise e
+            raise e unless SequelRollbackError === e
           ensure
             @transactions.delete(Thread.current)
           end
@@ -144,10 +174,10 @@ module Sequel
     class Dataset &lt; Sequel::Dataset
       UNQUOTABLE_FIELD_RE = /^(`(.+)`)|\*$/.freeze
       def quote_field(f)
-        f =~ UNQUOTABLE_FIELD_RE ? f : &quot;`#{f}`&quot;
+        (f.nil? || f.empty? || f =~ UNQUOTABLE_FIELD_RE) ? f : &quot;`#{f}`&quot;
       end
       
-      FIELD_EXPR_RE = /^([^\(]+\()?([^\.]+\.)?([^\s\)]+)(\))?(\sAS\s(.+))?$/i.freeze
+      FIELD_EXPR_RE = /^([^\(]+\()?([^\.]+\.)?([^\s\)]+)?(\))?(\sAS\s(.+))?$/i.freeze
       FIELD_ORDER_RE = /^(.*) (DESC|ASC)$/i.freeze
       def quoted_field_name(name)
         case name
@@ -166,6 +196,8 @@ module Sequel
       
       def literal(v)
         case v
+        when LiteralString: quoted_field_name(v)
+        when String: super(v.gsub(/\\/, '\&amp;\&amp;'))
         when true: TRUE
         when false: FALSE
         else
@@ -215,7 +247,7 @@ module Sequel
       
       def fetch_rows(sql)
         @db.synchronize do
-          r = @db.query(sql)
+          r = @db.execute_select(sql)
           begin
             @columns = r.columns
             r.each_hash {|row| yield row}
@@ -225,6 +257,19 @@ module Sequel
         end
         self
       end
+
+      def array_tuples_fetch_rows(sql, &amp;block)
+        @db.synchronize do
+          r = @db.execute_select(sql)
+          begin
+            @columns = r.columns
+            r.each_array(&amp;block)
+          ensure
+            r.free
+          end
+        end
+        self
+      end
     end
   end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/lib/sequel/mysql.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,6 +14,10 @@ module Sequel
         conn.autocommit = true
         conn
       end
+      
+      def disconnect
+        @pool.disconnect {|c| c.disconnect}
+      end
     
       def dataset(opts = nil)
         ODBC::Dataset.new(self, opts)
@@ -86,6 +90,43 @@ module Sequel
         end
       end
       
+      def array_tuples_fetch_rows(sql, &amp;block)
+        @db.synchronize do
+          s = @db.execute sql
+          begin
+            @columns = s.columns(true).map {|c| c.name.to_sym}
+            rows = s.fetch_all
+            rows.each {|r| yield array_tuples_make_row(r)}
+          ensure
+            s.drop unless s.nil? rescue nil
+          end
+        end
+        self
+      end
+      
+      def array_tuples_make_row(row)
+        row.fields = @columns
+        row.each_with_index do |v, idx|
+          # When fetching a result set, the Ruby ODBC driver converts all ODBC 
+          # SQL types to an equivalent Ruby type; with the exception of
+          # SQL_TYPE_DATE, SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP.
+          #
+          # The conversions below are consistent with the mappings in
+          # ODBCColumn#mapSqlTypeToGenericType and Column#klass.
+          case v
+          when ::ODBC::TimeStamp
+            row[idx] = DateTime.new(v.year, v.month, v.day, v.hour, v.minute, v.second)
+          when ::ODBC::Time
+            now = DateTime.now
+            row[idx] = Time.gm(now.year, now.month, now.day, v.hour, v.minute, v.second)
+          when ::ODBC::Date
+            row[idx] = Date.new(v.year, v.month, v.day)
+          end
+        end
+        row
+      end
+      
+
       def insert(*values)
         @db.do insert_sql(*values)
       end</diff>
      <filename>vendor/ruby-sequel/lib/sequel/odbc.rb</filename>
    </modified>
    <modified>
      <diff>@@ -154,6 +154,10 @@ module Sequel
         end
         conn
       end
+      
+      def disconnect
+        @pool.disconnect {|c| c.close}
+      end
     
       def dataset(opts = nil)
         Postgres::Dataset.new(self, opts)
@@ -260,7 +264,7 @@ module Sequel
             rescue =&gt; e
               @logger.info(SQL_ROLLBACK) if @logger
               conn.async_exec(SQL_ROLLBACK) rescue nil
-              raise e
+              raise e unless SequelRollbackError === e
             ensure
               conn.transaction_in_progress = nil
             end
@@ -280,6 +284,7 @@ module Sequel
     class Dataset &lt; Sequel::Dataset
       def literal(v)
         case v
+        when LiteralString: v
         when String, Fixnum, Float, TrueClass, FalseClass: PGconn.quote(v)
         else
           super
@@ -436,6 +441,46 @@ module Sequel
         end
         eval(&quot;lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}&quot;)
       end
+
+      def array_tuples_fetch_rows(sql, &amp;block)
+        @db.synchronize do
+          result = @db.execute(sql)
+          begin
+            conv = array_tuples_row_converter(result)
+            result.each {|r| yield conv[r]}
+          ensure
+            result.clear
+          end
+        end
+      end
+      
+      @@array_tuples_converters_mutex = Mutex.new
+      @@array_tuples_converters = {}
+
+      def array_tuples_row_converter(result)
+        fields = []; translators = []
+        result.fields.each_with_index do |f, idx|
+          fields &lt;&lt; f.to_sym
+          translators &lt;&lt; PG_TYPES[result.type(idx)]
+        end
+        @columns = fields
+        
+        # create result signature and memoize the converter
+        sig = [fields, translators].hash
+        @@array_tuples_converters_mutex.synchronize do
+          @@array_tuples_converters[sig] ||= array_tuples_compile_converter(fields, translators)
+        end
+      end
+    
+      def array_tuples_compile_converter(fields, translators)
+        tr = []
+        fields.each_with_index do |field, idx|
+          if t = translators[idx]
+            tr &lt;&lt; &quot;if (v = r[#{idx}]); r[#{idx}] = v.#{t}; end&quot;
+          end
+        end
+        eval(&quot;lambda {|r| r.fields = fields; #{tr.join(';')}; r}&quot;)
+      end
     end
   end
 end</diff>
      <filename>vendor/ruby-sequel/lib/sequel/postgres.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,17 +1,20 @@
-# Print nice-looking plain-text tables
-# +--+-------+
-# |id|name   |
-# |--+-------|
-# |1 |fasdfas|
-# |2 |test   |
-# +--+-------+
-
 module Sequel
+  # Prints nice-looking plain-text tables
+  # +--+-------+
+  # |id|name   |
+  # |--+-------|
+  # |1 |fasdfas|
+  # |2 |test   |
+  # +--+-------+
   module PrettyTable
-    def self.hash_columns(records)
+    def self.records_columns(records)
       columns = []
       records.each do |r|
-        r.keys.each {|k| columns &lt;&lt; k unless columns.include?(k)}
+        if Array === r &amp;&amp; (f = r.fields)
+          return r.fields
+        elsif Hash === r
+          r.keys.each {|k| columns &lt;&lt; k unless columns.include?(k)}
+        end
       end
       columns
     end
@@ -53,7 +56,7 @@ module Sequel
     end
     
     def self.print(records, columns = nil) # records is an array of hashes
-      columns ||= hash_columns(records)
+      columns ||= records_columns(records)
       sizes = column_sizes(records, columns)
       
       puts separator_line(columns, sizes)</diff>
      <filename>vendor/ruby-sequel/lib/sequel/pretty_table.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,6 +6,7 @@ module Sequel
         @table_name = table_name
         @columns = []
         @indexes = []
+        @primary_key = nil
         instance_eval(&amp;block)
       end
       
@@ -17,12 +18,18 @@ module Sequel
         @primary_key ? @primary_key[:name] : nil
       end
       
-      def primary_key(name, type = nil, opts = nil)
+      def primary_key(name, *args)
         @primary_key = @db.serial_primary_key_options.merge({
           :name =&gt; name
         })
-        @primary_key.merge!({:type =&gt; type}) if type
-        @primary_key.merge!(opts) if opts
+        
+        if opts = args.pop
+          opts = {:type =&gt; opts} unless opts.is_a?(Hash)
+          if type = args.pop
+            opts.merge!(:type =&gt; type)
+          end
+          @primary_key.merge!(opts)
+        end
         @primary_key
       end
       </diff>
      <filename>vendor/ruby-sequel/lib/sequel/schema/schema_generator.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,150 @@ end
 
 require 'sqlite3'
 
+class String
+  def sqlite_to_bool
+    !(strip.gsub(/00+/,&quot;0&quot;) == &quot;0&quot; ||
+      downcase == &quot;false&quot; ||
+      downcase == &quot;f&quot; ||
+      downcase == &quot;no&quot; ||
+      downcase == &quot;n&quot;)
+  end
+end
+
+module SQLite3
+  class ResultSet
+    SQLITE_TYPES = {
+      :date =&gt; :to_time,
+      :datetime =&gt; :to_time,
+      :time =&gt; :to_time,
+      :timestamp =&gt; :to_time,
+      
+      :decimal =&gt; :to_f,
+      :float =&gt; :to_f,
+      :numeric =&gt; :to_f,
+      :double =&gt; :to_f,
+      :real =&gt; :to_f,
+      :dec =&gt; :to_f,
+      :fixed =&gt; :to_f,
+      
+      :integer =&gt; :to_i,
+      :smallint =&gt; :to_i,
+      :mediumint =&gt; :to_i,
+      :int =&gt; :to_i,
+      :bigint =&gt; :to_i,
+      
+      :bit =&gt; :sqlite_to_bool,
+      :bool =&gt; :sqlite_to_bool,
+      :boolean =&gt; :sqlite_to_bool,
+      
+      :tinyint =&gt; :to_i
+    }
+    
+    COMMA_SEPARATOR = ', '.freeze
+    
+    @@fetchers_mutex = Mutex.new
+    @@fetchers = {}
+
+    def prepare_row_fetcher
+      column_count = @driver.data_count(@stmt.handle)
+      columns = @stmt.columns.map {|c| c.to_sym}
+      translators = []
+      column_count.times do |idx|
+        t = @driver.column_decltype(@stmt.handle, idx) || :text
+        translators &lt;&lt; SQLITE_TYPES[t.to_sym]
+      end
+      sig = [columns, translators].hash
+      @@fetchers_mutex.synchronize do
+        fetcher = (@@fetchers[sig] ||= compile_fetcher(columns, translators))
+        meta_def(:fetch_hash, &amp;fetcher)
+      end
+    end
+  
+    def compile_fetcher(columns, translators)
+      used_columns = []
+      kvs = []
+      columns.each_with_index do |column, idx|
+        next if used_columns.include?(column)
+        used_columns &lt;&lt; column
+      
+        if translator = translators[idx]
+          kvs &lt;&lt; &quot;#{column.inspect} =&gt; ((t = @driver.column_text(@stmt.handle, #{idx})) ? t.#{translator} : nil)&quot;
+        else
+          kvs &lt;&lt; &quot;#{column.inspect} =&gt; @driver.column_text(@stmt.handle, #{idx})&quot;
+        end
+      end
+      eval(&quot;lambda {{#{kvs.join(COMMA_SEPARATOR)}}}&quot;)
+    end
+
+
+    def next_hash_tuple
+      return nil if @eof
+      @stmt.must_be_open!
+
+      if @first_row
+        prepare_row_fetcher
+        # @row_fetcher = prepare_row_fetcher
+      else
+        result = @driver.step(@stmt.handle)
+        check result
+      end
+      @first_row = false
+      
+      @eof ? nil : fetch_hash # @row_fetcher.call
+    end
+
+    def next_array_tuple
+      return nil if @eof
+      @stmt.must_be_open!
+
+      unless @first_row
+        result = @driver.step(@stmt.handle)
+        check result
+      end
+      @first_row = false
+
+      columns = @stmt.columns
+
+      unless @eof
+        row = []
+        @driver.data_count( @stmt.handle ).times do |idx|
+          case @driver.column_type( @stmt.handle, idx )
+          when Constants::ColumnType::NULL then
+            row &lt;&lt; nil
+          when Constants::ColumnType::BLOB then
+            row &lt;&lt; @driver.column_blob( @stmt.handle, idx )
+          else
+            v = @driver.column_text( @stmt.handle, idx )
+            row &lt;&lt; @db.translator.translate(@driver.column_decltype(@stmt.handle, idx), v)
+          end
+        end
+
+        row.extend FieldsContainer unless row.respond_to?(:fields)
+        row.fields = @stmt.columns
+
+        row.extend TypesContainer
+        row.types = @stmt.types
+
+        return row
+      end
+
+      nil
+    end
+
+    def each_hash
+      while row=self.next_hash_tuple
+        yield row
+      end
+    end
+
+    def each_array
+      while row=self.next_array_tuple
+        yield row
+      end
+    end
+  end
+end
+
 module Sequel
   module SQLite
     class Database &lt; Sequel::Database
@@ -21,6 +165,10 @@ module Sequel
         db.type_translation = true
         db
       end
+      
+      def disconnect
+        @pool.disconnect {|c| c.close}
+      end
     
       def dataset(opts = nil)
         SQLite::Dataset.new(self, opts)
@@ -47,7 +195,7 @@ module Sequel
         @pool.hold {|conn| conn.get_first_value(sql)}
       end
       
-      def query(sql, &amp;block)
+      def execute_select(sql, &amp;block)
         @logger.info(sql) if @logger
         @pool.hold {|conn| conn.query(sql, &amp;block)}
       end
@@ -93,6 +241,20 @@ module Sequel
         pragma_set(:temp_store, value)
       end
       
+      def transaction(&amp;block)
+        @pool.hold do |conn|
+          if conn.transaction_active?
+            return yield(conn)
+          end
+          begin
+            result = nil
+            conn.transaction {result = yield(conn)}
+            result
+          rescue =&gt; e
+            raise e unless SequelRollbackError === e
+          end
+        end
+      end
     end
     
     class Dataset &lt; Sequel::Dataset
@@ -104,15 +266,25 @@ module Sequel
         end
       end
 
+      def insert_sql(*values)
+        if (values.size == 1) &amp;&amp; values.first.is_a?(Sequel::Dataset)
+          &quot;INSERT INTO #{@opts[:from]} #{values.first.sql};&quot;
+        else
+          super(*values)
+        end
+      end
+
       def fetch_rows(sql, &amp;block)
-        @db.query(sql) do |result|
+        @db.execute_select(sql) do |result|
           @columns = result.columns.map {|c| c.to_sym}
-          column_count = @columns.size
-          result.each do |values|
-            row = {}
-            column_count.times {|i| row[@columns[i]] = values[i]}
-            block.call(row)
-          end
+          result.each_hash(&amp;block)
+        end
+      end
+      
+      def array_tuples_fetch_rows(sql, &amp;block)
+        @db.execute_select(sql) do |result|
+          @columns = result.columns.map {|c| c.to_sym}
+          result.each_array(&amp;block)
         end
       end
     </diff>
      <filename>vendor/ruby-sequel/lib/sequel/sqlite.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,6 +11,19 @@ MYSQL_DB.create_table :items do
   index :value
 end
 
+context &quot;A MySQL database&quot; do
+  setup do
+    @db = MYSQL_DB
+  end
+  
+  specify &quot;should provide disconnect functionality&quot; do
+    @db.tables
+    @db.pool.size.should == 1
+    @db.disconnect
+    @db.pool.size.should == 0
+  end
+end
+
 context &quot;A MySQL dataset&quot; do
   setup do
     @d = MYSQL_DB[:items]
@@ -25,24 +38,19 @@ context &quot;A MySQL dataset&quot; do
     @d.count.should == 3
   end
   
-  # specify &quot;should return the last inserted id when inserting records&quot; do
-  #   id = @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 1.23}
-  #   id.should == @d.first[:id]
-  # end
-  #
-  
-  specify &quot;should return all records&quot; do
+  specify &quot;should return the correct records&quot; do
+    @d.to_a.should == []
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
     @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 789}
-    
-    @d.order(:value).all.should == [
+
+    @d.order(:value).to_a.should == [
       {:name =&gt; 'abc', :value =&gt; 123},
       {:name =&gt; 'abc', :value =&gt; 456},
       {:name =&gt; 'def', :value =&gt; 789}
     ]
   end
-   
+  
   specify &quot;should update records correctly&quot; do
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
@@ -74,11 +82,14 @@ context &quot;A MySQL dataset&quot; do
     @d.select(:name).sql.should == \
       'SELECT `name` FROM items'
       
-    @d.select('COUNT(*)').sql.should == \
+    @d.select('COUNT(*)'.lit).sql.should == \
       'SELECT COUNT(*) FROM items'
 
     @d.select(:value.MAX).sql.should == \
       'SELECT max(`value`) FROM items'
+      
+    @d.select(:NOW[]).sql.should == \
+    'SELECT NOW() FROM items'
 
     @d.select(:items__value.MAX).sql.should == \
       'SELECT max(items.`value`) FROM items'
@@ -86,13 +97,13 @@ context &quot;A MySQL dataset&quot; do
     @d.order(:name.DESC).sql.should == \
       'SELECT * FROM items ORDER BY `name` DESC'
       
-    @d.select('items.name AS item_name').sql.should == \
+    @d.select('items.name AS item_name'.to_sym).sql.should == \
       'SELECT items.`name` AS `item_name` FROM items'
       
-    @d.select('`name`').sql.should == \
+    @d.select('`name`'.lit).sql.should == \
       'SELECT `name` FROM items'
 
-    @d.select('max(items.`name`) as `max_name`').sql.should == \
+    @d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
       'SELECT max(items.`name`) AS `max_name` FROM items'
 
     @d.insert_sql(:value =&gt; 333).should == \
@@ -123,4 +134,50 @@ context &quot;A MySQL dataset&quot; do
     @d.filter(:name =&gt; /bc/).count.should == 2
     @d.filter(:name =&gt; /^bc/).count.should == 1
   end
-end
\ No newline at end of file
+end
+
+context &quot;A MySQL dataset in array tuples mode&quot; do
+  setup do
+    @d = MYSQL_DB[:items]
+    @d.delete # remove all records
+    Sequel.use_array_tuples
+  end
+  
+  teardown do
+    Sequel.use_hash_tuples
+  end
+  
+  specify &quot;should return the correct records&quot; do
+    @d.to_a.should == []
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
+    @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 789}
+
+    @d.order(:value).select(:name, :value).to_a.should == [
+      ['abc', 123],
+      ['abc', 456],
+      ['def', 789]
+    ]
+  end
+  
+  specify &quot;should work correctly with transforms&quot; do
+    @d.transform(:value =&gt; [proc {|v| v.to_s}, proc {|v| v.to_i}])
+
+    @d.to_a.should == []
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
+    @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 789}
+
+    @d.order(:value).select(:name, :value).to_a.should == [
+      ['abc', '123'],
+      ['abc', '456'],
+      ['def', '789']
+    ]
+    
+    a = @d.order(:value).first
+    a.values.should == ['abc', '123']
+    a.keys.should == [:name, :value]
+    a[:name].should == 'abc'
+    a[:value].should == '123'
+  end
+end</diff>
      <filename>vendor/ruby-sequel/spec/adapters/mysql_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,7 +11,20 @@ PGSQL_DB.create_table :test do
   index :value
 end
 
-context &quot;A MySQL dataset&quot; do
+context &quot;A PostgreSQL database&quot; do
+  setup do
+    @db = PGSQL_DB
+  end
+  
+  specify &quot;should provide disconnect functionality&quot; do
+    @db.tables
+    @db.pool.size.should == 1
+    @db.disconnect
+    @db.pool.size.should == 0
+  end
+end
+
+context &quot;A PostgreSQL dataset&quot; do
   setup do
     @d = PGSQL_DB[:test]
     @d.delete # remove all records
@@ -25,24 +38,19 @@ context &quot;A MySQL dataset&quot; do
     @d.count.should == 3
   end
   
-  # specify &quot;should return the last inserted id when inserting records&quot; do
-  #   id = @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 1.23}
-  #   id.should == @d.first[:id]
-  # end
-  #
-  
-  specify &quot;should return all records&quot; do
+  specify &quot;should return the correct records&quot; do
+    @d.to_a.should == []
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
     @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 789}
-    
-    @d.order(:value).all.should == [
+
+    @d.order(:value).to_a.should == [
       {:name =&gt; 'abc', :value =&gt; 123},
       {:name =&gt; 'abc', :value =&gt; 456},
       {:name =&gt; 'def', :value =&gt; 789}
     ]
   end
-   
+  
   specify &quot;should update records correctly&quot; do
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
@@ -84,4 +92,50 @@ context &quot;A MySQL dataset&quot; do
     @d.filter(:name =&gt; /bc/).count.should == 2
     @d.filter(:name =&gt; /^bc/).count.should == 1
   end
-end
\ No newline at end of file
+end
+
+context &quot;A PostgreSQL dataset in array tuples mode&quot; do
+  setup do
+    @d = PGSQL_DB[:test]
+    @d.delete # remove all records
+    Sequel.use_array_tuples
+  end
+  
+  teardown do
+    Sequel.use_hash_tuples
+  end
+  
+  specify &quot;should return the correct records&quot; do
+    @d.to_a.should == []
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
+    @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 789}
+
+    @d.order(:value).select(:name, :value).to_a.should == [
+      ['abc', 123],
+      ['abc', 456],
+      ['def', 789]
+    ]
+  end
+  
+  specify &quot;should work correctly with transforms&quot; do
+    @d.transform(:value =&gt; [proc {|v| v.to_s}, proc {|v| v.to_i}])
+
+    @d.to_a.should == []
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 123}
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 456}
+    @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 789}
+
+    @d.order(:value).select(:name, :value).to_a.should == [
+      ['abc', '123'],
+      ['abc', '456'],
+      ['def', '789']
+    ]
+    
+    a = @d.order(:value).first
+    a.values.should == ['abc', '123']
+    a.keys.should == [:name, :value]
+    a[:name].should == 'abc'
+    a[:value].should == '123'
+  end
+end</diff>
      <filename>vendor/ruby-sequel/spec/adapters/postgres_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -72,6 +72,54 @@ context &quot;An SQLite database&quot; do
     
     @db[:t].order(:name).map(:name).should == ['abc', 'def']
   end
+  
+  specify &quot;should be able to execute transactions&quot; do
+    @db.transaction do
+      @db.create_table(:t) {text :name}
+    end
+    
+    @db.tables.should == [:t]
+
+    proc {@db.transaction do
+      @db.create_table(:u) {text :name}
+      raise ArgumentError
+    end}.should raise_error(ArgumentError)
+    # no commit
+    @db.tables.should == [:t]
+
+    proc {@db.transaction do
+      @db.create_table(:v) {text :name}
+      rollback!
+    end}.should_not raise_error
+    # no commit
+    @db.tables.should == [:t]
+  end
+
+  specify &quot;should support nested transactions&quot; do
+    @db.transaction do
+      @db.transaction do
+        @db.create_table(:t) {text :name}
+      end
+    end
+    
+    @db.tables.should == [:t]
+
+    proc {@db.transaction do
+      @db.create_table(:v) {text :name}
+      @db.transaction do
+        rollback! # should roll back the top-level transaction
+      end
+    end}.should_not raise_error
+    # no commit
+    @db.tables.should == [:t]
+  end
+  
+  specify &quot;should provide disconnect functionality&quot; do
+    @db.tables
+    @db.pool.size.should == 1
+    @db.disconnect
+    @db.pool.size.should == 0
+  end
 end
 
 context &quot;An SQLite dataset&quot; do
@@ -80,6 +128,18 @@ context &quot;An SQLite dataset&quot; do
     @d.delete # remove all records
   end
   
+  specify &quot;should return the correct records&quot; do
+    @d.to_a.should == []
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 1.23}
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 4.56}
+    @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 7.89}
+    @d.select(:name, :value).to_a.sort_by {|h| h[:value]}.should == [
+      {:name =&gt; 'abc', :value =&gt; 1.23},
+      {:name =&gt; 'abc', :value =&gt; 4.56},
+      {:name =&gt; 'def', :value =&gt; 7.89}
+    ]
+  end
+  
   specify &quot;should return the correct record count&quot; do
     @d.count.should == 0
     @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 1.23}
@@ -87,7 +147,7 @@ context &quot;An SQLite dataset&quot; do
     @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 7.89}
     @d.count.should == 3
   end
-  
+
   specify &quot;should return the last inserted id when inserting records&quot; do
     id = @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 1.23}
     id.should == @d.first[:id]
@@ -186,3 +246,55 @@ context &quot;SQLite::Dataset#update&quot; do
   end
 end
 
+context &quot;An SQLite dataset in array tuples mode&quot; do
+  setup do
+    @d = SQLITE_DB[:items]
+    @d.delete # remove all records
+    
+    Sequel.use_array_tuples
+  end
+  
+  teardown do
+    Sequel.use_hash_tuples
+  end
+  
+  specify &quot;should return the correct records&quot; do
+    @d.to_a.should == []
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 1.23}
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 4.56}
+    @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 7.89}
+    @d.select(:name, :value).to_a.sort_by {|h| h[:value]}.should == [
+      Array.from_hash({:name =&gt; 'abc', :value =&gt; 1.23}),
+      Array.from_hash({:name =&gt; 'abc', :value =&gt; 4.56}),
+      Array.from_hash({:name =&gt; 'def', :value =&gt; 7.89})
+    ]
+  end
+end  
+
+context &quot;SQLite dataset&quot; do
+  setup do
+    SQLITE_DB.create_table :test do
+      integer :id, :primary_key =&gt; true, :auto_increment =&gt; true
+      text :name
+      float :value
+    end
+
+    @d = SQLITE_DB[:items]
+    @d.delete # remove all records
+    @d &lt;&lt; {:name =&gt; 'abc', :value =&gt; 1.23}
+    @d &lt;&lt; {:name =&gt; 'def', :value =&gt; 4.56}
+    @d &lt;&lt; {:name =&gt; 'ghi', :value =&gt; 7.89}
+  end
+  
+  teardown do
+    SQLITE_DB.drop_table :test
+  end
+  
+  specify &quot;should be able to insert from a subquery&quot; do
+    SQLITE_DB[:test] &lt;&lt; @d
+    SQLITE_DB[:test].count.should == 3
+    SQLITE_DB[:test].select(:name, :value).order(:value).to_a.should == \
+      @d.select(:name, :value).order(:value).to_a
+  end
+end
+</diff>
      <filename>vendor/ruby-sequel/spec/adapters/sqlite_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -267,4 +267,87 @@ context &quot;A connection pool with a max size of 5&quot; do
     @pool.available_connections.size.should == 5
     @pool.allocated.should be_empty
   end
+end
+
+context &quot;ConnectionPool#disconnect&quot; do
+  setup do
+    @count = 0
+    @pool = Sequel::ConnectionPool.new(5) {{:id =&gt; @count += 1}}
+  end
+  
+  specify &quot;should invoke the given block for each available connection&quot; do
+    threads = []
+    stop = nil
+    5.times {|i| threads &lt;&lt; Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
+    while @pool.size &lt; 5
+      sleep 0.2
+    end
+    stop = true
+    sleep 0.2
+    
+    @pool.size.should == 5
+    @pool.available_connections.size.should == 5
+    @pool.available_connections.each {|c| c[:id].should_not be_nil}
+    conns = []
+    @pool.disconnect {|c| conns &lt;&lt; c}
+    conns.size.should == 5
+  end
+  
+  specify &quot;should remove all available connections&quot; do
+    threads = []
+    stop = nil
+    5.times {|i| threads &lt;&lt; Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
+    while @pool.size &lt; 5
+      sleep 0.2
+    end
+    stop = true
+    sleep 0.2
+    
+    @pool.size.should == 5
+    @pool.disconnect
+    @pool.size.should == 0
+  end
+
+  specify &quot;should not touch connections in use&quot; do
+    threads = []
+    stop = nil
+    5.times {|i| threads &lt;&lt; Thread.new {@pool.hold {|c| while !stop;sleep 0.1;end}}; sleep 0.1}
+    while @pool.size &lt; 5
+      sleep 0.2
+    end
+    stop = true
+    sleep 0.2
+    
+    @pool.size.should == 5
+    
+    @pool.hold do |conn|
+      @pool.available_connections.size.should == 4
+      @pool.available_connections.each {|c| c.should_not be(conn)}
+      conns = []
+      @pool.disconnect {|c| conns &lt;&lt; c}
+      conns.size.should == 4
+    end
+    @pool.size.should == 1
+  end
+end
+
+context &quot;SingleThreadedPool&quot; do
+  setup do
+    @pool = Sequel::SingleThreadedPool.new {1234}
+  end
+  
+  specify &quot;should provide a #hold method&quot; do
+    conn = nil
+    @pool.hold {|c| conn = c}
+    conn.should == 1234
+  end
+  
+  specify &quot;should provide a #disconnect method&quot; do
+    @pool.hold {|c|}
+    @pool.conn.should == 1234
+    conn = nil
+    @pool.disconnect {|c| conn = c}
+    conn.should == 1234
+    @pool.conn.should be_nil
+  end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/spec/connection_pool_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -34,6 +34,12 @@ context &quot;Database#connect&quot; do
   end
 end
 
+context &quot;Database#disconnect&quot; do
+  specify &quot;should raise NotImplementedError&quot; do
+    proc {Sequel::Database.new.disconnect}.should raise_error(NotImplementedError)
+  end
+end
+
 context &quot;Database#uri&quot; do
   setup do
     @c = Class.new(Sequel::Database) do
@@ -125,16 +131,21 @@ context &quot;Database#&lt;&lt;&quot; do
       &quot;CREATE TABLE items (a integer, b text, c integer); DROP TABLE old_items;&quot;
   end
   
-  specify &quot;should remove comments and whitespace from strings as well&quot; do
+  specify &quot;should remove comments and whitespace from arrays&quot; do
     s = %[
       --
       CREATE TABLE items (a integer, /*b integer*/
         b text, c integer); \r\n
       DROP TABLE old_items;
-    ]
+    ].split($/)
     (@db &lt;&lt; s).should == 
       &quot;CREATE TABLE items (a integer, b text, c integer); DROP TABLE old_items;&quot;
   end
+  
+  specify &quot;should not remove comments and whitespace from strings&quot; do
+    s = &quot;INSERT INTO items VALUES ('---abc')&quot;
+    (@db &lt;&lt; s).should == s
+  end
 end
 
 context &quot;Database#synchronize&quot; do
@@ -289,6 +300,16 @@ context &quot;Database#transaction&quot; do
     proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
   end
   
+  specify &quot;should issue ROLLBACK if rollback! is called in the transaction&quot; do
+    @db.transaction do
+      @db.drop_table(:a)
+      rollback!
+      @db.drop_table(:b)
+    end
+    
+    @db.sql.should == ['BEGIN', 'DROP TABLE a;', 'ROLLBACK']
+  end
+  
   specify &quot;should be re-entrant&quot; do
     stop = false
     cc = nil
@@ -476,6 +497,95 @@ context &quot;A database&quot; do
     db = Sequel::Database.new(:max_options =&gt; 4)
     db.should be_single_threaded
     db.should_not be_multi_threaded
+  end
+  
+  specify &quot;should accept a logger object&quot; do
+    db = Sequel::Database.new
+    s = &quot;I'm a logger&quot;
+    db.logger = s
+    db.logger.should be(s)
+    db.logger = nil
+    db.logger.should be_nil
+  end
+end
+
+context &quot;Database#dataset&quot; do
+  setup do
+    @db = Sequel::Database.new
+  end
+  
+  specify &quot;should delegate to Dataset#query if block is provided&quot; do
+    @d = @db.query {select :x; from :y}
+    @d.should be_a_kind_of(Sequel::Dataset)
+    @d.sql.should == &quot;SELECT x FROM y&quot;
+  end
+end
+
+context &quot;Database#fetch&quot; do
+  setup do
+    @db = Sequel::Database.new
+    c = Class.new(Sequel::Dataset) do
+      def fetch_rows(sql); yield({:sql =&gt; sql}); end
+    end
+    @db.meta_def(:dataset) {c.new(self)}
+  end
+  
+  specify &quot;should create a dataset and invoke its fetch_rows method with the given sql&quot; do
+    sql = nil
+    @db.fetch('select * from xyz') {|r| sql = r[:sql]}
+    sql.should == 'select * from xyz'
+  end
+  
+  specify &quot;should format the given sql with any additional arguments&quot; do
+    sql = nil
+    @db.fetch('select * from xyz where x = ? and y = ?', 15, 'abc') {|r| sql = r[:sql]}
+    sql.should == &quot;select * from xyz where x = 15 and y = 'abc'&quot;
     
+    # and Aman Gupta's example
+    @db.fetch('select name from table where name = ? or id in (?)',
+    'aman', [3,4,7]) {|r| sql = r[:sql]}
+    sql.should == &quot;select name from table where name = 'aman' or id in (3, 4, 7)&quot;
+  end
+  
+  specify &quot;should return the dataset if no block is given&quot; do
+    @db.fetch('select * from xyz').should be_a_kind_of(Sequel::Dataset)
+    
+    @db.fetch('select a from b').map {|r| r[:sql]}.should == ['select a from b']
+
+    @db.fetch('select c from d').inject([]) {|m, r| m &lt;&lt; r; m}.should == \
+      [{:sql =&gt; 'select c from d'}]
+  end
+  
+  specify &quot;should return a dataset that always uses the given sql for SELECTs&quot; do
+    ds = @db.fetch('select * from xyz')
+    ds.select_sql.should == 'select * from xyz'
+    ds.sql.should == 'select * from xyz'
+    
+    ds.filter! {:price &lt; 100}
+    ds.select_sql.should == 'select * from xyz'
+    ds.sql.should == 'select * from xyz'
+  end
+end
+
+context &quot;Database#[]&quot; do
+  setup do
+    @db = Sequel::Database.new
+  end
+  
+  specify &quot;should return a dataset when symbols are given&quot; do
+    ds = @db[:items]
+    ds.class.should == Sequel::Dataset
+    ds.opts[:from].should == [:items]
+  end
+  
+  specify &quot;should return an enumerator when a string is given&quot; do
+    c = Class.new(Sequel::Dataset) do
+      def fetch_rows(sql); yield({:sql =&gt; sql}); end
+    end
+    @db.meta_def(:dataset) {c.new(self)}
+
+    sql = nil
+    @db['select * from xyz where x = ? and y = ?', 15, 'abc'].each {|r| sql = r[:sql]}
+    sql.should == &quot;select * from xyz where x = 15 and y = 'abc'&quot;
   end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/spec/database_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -105,12 +105,28 @@ context &quot;A simple dataset&quot; do
   specify &quot;should format an insert statement with hash&quot; do
     @dataset.insert_sql(:name =&gt; 'wxyz', :price =&gt; 342).
       should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
+
+      @dataset.insert_sql({}).should == &quot;INSERT INTO test DEFAULT VALUES;&quot;
+  end
+
+  specify &quot;should format an insert statement with array fields&quot; do
+    v = [1, 2, 3]
+    v.fields = [:a, :b, :c]
+    @dataset.insert_sql(v).should == &quot;INSERT INTO test (a, b, c) VALUES (1, 2, 3);&quot;
+    
+    v = []
+    v.fields = [:a, :b]
+    @dataset.insert_sql(v).should == &quot;INSERT INTO test DEFAULT VALUES;&quot;
+  end
+  
+  specify &quot;should format an insert statement with an arbitrary value&quot; do
+    @dataset.insert_sql(123).should == &quot;INSERT INTO test VALUES (123);&quot;
   end
   
   specify &quot;should format an insert statement with sub-query&quot; do
     @sub = Sequel::Dataset.new(nil).from(:something).filter(:x =&gt; 2)
     @dataset.insert_sql(@sub).should == \
-      &quot;INSERT INTO test (SELECT * FROM something WHERE (x = 2))&quot;
+      &quot;INSERT INTO test (SELECT * FROM something WHERE (x = 2));&quot;
   end
   
   specify &quot;should format an insert statement with array&quot; do
@@ -123,6 +139,13 @@ context &quot;A simple dataset&quot; do
       &quot;UPDATE test SET name = 'abc'&quot;
   end
   
+  specify &quot;should format an update statement with array fields&quot; do
+    v = ['abc']
+    v.fields = [:name]
+    
+    @dataset.update_sql(v).should == &quot;UPDATE test SET name = 'abc'&quot;
+  end
+  
   specify &quot;should be able to return rows for arbitrary SQL&quot; do
     @dataset.select_sql(:sql =&gt; 'xxx yyy zzz').should ==
       &quot;xxx yyy zzz&quot;
@@ -244,6 +267,16 @@ context &quot;Dataset#where&quot; do
 
     @dataset.filter {:id.in?(4..7)}.sql.should ==
       'SELECT * FROM test WHERE (id &gt;= 4 AND id &lt;= 7)'
+
+    @dataset.filter(:table__id =&gt; 4..7).sql.should ==
+      'SELECT * FROM test WHERE (table.id &gt;= 4 AND table.id &lt;= 7)'
+    @dataset.filter(:table__id =&gt; 4...7).sql.should ==
+      'SELECT * FROM test WHERE (table.id &gt;= 4 AND table.id &lt; 7)'
+
+    @dataset.filter {:table__id == (4..7)}.sql.should ==
+      'SELECT * FROM test WHERE (table.id &gt;= 4 AND table.id &lt;= 7)'
+    @dataset.filter {:table__id.in?(4..7)}.sql.should ==
+      'SELECT * FROM test WHERE (table.id &gt;= 4 AND table.id &lt;= 7)'
   end
   
   specify &quot;should accept nil&quot; do
@@ -446,6 +479,24 @@ context &quot;a grouped dataset&quot; do
   end
 end
 
+context &quot;Dataset#group_by&quot; do
+  setup do
+    @dataset = Sequel::Dataset.new(nil).from(:test).group_by(:type_id)
+  end
+
+  specify &quot;should raise when trying to generate an update statement&quot; do
+    proc {@dataset.update_sql(:id =&gt; 0)}.should raise_error
+  end
+
+  specify &quot;should raise when trying to generate a delete statement&quot; do
+    proc {@dataset.delete_sql}.should raise_error
+  end
+
+  specify &quot;should specify the grouping in generated select statement&quot; do
+    @dataset.select_sql.should ==
+      &quot;SELECT * FROM test GROUP BY type_id&quot;
+  end
+end
 
 context &quot;Dataset#literal&quot; do
   setup do
@@ -519,7 +570,26 @@ context &quot;Dataset#from&quot; do
 
   specify &quot;should format a Dataset as a subquery if it has had options set&quot; do
     @dataset.from(@dataset.from(:a).where(:a=&gt;1)).select_sql.should ==
-      &quot;SELECT * FROM (SELECT * FROM a WHERE (a = 1))&quot;
+      &quot;SELECT * FROM (SELECT * FROM a WHERE (a = 1)) t1&quot;
+  end
+  
+  specify &quot;should automatically alias sub-queries&quot; do
+    @dataset.from(@dataset.from(:a).group(:b)).select_sql.should ==
+      &quot;SELECT * FROM (SELECT * FROM a GROUP BY b) t1&quot;
+      
+    d1 = @dataset.from(:a).group(:b)
+    d2 = @dataset.from(:c).group(:d)
+    
+    @dataset.from(d1, d2).sql.should == 
+      &quot;SELECT * FROM (SELECT * FROM a GROUP BY b) t1, (SELECT * FROM c GROUP BY d) t2&quot;
+  end
+  
+  specify &quot;should accept a hash for aliasing&quot; do
+    @dataset.from(:a =&gt; :b).sql.should ==
+      &quot;SELECT * FROM a b&quot;
+      
+    @dataset.from(@dataset.from(:a).group(:b) =&gt; :c).sql.should ==
+      &quot;SELECT * FROM (SELECT * FROM a GROUP BY b) c&quot;
   end
 
   specify &quot;should use the relevant table name if given a simple dataset&quot; do
@@ -542,12 +612,12 @@ context &quot;Dataset#select&quot; do
     @d.select(:a, :b, :test__c).sql.should == 'SELECT a, b, test.c FROM test'
   end
   
-  specify &quot;should accept mixed types (strings and symbols)&quot; do
-    @d.select('aaa').sql.should == 'SELECT aaa FROM test'
-    @d.select(:a, 'b').sql.should == 'SELECT a, b FROM test'
-    @d.select(:test__cc, 'test.d AS e').sql.should == 
+  specify &quot;should accept symbols and literal strings&quot; do
+    @d.select('aaa'.lit).sql.should == 'SELECT aaa FROM test'
+    @d.select(:a, 'b'.lit).sql.should == 'SELECT a, b FROM test'
+    @d.select(:test__cc, 'test.d AS e'.lit).sql.should == 
       'SELECT test.cc, test.d AS e FROM test'
-    @d.select('test.d AS e', :test__cc).sql.should == 
+    @d.select('test.d AS e'.lit, :test__cc).sql.should == 
       'SELECT test.d AS e, test.cc FROM test'
 
     # symbol helpers      
@@ -572,6 +642,14 @@ context &quot;Dataset#select&quot; do
     @d.select(:a, :b, :c).select.sql.should == 'SELECT * FROM test'
     @d.select(:price).select(:name).sql.should == 'SELECT name FROM test'
   end
+  
+  specify &quot;should accept arbitrary objects and literalize them correctly&quot; do
+    @d.select(1, :a, 't').sql.should == &quot;SELECT 1, a, 't' FROM test&quot;
+
+    @d.select(nil, :sum[:t], :x___y).sql.should == &quot;SELECT NULL, sum(t), x AS y FROM test&quot;
+
+    @d.select(nil, 1, :x =&gt; :y).sql.should == &quot;SELECT NULL, 1, x AS y FROM test&quot;
+  end
 end
 
 context &quot;Dataset#order&quot; do
@@ -595,7 +673,33 @@ context &quot;Dataset#order&quot; do
   end
   
   specify &quot;should accept a string&quot; do
-    @dataset.order('dada ASC').sql.should ==
+    @dataset.order('dada ASC'.lit).sql.should ==
+      'SELECT * FROM test ORDER BY dada ASC'
+  end
+end
+
+context &quot;Dataset#order_by&quot; do
+  setup do
+    @dataset = Sequel::Dataset.new(nil).from(:test)
+  end
+  
+  specify &quot;should include an ORDER BY clause in the select statement&quot; do
+    @dataset.order_by(:name).sql.should == 
+      'SELECT * FROM test ORDER BY name'
+  end
+  
+  specify &quot;should accept multiple arguments&quot; do
+    @dataset.order_by(:name, :price.DESC).sql.should ==
+      'SELECT * FROM test ORDER BY name, price DESC'
+  end
+  
+  specify &quot;should overrun a previous ordering&quot; do
+    @dataset.order_by(:name).order(:stamp).sql.should ==
+      'SELECT * FROM test ORDER BY stamp'
+  end
+  
+  specify &quot;should accept a string&quot; do
+    @dataset.order_by('dada ASC'.lit).sql.should ==
       'SELECT * FROM test ORDER BY dada ASC'
   end
 end
@@ -871,6 +975,17 @@ context &quot;Dataset#join_table&quot; do
     @d.from('stats s').join('players p', :id =&gt; :player_id).sql.should ==
       'SELECT * FROM stats s INNER JOIN players p ON (p.id = s.player_id)'
   end
+  
+  specify &quot;should allow for arbitrary conditions in the JOIN clause&quot; do
+    @d.join_table(:left_outer, :categories, :id =&gt; :category_id, :status =&gt; 0).sql.should ==
+      'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.id = items.category_id) AND (categories.status = 0)'
+    @d.join_table(:left_outer, :categories, :id =&gt; :category_id, :categorizable_type =&gt; &quot;Post&quot;).sql.should ==
+      &quot;SELECT * FROM items LEFT OUTER JOIN categories ON (categories.categorizable_type = 'Post') AND (categories.id = items.category_id)&quot;
+    @d.join_table(:left_outer, :categories, :id =&gt; :category_id, :timestamp =&gt; &quot;CURRENT_TIMESTAMP&quot;.lit).sql.should ==
+      &quot;SELECT * FROM items LEFT OUTER JOIN categories ON (categories.id = items.category_id) AND (categories.timestamp = CURRENT_TIMESTAMP)&quot;
+    @d.join_table(:left_outer, :categories, :id =&gt; :category_id, :status =&gt; [1, 2, 3]).sql.should ==
+      &quot;SELECT * FROM items LEFT OUTER JOIN categories ON (categories.id = items.category_id) AND (categories.status IN (1, 2, 3))&quot;
+  end
 end
 
 context &quot;Dataset#[]=&quot; do
@@ -985,6 +1100,11 @@ context &quot;Dataset#first&quot; do
     @c.last_opts[:where].should == ('z = 15')
   end
   
+  specify &quot;should return the first matching record if a block is given&quot; do
+    @d.first {:z &gt; 26}.should == {:a =&gt; 1, :b =&gt; 2}
+    @c.last_opts[:where].should == ('(z &gt; 26)')
+  end
+  
   specify &quot;should return a single record if no argument is given&quot; do
     @d.first.should == {:a =&gt; 1, :b =&gt; 2}
   end
@@ -1177,7 +1297,7 @@ context &quot;Dataset#single_value&quot; do
   end
 end
 
-context &quot;Dataset#set_row_filter&quot; do
+context &quot;Dataset#set_row_proc&quot; do
   setup do
     @c = Class.new(Sequel::Dataset) do
       def fetch_rows(sql, &amp;block)
@@ -1189,7 +1309,7 @@ context &quot;Dataset#set_row_filter&quot; do
   end
   
   specify &quot;should cause dataset to pass all rows through the filter&quot; do
-    @dataset.set_row_filter {|h| h[:der] = h[:kind] + 2; h}
+    @dataset.set_row_proc {|h| h[:der] = h[:kind] + 2; h}
     
     rows = @dataset.all
     rows.size.should == 10
@@ -1198,7 +1318,7 @@ context &quot;Dataset#set_row_filter&quot; do
   end
   
   specify &quot;should be copied over when dataset is cloned&quot; do
-    @dataset.set_row_filter {|h| h[:der] = h[:kind] + 2; h}
+    @dataset.set_row_proc {|h| h[:der] = h[:kind] + 2; h}
     
     @dataset.filter(:a =&gt; 1).first.should == {:kind =&gt; 1, :der =&gt; 3}
   end
@@ -1617,4 +1737,329 @@ context &quot;Dataset#multi_insert&quot; do
       'COMMIT;'
     ]
   end
+end
+
+context &quot;Dataset#query&quot; do
+  setup do
+    @d = Sequel::Dataset.new(nil)
+  end
+  
+  specify &quot;should support #from&quot; do
+    q = @d.query {from :xxx}
+    q.class.should == @d.class
+    q.sql.should == &quot;SELECT * FROM xxx&quot;
+  end
+  
+  specify &quot;should support #select&quot; do
+    q = @d.query do
+      select :a, :b___mongo
+      from :yyy
+    end
+    q.class.should == @d.class
+    q.sql.should == &quot;SELECT a, b AS mongo FROM yyy&quot;
+  end
+  
+  specify &quot;should support #where&quot; do
+    q = @d.query do
+      from :zzz
+      where {:x + 2 &gt; :y + 3}
+    end
+    q.class.should == @d.class
+    q.sql.should == &quot;SELECT * FROM zzz WHERE ((x + 2) &gt; (y + 3))&quot;
+
+    q = @d.from(:zzz).query do
+      where {:x &gt; 1 &amp;&amp; :y &gt; 2}
+    end
+    q.class.should == @d.class
+    q.sql.should == &quot;SELECT * FROM zzz WHERE ((x &gt; 1) AND (y &gt; 2))&quot;
+
+    q = @d.from(:zzz).query do
+      where :x =&gt; 33
+    end
+    q.class.should == @d.class
+    q.sql.should == &quot;SELECT * FROM zzz WHERE (x = 33)&quot;
+  end
+  
+  specify &quot;should support #group_by and #having&quot; do
+    q = @d.query do
+      from :abc
+      group_by :id
+      having {:x &gt;= 2}
+    end
+    q.class.should == @d.class
+    q.sql.should == &quot;SELECT * FROM abc GROUP BY id HAVING (x &gt;= 2)&quot;
+  end
+  
+  specify &quot;should support #order, #order_by&quot; do
+    q = @d.query do
+      from :xyz
+      order_by :stamp
+    end
+    q.class.should == @d.class
+    q.sql.should == &quot;SELECT * FROM xyz ORDER BY stamp&quot;
+  end
+  
+  specify &quot;should raise on non-chainable method calls&quot; do
+    proc {@d.query {count}}.should raise_error(SequelError)
+  end
+  
+  specify &quot;should raise on each, insert, update, delete&quot; do
+    proc {@d.query {each}}.should raise_error(SequelError)
+    proc {@d.query {insert(:x =&gt; 1)}}.should raise_error(SequelError)
+    proc {@d.query {update(:x =&gt; 1)}}.should raise_error(SequelError)
+    proc {@d.query {delete}}.should raise_error(SequelError)
+  end
+end
+
+context &quot;Dataset&quot; do
+  setup do
+    @d = Sequel::Dataset.new(nil).from(:x)
+  end
+
+  specify &quot;should support self-changing select!&quot; do
+    @d.select!(:y)
+    @d.sql.should == &quot;SELECT y FROM x&quot;
+  end
+  
+  specify &quot;should support self-changing from!&quot; do
+    @d.from!(:y)
+    @d.sql.should == &quot;SELECT * FROM y&quot;
+  end
+
+  specify &quot;should support self-changing order!&quot; do
+    @d.order!(:y)
+    @d.sql.should == &quot;SELECT * FROM x ORDER BY y&quot;
+  end
+  
+  specify &quot;should support self-changing filter!&quot; do
+    @d.filter!(:y =&gt; 1)
+    @d.sql.should == &quot;SELECT * FROM x WHERE (y = 1)&quot;
+  end
+
+  specify &quot;should support self-changing filter! with block&quot; do
+    @d.filter! {:y == 2}
+    @d.sql.should == &quot;SELECT * FROM x WHERE (y = 2)&quot;
+  end
+  
+  specify &quot;should raise for ! methods that don't return a dataset&quot; do
+    proc {@d.opts!}.should raise_error(NameError)
+  end
+  
+  specify &quot;should raise for missing methods&quot; do
+    proc {@d.xuyz}.should raise_error(NameError)
+    proc {@d.xyz!}.should raise_error(NameError)
+    proc {@d.xyz?}.should raise_error(NameError)
+  end
+  
+  specify &quot;should support chaining of bang methods&quot; do
+      @d.order!(:y)
+      @d.filter!(:y =&gt; 1)
+      @d.sql.should == &quot;SELECT * FROM x WHERE (y = 1) ORDER BY y&quot;
+  end
+end
+
+context &quot;Dataset#transform&quot; do
+  setup do
+    @c = Class.new(Sequel::Dataset) do
+      attr_accessor :raw
+      attr_accessor :sql
+      
+      def fetch_rows(sql, &amp;block)
+        block[@raw]
+      end
+      
+      def insert(v)
+        @sql = insert_sql(v)
+      end
+      
+      def update(v)
+        @sql = update_sql(v)
+      end
+    end
+
+    @ds = @c.new(nil).from(:items)
+    @ds.transform(:x =&gt; [
+      proc {|v| Marshal.load(v)},
+      proc {|v| Marshal.dump(v)}
+    ])
+  end
+  
+  specify &quot;should change the dataset to transform values loaded from the database&quot; do
+    @ds.raw = {:x =&gt; Marshal.dump([1, 2, 3]), :y =&gt; 'hello'}
+    @ds.first.should == {:x =&gt; [1, 2, 3], :y =&gt; 'hello'}
+  end
+  
+  specify &quot;should change the dataset to transform values saved to the database&quot; do
+    @ds.insert(:x =&gt; :toast)
+    @ds.sql.should == &quot;INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}');&quot;
+
+    @ds.insert(:y =&gt; 'butter')
+    @ds.sql.should == &quot;INSERT INTO items (y) VALUES ('butter');&quot;
+    
+    @ds.update(:x =&gt; ['dream'])
+    @ds.sql.should == &quot;UPDATE items SET x = '#{Marshal.dump(['dream'])}'&quot;
+  end
+  
+  specify &quot;should be transferred to cloned datasets&quot; do
+    @ds2 = @ds.filter(:a =&gt; 1)
+
+    @ds2.raw = {:x =&gt; Marshal.dump([1, 2, 3]), :y =&gt; 'hello'}
+    @ds2.first.should == {:x =&gt; [1, 2, 3], :y =&gt; 'hello'}
+
+    @ds2.insert(:x =&gt; :toast)
+    @ds2.sql.should == &quot;INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}');&quot;
+  end
+  
+  specify &quot;should work correctly together with set_row_proc&quot; do
+    @ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
+    @ds.raw = {:x =&gt; Marshal.dump(&quot;wow&quot;), :y =&gt; 'hello'}
+    @ds.first.should == {:x =&gt; &quot;wow&quot;, :y =&gt; 'hello', :z =&gt; &quot;wowwow&quot;}
+
+    f = nil
+    @ds.raw = {:x =&gt; Marshal.dump(&quot;wow&quot;), :y =&gt; 'hello'}
+    @ds.each(:naked =&gt; true) {|r| f = r}
+    f.should == {:x =&gt; &quot;wow&quot;, :y =&gt; 'hello'}
+  end
+end
+
+context &quot;Dataset#transform&quot; do
+  setup do
+    @c = Class.new(Sequel::Dataset) do
+      attr_accessor :raw
+      attr_accessor :sql
+      
+      def fetch_rows(sql, &amp;block)
+        block[@raw]
+      end
+      
+      def insert(v)
+        @sql = insert_sql(v)
+      end
+      
+      def update(v)
+        @sql = update_sql(v)
+      end
+    end
+
+    @ds = @c.new(nil).from(:items)
+  end
+  
+  specify &quot;should raise SequelError for invalid transformations&quot; do
+    proc {@ds.transform(:x =&gt; 'mau')}.should raise_error(SequelError)
+    proc {@ds.transform(:x =&gt; :mau)}.should raise_error(SequelError)
+    proc {@ds.transform(:x =&gt; [])}.should raise_error(SequelError)
+    proc {@ds.transform(:x =&gt; ['mau'])}.should raise_error(SequelError)
+    proc {@ds.transform(:x =&gt; [proc {|v|}, proc {|v|}])}.should_not raise_error(SequelError)
+  end
+  
+  specify &quot;should support stock YAML transformation&quot; do
+    @ds.transform(:x =&gt; :yaml)
+
+    @ds.raw = {:x =&gt; [1, 2, 3].to_yaml, :y =&gt; 'hello'}
+    @ds.first.should == {:x =&gt; [1, 2, 3], :y =&gt; 'hello'}
+
+    @ds.insert(:x =&gt; :toast)
+    @ds.sql.should == &quot;INSERT INTO items (x) VALUES ('#{:toast.to_yaml}');&quot;
+    @ds.insert(:y =&gt; 'butter')
+    @ds.sql.should == &quot;INSERT INTO items (y) VALUES ('butter');&quot;
+    @ds.update(:x =&gt; ['dream'])
+    @ds.sql.should == &quot;UPDATE items SET x = '#{['dream'].to_yaml}'&quot;
+
+    @ds2 = @ds.filter(:a =&gt; 1)
+    @ds2.raw = {:x =&gt; [1, 2, 3].to_yaml, :y =&gt; 'hello'}
+    @ds2.first.should == {:x =&gt; [1, 2, 3], :y =&gt; 'hello'}
+    @ds2.insert(:x =&gt; :toast)
+    @ds2.sql.should == &quot;INSERT INTO items (x) VALUES ('#{:toast.to_yaml}');&quot;
+
+    @ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
+    @ds.raw = {:x =&gt; &quot;wow&quot;.to_yaml, :y =&gt; 'hello'}
+    @ds.first.should == {:x =&gt; &quot;wow&quot;, :y =&gt; 'hello', :z =&gt; &quot;wowwow&quot;}
+    f = nil
+    @ds.raw = {:x =&gt; &quot;wow&quot;.to_yaml, :y =&gt; 'hello'}
+    @ds.each(:naked =&gt; true) {|r| f = r}
+    f.should == {:x =&gt; &quot;wow&quot;, :y =&gt; 'hello'}
+  end
+  
+  specify &quot;should support stock Marshal transformation&quot; do
+    @ds.transform(:x =&gt; :marshal)
+
+    @ds.raw = {:x =&gt; Marshal.dump([1, 2, 3]), :y =&gt; 'hello'}
+    @ds.first.should == {:x =&gt; [1, 2, 3], :y =&gt; 'hello'}
+
+    @ds.insert(:x =&gt; :toast)
+    @ds.sql.should == &quot;INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}');&quot;
+    @ds.insert(:y =&gt; 'butter')
+    @ds.sql.should == &quot;INSERT INTO items (y) VALUES ('butter');&quot;
+    @ds.update(:x =&gt; ['dream'])
+    @ds.sql.should == &quot;UPDATE items SET x = '#{Marshal.dump(['dream'])}'&quot;
+
+    @ds2 = @ds.filter(:a =&gt; 1)
+    @ds2.raw = {:x =&gt; Marshal.dump([1, 2, 3]), :y =&gt; 'hello'}
+    @ds2.first.should == {:x =&gt; [1, 2, 3], :y =&gt; 'hello'}
+    @ds2.insert(:x =&gt; :toast)
+    @ds2.sql.should == &quot;INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}');&quot;
+
+    @ds.set_row_proc {|r| r[:z] = r[:x] * 2; r}
+    @ds.raw = {:x =&gt; Marshal.dump(&quot;wow&quot;), :y =&gt; 'hello'}
+    @ds.first.should == {:x =&gt; &quot;wow&quot;, :y =&gt; 'hello', :z =&gt; &quot;wowwow&quot;}
+    f = nil
+    @ds.raw = {:x =&gt; Marshal.dump(&quot;wow&quot;), :y =&gt; 'hello'}
+    @ds.each(:naked =&gt; true) {|r| f = r}
+    f.should == {:x =&gt; &quot;wow&quot;, :y =&gt; 'hello'}
+  end
+end
+
+context &quot;Dataset#to_csv&quot; do
+  setup do
+    @c = Class.new(Sequel::Dataset) do
+      attr_accessor :data
+      attr_accessor :cols
+      
+      def fetch_rows(sql, &amp;block)
+        @columns = @cols
+        @data.each {|r| r.fields = @columns; block[r]}
+      end
+      
+      # naked should return self here because to_csv wants a naked result set.
+      def naked
+        self
+      end
+    end
+    
+    @ds = @c.new(nil).from(:items)
+
+    @ds.cols = [:a, :b, :c]
+    @ds.data = [
+      [1, 2, 3], [4, 5, 6], [7, 8, 9]
+    ]
+  end
+  
+  specify &quot;should format a CSV representation of the records&quot; do
+    @ds.to_csv.should ==
+      &quot;a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n&quot;
+  end
+
+  specify &quot;should exclude column titles if so specified&quot; do
+    @ds.to_csv(false).should ==
+      &quot;1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n&quot;
+  end
+end
+
+context &quot;Dataset#each_hash&quot; do
+  setup do
+    @c = Class.new(Sequel::Dataset) do
+      def each(&amp;block)
+        a = [[1, 2, 3], [4, 5, 6]]
+        a.each {|r| r.fields = [:a, :b, :c]; block[r]}
+      end
+    end
+    
+    @ds = @c.new(nil).from(:items)
+  end
+  
+  specify &quot;should yield records converted to hashes&quot; do
+    hashes = []
+    @ds.each_hash {|h| hashes &lt;&lt; h}
+    hashes.should == [{:a =&gt; 1, :b =&gt; 2, :c =&gt; 3}, {:a =&gt; 4, :b =&gt; 5, :c =&gt; 6}]
+  end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/spec/dataset_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,90 +2,154 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
 
 Sequel::Model.db = MODEL_DB = MockDatabase.new
 
-context &quot;A model class&quot; do
-  specify &quot;should be associated with a dataset&quot; do
-    @m = Class.new(Sequel::Model) do
-      set_dataset MODEL_DB[:items]
-    end
+describe Sequel::Model do
+
+  it &quot;should have class method aliased as model&quot; do
+    Sequel::Model.instance_methods.should include('model')
+
+    model_a = Class.new Sequel::Model
+    model_a.new.model.should be(model_a)
+  end
+
+  it &quot;should be associated with a dataset&quot; do
+    model_a = Class.new(Sequel::Model) { set_dataset MODEL_DB[:as] }
     
-    @m.dataset.should be_a_kind_of(MockDataset)
-    @m.dataset.opts[:from].should == [:items]
+    model_a.dataset.should be_a_kind_of(MockDataset)
+    model_a.dataset.opts[:from].should == [:as]
 
-    @m2 = Class.new(Sequel::Model) do
-      set_dataset MODEL_DB[:zzz]
-    end
+    model_b = Class.new(Sequel::Model) { set_dataset MODEL_DB[:bs] }
     
-    @m2.dataset.should be_a_kind_of(MockDataset)
-    @m2.dataset.opts[:from].should == [:zzz]
-    @m.dataset.opts[:from].should == [:items]
+    model_b.dataset.should be_a_kind_of(MockDataset)
+    model_b.dataset.opts[:from].should == [:bs]
+
+    model_a.dataset.opts[:from].should == [:as]
   end
+
 end
 
-context &quot;A model's primary key&quot; do
-  specify &quot;should default to id&quot; do
-    @m = Class.new(Sequel::Model) do
-    end
-    
-    @m.primary_key.should == :id
+describe Sequel::Model, 'w/ primary key' do
+
+  it &quot;should default to ':id'&quot; do
+    model_a = Class.new Sequel::Model
+    model_a.primary_key.should be_equal(:id)
   end
-  
-  specify &quot;should be changeable through Model.set_primary_key&quot; do
-    @m = Class.new(Sequel::Model) do
-      set_primary_key :xxx
-    end
-    
-    @m.primary_key.should == :xxx
+
+  it &quot;should be changed through 'set_primary_key'&quot; do
+    model_a = Class.new(Sequel::Model) { set_primary_key :a }
+    model_a.primary_key.should be_equal(:a)
   end
-  
-  specify &quot;should support composite primary keys&quot; do
-    @m = Class.new(Sequel::Model) do
-      set_primary_key [:node_id, :session_id]
-    end
-    @m.primary_key.should == [:node_id, :session_id]
+
+  it &quot;should support multi argument composite keys&quot; do
+    model_a = Class.new(Sequel::Model) { set_primary_key :a, :b }
+    model_a.primary_key.should be_eql([:a, :b])
+  end
+
+  it &quot;should accept single argument composite keys&quot; do
+    model_a = Class.new(Sequel::Model) { set_primary_key [:a, :b] }
+    model_a.primary_key.should be_eql([:a, :b])
   end
+
 end
 
-context &quot;A model without a primary key&quot; do
-  setup do
-    @m = Class.new(Sequel::Model) do
-      no_primary_key
-    end
+describe Sequel::Model, 'w/o primary key' do
+
+  it &quot;should return nil for primary key&quot; do
+    Class.new(Sequel::Model) { no_primary_key }.primary_key.should be_nil
   end
-  
-  specify &quot;should return nil for primary_key&quot; do
-    @m.primary_key.should be_nil
+
+  it &quot;should raise a SequelError on 'this'&quot; do
+    instance = Class.new(Sequel::Model) { no_primary_key }.new
+    proc { instance.this }.should raise_error(SequelError)
   end
-  
-  specify &quot;should raise on #this&quot; do
-    o = @m.new
-    proc {o.this}.should raise_error(SequelError)
+
+end
+
+describe Sequel::Model, 'with this' do
+
+  before { @example = Class.new Sequel::Model(:examples) }
+
+  it &quot;should return a dataset identifying the record&quot; do
+    instance = @example.new :id =&gt; 3
+    instance.this.sql.should be_eql(&quot;SELECT * FROM examples WHERE (id = 3) LIMIT 1&quot;)
+  end
+
+  it &quot;should support arbitary primary keys&quot; do
+    @example.set_primary_key :a
+
+    instance = @example.new :a =&gt; 3
+    instance.this.sql.should be_eql(&quot;SELECT * FROM examples WHERE (a = 3) LIMIT 1&quot;)
   end
+
+  it &quot;should support composite primary keys&quot; do
+    @example.set_primary_key :x, :y
+    instance = @example.new :x =&gt; 4, :y =&gt; 5
+
+    parts = ['SELECT * FROM examples WHERE %s LIMIT 1',
+      '(x = 4) AND (y = 5)', '(y = 5) AND (x = 4)'
+    ].map { |expr| Regexp.escape expr }
+    regexp = Regexp.new parts.first % &quot;(?:#{parts[1]}|#{parts[2]})&quot;
+
+    instance.this.sql.should match(regexp)
+  end
+
 end
 
-context &quot;Model#this&quot; do
-  setup do
-    @m = Class.new(Sequel::Model(:items)) do
-    end
+describe Sequel::Model, 'with hooks' do
+
+  before do
+    MODEL_DB.reset
+    Sequel::Model.hooks.clear
+
+    @hooks = %w{
+      before_save before_create before_update before_destroy
+      after_save after_create after_update after_destroy
+    }.select { |hook| !hook.empty? }
   end
-  
-  specify &quot;should return a dataset identifying the record&quot; do
-    o = @m.new(:id =&gt; 3)
-    o.this.sql.should == &quot;SELECT * FROM items WHERE (id = 3)&quot;
+
+  it &quot;should have hooks for everything&quot; do
+    Sequel::Model.methods.should include('hooks')
+    Sequel::Model.methods.should include(*@hooks)
+    @hooks.each do |hook|
+      Sequel::Model.hooks[hook.to_sym].should be_an_instance_of(Array)
+    end
   end
-  
-  specify &quot;should support arbitrary primary keys&quot; do
-    @m.set_primary_key(:xxx)
-    
-    o = @m.new(:xxx =&gt; 3)
-    o.this.sql.should == &quot;SELECT * FROM items WHERE (xxx = 3)&quot;
+  it &quot;should be inherited&quot; do
+    pending 'soon'
+
+    @hooks.each do |hook|
+      Sequel::Model.send(hook.to_sym) { nil }
+    end
+
+    model = Class.new Sequel::Model(:models)
+    model.hooks.should == Sequel::Model.hooks
   end
-  
-  specify &quot;should support composite primary keys&quot; do
-    @m.set_primary_key [:x, :y]
-    o = @m.new(:x =&gt; 4, :y =&gt; 5)
 
-    o.this.sql.should =~ /^SELECT \* FROM items WHERE (\(x = 4\) AND \(y = 5\))|(\(y = 5\) AND \(x = 4\))$/
+  it &quot;should run hooks&quot; do
+    pending 'soon'
+
+    test = mock 'Test'
+    test.should_receive(:run).exactly(@hooks.length)
+
+    @hooks.each do |hook|
+      Sequel::Model.send(hook.to_sym) { test.run }
+    end
+
+    model = Class.new Sequel::Model(:models)
+    model.hooks.should == Sequel::Model.hooks
+
+    model_instance = model.new
+    @hooks.each { |hook| model_instance.run_hooks(hook) }
+  end
+  it &quot;should run hooks around save and create&quot; do
+    pending 'test execution'
+  end
+  it &quot;should run hooks around save and update&quot; do
+    pending 'test execution'
   end
+  it &quot;should run hooks around delete&quot; do
+    pending 'test execution'
+  end
+
 end
 
 context &quot;A new model instance&quot; do
@@ -114,6 +178,10 @@ context &quot;A new model instance&quot; do
       1234
     end
     
+    def d.first
+      {:x =&gt; 1, :id =&gt; 1234}
+    end
+    
     o = @m.new(:x =&gt; 1)
     o.save
     o.id.should == 1234
@@ -210,7 +278,7 @@ context &quot;A model class without a primary key&quot; do
     i = nil
     proc {i = @c.create(:x =&gt; 1)}.should_not raise_error
     i.class.should be(@c)
-    i.values.should == {:x =&gt; 1}
+    i.values.to_hash.should == {:x =&gt; 1}
     
     MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1);']
   end
@@ -239,10 +307,8 @@ context &quot;Model#serialize&quot; do
       serialize :abc
     end
 
-    o = @c.create(:abc =&gt; 1)
-    o.values[:abc].should == &quot;--- 1\n&quot;
-    o = @c.create(:abc =&gt; &quot;hello&quot;)
-    o.values[:abc].should == &quot;--- hello\n&quot;
+    @c.create(:abc =&gt; 1)
+    @c.create(:abc =&gt; &quot;hello&quot;)
     
     MODEL_DB.sqls.should == [ \
       &quot;INSERT INTO items (abc) VALUES ('--- 1\n');&quot;, \
@@ -250,20 +316,449 @@ context &quot;Model#serialize&quot; do
     ]
   end
   
+
+  specify &quot;should support calling after the class is defined&quot; do
+    @c = Class.new(Sequel::Model(:items)) do
+      no_primary_key
+    end
+    
+    @c.serialize :def
+
+    @c.create(:def =&gt; 1)
+    @c.create(:def =&gt; &quot;hello&quot;)
+    
+    MODEL_DB.sqls.should == [ \
+      &quot;INSERT INTO items (def) VALUES ('--- 1\n');&quot;, \
+      &quot;INSERT INTO items (def) VALUES ('--- hello\n');&quot;, \
+    ]
+  end
+  
+  specify &quot;should support using the Marshal format&quot; do
+    @c = Class.new(Sequel::Model(:items)) do
+      no_primary_key
+      serialize :abc, :format =&gt; :marshal
+    end
+
+    @c.create(:abc =&gt; 1)
+    @c.create(:abc =&gt; &quot;hello&quot;)
+    
+    MODEL_DB.sqls.should == [ \
+      &quot;INSERT INTO items (abc) VALUES ('\004\bi\006');&quot;, \
+      &quot;INSERT INTO items (abc) VALUES ('\004\b\&quot;\nhello');&quot;, \
+    ]
+  end
+  
   specify &quot;should translate values to and from YAML using accessor methods&quot; do
     @c = Class.new(Sequel::Model(:items)) do
       serialize :abc, :def
     end
     
-    o = @c.new(:id =&gt; 1, :abc =&gt; &quot;--- 1\n&quot;, :def =&gt; &quot;--- hello\n&quot;)
-    o.values.should == {:id =&gt; 1, :abc =&gt; &quot;--- 1\n&quot;, :def =&gt; &quot;--- hello\n&quot;}
+    ds = @c.dataset
+    ds.extend(Module.new {
+      attr_accessor :raw
+      
+      def fetch_rows(sql, &amp;block)
+        block.call(@raw)
+      end
+      
+      @@sqls = nil
+      
+      def insert(*args)
+        @@sqls = insert_sql(*args)
+      end
+
+      def update(*args)
+        @@sqls = update_sql(*args)
+      end
+      
+      def sqls
+        @@sqls
+      end
+      
+      def columns
+        [:id, :abc, :def]
+      end
+    })
+      
+    ds.raw = {:id =&gt; 1, :abc =&gt; &quot;--- 1\n&quot;, :def =&gt; &quot;--- hello\n&quot;}
+    o = @c.first
+    o.id.should == 1
     o.abc.should == 1
-    o.def.should == 'hello'
+    o.def.should == &quot;hello&quot;
     
-    o.abc = 23
-    o.values[:abc].should == &quot;--- 23\n&quot;
-    o.save
+    o.set(:abc =&gt; 23)
+    ds.sqls.should == &quot;UPDATE items SET abc = '#{23.to_yaml}' WHERE (id = 1)&quot;
     
-    MODEL_DB.sqls.first.should =~ /abc = '--- 23\n'/
+    ds.raw = {:id =&gt; 1, :abc =&gt; &quot;--- 1\n&quot;, :def =&gt; &quot;--- hello\n&quot;}
+    o = @c.create(:abc =&gt; [1, 2, 3])
+    ds.sqls.should == &quot;INSERT INTO items (abc) VALUES ('#{[1, 2, 3].to_yaml}');&quot;
   end
 end
+
+context &quot;Model attribute accessors&quot; do
+  setup do
+    MODEL_DB.reset
+
+    @c = Class.new(Sequel::Model(:items)) do
+      def columns
+        [:id, :x, :y]
+      end
+    end
+    
+    ds = @c.dataset
+    ds.extend(Module.new {
+      def columns
+        [:id, :x, :y]
+      end
+    })
+  end
+  
+  specify &quot;should be created dynamically&quot; do
+    o = @c.new
+    
+    o.should_not be_respond_to(:x)
+    o.x.should be_nil
+    o.should be_respond_to(:x)
+    
+    o.should_not be_respond_to(:x=)
+    o.x = 34
+    o.x.should == 34
+    o.should be_respond_to(:x=)
+  end
+  
+  specify &quot;should raise for a column that doesn't exist in the dataset&quot; do
+    o = @c.new
+    
+    proc {o.x}.should_not raise_error
+    proc {o.xx}.should raise_error(SequelError)
+    
+    proc {o.x = 3}.should_not raise_error
+    proc {o.yy = 4}.should raise_error
+  end
+end
+
+context &quot;Model#new?&quot; do
+  setup do
+    MODEL_DB.reset
+
+    @c = Class.new(Sequel::Model(:items)) do
+    end
+  end
+  
+  specify &quot;should be true for a new instance&quot; do
+    n = @c.new(:x =&gt; 1)
+    n.should be_new
+  end
+  
+  specify &quot;should be false after saving&quot; do
+    n = @c.new(:x =&gt; 1)
+    n.save
+    n.should_not be_new
+  end
+end
+
+context &quot;Model.after_create&quot; do
+  setup do
+    MODEL_DB.reset
+
+    @c = Class.new(Sequel::Model(:items)) do
+      def columns
+        [:id, :x, :y]
+      end
+    end
+    
+    ds = @c.dataset
+    def ds.insert(*args)
+      super(*args)
+      1
+    end
+  end
+
+  specify &quot;should be called after creation&quot; do
+    s = []
+    
+    @c.after_create do
+      s = MODEL_DB.sqls.dup
+    end
+    
+    n = @c.create(:x =&gt; 1)
+    MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1);']
+    s.should == ['INSERT INTO items (x) VALUES (1);']
+  end
+  
+  specify &quot;should allow calling save in the hook&quot; do
+    @c.after_create do
+      values.delete(:x)
+      self.id = 2
+      save
+    end
+    
+    n = @c.create(:id =&gt; 1)
+    MODEL_DB.sqls.should == ['INSERT INTO items (id) VALUES (1);', 'UPDATE items SET id = 2 WHERE (id = 1)']
+  end
+end
+
+context &quot;Model.subset&quot; do
+  setup do
+    MODEL_DB.reset
+
+    @c = Class.new(Sequel::Model(:items)) do
+      def columns
+        [:id, :x, :y]
+      end
+    end
+  end
+
+  specify &quot;should create a filter on the underlying dataset&quot; do
+    proc {@c.new_only}.should raise_error(NoMethodError)
+    
+    @c.subset(:new_only) {:age == 'new'}
+    
+    @c.new_only.sql.should == &quot;SELECT * FROM items WHERE (age = 'new')&quot;
+    @c.dataset.new_only.sql.should == &quot;SELECT * FROM items WHERE (age = 'new')&quot;
+    
+    @c.subset(:pricey) {:price &gt; 100}
+    
+    @c.pricey.sql.should == &quot;SELECT * FROM items WHERE (price &gt; 100)&quot;
+    @c.dataset.pricey.sql.should == &quot;SELECT * FROM items WHERE (price &gt; 100)&quot;
+    
+    # check if subsets are composable
+    @c.pricey.new_only.sql.should == &quot;SELECT * FROM items WHERE (price &gt; 100) AND (age = 'new')&quot;
+    @c.new_only.pricey.sql.should == &quot;SELECT * FROM items WHERE (age = 'new') AND (price &gt; 100)&quot;
+  end
+end
+
+context &quot;Model.find&quot; do
+  setup do
+    MODEL_DB.reset
+    
+    @c = Class.new(Sequel::Model(:items)) do
+      def self.columns
+        [:name, :id]
+      end
+    end
+    
+    $cache_dataset_row = {:name =&gt; 'sharon', :id =&gt; 1}
+    @dataset = @c.dataset
+    $sqls = []
+    @dataset.extend(Module.new {
+      def fetch_rows(sql)
+        $sqls &lt;&lt; sql
+        yield $cache_dataset_row
+      end
+    })
+  end
+  
+  specify &quot;should return the first record matching the given filter&quot; do
+    @c.find(:name =&gt; 'sharon').should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (name = 'sharon') LIMIT 1&quot;
+
+    @c.find {&quot;name LIKE 'abc%'&quot;.lit}.should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE name LIKE 'abc%' LIMIT 1&quot;
+  end
+  
+  specify &quot;should accept filter blocks&quot; do
+    @c.find {:id == 1}.should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (id = 1) LIMIT 1&quot;
+
+    @c.find {:x &gt; 1 &amp;&amp; :y &lt; 2}.should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE ((x &gt; 1) AND (y &lt; 2)) LIMIT 1&quot;
+  end
+end
+
+context &quot;Model.[]&quot; do
+  setup do
+    MODEL_DB.reset
+    
+    @c = Class.new(Sequel::Model(:items)) do
+      def self.columns
+        [:name, :id]
+      end
+    end
+    
+    $cache_dataset_row = {:name =&gt; 'sharon', :id =&gt; 1}
+    @dataset = @c.dataset
+    $sqls = []
+    @dataset.extend(Module.new {
+      def fetch_rows(sql)
+        $sqls &lt;&lt; sql
+        yield $cache_dataset_row
+      end
+    })
+  end
+  
+  specify &quot;should return the first record for the given pk&quot; do
+    @c[1].should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (id = 1) LIMIT 1&quot;
+    @c[9999].should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (id = 9999) LIMIT 1&quot;
+  end
+  
+  specify &quot;should work correctly for custom primary key&quot; do
+    @c.set_primary_key :name
+    @c['sharon'].should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (name = 'sharon') LIMIT 1&quot;
+  end
+  
+  specify &quot;should work correctly for composite primary key&quot; do
+    @c.set_primary_key [:node_id, :kind]
+    @c[3921, 201].should be_a_kind_of(@c)
+    $sqls.last.should =~ \
+      /^SELECT \* FROM items WHERE (\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\)) LIMIT 1$/
+  end
+  
+  specify &quot;should act as shortcut to find if a hash is given&quot; do
+    @c[:id =&gt; 1].should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (id = 1) LIMIT 1&quot;
+    
+    @c[:name =&gt; ['abc', 'def']].should be_a_kind_of(@c)
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (name IN ('abc', 'def')) LIMIT 1&quot;
+  end
+end
+
+context &quot;A cached model&quot; do
+  setup do
+    MODEL_DB.reset
+    
+    @cache_class = Class.new(Hash) do
+      attr_accessor :ttl
+      def set(k, v, ttl); self[k] = v; @ttl = ttl; end
+      def get(k); self[k]; end
+    end
+    cache = @cache_class.new
+    @cache = cache
+    
+    @c = Class.new(Sequel::Model(:items)) do
+      set_cache cache
+      
+      def self.columns
+        [:name, :id]
+      end
+    end
+    
+    $cache_dataset_row = {:name =&gt; 'sharon', :id =&gt; 1}
+    @dataset = @c.dataset
+    $sqls = []
+    @dataset.extend(Module.new {
+      def fetch_rows(sql)
+        $sqls &lt;&lt; sql
+        yield $cache_dataset_row
+      end
+      
+      def update(values)
+        $sqls &lt;&lt; update_sql(values)
+        $cache_dataset_row.merge!(values)
+      end
+      
+      def delete
+        $sqls &lt;&lt; delete_sql
+      end
+    })
+  end
+  
+  specify &quot;should set the model's cache store&quot; do
+    @c.cache_store.should be(@cache)
+  end
+  
+  specify &quot;should have a default ttl of 3600&quot; do
+    @c.cache_ttl.should == 3600
+  end
+  
+  specify &quot;should take a ttl option&quot; do
+    @c.set_cache @cache, :ttl =&gt; 1234
+    @c.cache_ttl.should == 1234
+  end
+  
+  specify &quot;should offer a set_cache_ttl method for setting the ttl&quot; do
+    @c.cache_ttl.should == 3600
+    @c.set_cache_ttl 1234
+    @c.cache_ttl.should == 1234
+  end
+  
+  specify &quot;should generate a cache key appropriate to the class&quot; do
+    m = @c.new
+    m.values[:id] = 1
+    m.cache_key.should == &quot;#{m.class}:1&quot;
+    
+    # custom primary key
+    @c.set_primary_key :ttt
+    m = @c.new
+    m.values[:ttt] = 333
+    m.cache_key.should == &quot;#{m.class}:333&quot;
+    
+    # composite primary key
+    @c.set_primary_key [:a, :b, :c]
+    m = @c.new
+    m.values[:a] = 123
+    m.values[:c] = 456
+    m.values[:b] = 789
+    m.cache_key.should == &quot;#{m.class}:123,789,456&quot;
+  end
+  
+  specify &quot;should raise error if attempting to generate cache_key and primary key value is null&quot; do
+    m = @c.new
+    proc {m.cache_key}.should raise_error(SequelError)
+    
+    m.values[:id] = 1
+    proc {m.cache_key}.should_not raise_error(SequelError)
+  end
+  
+  specify &quot;should set the cache when reading from the database&quot; do
+    $sqls.should == []
+    @cache.should be_empty
+    
+    m = @c[1]
+    $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
+    m.values.should == $cache_dataset_row
+    @cache[m.cache_key].should == m
+    
+    # read from cache
+    m2 = @c[1]
+    $sqls.should == ['SELECT * FROM items WHERE (id = 1) LIMIT 1']
+    m2.should == m
+    m2.values.should == $cache_dataset_row
+  end
+  
+  specify &quot;should delete the cache when writing to the database&quot; do
+    # fill the cache
+    m = @c[1]
+    @cache[m.cache_key].should == m
+    
+    m.set(:name =&gt; 'tutu')
+    @cache.has_key?(m.cache_key).should be_false
+    $sqls.last.should == &quot;UPDATE items SET name = 'tutu' WHERE (id = 1)&quot;
+    
+    m = @c[1]
+    @cache[m.cache_key].should == m
+    m.name = 'hey'
+    m.save
+    @cache.has_key?(m.cache_key).should be_false
+    $sqls.last.should == &quot;UPDATE items SET name = 'hey', id = 1 WHERE (id = 1)&quot;
+  end
+  
+  specify &quot;should delete the cache when deleting the record&quot; do
+    # fill the cache
+    m = @c[1]
+    @cache[m.cache_key].should == m
+    
+    m.delete
+    @cache.has_key?(m.cache_key).should be_false
+    $sqls.last.should == &quot;DELETE FROM items WHERE (id = 1)&quot;
+  end
+  
+  specify &quot;should support #[] as a shortcut to #find with hash&quot; do
+    m = @c[:id =&gt; 3]
+    @cache[m.cache_key].should be_nil
+    $sqls.last.should == &quot;SELECT * FROM items WHERE (id = 3) LIMIT 1&quot;
+    
+    m = @c[1]
+    @cache[m.cache_key].should == m
+    $sqls.should == [&quot;SELECT * FROM items WHERE (id = 3) LIMIT 1&quot;, \
+      &quot;SELECT * FROM items WHERE (id = 1) LIMIT 1&quot;]
+    
+    @c[:id =&gt; 4]
+    $sqls.should == [&quot;SELECT * FROM items WHERE (id = 3) LIMIT 1&quot;, \
+      &quot;SELECT * FROM items WHERE (id = 1) LIMIT 1&quot;, \
+      &quot;SELECT * FROM items WHERE (id = 4) LIMIT 1&quot;]
+  end
+end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/spec/model_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,11 +40,18 @@ context &quot;DB#create_table&quot; do
       primary_key :id
     end
     @db.sqls.should == ['CREATE TABLE cats (id integer PRIMARY KEY AUTOINCREMENT);']
+
     @db.sqls.clear
     @db.create_table(:cats) do
       primary_key :id, :serial, :auto_increment =&gt; false
     end
     @db.sqls.should == ['CREATE TABLE cats (id serial PRIMARY KEY);']
+
+    @db.sqls.clear
+    @db.create_table(:cats) do
+      primary_key :id, :type =&gt; :serial, :auto_increment =&gt; false
+    end
+    @db.sqls.should == ['CREATE TABLE cats (id serial PRIMARY KEY);']
   end
 
   specify &quot;should accept and literalize default values&quot; do</diff>
      <filename>vendor/ruby-sequel/spec/schema_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,18 @@ context &quot;Proc#to_sql&quot; do
       DS.proc_to_sql(self)
     end
   end
-
+  
+  def DS.match_expr(l, r)
+    case r
+    when String:
+      &quot;(#{literal(l)} LIKE #{literal(r)})&quot;
+    when Regexp:
+      &quot;(#{literal(l)} ~ #{literal(r.source)})&quot;
+    else
+      raise SequelError, &quot;Unsupported match pattern class (#{r.class}).&quot;
+    end
+  end
+  
   specify &quot;should support &lt;sym&gt; &lt;op&gt; &lt;lit&gt;&quot; do
     proc {:x &gt; 100}.to_sql.should == '(x &gt; 100)'
     proc {:x &lt; 100}.to_sql.should == '(x &lt; 100)'
@@ -45,6 +56,12 @@ context &quot;Proc#to_sql&quot; do
     def xyz; 321; end
     proc {:x == xyz}.to_sql.should == &quot;(x = 321)&quot;
     proc {:x == xyz.to_s}.to_sql.should == &quot;(x = '321')&quot;
+    
+    def y1(x); x; end
+    def y2; 111; end
+    
+    proc {:x == y1(222)}.to_sql.should == &quot;(x = 222)&quot;
+    proc {:x == y2}.to_sql.should == &quot;(x = 111)&quot;
   end
   
   specify &quot;should support constants&quot; do
@@ -73,11 +90,11 @@ context &quot;Proc#to_sql&quot; do
   specify &quot;should support =~ operator&quot; do
     # stock SQL version does not know about regexps
     proc {:x =~ '123'}.to_sql.should == &quot;(x LIKE '123')&quot;
+
+    proc {:x =~ /^123/}.to_sql.should == &quot;(x ~ '^123')&quot;
   end
   
   specify &quot;should raise on =~ operator for unsupported types&quot; do
-    # stock SQL version does not know about regexps
-    proc {proc {:x =~ /123/}.to_sql}.should raise_error(SequelError)
     proc {proc {:x =~ 123}.to_sql}.should raise_error(SequelError)
   end
   
@@ -267,4 +284,38 @@ context &quot;Proc#to_sql&quot; do
     &quot;abc&quot; =~ /(ab)/
     proc {:x == $1}.to_sql.should == &quot;(x = 'ab')&quot;
   end
+  
+  specify &quot;should evaluate expression not referring to symbols or literal strings.&quot; do
+    proc {:x &gt; 2 * 3}.to_sql.should == &quot;(x &gt; 6)&quot;
+    y = 3
+    proc {:x &gt; y * 4}.to_sql.should == &quot;(x &gt; 12)&quot;
+
+    proc {:AVG[:x] &gt; 4}.to_sql.should == &quot;(AVG(x) &gt; 4)&quot;
+
+    proc {:AVG[:x] &gt; 4}.to_sql.should == &quot;(AVG(x) &gt; 4)&quot;
+    
+    proc {:y == (1 &gt; 2)}.to_sql.should == &quot;(y = 'f')&quot;
+  end
+  
+  specify &quot;should support ternary operator&quot; do
+    y = true
+    proc {:x &gt; (y ? 1 : 2)}.to_sql.should == &quot;(x &gt; 1)&quot;
+    
+    proc {((1 &gt; 2) ? :x : :y) &gt; 3}.to_sql.should == &quot;(y &gt; 3)&quot;
+  end
+  
+  specify &quot;should support strings with embedded Ruby code in them and literalize them&quot; do
+    proc {:n == &quot;#{1+2}&quot;}.to_sql.should == &quot;(n = '3')&quot;
+    
+    y = &quot;12'34&quot;
+    
+    proc {:x &gt; &quot;#{y}&quot;}.to_sql.should == &quot;(x &gt; '12''34')&quot;
+  end
+  
+  specify &quot;should support format strings and literalize the result&quot; do
+    prod = 1
+    proc {:x == &quot;abc%d&quot; % prod}.to_sql.should == &quot;(x = 'abc1')&quot;
+    
+    proc {:x == (&quot;%d&quot; % prod).lit}.to_sql.should == &quot;(x = 1)&quot;
+  end
 end</diff>
      <filename>vendor/ruby-sequel/spec/sequelizer_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@ require File.join(File.dirname(__FILE__), '../lib/sequel/sqlite')
 
 DB = Sequel.sqlite
 
-N = 100_000
+N = 10_000
 
 Benchmark::bm(20) do |x|
   x.report('from') do
@@ -18,12 +18,12 @@ Benchmark::bm(20) do |x|
   end
 
   x.report('filter') do
-    t = DB[:test].filter {stamp &lt; (Time.now - 3600)}
+    t = DB[:test].filter {:stamp &lt; (Time.now - 3600)}
     N.times { t.sql }
   end
 
   x.report('filter.order') do
-    t = DB[:test].filter {stamp &lt; (Time.now - 3600)}.order(:stamp)
+    t = DB[:test].filter {:stamp &lt; (Time.now - 3600)}.order(:stamp)
     N.times { t.sql }
   end
 end
\ No newline at end of file</diff>
      <filename>vendor/ruby-sequel/stress/dataset_perf.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>53ad7f8ff3f95b061ed014632ab53f85e7057dfe</id>
    </parent>
  </parents>
  <author>
    <name>rick</name>
    <email>rick@8456e5f2-bb15-0410-bd20-b44782a1fb1e</email>
  </author>
  <url>http://github.com/entp/warehouse/commit/93f9b9f04881bbec18206ef8a9faa9b72e198c2f</url>
  <id>93f9b9f04881bbec18206ef8a9faa9b72e198c2f</id>
  <committed-date>2007-10-23T22:50:54-07:00</committed-date>
  <authored-date>2007-10-23T22:50:54-07:00</authored-date>
  <message>update ruby-sequel with patch from #75

git-svn-id: http://activereload.svn.engineyard.com/warehouse/trunk@2279 8456e5f2-bb15-0410-bd20-b44782a1fb1e</message>
  <tree>37f9a0f70b06f721918694dee709b83f926f0a9e</tree>
  <committer>
    <name>rick</name>
    <email>rick@8456e5f2-bb15-0410-bd20-b44782a1fb1e</email>
  </committer>
</commit>
