mongo 0.18.1 → 0.18.2

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/lib/mongo/db.rb CHANGED
@@ -48,7 +48,7 @@ module Mongo
48
48
 
49
49
  # The Mongo::Connection instance connecting to the MongoDB server.
50
50
  attr_reader :connection
51
-
51
+
52
52
  # An array of [host, port] pairs.
53
53
  attr_reader :nodes
54
54
 
@@ -132,7 +132,7 @@ module Mongo
132
132
  names.map {|name| name.sub(@name + '.', '')}
133
133
  end
134
134
 
135
- # Retruns an array of Collection instances, one for each collection in this
135
+ # Retuns an array of Collection instances, one for each collection in this
136
136
  # database.
137
137
  def collections
138
138
  collection_names.map do |collection_name|
@@ -316,7 +316,7 @@ module Mongo
316
316
  def create_index(collection_name, field_or_spec, unique=false)
317
317
  self.collection(collection_name).create_index(field_or_spec, unique)
318
318
  end
319
-
319
+
320
320
  # Return +true+ if +doc+ contains an 'ok' field with the value 1.
321
321
  def ok?(doc)
322
322
  ok = doc['ok']
@@ -334,9 +334,9 @@ module Mongo
334
334
  end
335
335
 
336
336
  cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin => use_admin_db, :limit => -1, :selector => selector, :socket => sock)
337
- cursor.next_object
337
+ cursor.next_document
338
338
  end
339
-
339
+
340
340
  # Sends a command to the database.
341
341
  #
342
342
  # :selector (required) :: An OrderedHash, or a standard Hash with just one
@@ -352,12 +352,12 @@ module Mongo
352
352
  # any selector containing more than one key must be an OrderedHash.
353
353
  def command(selector, admin=false, check_response=false, sock=nil)
354
354
  raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
355
- if selector.class.eql?(Hash) && selector.keys.length > 1
355
+ if selector.class.eql?(Hash) && selector.keys.length > 1
356
356
  raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
357
357
  end
358
358
 
359
- result = Cursor.new(system_command_collection, :admin => admin,
360
- :limit => -1, :selector => selector, :socket => sock).next_object
359
+ result = Cursor.new(system_command_collection, :admin => admin,
360
+ :limit => -1, :selector => selector, :socket => sock).next_document
361
361
 
362
362
  if check_response && !ok?(result)
363
363
  raise OperationFailure, "Database command '#{selector.keys.first}' failed."
data/lib/mongo/errors.rb CHANGED
@@ -30,16 +30,22 @@ module Mongo
30
30
  # Raised when given a string is not valid utf-8 (Ruby 1.8 only).
31
31
  class InvalidStringEncoding < MongoRubyError; end
32
32
 
33
+ # Raised when attempting to initialize an invalid ObjectID.
34
+ class InvalidObjectID < MongoRubyError; end
35
+
33
36
  # Raised on failures in connection to the database server.
34
37
  class ConnectionError < MongoRubyError; end
35
38
 
36
39
  # Raised on failures in connection to the database server.
37
40
  class ConnectionTimeoutError < MongoRubyError; end
38
41
 
42
+ # Raised when trying to insert a document that exceeds the 4MB limit.
43
+ class InvalidDocument < MongoDBError; end
44
+
39
45
  # Raised when a database operation fails.
40
46
  class OperationFailure < MongoDBError; end
41
-
42
- # Raised when a database operation fails.
47
+
48
+ # Raised when a connection operation fails.
43
49
  class ConnectionFailure < MongoDBError; end
44
50
 
45
51
  # Raised when a client attempts to perform an invalid operation.
@@ -55,7 +55,6 @@ module GridFS
55
55
  def size; @data.size; end
56
56
  alias_method :length, :size
57
57
 
58
- # Erase all data after current position.
59
58
  def truncate
60
59
  if @data.position < @data.length
61
60
  curr_data = @data
@@ -54,6 +54,9 @@ module GridFS
54
54
  # Default is DEFAULT_CONTENT_TYPE
55
55
  attr_accessor :content_type
56
56
 
57
+ # Size of file in bytes
58
+ attr_reader :length
59
+
57
60
  attr_accessor :metadata
58
61
 
59
62
  attr_reader :files_id
@@ -70,7 +73,7 @@ module GridFS
70
73
  class << self
71
74
 
72
75
  def exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
73
- db.collection("#{root_collection}.files").find({'filename' => name}).next_object != nil
76
+ db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
74
77
  end
75
78
 
76
79
  def open(db, name, mode, options={})
@@ -91,12 +94,12 @@ module GridFS
91
94
  }
92
95
  end
93
96
 
94
- # List the contains of all GridFS files stored in the given db and
97
+ # List the contents of all GridFS files stored in the given db and
95
98
  # root collection.
96
99
  #
97
100
  # :db :: the database to use
98
101
  #
99
- # :root_collection :: the root collection to use
102
+ # :root_collection :: the root collection to use. If not specified, will use default root collection.
100
103
  def list(db, root_collection=DEFAULT_ROOT_COLLECTION)
101
104
  db.collection("#{root_collection}.files").find().map { |f|
102
105
  f['filename']
@@ -145,7 +148,7 @@ module GridFS
145
148
  @db, @filename, @mode = db, name, mode
146
149
  @root = options[:root] || DEFAULT_ROOT_COLLECTION
147
150
 
148
- doc = collection.find({'filename' => @filename}).next_object
151
+ doc = collection.find({'filename' => @filename}).next_document
149
152
  if doc
150
153
  @files_id = doc['_id']
151
154
  @content_type = doc['contentType']
@@ -242,7 +245,7 @@ module GridFS
242
245
  str
243
246
  end
244
247
 
245
- def read(len=nil, buf=nil)
248
+ def old_read(len=nil, buf=nil)
246
249
  buf ||= ''
247
250
  byte = self.getc
248
251
  while byte != nil && (len == nil || len > 0)
@@ -253,6 +256,14 @@ module GridFS
253
256
  buf
254
257
  end
255
258
 
259
+ def read(len=nil, buf=nil)
260
+ if len
261
+ read_partial(len, buf)
262
+ else
263
+ read_all(buf)
264
+ end
265
+ end
266
+
256
267
  def readchar
257
268
  byte = self.getc
258
269
  raise EOFError.new if byte == nil
@@ -331,15 +342,21 @@ module GridFS
331
342
  write(obj.to_s)
332
343
  end
333
344
 
334
- # Writes +string+ as bytes and returns the number of bytes written.
335
345
  def write(string)
336
346
  raise "#@filename not opened for write" unless @mode[0] == ?w
337
- count = 0
338
- string.each_byte { |byte|
339
- self.putc byte
340
- count += 1
341
- }
342
- count
347
+ to_write = string.length
348
+ while (to_write > 0) do
349
+ if @curr_chunk && @curr_chunk.data.position == @chunk_size
350
+ prev_chunk_number = @curr_chunk.chunk_number
351
+ @curr_chunk = GridFS::Chunk.new(self, 'n' => prev_chunk_number + 1)
352
+ end
353
+ chunk_available = @chunk_size - @curr_chunk.data.position
354
+ step_size = (to_write > chunk_available) ? chunk_available : to_write
355
+ @curr_chunk.data.put_array(ByteBuffer.new(string[-to_write,step_size]).to_a)
356
+ to_write -= step_size
357
+ @curr_chunk.save
358
+ end
359
+ string.length - to_write
343
360
  end
344
361
 
345
362
  # A no-op.
@@ -396,7 +413,7 @@ module GridFS
396
413
 
397
414
  def tell
398
415
  @position
399
- end
416
+ end
400
417
 
401
418
  #---
402
419
  # ================ closing ================
@@ -446,13 +463,39 @@ module GridFS
446
463
  h
447
464
  end
448
465
 
466
+ def read_partial(len, buf=nil)
467
+ buf ||= ''
468
+ byte = self.getc
469
+ while byte != nil && (len == nil || len > 0)
470
+ buf << byte.chr
471
+ len -= 1 if len
472
+ byte = self.getc if (len == nil || len > 0)
473
+ end
474
+ buf
475
+ end
476
+
477
+ def read_all(buf=nil)
478
+ buf ||= ''
479
+ while true do
480
+ if (@curr_chunk.pos > 0)
481
+ data = @curr_chunk.data.to_s
482
+ buf += data[@position, data.length]
483
+ else
484
+ buf += @curr_chunk.data.to_s
485
+ end
486
+ break if @curr_chunk.chunk_number == last_chunk_number
487
+ @curr_chunk = nth_chunk(@curr_chunk.chunk_number + 1)
488
+ end
489
+ buf
490
+ end
491
+
449
492
  def delete_chunks
450
493
  chunk_collection.remove({'files_id' => @files_id}) if @files_id
451
494
  @curr_chunk = nil
452
495
  end
453
496
 
454
497
  def nth_chunk(n)
455
- mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_object
498
+ mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_document
456
499
  Chunk.new(self, mongo_chunk || {})
457
500
  end
458
501
 
@@ -18,6 +18,7 @@ module Mongo
18
18
 
19
19
  # JavaScript code to be evaluated by MongoDB
20
20
  class Code < String
21
+
21
22
  # Hash mapping identifiers to their values
22
23
  attr_accessor :scope
23
24
 
@@ -44,7 +44,7 @@ module Mongo
44
44
 
45
45
  # Adds a primary key to the given document if needed.
46
46
  def self.create_pk(doc)
47
- doc[:_id] || doc['_id'] ? doc : doc.merge!(:_id => self.new)
47
+ doc.has_key?(:_id) || doc.has_key?('_id') ? doc : doc.merge!(:_id => self.new)
48
48
  end
49
49
 
50
50
  # +data+ is an array of bytes. If nil, a new id will be generated.
@@ -70,7 +70,7 @@ module Mongo
70
70
  # Given a string representation of an ObjectID, return a new ObjectID
71
71
  # with that value.
72
72
  def self.from_string(str)
73
- raise "illegal ObjectID format" unless legal?(str)
73
+ raise InvalidObjectID, "illegal ObjectID format" unless legal?(str)
74
74
  data = []
75
75
  12.times do |i|
76
76
  data[i] = str[i * 2, 2].to_i(16)
@@ -83,7 +83,7 @@ module Mongo
83
83
  # removed. If you are not sure that you need this method you should be
84
84
  # using the regular from_string.
85
85
  def self.from_string_legacy(str)
86
- raise "illegal ObjectID format" unless legal?(str)
86
+ raise InvalidObjectID, "illegal ObjectID format" unless legal?(str)
87
87
  data = []
88
88
  BYTE_ORDER.each_with_index { |string_position, data_index|
89
89
  data[data_index] = str[string_position * 2, 2].to_i(16)
@@ -132,9 +132,8 @@ module Mongo
132
132
 
133
133
  private
134
134
 
135
- begin
136
- require 'mongo_ext/cbson'
137
- rescue LoadError
135
+ # We need to define this method only if CBson isn't loaded.
136
+ unless defined? CBson
138
137
  def generate
139
138
  oid = ''
140
139
 
@@ -72,6 +72,11 @@ class BSON_RUBY
72
72
  buf.put_array(to_utf8(val.to_s).unpack("C*") << 0)
73
73
  end
74
74
 
75
+ def self.serialize_key(buf, key)
76
+ raise InvalidDocument, "Key names / regex patterns must not contain the NULL byte" if key.include? "\x00"
77
+ self.serialize_cstr(buf, key)
78
+ end
79
+
75
80
  def to_a
76
81
  @buf.to_a
77
82
  end
@@ -107,6 +112,9 @@ class BSON_RUBY
107
112
  obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
108
113
 
109
114
  serialize_eoo_element(@buf)
115
+ if @buf.size > 4 * 1024 * 1024
116
+ raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
117
+ end
110
118
  @buf.put_int(@buf.size, 0)
111
119
  self
112
120
  end
@@ -362,7 +370,7 @@ class BSON_RUBY
362
370
 
363
371
  def serialize_null_element(buf, key)
364
372
  buf.put(NULL)
365
- self.class.serialize_cstr(buf, key)
373
+ self.class.serialize_key(buf, key)
366
374
  end
367
375
 
368
376
  def serialize_dbref_element(buf, key, val)
@@ -374,7 +382,7 @@ class BSON_RUBY
374
382
 
375
383
  def serialize_binary_element(buf, key, val)
376
384
  buf.put(BINARY)
377
- self.class.serialize_cstr(buf, key)
385
+ self.class.serialize_key(buf, key)
378
386
 
379
387
  bytes = val.to_a
380
388
  num_bytes = bytes.length
@@ -393,13 +401,13 @@ class BSON_RUBY
393
401
 
394
402
  def serialize_boolean_element(buf, key, val)
395
403
  buf.put(BOOLEAN)
396
- self.class.serialize_cstr(buf, key)
404
+ self.class.serialize_key(buf, key)
397
405
  buf.put(val ? 1 : 0)
398
406
  end
399
407
 
400
408
  def serialize_date_element(buf, key, val)
401
409
  buf.put(DATE)
402
- self.class.serialize_cstr(buf, key)
410
+ self.class.serialize_key(buf, key)
403
411
  millisecs = (val.to_f * 1000).to_i
404
412
  buf.put_long(millisecs)
405
413
  end
@@ -407,7 +415,7 @@ class BSON_RUBY
407
415
  def serialize_number_element(buf, key, val, type)
408
416
  if type == NUMBER
409
417
  buf.put(type)
410
- self.class.serialize_cstr(buf, key)
418
+ self.class.serialize_key(buf, key)
411
419
  buf.put_double(val)
412
420
  else
413
421
  if val > 2**64 / 2 - 1 or val < -2**64 / 2
@@ -415,11 +423,11 @@ class BSON_RUBY
415
423
  end
416
424
  if val > 2**32 / 2 - 1 or val < -2**32 / 2
417
425
  buf.put(NUMBER_LONG)
418
- self.class.serialize_cstr(buf, key)
426
+ self.class.serialize_key(buf, key)
419
427
  buf.put_long(val)
420
428
  else
421
429
  buf.put(type)
422
- self.class.serialize_cstr(buf, key)
430
+ self.class.serialize_key(buf, key)
423
431
  buf.put_int(val)
424
432
  end
425
433
  end
@@ -427,7 +435,7 @@ class BSON_RUBY
427
435
 
428
436
  def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
429
437
  buf.put(opcode)
430
- self.class.serialize_cstr(buf, key)
438
+ self.class.serialize_key(buf, key)
431
439
  buf.put_array(BSON.new.serialize(val, check_keys).to_a)
432
440
  end
433
441
 
@@ -441,10 +449,12 @@ class BSON_RUBY
441
449
 
442
450
  def serialize_regex_element(buf, key, val)
443
451
  buf.put(REGEX)
444
- self.class.serialize_cstr(buf, key)
452
+ self.class.serialize_key(buf, key)
445
453
 
446
- str = val.to_s.sub(/.*?:/, '')[0..-2] # Turn "(?xxx:yyy)" into "yyy"
447
- self.class.serialize_cstr(buf, str)
454
+ str = val.source
455
+ # We use serialize_key here since regex patterns aren't prefixed with
456
+ # length (can't contain the NULL byte).
457
+ self.class.serialize_key(buf, str)
448
458
 
449
459
  options = val.options
450
460
  options_str = ''
@@ -458,14 +468,14 @@ class BSON_RUBY
458
468
 
459
469
  def serialize_oid_element(buf, key, val)
460
470
  buf.put(OID)
461
- self.class.serialize_cstr(buf, key)
471
+ self.class.serialize_key(buf, key)
462
472
 
463
473
  buf.put_array(val.to_a)
464
474
  end
465
475
 
466
476
  def serialize_string_element(buf, key, val, type)
467
477
  buf.put(type)
468
- self.class.serialize_cstr(buf, key)
478
+ self.class.serialize_key(buf, key)
469
479
 
470
480
  # Make a hole for the length
471
481
  len_pos = buf.position
@@ -485,7 +495,7 @@ class BSON_RUBY
485
495
 
486
496
  def serialize_code_w_scope(buf, key, val)
487
497
  buf.put(CODE_W_SCOPE)
488
- self.class.serialize_cstr(buf, key)
498
+ self.class.serialize_key(buf, key)
489
499
 
490
500
  # Make a hole for the length
491
501
  len_pos = buf.position
@@ -544,7 +554,7 @@ class BSON_RUBY
544
554
  when Symbol
545
555
  SYMBOL
546
556
  else
547
- raise "Unknown type of object: #{o.class.name}"
557
+ raise InvalidDocument, "Unknown type of object: #{o.class.name}"
548
558
  end
549
559
  end
550
560
 
@@ -32,11 +32,19 @@ module Mongo #:nodoc:
32
32
  # <tt>{ "field1" => 1, "field2" => -1}</tt>
33
33
  def array_as_sort_parameters(value)
34
34
  order_by = OrderedHash.new
35
- value.each do |param|
36
- if (param.class.name == "String")
37
- order_by[param] = 1
35
+ if value.first.is_a? Array
36
+ value.each do |param|
37
+ if (param.class.name == "String")
38
+ order_by[param] = 1
39
+ else
40
+ order_by[param[0]] = sort_value(param[1]) unless param[1].nil?
41
+ end
42
+ end
43
+ elsif !value.empty?
44
+ if order_by.size == 1
45
+ order_by[value.first] = 1
38
46
  else
39
- order_by[param[0]] = sort_value(param[1]) unless param[1].nil?
47
+ order_by[value.first] = sort_value(value[1])
40
48
  end
41
49
  end
42
50
  order_by
@@ -111,5 +111,23 @@ class OrderedHash < Hash
111
111
  super
112
112
  @ordered_keys = []
113
113
  end
114
+
115
+ def hash
116
+ code = 17
117
+ each_pair do |key, value|
118
+ code = 37 * code + key.hash
119
+ code = 37 * code + value.hash
120
+ end
121
+ code
122
+ end
123
+
124
+ def eql?(o)
125
+ if o.instance_of? OrderedHash
126
+ self.hash == o.hash
127
+ else
128
+ false
129
+ end
130
+ end
131
+
114
132
  end
115
133
  end