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

This commit is contained in:
Mike Dirolf 2009-08-21 14:11:59 -04:00
parent 6a43aaa94f
commit fd3b927771
3 changed files with 24 additions and 49 deletions

View File

@ -35,9 +35,7 @@ module Mongo
@num_to_return = @query.number_to_return || 0 @num_to_return = @query.number_to_return || 0
@cache = [] @cache = []
@closed = false @closed = false
@can_call_to_a = true
@query_run = false @query_run = false
@rows = nil
end end
# Return the next object or nil if there are no more. Raises an error # 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']}" raise OperationFailure, "Count failed: #{response['errmsg']}"
end end
# Iterate over each object, yielding it to the given block. At most # Iterate over each document in this cursor, yielding it to the given
# @num_to_return records are returned (or all of them, if # block.
# @num_to_return is 0).
# #
# If #to_a has already been called then this method uses the array # Iterating over an entire cursor will close it.
# 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.
def each def each
if @rows # Already turned into an array num_returned = 0
@rows.each { |row| yield row } while more? && (@num_to_return <= 0 || num_returned < @num_to_return)
else yield next_object()
num_returned = 0 num_returned += 1
while more? && (@num_to_return <= 0 || num_returned < @num_to_return)
yield next_object()
num_returned += 1
end
@can_call_to_a = false
end end
end end
# Return all of the rows (up to the +num_to_return+ value specified in # Return all of the documents in this cursor as an array of hashes.
# #new) as an array. Calling this multiple times will work fine; it
# always returns the same array.
# #
# Don't use this if you're expecting large amounts of data, of course. # Raises InvalidOperation if this cursor has already been used (including
# All of the returned rows are kept in an array stored in this object # any previous calls to this method).
# so it can be reused.
# #
# You can call #each after calling #to_a (multiple times even, because # Use of this method is discouraged - iterating over a cursor is much
# it will use the internally-stored array), but you can't call #to_a # more efficient in most cases.
# after calling #each unless you also called it before calling #each.
# If you try to do that, an error will be raised.
def to_a def to_a
return @rows if @rows raise InvalidOperation, "can't call Cursor#to_a on a used cursor" if @query_run
raise "can't call Cursor#to_a after calling Cursor#each" unless @can_call_to_a rows = []
@rows = []
num_returned = 0 num_returned = 0
while more? && (@num_to_return <= 0 || num_returned < @num_to_return) while more? && (@num_to_return <= 0 || num_returned < @num_to_return)
@rows << next_object() rows << next_object()
num_returned += 1 num_returned += 1
end end
@rows rows
end end
# Returns an explain plan record for this cursor. # Returns an explain plan record for this cursor.

View File

@ -18,6 +18,9 @@ module Mongo
# Raised when a database operation fails. # Raised when a database operation fails.
class OperationFailure < RuntimeError; end 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. # Raised when an invalid name is used.
class InvalidName < RuntimeError; end class InvalidName < RuntimeError; end
end end

View File

@ -500,25 +500,18 @@ class DBAPITest < Test::Unit::TestCase
cursor = @@coll.find() cursor = @@coll.find()
rows = cursor.to_a rows = cursor.to_a
# Make sure we get back exactly the same array the next time we ask assert_raise InvalidOperation do
rows2 = cursor.to_a cursor.to_a
assert_same rows, rows2 end
# Make sure we can still iterate after calling to_a cursor.each { |doc| fail "should be no docs in each now" }
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
end end
def test_to_a_after_each def test_to_a_after_each
cursor = @@coll.find cursor = @@coll.find
cursor.each { |row| row } cursor.each { |row| row }
begin assert_raise InvalidOperation do
cursor.to_a 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
end end