<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/mongo/util/support.rb</filename>
    </added>
    <added>
      <filename>test/unit/cursor_test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -14,11 +14,23 @@ include Config
 gem_command = &quot;gem&quot;
 gem_command = &quot;gem1.9&quot; if $0.match(/1\.9$/) # use gem1.9 if we used rake1.9
 
-# NOTE: some of the tests assume Mongo is running
+# NOTE: the functional tests assume MongoDB is running.
 desc &quot;Test the MongoDB Ruby driver.&quot;
-Rake::TestTask.new(:test) do |t|
-  t.test_files = FileList['test/test*.rb']
-  t.verbose    = true
+task :test do
+  Rake::Task['test:unit'].invoke
+  Rake::Task['test:functional'].invoke
+end
+
+namespace :test do 
+  Rake::TestTask.new(:unit) do |t|
+    t.test_files = FileList['test/unit/*_test.rb']
+    t.verbose    = true
+  end
+
+  Rake::TestTask.new(:functional) do |t|
+    t.test_files = FileList['test/test*.rb']
+    t.verbose    = true
+  end
 end
 
 desc &quot;Generate documentation&quot;</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,11 @@
 require 'mongo/types/binary'
+require 'mongo/types/code'
 require 'mongo/types/dbref'
 require 'mongo/types/objectid'
 require 'mongo/types/regexp_of_holding'
 
 require 'mongo/util/conversions'
+require 'mongo/util/support'
 
 require 'mongo/errors'
 require 'mongo/constants'</diff>
      <filename>lib/mongo.rb</filename>
    </modified>
    <modified>
      <diff>@@ -64,7 +64,7 @@ module Mongo
     # Return an array contining current profiling information from the
     # database.
     def profiling_info
-      @db.query(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), Query.new({})).to_a
+      Cursor.new(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), :selector =&gt; {}).to_a
     end
 
     # Validate a named collection by raising an exception if there is a</diff>
      <filename>lib/mongo/admin.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,8 +14,6 @@
 # limitations under the License.
 # ++
 
-require 'mongo/query'
-
 module Mongo
 
   # A named collection of records in a database.
@@ -131,7 +129,8 @@ module Mongo
       end
       raise RuntimeError, &quot;Unknown options [#{options.inspect}]&quot; unless options.empty?
 
-      cursor = @db.query(self, Query.new(selector, fields, skip, limit, sort, hint, snapshot, timeout, @db.slave_ok?))
+      cursor = Cursor.new(self, :selector =&gt; selector, :fields =&gt; fields, :skip =&gt; skip, :limit =&gt; limit, 
+                          :order =&gt; sort, :hint =&gt; hint, :snapshot =&gt; snapshot, :timeout =&gt; timeout)
       if block_given?
         yield cursor
         cursor.close()</diff>
      <filename>lib/mongo/collection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,13 +25,29 @@ module Mongo
 
     RESPONSE_HEADER_SIZE = 20
 
-    attr_reader :db, :collection, :query
+    attr_reader :collection, :selector, :admin, :fields, 
+      :order, :hint, :snapshot, :timeout,
+      :full_collection_name
 
     # Create a new cursor.
     #
     # Should not be called directly by application developers.
-    def initialize(db, collection, query, admin=false)
-      @db, @collection, @query, @admin = db, collection, query, admin
+    def initialize(collection, options={})
+      @db         = collection.db
+      @collection = collection
+
+      @selector   = convert_selector_for_query(options[:selector])
+      @fields     = convert_fields_for_query(options[:fields])
+      @admin      = options[:admin]    || false
+      @skip       = options[:skip]     || 0
+      @limit      = options[:limit]    || 0
+      @order      = options[:order]
+      @hint       = options[:hint]
+      @snapshot   = options[:snapshot]
+      @timeout    = options[:timeout]  || false
+      @explain    = options[:explain]
+
+      @full_collection_name   = &quot;#{@collection.db.name}.#{@collection.name}&quot;
       @cache = []
       @closed = false
       @query_run = false
@@ -64,9 +80,9 @@ module Mongo
     # not take limit and skip into account. Raises OperationFailure on a
     # database error.
     def count
-      command = OrderedHash[&quot;count&quot;, @collection.name,
-                            &quot;query&quot;, @query.selector,
-                            &quot;fields&quot;, @query.fields()]
+      command = OrderedHash[&quot;count&quot;,  @collection.name,
+                            &quot;query&quot;,  @selector,
+                            &quot;fields&quot;, @fields]
       response = @db.db_command(command)
       return response['n'].to_i if response['ok'] == 1
       return 0 if response['errmsg'] == &quot;ns missing&quot;
@@ -93,35 +109,39 @@ module Mongo
         order = key_or_list
       end
 
-      @query.order_by = order
+      @order = order
       self
     end
 
     # Limits the number of results to be returned by this cursor.
+    # Returns the current number_to_return if no parameter is given.
     #
     # Raises InvalidOperation if this cursor has already been used.
     #
     # This method overrides any limit specified in the Collection#find method,
     # and only the last limit applied has an effect.
-    def limit(number_to_return)
+    def limit(number_to_return=nil)
+      return @limit unless number_to_return
       check_modifiable
       raise ArgumentError, &quot;limit requires an integer&quot; unless number_to_return.is_a? Integer
 
-      @query.number_to_return = number_to_return
+      @limit = number_to_return
       self
     end
 
     # Skips the first +number_to_skip+ results of this cursor.
-    #
+    # Returns the current number_to_skip if no parameter is given.
+    # 
     # Raises InvalidOperation if this cursor has already been used.
     #
     # This method overrides any skip specified in the Collection#find method,
     # and only the last skip applied has an effect.
-    def skip(number_to_skip)
+    def skip(number_to_skip=nil)
+      return @skip unless number_to_skip
       check_modifiable
       raise ArgumentError, &quot;skip requires an integer&quot; unless number_to_skip.is_a? Integer
 
-      @query.number_to_skip = number_to_skip
+      @skip = number_to_skip
       self
     end
 
@@ -131,7 +151,7 @@ module Mongo
     # Iterating over an entire cursor will close it.
     def each
       num_returned = 0
-      while more? &amp;&amp; (@query.number_to_return &lt;= 0 || num_returned &lt; @query.number_to_return)
+      while more? &amp;&amp; (@limit &lt;= 0 || num_returned &lt; @limit)
         yield next_object()
         num_returned += 1
       end
@@ -148,7 +168,7 @@ module Mongo
       raise InvalidOperation, &quot;can't call Cursor#to_a on a used cursor&quot; if @query_run
       rows = []
       num_returned = 0
-      while more? &amp;&amp; (@query.number_to_return &lt;= 0 || num_returned &lt; @query.number_to_return)
+      while more? &amp;&amp; (@limit &lt;= 0 || num_returned &lt; @limit)
         rows &lt;&lt; next_object()
         num_returned += 1
       end
@@ -157,16 +177,10 @@ module Mongo
 
     # Returns an explain plan record for this cursor.
     def explain
-      limit = @query.number_to_return
-      @query.explain = true
-      @query.number_to_return = -limit.abs
-
-      c = Cursor.new(@db, @collection, @query)
+      c = Cursor.new(@collection, query_options_hash.merge(:limit =&gt; -@limit.abs, :explain =&gt; true))
       explanation = c.next_object
       c.close
 
-      @query.explain = false
-      @query.number_to_return = limit
       explanation
     end
 
@@ -194,8 +208,65 @@ module Mongo
     # Returns true if this cursor is closed, false otherwise.
     def closed?; @closed; end
 
+    # Returns an integer indicating which query options have been selected.
+    # See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
+    def query_opts
+      timeout  = @timeout ? 0 : Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT
+      slave_ok = @db.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0 
+      slave_ok + timeout
+    end
+
+    # Returns the query options set on this Cursor.
+    def query_options_hash
+      { :selector =&gt; @selector,
+        :fields   =&gt; @fields,   
+        :admin    =&gt; @admin,   
+        :skip     =&gt; @skip_num, 
+        :limit    =&gt; @limit_num, 
+        :order    =&gt; @order,   
+        :hint     =&gt; @hint,   
+        :snapshot =&gt; @snapshot, 
+        :timeout  =&gt; @timeout }
+    end
+
     private
 
+    # Converts the +:fields+ parameter from a single field name or an array
+    # of fields names to a hash, with the field names for keys and '1' for each
+    # value.
+    def convert_fields_for_query(fields)
+      case fields
+        when String, Symbol
+          {fields =&gt; 1}
+        when Array
+          return nil if fields.length.zero?
+          returning({}) do |hash|
+            fields.each { |field| hash[field] = 1 }
+          end
+      end
+    end
+
+    # Set query selector hash. If the selector is a Code or String object, 
+    # the selector will be used in a $where clause.
+    # See http://www.mongodb.org/display/DOCS/Server-side+Code+Execution
+    def convert_selector_for_query(selector)
+       case selector
+         when Hash
+          selector
+         when nil
+           {}
+         when String
+          {&quot;$where&quot; =&gt; Code.new(selector)}
+         when Code
+          {&quot;$where&quot; =&gt; selector}
+      end
+    end
+
+    # Returns true if the query contains order, explain, hint, or snapshot.
+    def query_contains_special_fields?
+      @order || @explain || @hint || @snapshot
+    end
+
     def read_all
       read_message_header
       read_response_header
@@ -243,7 +314,7 @@ module Mongo
     end
 
     # Internal method, not for general use. Return +true+ if there are
-    # more records to retrieve. We do not check @query.number_to_return;
+    # more records to retrieve. This methods does not check @limit;
     # #each is responsible for doing that.
     def more?
       num_remaining &gt; 0
@@ -308,41 +379,40 @@ module Mongo
 
     def construct_query_message(query)
       message = ByteBuffer.new
-      message.put_int(query.query_opts)
+      message.put_int(query_opts)
       db_name = @admin ? 'admin' : @db.name
       BSON.serialize_cstr(message, &quot;#{db_name}.#{@collection.name}&quot;)
-      message.put_int(query.number_to_skip)
-      message.put_int(query.number_to_return)
-      sel = query.selector
-      if query.contains_special_fields
-        sel = add_special_query_fields(sel, query)
+      message.put_int(@skip)
+      message.put_int(@limit)
+      selector = @selector
+      if query_contains_special_fields?
+        selector = selector_with_special_query_fields
       end
-      message.put_array(BSON.new.serialize(sel).to_a)
-      message.put_array(BSON.new.serialize(query.fields).to_a) if query.fields
+      message.put_array(BSON.new.serialize(selector).to_a)
+      message.put_array(BSON.new.serialize(@fields).to_a) if @fields
       message
     end
 
-    def add_special_query_fields(sel, query)
+    def selector_with_special_query_fields
       sel = OrderedHash.new
-      sel['query']     = query.selector
-      order_by         = query.order_by
-      sel['orderby']   = get_query_order_by(order_by) if order_by
-      sel['$hint']     = query.hint if query.hint &amp;&amp; query.hint.length &gt; 0
-      sel['$explain']  = true if query.explain
-      sel['$snapshot'] = true if query.snapshot
+      sel['query']     = @selector
+      sel['orderby']   = formatted_order_clause if @order
+      sel['$hint']     = @hint if @hint &amp;&amp; @hint.length &gt; 0
+      sel['$explain']  = true if @explain
+      sel['$snapshot'] = true if @snapshot
       sel
     end
 
-    def get_query_order_by(order_by)
-      case order_by
-        when String then string_as_sort_parameters(order_by)
-        when Symbol then symbol_as_sort_parameters(order_by)
-        when Array  then array_as_sort_parameters(order_by)
+    def formatted_order_clause
+      case @order
+        when String then string_as_sort_parameters(@order)
+        when Symbol then symbol_as_sort_parameters(@order)
+        when Array  then array_as_sort_parameters(@order)
         when Hash # Should be an ordered hash, but this message doesn't care
-          warn_if_deprecated(order_by)
-          order_by
+          warn_if_deprecated(@order)
+          @order 
         else
-          raise InvalidSortValueError, &quot;Illegal order_by, '#{query.order_by.class.name}'; must be String, Array, Hash, or OrderedHash&quot;
+          raise InvalidSortValueError, &quot;Illegal order_by, '#{@order.class.name}'; must be String, Array, Hash, or OrderedHash&quot;
       end
     end
 
@@ -351,7 +421,7 @@ module Mongo
     end
 
     def close_cursor_if_query_complete
-      close if @query.number_to_return &gt; 0 &amp;&amp; @n_received &gt;= @query.number_to_return
+      close if @limit &gt; 0 &amp;&amp; @n_received &gt;= @limit
     end
 
     def check_modifiable</diff>
      <filename>lib/mongo/cursor.rb</filename>
    </modified>
    <modified>
      <diff>@@ -18,7 +18,6 @@ require 'socket'
 require 'digest/md5'
 require 'thread'
 require 'mongo/collection'
-require 'mongo/query'
 require 'mongo/util/ordered_hash.rb'
 require 'mongo/admin'
 
@@ -216,7 +215,7 @@ module Mongo
     def collections_info(coll_name=nil)
       selector = {}
       selector[:name] = full_collection_name(coll_name) if coll_name
-      query(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), Query.new(selector))
+      Cursor.new(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), :selector =&gt; selector)
     end
 
     # Create a collection. If +strict+ is false, will return existing or
@@ -416,7 +415,7 @@ module Mongo
     def index_information(collection_name)
       sel = {:ns =&gt; full_collection_name(collection_name)}
       info = {}
-      query(Collection.new(self, SYSTEM_INDEX_COLLECTION), Query.new(sel)).each { |index|
+      Cursor.new(Collection.new(self, SYSTEM_INDEX_COLLECTION), :selector =&gt; sel).each { |index|
         info[index['name']] = index['key'].to_a
       }
       info
@@ -499,9 +498,8 @@ module Mongo
         end
       end
 
-      q = Query.new(selector)
-      q.number_to_return = -1
-      query(Collection.new(self, SYSTEM_COMMAND_COLLECTION), q, use_admin_db).next_object
+      cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin =&gt; use_admin_db, :limit =&gt; -1, :selector =&gt; selector)
+      cursor.next_object
     end
 
     def _synchronize &amp;block</diff>
      <filename>lib/mongo/db.rb</filename>
    </modified>
    <modified>
      <diff>@@ -27,7 +27,6 @@ PACKAGE_FILES = ['README.rdoc', 'Rakefile', 'mongo-ruby-driver.gemspec',
                  'lib/mongo/gridfs/grid_store.rb',
                  'lib/mongo/gridfs.rb',
                  'lib/mongo/errors.rb',
-                 'lib/mongo/query.rb',
                  'lib/mongo/types/binary.rb',
                  'lib/mongo/types/code.rb',
                  'lib/mongo/types/dbref.rb',
@@ -37,6 +36,7 @@ PACKAGE_FILES = ['README.rdoc', 'Rakefile', 'mongo-ruby-driver.gemspec',
                  'lib/mongo/util/byte_buffer.rb',
                  'lib/mongo/util/conversions.rb',
                  'lib/mongo/util/ordered_hash.rb',
+                 'lib/mongo/util/support.rb',
                  'lib/mongo/util/xml_to_ruby.rb',
                  'lib/mongo.rb']
 TEST_FILES = ['test/mongo-qa/_common.rb',</diff>
      <filename>mongo-ruby-driver.gemspec</filename>
    </modified>
    <modified>
      <diff>@@ -24,19 +24,14 @@ class SlaveConnectionTest &lt; Test::Unit::TestCase
 
     def test_slave_ok_sent_to_queries
       @db   = Connection.new(@@host, @@port, :slave_ok =&gt; true).db('ruby-mongo-demo')
-      @coll = @db['test-collection']
-      @cursor = @coll.find({})
-      assert_equal true, @cursor.query.instance_variable_get(:@slave_ok)
+      assert_equal true, @db.slave_ok?
     end
   else
     puts &quot;Not connected to slave; skipping slave connection tests.&quot;
 
     def test_slave_ok_false_on_queries
-      @db   = Connection.new(@@host, @@port).db('ruby-mongo-demo')
-      @coll = @db['test-collection']
-      @cursor = @coll.find({})
-      assert_nil @cursor.query.instance_variable_get(:@slave_ok)
+      @db = Connection.new(@@host, @@port).db('ruby-mongo-demo')
+      assert !@db.slave_ok?
     end
   end
-
 end</diff>
      <filename>test/test_slave_connection.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>lib/mongo/query.rb</filename>
    </removed>
    <removed>
      <filename>test/test_query.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>de5c078cec164094c7fdc4f6a545e64f1c534fd6</id>
    </parent>
  </parents>
  <author>
    <name>Kyle Banker</name>
    <email>kylebanker@gmail.com</email>
  </author>
  <url>http://github.com/jnunemaker/mongo-ruby-driver/commit/67b7f6b3758fa230edbaa412785fc4ca289b7881</url>
  <id>67b7f6b3758fa230edbaa412785fc4ca289b7881</id>
  <committed-date>2009-10-22T11:10:12-07:00</committed-date>
  <authored-date>2009-10-22T11:10:12-07:00</authored-date>
  <message>Refactored Query class into Cursor class.</message>
  <tree>9c22e23438a5f91ef92544303f0522c93ebb9995</tree>
  <committer>
    <name>Kyle Banker</name>
    <email>kylebanker@gmail.com</email>
  </committer>
</commit>
