From fd3b927771e649c67aff43aff6e47a20dfd1df70 Mon Sep 17 00:00:00 2001 From: Mike Dirolf Date: Fri, 21 Aug 2009 14:11:59 -0400 Subject: [PATCH] API CHANGE no longer save internal array for to_a - means you can't call each or to_a after calling to_a already, doc updates --- lib/mongo/cursor.rb | 53 ++++++++++++++------------------------------- lib/mongo/errors.rb | 3 +++ test/test_db_api.rb | 17 +++++---------- 3 files changed, 24 insertions(+), 49 deletions(-) diff --git a/lib/mongo/cursor.rb b/lib/mongo/cursor.rb index ca55b3b..d3c9683 100644 --- a/lib/mongo/cursor.rb +++ b/lib/mongo/cursor.rb @@ -35,9 +35,7 @@ module Mongo @num_to_return = @query.number_to_return || 0 @cache = [] @closed = false - @can_call_to_a = true @query_run = false - @rows = nil end # Return the next object or nil if there are no more. Raises an error @@ -75,53 +73,34 @@ module Mongo raise OperationFailure, "Count failed: #{response['errmsg']}" end - # Iterate over each object, yielding it to the given block. At most - # @num_to_return records are returned (or all of them, if - # @num_to_return is 0). + # Iterate over each document in this cursor, yielding it to the given + # block. # - # If #to_a has already been called then this method uses the array - # that we store internally. In that case, #each can be called multiple - # times because it re-uses that array. - # - # You can call #each after calling #to_a (multiple times even, because - # it will use the internally-stored array), but you can't call #to_a - # after calling #each unless you also called it before calling #each. - # If you try to do that, an error will be raised. + # Iterating over an entire cursor will close it. def each - if @rows # Already turned into an array - @rows.each { |row| yield row } - else - num_returned = 0 - while more? && (@num_to_return <= 0 || num_returned < @num_to_return) - yield next_object() - num_returned += 1 - end - @can_call_to_a = false + num_returned = 0 + while more? && (@num_to_return <= 0 || num_returned < @num_to_return) + yield next_object() + num_returned += 1 end end - # Return all of the rows (up to the +num_to_return+ value specified in - # #new) as an array. Calling this multiple times will work fine; it - # always returns the same array. + # Return all of the documents in this cursor as an array of hashes. # - # Don't use this if you're expecting large amounts of data, of course. - # All of the returned rows are kept in an array stored in this object - # so it can be reused. + # Raises InvalidOperation if this cursor has already been used (including + # any previous calls to this method). # - # You can call #each after calling #to_a (multiple times even, because - # it will use the internally-stored array), but you can't call #to_a - # after calling #each unless you also called it before calling #each. - # If you try to do that, an error will be raised. + # Use of this method is discouraged - iterating over a cursor is much + # more efficient in most cases. def to_a - return @rows if @rows - raise "can't call Cursor#to_a after calling Cursor#each" unless @can_call_to_a - @rows = [] + raise InvalidOperation, "can't call Cursor#to_a on a used cursor" if @query_run + rows = [] num_returned = 0 while more? && (@num_to_return <= 0 || num_returned < @num_to_return) - @rows << next_object() + rows << next_object() num_returned += 1 end - @rows + rows end # Returns an explain plan record for this cursor. diff --git a/lib/mongo/errors.rb b/lib/mongo/errors.rb index 5e73312..60bd370 100644 --- a/lib/mongo/errors.rb +++ b/lib/mongo/errors.rb @@ -18,6 +18,9 @@ module Mongo # Raised when a database operation fails. class OperationFailure < RuntimeError; end + # Raised when a client attempts to perform an invalid operation. + class InvalidOperation < RuntimeError; end + # Raised when an invalid name is used. class InvalidName < RuntimeError; end end diff --git a/test/test_db_api.rb b/test/test_db_api.rb index 7dd5bae..37e5932 100644 --- a/test/test_db_api.rb +++ b/test/test_db_api.rb @@ -500,25 +500,18 @@ class DBAPITest < Test::Unit::TestCase cursor = @@coll.find() rows = cursor.to_a - # Make sure we get back exactly the same array the next time we ask - rows2 = cursor.to_a - assert_same rows, rows2 + assert_raise InvalidOperation do + cursor.to_a + end - # Make sure we can still iterate after calling to_a - rows_with_each = cursor.collect{|row| row} - assert_equal rows, rows_with_each - - # Make sure we can iterate more than once after calling to_a + cursor.each { |doc| fail "should be no docs in each now" } end def test_to_a_after_each cursor = @@coll.find cursor.each { |row| row } - begin + assert_raise InvalidOperation do cursor.to_a - fail "expected \"can't call\" error" - rescue => ex - assert_equal "can't call Cursor#to_a after calling Cursor#each", ex.to_s end end