mongo 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/HISTORY CHANGED
@@ -1,3 +1,20 @@
1
+ 1.0.4 2010-7-13
2
+
3
+ * Removed deprecated
4
+ - Cursor admin option
5
+ - DB#query
6
+ - DB#create_index (use Collection#create_index)
7
+ - DB#command only takes hash options now
8
+ * j2bson executable (neomantra)
9
+ * Fixed bson_ext compilation on Solaris (slyphon)
10
+ * System JS helpers (neovintage)
11
+ * Use one mutex per thread on pooled connections (cremes)
12
+ * Check for CursorNotFound response flag
13
+ * MapReduce can return raw command output using :raw
14
+ * BSON::OrderedHash equality with other Ruby hashes (Ryan Angilly)
15
+ * Fix for broken Socket.send with large payloads (Frédéric De Jaeger)
16
+ * Lots of minor improvements. See commmits.
17
+
1
18
  1.0.3 2010-6-15
2
19
 
3
20
  * Optimiztion for BSON::OrderedHash
@@ -3,7 +3,7 @@
3
3
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
4
 
5
5
  module Mongo
6
- VERSION = "1.0.3"
6
+ VERSION = "1.0.4"
7
7
  end
8
8
 
9
9
  module Mongo
@@ -21,11 +21,18 @@ module Mongo
21
21
  OP_DELETE = 2006
22
22
  OP_KILL_CURSORS = 2007
23
23
 
24
- OP_QUERY_TAILABLE = 2
25
- OP_QUERY_SLAVE_OK = 4
26
- OP_QUERY_NO_CURSOR_TIMEOUT = 16
24
+ OP_QUERY_TAILABLE = 2 ** 1
25
+ OP_QUERY_SLAVE_OK = 2 ** 2
26
+ OP_QUERY_OPLOG_REPLAY = 2 ** 3
27
+ OP_QUERY_NO_CURSOR_TIMEOUT = 2 ** 4
28
+ OP_QUERY_AWAIT_DATA = 2 ** 5
29
+ OP_QUERY_EXHAUST = 2 ** 6
30
+
31
+ REPLY_CURSOR_NOT_FOUND = 2 ** 0
32
+ REPLY_QUERY_FAILURE = 2 ** 1
33
+ REPLY_SHARD_CONFIG_STALE = 2 ** 2
34
+ REPLY_AWAIT_CAPABLE = 2 ** 3
27
35
  end
28
-
29
36
  end
30
37
 
31
38
  require 'bson'
@@ -157,7 +157,8 @@ module Mongo
157
157
  raise RuntimeError, "Unknown options [#{opts.inspect}]" unless opts.empty?
158
158
 
159
159
  cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
160
- :order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout, :batch_size => batch_size)
160
+ :order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout, :batch_size => batch_size)
161
+
161
162
  if block_given?
162
163
  yield cursor
163
164
  cursor.close()
@@ -470,6 +471,8 @@ module Mongo
470
471
  # @option opts [String] :out (nil) the name of the output collection. If specified, the collection will not be treated as temporary.
471
472
  # @option opts [Boolean] :keeptemp (false) if true, the generated collection will be persisted. default is false.
472
473
  # @option opts [Boolean ] :verbose (false) if true, provides statistics on job execution time.
474
+ # @options opts [Boolean] :raw (false) if true, return the raw result object from the map_reduce command, and not
475
+ # the instantiated collection that's returned by default.
473
476
  #
474
477
  # @return [Collection] a collection containing the results of the operation.
475
478
  #
@@ -477,8 +480,10 @@ module Mongo
477
480
  #
478
481
  # @core mapreduce map_reduce-instance_method
479
482
  def map_reduce(map, reduce, opts={})
483
+ opts.assert_valid_keys(:query, :sort, :limit, :finalize, :keeptemp, :verbose, :raw)
480
484
  map = BSON::Code.new(map) unless map.is_a?(BSON::Code)
481
485
  reduce = BSON::Code.new(reduce) unless reduce.is_a?(BSON::Code)
486
+ raw = opts.delete(:raw)
482
487
 
483
488
  hash = BSON::OrderedHash.new
484
489
  hash['mapreduce'] = self.name
@@ -487,10 +492,15 @@ module Mongo
487
492
  hash.merge! opts
488
493
 
489
494
  result = @db.command(hash)
490
- unless result["ok"] == 1
495
+ unless Mongo::Support.ok?(result)
491
496
  raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
492
497
  end
493
- @db[result["result"]]
498
+
499
+ if raw
500
+ result
501
+ else
502
+ @db[result["result"]]
503
+ end
494
504
  end
495
505
  alias :mapreduce :map_reduce
496
506
 
@@ -32,7 +32,7 @@ module Mongo
32
32
  STANDARD_HEADER_SIZE = 16
33
33
  RESPONSE_HEADER_SIZE = 20
34
34
 
35
- MONGODB_URI_MATCHER = /(([.\w\d]+):([\w\d]+)@)?([-.\w\d]+)(:([\w\d]+))?(\/([-\d\w]+))?/
35
+ MONGODB_URI_MATCHER = /(([-_.\w\d]+):([-_\w\d]+)@)?([-.\w\d]+)(:([\w\d]+))?(\/([-\d\w]+))?/
36
36
  MONGODB_URI_SPEC = "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]"
37
37
 
38
38
  attr_reader :logger, :size, :host, :port, :nodes, :auths, :sockets, :checked_out
@@ -97,7 +97,11 @@ module Mongo
97
97
 
98
98
  # Mutex for synchronizing pool access
99
99
  @connection_mutex = Mutex.new
100
- @safe_mutex = Mutex.new
100
+
101
+
102
+ # Create a mutex when a new key, in this case a socket,
103
+ # is added to the hash.
104
+ @safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
101
105
 
102
106
  # Condition variable for signal and wait
103
107
  @queue = ConditionVariable.new
@@ -368,7 +372,7 @@ module Mongo
368
372
  sock = checkout
369
373
  packed_message = message_with_headers.append!(message_with_check).to_s
370
374
  docs = num_received = cursor_id = ''
371
- @safe_mutex.synchronize do
375
+ @safe_mutexes[sock].synchronize do
372
376
  send_message_on_socket(packed_message, sock)
373
377
  docs, num_received, cursor_id = receive(sock)
374
378
  end
@@ -398,7 +402,7 @@ module Mongo
398
402
  sock = socket || checkout
399
403
 
400
404
  result = ''
401
- @safe_mutex.synchronize do
405
+ @safe_mutexes[sock].synchronize do
402
406
  send_message_on_socket(packed_message, sock)
403
407
  result = receive(sock)
404
408
  end
@@ -632,13 +636,22 @@ module Mongo
632
636
  "expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}"
633
637
  end
634
638
  header_buf.rewind
635
- result_flags = header_buf.get_int
639
+ check_response_flags(header_buf.get_int)
636
640
  cursor_id = header_buf.get_long
637
641
  starting_from = header_buf.get_int
638
642
  number_remaining = header_buf.get_int
639
643
  [number_remaining, cursor_id]
640
644
  end
641
645
 
646
+ def check_response_flags(flags)
647
+ if flags & Mongo::Constants::REPLY_CURSOR_NOT_FOUND != 0
648
+ raise Mongo::OperationFailure, "Query response returned CURSOR_NOT_FOUND. " +
649
+ "Either an invalid cursor was specified, or the cursor may have timed out on the server."
650
+ elsif flags & Mongo::Constants::REPLY_QUERY_FAILURE != 0
651
+ # Getting odd failures when a exception is raised here.
652
+ end
653
+ end
654
+
642
655
  def read_documents(number_received, cursor_id, sock)
643
656
  docs = []
644
657
  number_remaining = number_received
@@ -696,7 +709,10 @@ module Mongo
696
709
  # Requires a packed message and an available socket,
697
710
  def send_message_on_socket(packed_message, socket)
698
711
  begin
699
- socket.send(packed_message, 0)
712
+ while packed_message.size > 0
713
+ byte_sent = socket.send(packed_message, 0)
714
+ packed_message.slice!(0, byte_sent)
715
+ end
700
716
  rescue => ex
701
717
  close
702
718
  raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
@@ -21,9 +21,9 @@ module Mongo
21
21
  include Mongo::Conversions
22
22
  include Enumerable
23
23
 
24
- attr_reader :collection, :selector, :admin, :fields,
24
+ attr_reader :collection, :selector, :fields,
25
25
  :order, :hint, :snapshot, :timeout,
26
- :full_collection_name
26
+ :full_collection_name, :batch_size
27
27
 
28
28
  # Create a new cursor.
29
29
  #
@@ -40,10 +40,6 @@ module Mongo
40
40
 
41
41
  @selector = convert_selector_for_query(options[:selector])
42
42
  @fields = convert_fields_for_query(options[:fields])
43
- if options[:admin]
44
- warn "The admin option to Cursor#new has been deprecated. The cursor should now be passed the admin collection explicitly."
45
- end
46
- @admin = options[:admin] || false
47
43
  @skip = options[:skip] || 0
48
44
  @limit = options[:limit] || 0
49
45
  @order = options[:order]
@@ -237,7 +233,7 @@ module Mongo
237
233
  message = BSON::ByteBuffer.new([0, 0, 0, 0])
238
234
  message.put_int(1)
239
235
  message.put_long(@cursor_id)
240
- @connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, "cursor.close")
236
+ @connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, "cursor.close #{@cursor_id}")
241
237
  end
242
238
  @cursor_id = 0
243
239
  @closed = true
@@ -255,10 +251,11 @@ module Mongo
255
251
  # @see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
256
252
  # The MongoDB wire protocol.
257
253
  def query_opts
258
- timeout = @timeout ? 0 : Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT
259
- slave_ok = @connection.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0
260
- tailable = @tailable ? Mongo::Constants::OP_QUERY_TAILABLE : 0
261
- slave_ok + timeout + tailable
254
+ opts = 0
255
+ opts |= Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT unless @timeout
256
+ opts |= Mongo::Constants::OP_QUERY_SLAVE_OK if @connection.slave_ok?
257
+ opts |= Mongo::Constants::OP_QUERY_TAILABLE if @tailable
258
+ opts
262
259
  end
263
260
 
264
261
  # Get the query options for this Cursor.
@@ -275,6 +272,12 @@ module Mongo
275
272
  :timeout => @timeout }
276
273
  end
277
274
 
275
+ # Clean output for inspect.
276
+ def inspect
277
+ "<Mongo::Cursor:0x#{object_id.to_s(16)} namespace='#{@db.name}.#{@collection.name}' " +
278
+ "@selector=#{@selector.inspect}>"
279
+ end
280
+
278
281
  private
279
282
 
280
283
  # Convert the +:fields+ parameter from a single field name or an array
@@ -325,8 +328,7 @@ module Mongo
325
328
  message = BSON::ByteBuffer.new([0, 0, 0, 0])
326
329
 
327
330
  # DB name.
328
- db_name = @admin ? 'admin' : @db.name
329
- BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
331
+ BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@collection.name}")
330
332
 
331
333
  # Number of results to return.
332
334
  message.put_int(@batch_size)
@@ -356,8 +358,7 @@ module Mongo
356
358
  def construct_query_message
357
359
  message = BSON::ByteBuffer.new
358
360
  message.put_int(query_opts)
359
- db_name = @admin ? 'admin' : @db.name
360
- BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
361
+ BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@collection.name}")
361
362
  message.put_int(@skip)
362
363
  message.put_int(@limit)
363
364
  spec = query_contains_special_fields? ? construct_query_spec : @selector
@@ -367,7 +368,7 @@ module Mongo
367
368
  end
368
369
 
369
370
  def query_log_message
370
- "#{@admin ? 'admin' : @db.name}['#{@collection.name}'].find(#{@selector.inspect}, #{@fields ? @fields.inspect : '{}'})" +
371
+ "#{@db.name}['#{@collection.name}'].find(#{@selector.inspect}, #{@fields ? @fields.inspect : '{}'})" +
371
372
  "#{@skip != 0 ? ('.skip(' + @skip.to_s + ')') : ''}#{@limit != 0 ? ('.limit(' + @limit.to_s + ')') : ''}" +
372
373
  "#{@order ? ('.sort(' + @order.inspect + ')') : ''}"
373
374
  end
@@ -29,6 +29,7 @@ module Mongo
29
29
  SYSTEM_INDEX_COLLECTION = "system.indexes"
30
30
  SYSTEM_PROFILE_COLLECTION = "system.profile"
31
31
  SYSTEM_USER_COLLECTION = "system.users"
32
+ SYSTEM_JS_COLLECTION = "system.js"
32
33
  SYSTEM_COMMAND_COLLECTION = "$cmd"
33
34
 
34
35
  # Counter for generating unique request ids.
@@ -106,6 +107,36 @@ module Mongo
106
107
  end
107
108
  end
108
109
 
110
+ # Adds a stored Javascript function to the database which can executed
111
+ # server-side in map_reduce, db.eval and $where clauses.
112
+ #
113
+ # @param [String] function_name
114
+ # @param [String] code
115
+ #
116
+ # @return [String] the function name saved to the database
117
+ def add_stored_function(function_name, code)
118
+ self[SYSTEM_JS_COLLECTION].save(
119
+ {
120
+ "_id" => function_name,
121
+ :value => BSON::Code.new(code)
122
+ }
123
+ )
124
+ end
125
+
126
+ # Removes stored Javascript function from the database. Returns
127
+ # false if the function does not exist
128
+ #
129
+ # @param [String] function_name
130
+ #
131
+ # @return [Boolean]
132
+ def remove_stored_function(function_name)
133
+ if self[SYSTEM_JS_COLLECTION].find_one({"_id" => function_name})
134
+ self[SYSTEM_JS_COLLECTION].remove({"_id" => function_name}, :safe => true)
135
+ else
136
+ return false
137
+ end
138
+ end
139
+
109
140
  # Adds a user to this database for use with authentication. If the user already
110
141
  # exists in the system, the password will be updated.
111
142
  #
@@ -238,7 +269,7 @@ module Mongo
238
269
  #
239
270
  # @param [String] name
240
271
  #
241
- # @return [Boolean] True on success or if the collection names doesn't exist.
272
+ # @return [Boolean] +true+ on success or +false+ if the collection name doesn't exist.
242
273
  def drop_collection(name)
243
274
  return true unless collection_names.include?(name)
244
275
 
@@ -304,17 +335,6 @@ module Mongo
304
335
  command(:reseterror => 1)
305
336
  end
306
337
 
307
- # @deprecated please use Collection#find to create queries.
308
- #
309
- # Returns a Cursor over the query results.
310
- #
311
- # Note that the query gets sent lazily; the cursor calls
312
- # Connection#send_message when needed. If the caller never requests an
313
- # object from the cursor, the query never gets sent.
314
- def query(collection, query, admin=false)
315
- Cursor.new(self, collection, query, admin)
316
- end
317
-
318
338
  # Dereference a DBRef, returning the document it points to.
319
339
  #
320
340
  # @param [Mongo::DBRef] dbref
@@ -375,7 +395,7 @@ module Mongo
375
395
  oh = BSON::OrderedHash.new
376
396
  oh[:deleteIndexes] = collection_name
377
397
  oh[:index] = index_name
378
- doc = command(oh)
398
+ doc = command(oh, :check_response => false)
379
399
  ok?(doc) || raise(MongoDBError, "Error with drop_index command: #{doc.inspect}")
380
400
  end
381
401
 
@@ -403,23 +423,6 @@ module Mongo
403
423
  self.command({:dbstats => 1})
404
424
  end
405
425
 
406
- # Create a new index on the given collection.
407
- # Normally called by Collection#create_index.
408
- #
409
- # @param [String] collection_name
410
- # @param [String, Array] field_or_spec either either a single field name
411
- # or an array of [field name, direction] pairs. Directions should be specified as
412
- # Mongo::ASCENDING or Mongo::DESCENDING.
413
- # @param [Boolean] unique if +true+, the created index will enforce a uniqueness constraint.
414
- #
415
- # @return [String] the name of the index created.
416
- #
417
- # @deprecated
418
- def create_index(collection_name, field_or_spec, unique=false)
419
- warn "DB#create_index is now deprecated. Please use Collection#create_index instead."
420
- self.collection(collection_name).create_index(field_or_spec, :unique => unique)
421
- end
422
-
423
426
  # Return +true+ if the supplied +doc+ contains an 'ok' field with the value 1.
424
427
  #
425
428
  # @param [Hash] doc
@@ -453,24 +456,19 @@ module Mongo
453
456
  #
454
457
  # @core commands command_instance-method
455
458
  def command(selector, opts={}, old_check_response=false, old_sock=nil)
456
- if opts.is_a?(Hash)
457
- check_response = opts[:check_response].nil? ? true : opts[:check_response]
458
- sock = opts[:sock]
459
- else
460
- warn "The options passed to DB#command should now be passed as hash keys; the admin option has been deprecated."
461
- admin = opts
462
- check_response = old_check_response
463
- sock = old_sock
464
- end
459
+ check_response = opts[:check_response].nil? ? true : opts[:check_response]
460
+ sock = opts[:sock]
465
461
  raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
466
462
  if selector.keys.length > 1 && RUBY_VERSION < '1.9' && selector.class != BSON::OrderedHash
467
463
  raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
468
464
  end
469
465
 
470
- result = Cursor.new(system_command_collection, :admin => admin,
466
+ result = Cursor.new(system_command_collection,
471
467
  :limit => -1, :selector => selector, :socket => sock).next_document
472
468
 
473
- if result.nil? || (check_response && !ok?(result))
469
+ if result.nil?
470
+ raise OperationFailure, "Database command '#{selector.keys.first}' failed: returned null."
471
+ elsif (check_response && !ok?(result))
474
472
  raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{result.inspect}"
475
473
  else
476
474
  result
@@ -41,10 +41,11 @@ module Mongo
41
41
  @chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]], :unique => true)
42
42
  end
43
43
 
44
- # Store a file in the file store.
44
+ # Store a file in the file store. This method is designed only for writing new files;
45
+ # if you need to update a given file, first delete it using #Grid#delete.
45
46
  #
46
47
  # Note that arbitary metadata attributes can be saved to the file by passing
47
- # them is as options.
48
+ # them in as options.
48
49
  #
49
50
  # @param [String, #read] data a string or io-like object to store.
50
51
  #
@@ -315,11 +315,18 @@ module Mongo
315
315
  @aliases = opts.delete(:aliases) if opts[:aliases]
316
316
  @file_length = 0
317
317
  opts.each {|k, v| self[k] = v}
318
+ check_existing_file if @safe
318
319
 
319
320
  @current_chunk = create_chunk(0)
320
321
  @file_position = 0
321
322
  end
322
323
 
324
+ def check_existing_file
325
+ if @files.find_one('_id' => @files_id)
326
+ raise GridError, "Attempting to overwrite with Grid#put. You must delete the file first."
327
+ end
328
+ end
329
+
323
330
  def to_mongo_object
324
331
  h = BSON::OrderedHash.new
325
332
  h['_id'] = @files_id
@@ -393,7 +393,7 @@ class TestCollection < Test::Unit::TestCase
393
393
  assert c.closed?
394
394
  end
395
395
 
396
- if @@version < "1.1.1"
396
+ if @@version > "1.1.1"
397
397
  def test_map_reduce
398
398
  @@test << { "user_id" => 1 }
399
399
  @@test << { "user_id" => 2 }
@@ -429,6 +429,23 @@ class TestCollection < Test::Unit::TestCase
429
429
  assert res.find_one({"_id" => 2})
430
430
  assert res.find_one({"_id" => 3})
431
431
  end
432
+
433
+ def test_map_reduce_with_raw_response
434
+ m = Code.new("function() { emit(this.user_id, 1); }")
435
+ r = Code.new("function(k,vals) { return 1; }")
436
+ res = @@test.map_reduce(m, r, :raw => true)
437
+ assert res["result"]
438
+ assert res["counts"]
439
+ assert res["timeMillis"]
440
+ end
441
+
442
+ def test_allows_only_valid_keys
443
+ m = Code.new("function() { emit(this.user_id, 1); }")
444
+ r = Code.new("function(k,vals) { return 1; }")
445
+ assert_raise ArgumentError do
446
+ @@test.map_reduce(m, r, :foo => true)
447
+ end
448
+ end
432
449
  end
433
450
 
434
451
  if @@version > "1.3.0"
@@ -686,7 +703,7 @@ class TestCollection < Test::Unit::TestCase
686
703
  assert_nil cursor.next_document
687
704
  end
688
705
 
689
- should "" do
706
+ should "fail tailable cursor on a non-capped collection" do
690
707
  col = @@db['regular-collection']
691
708
  col.insert({:a => 1000})
692
709
  tail = Cursor.new(col, :tailable => true, :order => [['$natural', 1]])
@@ -0,0 +1,76 @@
1
+ require 'test/test_helper'
2
+ require 'logger'
3
+
4
+ class CursorTest < Test::Unit::TestCase
5
+
6
+ include Mongo
7
+
8
+ @@connection = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
9
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT)
10
+ @@db = @@connection.db(MONGO_TEST_DB)
11
+ @@coll = @@db.collection('test')
12
+ @@version = @@connection.server_version
13
+
14
+ def setup
15
+ @@coll.remove
16
+ @@coll.insert('a' => 1) # collection not created until it's used
17
+ @@coll_full_name = "#{MONGO_TEST_DB}.test"
18
+ end
19
+
20
+ def test_refill_via_get_more
21
+ assert_equal 1, @@coll.count
22
+ 1000.times { |i|
23
+ assert_equal 1 + i, @@coll.count
24
+ @@coll.insert('a' => i)
25
+ }
26
+
27
+ assert_equal 1001, @@coll.count
28
+ count = 0
29
+ @@coll.find.each { |obj|
30
+ count += obj['a']
31
+ }
32
+ assert_equal 1001, @@coll.count
33
+
34
+ # do the same thing again for debugging
35
+ assert_equal 1001, @@coll.count
36
+ count2 = 0
37
+ @@coll.find.each { |obj|
38
+ count2 += obj['a']
39
+ }
40
+ assert_equal 1001, @@coll.count
41
+
42
+ assert_equal count, count2
43
+ assert_equal 499501, count
44
+ end
45
+
46
+ def test_refill_via_get_more_alt_coll
47
+ coll = @@db.collection('test-alt-coll')
48
+ coll.remove
49
+ coll.insert('a' => 1) # collection not created until it's used
50
+ assert_equal 1, coll.count
51
+
52
+ 1000.times { |i|
53
+ assert_equal 1 + i, coll.count
54
+ coll.insert('a' => i)
55
+ }
56
+
57
+ assert_equal 1001, coll.count
58
+ count = 0
59
+ coll.find.each { |obj|
60
+ count += obj['a']
61
+ }
62
+ assert_equal 1001, coll.count
63
+
64
+ # do the same thing again for debugging
65
+ assert_equal 1001, coll.count
66
+ count2 = 0
67
+ coll.find.each { |obj|
68
+ count2 += obj['a']
69
+ }
70
+ assert_equal 1001, coll.count
71
+
72
+ assert_equal count, count2
73
+ assert_equal 499501, count
74
+ end
75
+
76
+ end
@@ -0,0 +1,32 @@
1
+ require 'test/test_helper'
2
+ require 'logger'
3
+
4
+ class CursorTest < Test::Unit::TestCase
5
+
6
+ include Mongo
7
+
8
+ @@connection = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
9
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT)
10
+ @@db = @@connection.db(MONGO_TEST_DB)
11
+ @@coll = @@db.collection('test')
12
+ @@version = @@connection.server_version
13
+
14
+ def setup
15
+ @@coll.remove
16
+ @@coll.insert('a' => 1) # collection not created until it's used
17
+ @@coll_full_name = "#{MONGO_TEST_DB}.test"
18
+ end
19
+
20
+ def test_cursor_invalid
21
+ 100.times do |n|
22
+ @@coll.insert({:a => "HELLO" * 50000})
23
+ end
24
+
25
+ cursor = @@coll.find({}, :batch_size => 50)
26
+ cursor.next_document
27
+ p @@db.command("cursorInfo" => 1)
28
+ cursor.close
29
+ p @@db.command("cursorInfo" => 1)
30
+ cursor.next_document
31
+ end
32
+ end
@@ -17,6 +17,13 @@ class CursorTest < Test::Unit::TestCase
17
17
  @@coll_full_name = "#{MONGO_TEST_DB}.test"
18
18
  end
19
19
 
20
+ def test_inspect
21
+ selector = {:a => 1}
22
+ cursor = @@coll.find(selector)
23
+ assert_equal "<Mongo::Cursor:0x#{cursor.object_id.to_s(16)} namespace='#{@@db.name}.#{@@coll.name}' " +
24
+ "@selector=#{selector.inspect}>", cursor.inspect
25
+ end
26
+
20
27
  def test_explain
21
28
  cursor = @@coll.find('a' => 1)
22
29
  explaination = cursor.explain
@@ -130,7 +137,7 @@ class CursorTest < Test::Unit::TestCase
130
137
  @@coll.save({:t => 't2'})
131
138
  @@coll.save({:t => 't2'})
132
139
 
133
- assert_equal 3, @@coll.find({'_id' => {'$gt' => t1_id}, '_id' => {'$lt' => t2_id}}).count
140
+ assert_equal 3, @@coll.find({'_id' => {'$gt' => t1_id, '$lt' => t2_id}}).count
134
141
  @@coll.find({'_id' => {'$gt' => t2_id}}).each do |doc|
135
142
  assert_equal 't2', doc['t']
136
143
  end
@@ -389,4 +396,21 @@ class CursorTest < Test::Unit::TestCase
389
396
 
390
397
  assert_equal false, cursor.has_next?
391
398
  end
399
+
400
+ def test_cursor_invalid
401
+ @@coll.remove
402
+ 1000.times do |n|
403
+ @@coll.insert({:a => n})
404
+ end
405
+
406
+ cursor = @@coll.find({})
407
+ cursor.next_document
408
+ cursor.close
409
+
410
+ assert_raise_error(Mongo::OperationFailure, "CURSOR_NOT_FOUND") do
411
+ 999.times do
412
+ cursor.next_document
413
+ end
414
+ end
415
+ end
392
416
  end
@@ -289,7 +289,7 @@ class DBAPITest < Test::Unit::TestCase
289
289
  def test_index_create_with_symbol
290
290
  assert_equal @@coll.index_information.length, 1
291
291
 
292
- name = @@db.create_index(@@coll.name, :a)
292
+ name = @@coll.create_index([['a', 1]])
293
293
  info = @@db.index_information(@@coll.name)
294
294
  assert_equal name, "a_1"
295
295
  assert_equal @@coll.index_information, info
@@ -230,7 +230,7 @@ class DBTest < Test::Unit::TestCase
230
230
  def test_text_port_number_raises_no_errors
231
231
  conn = Connection.new(@@host, @@port.to_s)
232
232
  db = conn[MONGO_TEST_DB]
233
- assert db.collection('users').remove
233
+ db.collection('users').remove
234
234
  end
235
235
 
236
236
  def test_user_management
@@ -247,6 +247,14 @@ class DBTest < Test::Unit::TestCase
247
247
  assert !@@db.remove_user("joe")
248
248
  end
249
249
 
250
+ def test_stored_function_management
251
+ @@db.add_stored_function("sum", "function (x, y) { return x + y; }")
252
+ assert_equal @@db.eval("return sum(2,3);"), 5
253
+ assert @@db.remove_stored_function("sum")
254
+ assert_raise OperationFailure do
255
+ @@db.eval("return sum(2,3);")
256
+ end
257
+ end
250
258
 
251
259
  if @@version >= "1.3.5"
252
260
  def test_db_stats
@@ -52,9 +52,7 @@ class GridIOTest < Test::Unit::TestCase
52
52
 
53
53
  should "raise an exception when check fails" do
54
54
  io = File.open(File.join(File.dirname(__FILE__), 'data', 'sample_file.pdf'), 'r')
55
- db = mock()
56
- db.stubs(:command).returns({'md5' => '12345'})
57
- @files.expects(:db).returns(db)
55
+ @db.stubs(:command).returns({'md5' => '12345'})
58
56
  file = GridIO.new(@files, @chunks, 'bigfile', 'w', :safe => true)
59
57
  file.write(io)
60
58
  assert_raise GridMD5Failure do
@@ -27,6 +27,12 @@ class GridTest < Test::Unit::TestCase
27
27
  assert_equal 'sample', file['filename']
28
28
  end
29
29
 
30
+ should "not be able to overwrite an exising file" do
31
+ assert_raise GridError do
32
+ @grid.put(@data, :filename => 'sample', :_id => @id, :safe => true)
33
+ end
34
+ end
35
+
30
36
  should "return nil if it doesn't exist" do
31
37
  assert_nil @grid.exist?(:metadata => 'foo')
32
38
  end
@@ -88,6 +88,13 @@ class ConnectionTest < Test::Unit::TestCase
88
88
  assert_equal auth_hash, @conn.auths[1]
89
89
  end
90
90
 
91
+ should "parse a uri with a hyphen & underscore in the username or password" do
92
+ @conn = Connection.from_uri("mongodb://hyphen-user_name:p-s_s@localhost:27017/db", :connect => false)
93
+ assert_equal ['localhost', 27017], @conn.nodes[0]
94
+ auth_hash = { 'db_name' => 'db', 'username' => 'hyphen-user_name', "password" => 'p-s_s' }
95
+ assert_equal auth_hash, @conn.auths[0]
96
+ end
97
+
91
98
  should "attempt to connect" do
92
99
  TCPSocket.stubs(:new).returns(new_mock_socket)
93
100
  @conn = Connection.from_uri("mongodb://localhost", :connect => false)
@@ -9,13 +9,6 @@ class CursorTest < Test::Unit::TestCase
9
9
  @cursor = Cursor.new(@collection)
10
10
  end
11
11
 
12
- should "set admin to false" do
13
- assert_equal false, @cursor.admin
14
-
15
- @cursor = Cursor.new(@collection, :admin => true)
16
- assert_equal true, @cursor.admin
17
- end
18
-
19
12
  should "set selector" do
20
13
  assert @cursor.selector == {}
21
14
 
@@ -31,25 +31,25 @@ class DBTest < Test::Unit::TestCase
31
31
 
32
32
  should "raise an error if the selector is omitted" do
33
33
  assert_raise MongoArgumentError do
34
- @db.command({}, true)
34
+ @db.command({}, :check_response => true)
35
35
  end
36
36
  end
37
37
 
38
38
  should "create the proper cursor" do
39
39
  @cursor = mock(:next_document => {"ok" => 1})
40
- Cursor.expects(:new).with(@collection, :admin => true,
40
+ Cursor.expects(:new).with(@collection,
41
41
  :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
42
42
  command = {:buildinfo => 1}
43
- @db.command(command, true)
43
+ @db.command(command, :check_response => true)
44
44
  end
45
45
 
46
46
  should "raise an error when the command fails" do
47
47
  @cursor = mock(:next_document => {"ok" => 0})
48
- Cursor.expects(:new).with(@collection, :admin => true,
48
+ Cursor.expects(:new).with(@collection,
49
49
  :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
50
50
  assert_raise OperationFailure do
51
51
  command = {:buildinfo => 1}
52
- @db.command(command, true, true)
52
+ @db.command(command, :check_response => true)
53
53
  end
54
54
  end
55
55
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 0
8
- - 3
9
- version: 1.0.3
8
+ - 4
9
+ version: 1.0.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jim Menard
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-06-15 00:00:00 -04:00
19
+ date: 2010-07-13 00:00:00 -04:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -29,8 +29,8 @@ dependencies:
29
29
  segments:
30
30
  - 1
31
31
  - 0
32
- - 3
33
- version: 1.0.3
32
+ - 4
33
+ version: 1.0.4
34
34
  type: :runtime
35
35
  version_requirements: *id001
36
36
  description: A Ruby driver for MongoDB. For more information about Mongo, see http://www.mongodb.org.
@@ -112,6 +112,8 @@ test_files:
112
112
  - test/collection_test.rb
113
113
  - test/connection_test.rb
114
114
  - test/conversions_test.rb
115
+ - test/cursor_fail_test.rb
116
+ - test/cursor_message_test.rb
115
117
  - test/cursor_test.rb
116
118
  - test/db_api_test.rb
117
119
  - test/db_connection_test.rb