mongo 0.19.3 → 0.20

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.
Files changed (65) hide show
  1. data/README.rdoc +7 -3
  2. data/Rakefile +16 -6
  3. data/bin/bson_benchmark.rb +2 -2
  4. data/examples/gridfs.rb +3 -2
  5. data/lib/mongo.rb +3 -26
  6. data/lib/mongo/collection.rb +69 -50
  7. data/lib/mongo/connection.rb +16 -41
  8. data/lib/mongo/cursor.rb +22 -32
  9. data/lib/mongo/db.rb +13 -5
  10. data/lib/mongo/exceptions.rb +11 -15
  11. data/lib/mongo/gridfs/grid.rb +14 -3
  12. data/lib/mongo/gridfs/grid_file_system.rb +28 -5
  13. data/lib/mongo/gridfs/grid_io.rb +42 -24
  14. data/lib/mongo/util/support.rb +13 -2
  15. data/mongo-ruby-driver.gemspec +3 -1
  16. data/test/collection_test.rb +62 -9
  17. data/test/connection_test.rb +21 -32
  18. data/test/conversions_test.rb +1 -1
  19. data/test/cursor_test.rb +2 -2
  20. data/test/db_api_test.rb +28 -27
  21. data/test/db_connection_test.rb +1 -1
  22. data/test/db_test.rb +23 -13
  23. data/test/grid_file_system_test.rb +30 -4
  24. data/test/grid_io_test.rb +14 -1
  25. data/test/grid_test.rb +59 -3
  26. data/test/test_helper.rb +4 -1
  27. data/test/threading/test_threading_large_pool.rb +1 -1
  28. data/test/threading_test.rb +1 -1
  29. data/test/unit/collection_test.rb +2 -2
  30. data/test/unit/cursor_test.rb +7 -0
  31. data/test/unit/db_test.rb +8 -8
  32. metadata +6 -46
  33. data/bin/gr.rb +0 -14
  34. data/lib/bson.rb +0 -46
  35. data/lib/bson/bson_c.rb +0 -20
  36. data/lib/bson/bson_ruby.rb +0 -601
  37. data/lib/bson/byte_buffer.rb +0 -224
  38. data/lib/bson/exceptions.rb +0 -39
  39. data/lib/bson/ordered_hash.rb +0 -140
  40. data/lib/bson/types/binary.rb +0 -54
  41. data/lib/bson/types/code.rb +0 -36
  42. data/lib/bson/types/dbref.rb +0 -40
  43. data/lib/bson/types/min_max_keys.rb +0 -58
  44. data/lib/bson/types/objectid.rb +0 -180
  45. data/lib/bson/types/regexp_of_holding.rb +0 -45
  46. data/lib/mongo/gridfs.rb +0 -29
  47. data/lib/mongo/gridfs/chunk.rb +0 -91
  48. data/lib/mongo/gridfs/grid_store.rb +0 -580
  49. data/lib/mongo/types/binary.rb +0 -52
  50. data/lib/mongo/types/code.rb +0 -36
  51. data/lib/mongo/types/dbref.rb +0 -40
  52. data/lib/mongo/types/min_max_keys.rb +0 -58
  53. data/lib/mongo/types/objectid.rb +0 -180
  54. data/lib/mongo/types/regexp_of_holding.rb +0 -45
  55. data/lib/mongo/util/bson_c.rb +0 -18
  56. data/lib/mongo/util/bson_ruby.rb +0 -606
  57. data/lib/mongo/util/byte_buffer.rb +0 -222
  58. data/lib/mongo/util/ordered_hash.rb +0 -140
  59. data/test/binary_test.rb +0 -15
  60. data/test/bson_test.rb +0 -459
  61. data/test/byte_buffer_test.rb +0 -81
  62. data/test/chunk_test.rb +0 -82
  63. data/test/grid_store_test.rb +0 -337
  64. data/test/objectid_test.rb +0 -125
  65. data/test/ordered_hash_test.rb +0 -172
@@ -227,7 +227,7 @@ module Mongo
227
227
  # @return [True]
228
228
  def close
229
229
  if @cursor_id
230
- message = ByteBuffer.new([0, 0, 0, 0])
230
+ message = BSON::ByteBuffer.new([0, 0, 0, 0])
231
231
  message.put_int(1)
232
232
  message.put_long(@cursor_id)
233
233
  @connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, "cursor.close")
@@ -282,6 +282,8 @@ module Mongo
282
282
  returning({}) do |hash|
283
283
  fields.each { |field| hash[field] = 1 }
284
284
  end
285
+ when Hash
286
+ return fields
285
287
  end
286
288
  end
287
289
 
@@ -301,11 +303,6 @@ module Mongo
301
303
  end
302
304
  end
303
305
 
304
- # Returns true if the query contains order, explain, hint, or snapshot.
305
- def query_contains_special_fields?
306
- @order || @explain || @hint || @snapshot
307
- end
308
-
309
306
  # Return a number of documents remaining for this cursor.
310
307
  def num_remaining
311
308
  refill_via_get_more if @cache.length == 0
@@ -314,11 +311,11 @@ module Mongo
314
311
 
315
312
  def refill_via_get_more
316
313
  return if send_initial_query || @cursor_id.zero?
317
- message = ByteBuffer.new([0, 0, 0, 0])
314
+ message = BSON::ByteBuffer.new([0, 0, 0, 0])
318
315
 
319
316
  # DB name.
320
317
  db_name = @admin ? 'admin' : @db.name
321
- BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
318
+ BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
322
319
 
323
320
  # Number of results to return; db decides for now.
324
321
  message.put_int(0)
@@ -346,18 +343,15 @@ module Mongo
346
343
  end
347
344
 
348
345
  def construct_query_message
349
- message = ByteBuffer.new
346
+ message = BSON::ByteBuffer.new
350
347
  message.put_int(query_opts)
351
348
  db_name = @admin ? 'admin' : @db.name
352
- BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
349
+ BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
353
350
  message.put_int(@skip)
354
351
  message.put_int(@limit)
355
- selector = @selector
356
- if query_contains_special_fields?
357
- selector = selector_with_special_query_fields
358
- end
359
- message.put_array(BSON.serialize(selector, false).to_a)
360
- message.put_array(BSON.serialize(@fields, false).to_a) if @fields
352
+ spec = query_contains_special_fields? ? construct_query_spec : @selector
353
+ message.put_array(BSON::BSON_CODER.serialize(spec, false).to_a)
354
+ message.put_array(BSON::BSON_CODER.serialize(@fields, false).to_a) if @fields
361
355
  message
362
356
  end
363
357
 
@@ -367,24 +361,20 @@ module Mongo
367
361
  "#{@order ? ('.sort(' + @order.inspect + ')') : ''}"
368
362
  end
369
363
 
370
- def selector_with_special_query_fields
371
- sel = OrderedHash.new
372
- sel['query'] = @selector
373
- sel['orderby'] = formatted_order_clause if @order
374
- sel['$hint'] = @hint if @hint && @hint.length > 0
375
- sel['$explain'] = true if @explain
376
- sel['$snapshot'] = true if @snapshot
377
- sel
364
+ def construct_query_spec
365
+ return @selector if @selector.has_key?('$query')
366
+ spec = OrderedHash.new
367
+ spec['$query'] = @selector
368
+ spec['$orderby'] = Mongo::Support.format_order_clause(@order) if @order
369
+ spec['$hint'] = @hint if @hint && @hint.length > 0
370
+ spec['$explain'] = true if @explain
371
+ spec['$snapshot'] = true if @snapshot
372
+ spec
378
373
  end
379
374
 
380
- def formatted_order_clause
381
- case @order
382
- when String, Symbol then string_as_sort_parameters(@order)
383
- when Array then array_as_sort_parameters(@order)
384
- else
385
- raise InvalidSortValueError, "Illegal sort clause, '#{@order.class.name}'; must be of the form " +
386
- "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
387
- end
375
+ # Returns true if the query contains order, explain, hint, or snapshot.
376
+ def query_contains_special_fields?
377
+ @order || @explain || @hint || @snapshot
388
378
  end
389
379
 
390
380
  def to_s
@@ -54,10 +54,10 @@ module Mongo
54
54
  # @param [Mongo::Connection] connection a connection object pointing to MongoDB. Note
55
55
  # that databases are usually instantiated via the Connection class. See the examples below.
56
56
  #
57
- # @option options [Boolean] strict (False) If true, collections must exist to be accessed and must
57
+ # @option options [Boolean] :strict (False) If true, collections must exist to be accessed and must
58
58
  # not exist to be created. See DB#collection and DB#create_collection.
59
59
  #
60
- # @option options [Object, #create_pk(doc)] pk (Mongo::ObjectID) A primary key factory object,
60
+ # @option options [Object, #create_pk(doc)] :pk (Mongo::ObjectID) A primary key factory object,
61
61
  # which should take a hash and return a hash which merges the original hash with any primary key
62
62
  # fields the factory wishes to inject. (NOTE: if the object already has a primary key,
63
63
  # the factory should not inject a new key).
@@ -229,7 +229,7 @@ module Mongo
229
229
  # @return [Mongo::Collection]
230
230
  def collection(name)
231
231
  return Collection.new(self, name, @pk_factory) if !strict? || collection_names.include?(name)
232
- raise MongoDBError, "Collection #{name} doesn't exist. Currently in strict mode."
232
+ raise Mongo::MongoDBError, "Collection #{name} doesn't exist. Currently in strict mode."
233
233
  end
234
234
  alias_method :[], :collection
235
235
 
@@ -325,8 +325,8 @@ module Mongo
325
325
  #
326
326
  # @return [String] the return value of the function.
327
327
  def eval(code, *args)
328
- if not code.is_a? Code
329
- code = Code.new(code)
328
+ if not code.is_a? BSON::Code
329
+ code = BSON::Code.new(code)
330
330
  end
331
331
 
332
332
  oh = OrderedHash.new
@@ -386,6 +386,14 @@ module Mongo
386
386
  info
387
387
  end
388
388
 
389
+
390
+ # Return stats on this database. Uses MongoDB's dbstats command.
391
+ #
392
+ # @return [Hash]
393
+ def stats
394
+ self.command({:dbstats => 1})
395
+ end
396
+
389
397
  # Create a new index on the given collection.
390
398
  # Normally called by Collection#create_index.
391
399
  #
@@ -24,17 +24,17 @@ module Mongo
24
24
  # Raised when configuration options cause connections, queries, etc., to fail.
25
25
  class ConfigurationError < MongoRubyError; end
26
26
 
27
- # Raised with fatal errors to GridFS.
27
+ # Raised on fatal errors to GridFS.
28
28
  class GridError < MongoRubyError; end
29
29
 
30
- # Raised when invalid arguments are sent to Mongo Ruby methods.
31
- class MongoArgumentError < MongoRubyError; end
30
+ # Raised on fatal errors to GridFS.
31
+ class GridFileNotFound < GridError; end
32
32
 
33
- # Raised when given a string is not valid utf-8 (Ruby 1.8 only).
34
- class InvalidStringEncoding < MongoRubyError; end
33
+ # Raised on fatal errors to GridFS.
34
+ class GridMD5Failure < GridError; end
35
35
 
36
- # Raised when attempting to initialize an invalid ObjectID.
37
- class InvalidObjectID < MongoRubyError; end
36
+ # Raised when invalid arguments are sent to Mongo Ruby methods.
37
+ class MongoArgumentError < MongoRubyError; end
38
38
 
39
39
  # Raised on failures in connection to the database server.
40
40
  class ConnectionError < MongoRubyError; end
@@ -42,9 +42,8 @@ module Mongo
42
42
  # Raised on failures in connection to the database server.
43
43
  class ConnectionTimeoutError < MongoRubyError; end
44
44
 
45
- # Raised when trying to insert a document that exceeds the 4MB limit or
46
- # when the document contains objects that can't be serialized as BSON.
47
- class InvalidDocument < MongoDBError; end
45
+ # Raised when a connection operation fails.
46
+ class ConnectionFailure < MongoDBError; end
48
47
 
49
48
  # Raised when authentication fails.
50
49
  class AuthenticationError < MongoDBError; end
@@ -52,14 +51,11 @@ module Mongo
52
51
  # Raised when a database operation fails.
53
52
  class OperationFailure < MongoDBError; end
54
53
 
55
- # Raised when a connection operation fails.
56
- class ConnectionFailure < MongoDBError; end
57
-
58
54
  # Raised when a client attempts to perform an invalid operation.
59
55
  class InvalidOperation < MongoDBError; end
60
56
 
61
- # Raised when an invalid name is used.
62
- class InvalidName < RuntimeError; end
57
+ # Raised when an invalid collection or database name is used (invalid namespace name).
58
+ class InvalidNSName < RuntimeError; end
63
59
 
64
60
  # Raised when the client supplies an invalid value to sort by.
65
61
  class InvalidSortValueError < MongoRubyError; end
@@ -34,14 +34,14 @@ module Mongo
34
34
  @chunks = @db["#{fs_name}.chunks"]
35
35
  @fs_name = fs_name
36
36
 
37
- @chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
37
+ @chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]], :unique => true)
38
38
  end
39
39
 
40
40
  # Store a file in the file store.
41
41
  #
42
42
  # @param [String, #read] data a string or io-like object to store.
43
- # @param [String] filename a name for the file.
44
43
  #
44
+ # @options opts [String] :filename (nil) a name for the file.
45
45
  # @options opts [Hash] :metadata ({}) any additional data to store with the file.
46
46
  # @options opts [ObjectID] :_id (ObjectID) a unique id for
47
47
  # the file to be use in lieu of an automatically generated one.
@@ -53,7 +53,14 @@ module Mongo
53
53
  # will be validated using an md5 hash. If validation fails, an exception will be raised.
54
54
  #
55
55
  # @return [Mongo::ObjectID] the file's id.
56
- def put(data, filename, opts={})
56
+ def put(data, opts={}, old_opts={})
57
+ if opts.is_a?(String)
58
+ warn "The filename is now optional. Please pass the filename as a hash option: Grid#put(data, :filename => 'file.jpg')."
59
+ filename = opts
60
+ opts = old_opts
61
+ else
62
+ filename = opts[:filename]
63
+ end
57
64
  opts.merge!(default_grid_io_opts)
58
65
  file = GridIO.new(@files, @chunks, filename, 'w', opts=opts)
59
66
  file.write(data)
@@ -73,6 +80,10 @@ module Mongo
73
80
 
74
81
  # Delete a file from the store.
75
82
  #
83
+ # Note that deleting a GridFS file can result in read errors if another process
84
+ # is attempting to read a file while it's being deleted. While the odds for this
85
+ # kind of race condition are small, it's important to be aware of.
86
+ #
76
87
  # @param [] id
77
88
  #
78
89
  # @return [Boolean]
@@ -34,8 +34,10 @@ module Mongo
34
34
  @chunks = @db["#{fs_name}.chunks"]
35
35
  @fs_name = fs_name
36
36
 
37
- @files.create_index([['filename', 1], ['uploadDate', -1]])
38
37
  @default_query_opts = {:sort => [['filename', 1], ['uploadDate', -1]], :limit => 1}
38
+
39
+ @files.create_index([['filename', 1], ['uploadDate', -1]])
40
+ @chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]], :unique => true)
39
41
  end
40
42
 
41
43
  # Open a file for reading or writing. Note that the options for this method only apply
@@ -53,6 +55,9 @@ module Mongo
53
55
  # the content type will may be inferred from the filename extension if the mime-types gem can be
54
56
  # loaded. Otherwise, the content type 'binary/octet-stream' will be used.
55
57
  # @options opts [Integer] (262144) :chunk_size size of file chunks in bytes.
58
+ # @options opts [Boolean] :delete_old (false) ensure that old versions of the file are deleted. This option
59
+ # only work in 'w' mode. Certain precautions must be taken when deleting GridFS files. See the notes under
60
+ # GridFileSystem#delete.
56
61
  # @options opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
57
62
  # will be validated using an md5 hash. If validation fails, an exception will be raised.
58
63
  #
@@ -76,15 +81,23 @@ module Mongo
76
81
  # @grid.open('image.jpg, 'w') do |f|
77
82
  # f.write @file
78
83
  # end
84
+ #
85
+ # @return [Mongo::GridIO]
79
86
  def open(filename, mode, opts={})
80
87
  opts.merge!(default_grid_io_opts(filename))
81
- file = GridIO.new(@files, @chunks, filename, mode, opts)
88
+ del = opts.delete(:delete_old) && mode == 'w'
89
+ file = GridIO.new(@files, @chunks, filename, mode, opts)
82
90
  return file unless block_given?
83
91
  result = nil
84
92
  begin
85
93
  result = yield file
86
94
  ensure
87
- file.close
95
+ id = file.close
96
+ if del
97
+ self.delete do
98
+ @files.find({'filename' => filename, '_id' => {'$ne' => id}}, :fields => ['_id'])
99
+ end
100
+ end
88
101
  end
89
102
  result
90
103
  end
@@ -92,11 +105,21 @@ module Mongo
92
105
  # Delete the file with the given filename. Note that this will delete
93
106
  # all versions of the file.
94
107
  #
108
+ # Note that deleting a GridFS file can result in read errors if another process
109
+ # is attempting to read a file while it's being deleted. While the odds for this
110
+ # kind of race condition are small, it's important to be aware of.
111
+ #
95
112
  # @param [String] filename
96
113
  #
114
+ # @yield [] pass a block that returns an array of documents to be deleted.
115
+ #
97
116
  # @return [Boolean]
98
- def delete(filename)
99
- files = @files.find({'filename' => filename}, :fields => ['_id'])
117
+ def delete(filename=nil)
118
+ if block_given?
119
+ files = yield
120
+ else
121
+ files = @files.find({'filename' => filename}, :fields => ['_id'])
122
+ end
100
123
  files.each do |file|
101
124
  @files.remove({'_id' => file['_id']})
102
125
  @chunks.remove({'files_id' => file['_id']})
@@ -27,6 +27,7 @@ module Mongo
27
27
  class GridIO
28
28
  DEFAULT_CHUNK_SIZE = 256 * 1024
29
29
  DEFAULT_CONTENT_TYPE = 'binary/octet-stream'
30
+ PROTECTED_ATTRS = [:files_id, :file_length, :client_md5, :server_md5]
30
31
 
31
32
  attr_reader :content_type, :chunk_size, :upload_date, :files_id, :filename,
32
33
  :metadata, :server_md5, :client_md5, :file_length
@@ -52,15 +53,16 @@ module Mongo
52
53
  # @options opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
53
54
  # will be validated using an md5 hash. If validation fails, an exception will be raised.
54
55
  def initialize(files, chunks, filename, mode, opts={})
55
- @files = files
56
- @chunks = chunks
57
- @filename = filename
58
- @mode = mode
59
- @query = opts[:query] || {}
60
- @query_opts = opts[:query_opts] || {}
61
- @fs_name = opts[:fs_name] || Grid::DEFAULT_FS_NAME
62
- @safe = opts[:safe] || false
63
- @local_md5 = Digest::MD5.new if @safe
56
+ @files = files
57
+ @chunks = chunks
58
+ @filename = filename
59
+ @mode = mode
60
+ @query = opts.delete(:query) || {}
61
+ @query_opts = opts.delete(:query_opts) || {}
62
+ @fs_name = opts.delete(:fs_name) || Grid::DEFAULT_FS_NAME
63
+ @safe = opts.delete(:safe) || false
64
+ @local_md5 = Digest::MD5.new if @safe
65
+ @custom_attrs = {}
64
66
 
65
67
  case @mode
66
68
  when 'r' then init_read
@@ -70,6 +72,19 @@ module Mongo
70
72
  end
71
73
  end
72
74
 
75
+ def [](key)
76
+ @custom_attrs[key] || instance_variable_get("@#{key.to_s}")
77
+ end
78
+
79
+ def []=(key, value)
80
+ if PROTECTED_ATTRS.include?(key.to_sym)
81
+ warn "Attempting to overwrite protected value."
82
+ return nil
83
+ else
84
+ @custom_attrs[key] = value
85
+ end
86
+ end
87
+
73
88
  # Read the data from the file. If a length if specified, will read from the
74
89
  # current file position.
75
90
  #
@@ -163,16 +178,16 @@ module Mongo
163
178
  # This method will be invoked automatically when
164
179
  # on GridIO#open is passed a block. Otherwise, it must be called manually.
165
180
  #
166
- # @return [True]
181
+ # @return [BSON::ObjectID]
167
182
  def close
168
183
  if @mode[0] == ?w
169
184
  if @current_chunk['n'].zero? && @chunk_position.zero?
170
185
  warn "Warning: Storing a file with zero length."
171
186
  end
172
187
  @upload_date = Time.now.utc
173
- @files.insert(to_mongo_object)
188
+ id = @files.insert(to_mongo_object)
174
189
  end
175
- true
190
+ id
176
191
  end
177
192
 
178
193
  def inspect
@@ -183,7 +198,7 @@ module Mongo
183
198
 
184
199
  def create_chunk(n)
185
200
  chunk = OrderedHash.new
186
- chunk['_id'] = Mongo::ObjectID.new
201
+ chunk['_id'] = BSON::ObjectID.new
187
202
  chunk['n'] = n
188
203
  chunk['files_id'] = @files_id
189
204
  chunk['data'] = ''
@@ -261,7 +276,7 @@ module Mongo
261
276
  end
262
277
  chunk_available = @chunk_size - @chunk_position
263
278
  step_size = (to_write > chunk_available) ? chunk_available : to_write
264
- @current_chunk['data'] = Binary.new((@current_chunk['data'].to_s << string[-to_write, step_size]).unpack("c*"))
279
+ @current_chunk['data'] = BSON::Binary.new((@current_chunk['data'].to_s << string[-to_write, step_size]).unpack("c*"))
265
280
  @chunk_position += step_size
266
281
  to_write -= step_size
267
282
  save_chunk(@current_chunk)
@@ -272,7 +287,7 @@ module Mongo
272
287
  # Initialize the class for reading a file.
273
288
  def init_read
274
289
  doc = @files.find(@query, @query_opts).next_document
275
- raise GridError, "Could not open file matching #{@query.inspect} #{@query_opts.inspect}" unless doc
290
+ raise GridFileNotFound, "Could not open file matching #{@query.inspect} #{@query_opts.inspect}" unless doc
276
291
 
277
292
  @files_id = doc['_id']
278
293
  @content_type = doc['contentType']
@@ -283,6 +298,7 @@ module Mongo
283
298
  @metadata = doc['metadata']
284
299
  @md5 = doc['md5']
285
300
  @filename = doc['filename']
301
+ @custom_attrs = doc
286
302
 
287
303
  @current_chunk = get_chunk(0)
288
304
  @file_position = 0
@@ -290,12 +306,13 @@ module Mongo
290
306
 
291
307
  # Initialize the class for writing a file.
292
308
  def init_write(opts)
293
- @files_id = opts[:_id] || Mongo::ObjectID.new
294
- @content_type = opts[:content_type] || (defined? MIME) && get_content_type || DEFAULT_CONTENT_TYPE
295
- @chunk_size = opts[:chunk_size] || DEFAULT_CHUNK_SIZE
296
- @metadata = opts[:metadata] if opts[:metadata]
297
- @aliases = opts[:aliases] if opts[:aliases]
309
+ @files_id = opts.delete(:_id) || BSON::ObjectID.new
310
+ @content_type = opts.delete(:content_type) || (defined? MIME) && get_content_type || DEFAULT_CONTENT_TYPE
311
+ @chunk_size = opts.delete(:chunk_size) || DEFAULT_CHUNK_SIZE
312
+ @metadata = opts.delete(:metadata) if opts[:metadata]
313
+ @aliases = opts.delete(:aliases) if opts[:aliases]
298
314
  @file_length = 0
315
+ opts.each {|k, v| self[k] = v}
299
316
 
300
317
  @current_chunk = create_chunk(0)
301
318
  @file_position = 0
@@ -304,14 +321,15 @@ module Mongo
304
321
  def to_mongo_object
305
322
  h = OrderedHash.new
306
323
  h['_id'] = @files_id
307
- h['filename'] = @filename
324
+ h['filename'] = @filename if @filename
308
325
  h['contentType'] = @content_type
309
326
  h['length'] = @current_chunk ? @current_chunk['n'] * @chunk_size + @chunk_position : 0
310
327
  h['chunkSize'] = @chunk_size
311
328
  h['uploadDate'] = @upload_date
312
- h['aliases'] = @aliases
313
- h['metadata'] = @metadata
329
+ h['aliases'] = @aliases if @aliases
330
+ h['metadata'] = @metadata if @metadata
314
331
  h['md5'] = get_md5
332
+ h.merge!(@custom_attrs)
315
333
  h
316
334
  end
317
335
 
@@ -324,7 +342,7 @@ module Mongo
324
342
  if @safe
325
343
  @client_md5 = @local_md5.hexdigest
326
344
  if @local_md5 != @server_md5
327
- raise GridError, "File on server failed MD5 check"
345
+ raise GridMD5Failure, "File on server failed MD5 check"
328
346
  end
329
347
  else
330
348
  @server_md5