mongo 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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