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
@@ -1,580 +0,0 @@
1
- # --
2
- # Copyright (C) 2008-2010 10gen Inc.
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
- # ++
16
-
17
- require 'mongo/types/objectid'
18
- require 'mongo/util/ordered_hash'
19
- require 'mongo/gridfs/chunk'
20
-
21
- module GridFS
22
-
23
- # GridStore is an IO-like class that provides input and output for
24
- # streams of data to MongoDB.
25
- #
26
- # @example
27
- #
28
- # include GridFS
29
- #
30
- # #Store the text "Hello, world!" in the grid store.
31
- # GridStore.open(database, 'filename', 'w') do |f|
32
- # f.puts "Hello, world!"
33
- # end
34
- #
35
- # # Output "Hello, world!"
36
- # GridStore.open(database, 'filename', 'r') do |f|
37
- # puts f.read
38
- # end
39
- #
40
- # # Add text to the grid store.
41
- # GridStore.open(database, 'filename', 'w+') do |f|
42
- # f.puts "But wait, there's more!"
43
- # end
44
- #
45
- # # Retrieve everything, outputting "Hello, world!\nBut wait, there's more!\n"
46
- # GridStore.open(database, 'filename', 'r') do |f|
47
- # puts f.read
48
- # end
49
- #
50
- # @deprecated
51
- class GridStore
52
- include Enumerable
53
-
54
- DEFAULT_ROOT_COLLECTION = 'fs'
55
-
56
- DEFAULT_CONTENT_TYPE = 'text/plain'
57
-
58
- DEPRECATION_WARNING = "GridFS::GridStore is deprecated. Use either Grid or GridFileSystem."
59
-
60
- attr_accessor :filename
61
-
62
- # Array of strings; may be +nil+
63
- attr_accessor :aliases
64
-
65
- # Default is DEFAULT_CONTENT_TYPE
66
- attr_accessor :content_type
67
-
68
- # Size of file in bytes
69
- attr_reader :length
70
-
71
- attr_accessor :metadata
72
-
73
- attr_reader :files_id
74
-
75
- # Time that the file was first saved.
76
- attr_reader :upload_date
77
-
78
- attr_reader :chunk_size
79
-
80
- attr_accessor :lineno
81
-
82
- attr_reader :md5
83
-
84
- def self.default_root_collection
85
- @@default_root_collection ||= DEFAULT_ROOT_COLLECTION
86
- end
87
-
88
- def self.default_root_collection=(name)
89
- @@default_root_collection = name
90
- end
91
-
92
- # Determine whether a given file exists in the GridStore.
93
- #
94
- # @param [Mongo::DB] a MongoDB database.
95
- # @param [String] name the filename.
96
- # @param [String] root_collection the name of the gridfs root collection.
97
- #
98
- # @return [Boolean]
99
- # @deprecated
100
- def self.exist?(db, name, root_collection=GridStore.default_root_collection)
101
- warn DEPRECATION_WARNING
102
- db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
103
- end
104
-
105
- # Open a GridFS file for reading, writing, or appending. Note that
106
- # this method must be used with a block.
107
- #
108
- # @param [Mongo::DB] a MongoDB database.
109
- # @param [String] name the filename.
110
- # @param [String] mode one of 'r', 'w', or 'w+' for reading, writing,
111
- # and appending, respectively.
112
- # @param [Hash] options any of the options available on
113
- # GridStore initialization.
114
- #
115
- # @see GridStore#initialize.
116
- # @see The various GridStore class methods, e.g., GridStore.open, GridStore.read etc.
117
- # @deprecated
118
- def self.open(db, name, mode, options={})
119
- gs = self.new(db, name, mode, options)
120
- result = nil
121
- begin
122
- result = yield gs if block_given?
123
- ensure
124
- gs.close
125
- end
126
- result
127
- end
128
-
129
- # Read a file stored in GridFS.
130
- #
131
- # @param [Mongo::DB] db a MongoDB database.
132
- # @param [String] name the name of the file.
133
- # @param [Integer] length the number of bytes to read.
134
- # @param [Integer] offset the number of bytes beyond the
135
- # beginning of the file to start reading.
136
- #
137
- # @return [String] the file data
138
- # @deprecated
139
- def self.read(db, name, length=nil, offset=nil)
140
- GridStore.open(db, name, 'r') do |gs|
141
- gs.seek(offset) if offset
142
- gs.read(length)
143
- end
144
- end
145
-
146
- # List the contents of all GridFS files stored in the given db and
147
- # root collection.
148
- #
149
- # @param [Mongo::DB] db a MongoDB database.
150
- # @param [String] root_collection the name of the root collection.
151
- #
152
- # @return [Array]
153
- # @deprecated
154
- def self.list(db, root_collection=GridStore.default_root_collection)
155
- warn DEPRECATION_WARNING
156
- db.collection("#{root_collection}.files").find().map do |f|
157
- f['filename']
158
- end
159
- end
160
-
161
- # Get each line of data from the specified file
162
- # as an array of strings.
163
- #
164
- # @param [Mongo::DB] db a MongoDB database.
165
- # @param [String] name the filename.
166
- # @param [String, Reg] separator
167
- #
168
- # @return [Array]
169
- # @deprecated
170
- def self.readlines(db, name, separator=$/)
171
- GridStore.open(db, name, 'r') do |gs|
172
- gs.readlines(separator)
173
- end
174
- end
175
-
176
- # Remove one for more files from the given db.
177
- #
178
- # @param [Mongo::Database] db a MongoDB database.
179
- # @param [Array<String>] names the filenames to remove
180
- #
181
- # @return [True]
182
- # @deprecated
183
- def self.unlink(db, *names)
184
- names.each do |name|
185
- gs = GridStore.new(db, name)
186
- gs.delete_chunks
187
- gs.collection.remove('_id' => gs.files_id)
188
- end
189
- end
190
- class << self
191
- alias_method :delete, :unlink
192
- end
193
-
194
- # Rename a file in this collection. Note that this method uses
195
- # Collection#update, which means that you will not be notified of the
196
- # success of the operation.
197
- #
198
- # @param [Mongo::DB] a MongoDB database.
199
- # @param [String] src the name of the source file.
200
- # @param [String] dest the name of the destination file.
201
- # @param [String] root_collection the name of the default root collection.
202
- # @deprecated
203
- def self.mv(db, src, dest, root_collection=GridStore.default_root_collection)
204
- warn DEPRECATION_WARNING
205
- db.collection("#{root_collection}.files").update({ :filename => src }, { '$set' => { :filename => dest } })
206
- end
207
-
208
- # Initialize a GridStore instance for reading, writing, or modifying a given file.
209
- # Note that it's often easier to work with the various GridStore class methods (open, read, etc.).
210
- #
211
- # @param [Mongo::DB] db a MongoDB database.
212
- # @param [String] name a filename.
213
- # @param [String] mode either 'r', 'w', or 'w+' for reading, writing, or appending, respectively.
214
- #
215
- # @option options [String] :root DEFAULT_ROOT_COLLECTION ('r', 'w', 'w+') the name of the root collection to use.
216
- #
217
- # @option options [String] :metadata ({}) (w, w+) A hash containing any data you want persisted as
218
- # this file's metadata.
219
- #
220
- # @option options [Integer] :chunk_size (Chunk::DEFAULT_CHUNK_SIZE) (w) Sets chunk size for files opened for writing.
221
- # See also GridStore#chunk_size=.
222
- #
223
- # @option options [String] :content_type ('text/plain') Set the content type stored as the
224
- # file's metadata. See also GridStore#content_type=.
225
- # @deprecated
226
- def initialize(db, name, mode='r', options={})
227
- warn DEPRECATION_WARNING
228
- @db, @filename, @mode = db, name, mode
229
- @root = options[:root] || GridStore.default_root_collection
230
-
231
- doc = collection.find({'filename' => @filename}).next_document
232
- if doc
233
- @files_id = doc['_id']
234
- @content_type = doc['contentType']
235
- @chunk_size = doc['chunkSize']
236
- @upload_date = doc['uploadDate']
237
- @aliases = doc['aliases']
238
- @length = doc['length']
239
- @metadata = doc['metadata']
240
- @md5 = doc['md5']
241
- else
242
- @files_id = Mongo::ObjectID.new
243
- @content_type = DEFAULT_CONTENT_TYPE
244
- @chunk_size = Chunk::DEFAULT_CHUNK_SIZE
245
- @length = 0
246
- end
247
-
248
- case mode
249
- when 'r'
250
- @curr_chunk = nth_chunk(0)
251
- @position = 0
252
- when 'w'
253
- chunk_collection.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
254
- delete_chunks
255
- @curr_chunk = Chunk.new(self, 'n' => 0)
256
- @content_type = options[:content_type] if options[:content_type]
257
- @chunk_size = options[:chunk_size] if options[:chunk_size]
258
- @metadata = options[:metadata] if options[:metadata]
259
- @position = 0
260
- when 'w+'
261
- chunk_collection.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
262
- @curr_chunk = nth_chunk(last_chunk_number) || Chunk.new(self, 'n' => 0) # might be empty
263
- @curr_chunk.pos = @curr_chunk.data.length if @curr_chunk
264
- @metadata = options[:metadata] if options[:metadata]
265
- @position = @length
266
- else
267
- raise "error: illegal mode #{mode}"
268
- end
269
-
270
- @lineno = 0
271
- @pushback_byte = nil
272
- end
273
-
274
- # Get the files collection referenced by this GridStore instance.
275
- #
276
- # @return [Mongo::Collection]
277
- def collection
278
- @db.collection("#{@root}.files")
279
- end
280
-
281
- # Get the chunk collection referenced by this GridStore.
282
- #
283
- # @return [Mongo::Collection]
284
- def chunk_collection
285
- @db.collection("#{@root}.chunks")
286
- end
287
-
288
- # Change the chunk size. This is permitted only when the file is opened for write
289
- # and no data has yet been written.
290
- #
291
- # @param [Integer] size the new chunk size, in bytes.
292
- #
293
- # @return [Integer] the new chunk size.
294
- def chunk_size=(size)
295
- unless @mode[0] == ?w && @position == 0 && @upload_date == nil
296
- raise "error: can only change chunk size if open for write and no data written."
297
- end
298
- @chunk_size = size
299
- end
300
-
301
- # ================ reading ================
302
-
303
- def getc
304
- if @pushback_byte
305
- byte = @pushback_byte
306
- @pushback_byte = nil
307
- @position += 1
308
- byte
309
- elsif eof?
310
- nil
311
- else
312
- if @curr_chunk.eof?
313
- @curr_chunk = nth_chunk(@curr_chunk.chunk_number + 1)
314
- end
315
- @position += 1
316
- @curr_chunk.getc
317
- end
318
- end
319
-
320
- def gets(separator=$/)
321
- str = ''
322
- byte = self.getc
323
- return nil if byte == nil # EOF
324
- while byte != nil
325
- s = byte.chr
326
- str << s
327
- break if s == separator
328
- byte = self.getc
329
- end
330
- @lineno += 1
331
- str
332
- end
333
-
334
- def read(len=nil, buf=nil)
335
- if len
336
- read_partial(len, buf)
337
- else
338
- read_all(buf)
339
- end
340
- end
341
-
342
- def readchar
343
- byte = self.getc
344
- raise EOFError.new if byte == nil
345
- byte
346
- end
347
-
348
- def readline(separator=$/)
349
- line = gets
350
- raise EOFError.new if line == nil
351
- line
352
- end
353
-
354
- def readlines(separator=$/)
355
- read.split(separator).collect { |line| "#{line}#{separator}" }
356
- end
357
-
358
- def each
359
- line = gets
360
- while line
361
- yield line
362
- line = gets
363
- end
364
- end
365
- alias_method :each_line, :each
366
-
367
- def each_byte
368
- byte = self.getc
369
- while byte
370
- yield byte
371
- byte = self.getc
372
- end
373
- end
374
-
375
- def ungetc(byte)
376
- @pushback_byte = byte
377
- @position -= 1
378
- end
379
-
380
- # ================ writing ================
381
-
382
- def putc(byte)
383
- if @curr_chunk.pos == @chunk_size
384
- prev_chunk_number = @curr_chunk.chunk_number
385
- @curr_chunk.save
386
- @curr_chunk = Chunk.new(self, 'n' => prev_chunk_number + 1)
387
- end
388
- @position += 1
389
- @curr_chunk.putc(byte)
390
- end
391
-
392
- def print(*objs)
393
- objs = [$_] if objs == nil || objs.empty?
394
- objs.each { |obj|
395
- str = obj.to_s
396
- str.each_byte { |byte| self.putc(byte) }
397
- }
398
- nil
399
- end
400
-
401
- def puts(*objs)
402
- if objs == nil || objs.empty?
403
- self.putc(10)
404
- else
405
- print(*objs.collect{ |obj|
406
- str = obj.to_s
407
- str << "\n" unless str =~ /\n$/
408
- str
409
- })
410
- end
411
- nil
412
- end
413
-
414
- def <<(obj)
415
- write(obj.to_s)
416
- end
417
-
418
- def write(string)
419
- raise "#@filename not opened for write" unless @mode[0] == ?w
420
- # Since Ruby 1.9.1 doesn't necessarily store one character per byte.
421
- if string.respond_to?(:force_encoding)
422
- string.force_encoding("binary")
423
- end
424
- to_write = string.length
425
- while (to_write > 0) do
426
- if @curr_chunk && @curr_chunk.data.position == @chunk_size
427
- prev_chunk_number = @curr_chunk.chunk_number
428
- @curr_chunk = GridFS::Chunk.new(self, 'n' => prev_chunk_number + 1)
429
- end
430
- chunk_available = @chunk_size - @curr_chunk.data.position
431
- step_size = (to_write > chunk_available) ? chunk_available : to_write
432
- @curr_chunk.data.put_array(ByteBuffer.new(string[-to_write,step_size]).to_a)
433
- to_write -= step_size
434
- @curr_chunk.save
435
- end
436
- string.length - to_write
437
- end
438
-
439
- # A no-op.
440
- def flush
441
- end
442
-
443
- # ================ status ================
444
-
445
- def eof
446
- raise IOError.new("stream not open for reading") unless @mode[0] == ?r
447
- @position >= @length
448
- end
449
- alias_method :eof?, :eof
450
-
451
- # ================ positioning ================
452
-
453
- def rewind
454
- if @curr_chunk.chunk_number != 0
455
- if @mode[0] == ?w
456
- delete_chunks
457
- @curr_chunk = Chunk.new(self, 'n' => 0)
458
- else
459
- @curr_chunk == nth_chunk(0)
460
- end
461
- end
462
- @curr_chunk.pos = 0
463
- @lineno = 0
464
- @position = 0
465
- end
466
-
467
- def seek(pos, whence=IO::SEEK_SET)
468
- target_pos = case whence
469
- when IO::SEEK_CUR
470
- @position + pos
471
- when IO::SEEK_END
472
- @length + pos
473
- when IO::SEEK_SET
474
- pos
475
- end
476
-
477
- new_chunk_number = (target_pos / @chunk_size).to_i
478
- if new_chunk_number != @curr_chunk.chunk_number
479
- @curr_chunk.save if @mode[0] == ?w
480
- @curr_chunk = nth_chunk(new_chunk_number)
481
- end
482
- @position = target_pos
483
- @curr_chunk.pos = @position % @chunk_size
484
- 0
485
- end
486
-
487
- def tell
488
- @position
489
- end
490
-
491
- #---
492
- # ================ closing ================
493
- #+++
494
-
495
- def close
496
- if @mode[0] == ?w
497
- if @curr_chunk
498
- @curr_chunk.truncate
499
- @curr_chunk.save if @curr_chunk.pos > 0
500
- end
501
- files = collection
502
- if @upload_date
503
- files.remove('_id' => @files_id)
504
- else
505
- @upload_date = Time.now
506
- end
507
- files.insert(to_mongo_object)
508
- end
509
- @db = nil
510
- end
511
-
512
- def closed?
513
- @db == nil
514
- end
515
-
516
- def delete_chunks
517
- chunk_collection.remove({'files_id' => @files_id}) if @files_id
518
- @curr_chunk = nil
519
- end
520
-
521
- #---
522
- # ================ protected ================
523
- #+++
524
-
525
- protected
526
-
527
- def to_mongo_object
528
- h = OrderedHash.new
529
- h['_id'] = @files_id
530
- h['filename'] = @filename
531
- h['contentType'] = @content_type
532
- h['length'] = @curr_chunk ? @curr_chunk.chunk_number * @chunk_size + @curr_chunk.pos : 0
533
- h['chunkSize'] = @chunk_size
534
- h['uploadDate'] = @upload_date
535
- h['aliases'] = @aliases
536
- h['metadata'] = @metadata
537
- md5_command = OrderedHash.new
538
- md5_command['filemd5'] = @files_id
539
- md5_command['root'] = @root
540
- h['md5'] = @db.command(md5_command)['md5']
541
- h
542
- end
543
-
544
- def read_partial(len, buf=nil)
545
- buf ||= ''
546
- byte = self.getc
547
- while byte != nil && (len == nil || len > 0)
548
- buf << byte.chr
549
- len -= 1 if len
550
- byte = self.getc if (len == nil || len > 0)
551
- end
552
- buf
553
- end
554
-
555
- def read_all(buf=nil)
556
- buf ||= ''
557
- while true do
558
- if (@curr_chunk.pos > 0)
559
- data = @curr_chunk.data.to_s
560
- buf += data[@position, data.length]
561
- else
562
- buf += @curr_chunk.data.to_s
563
- end
564
- break if @curr_chunk.chunk_number == last_chunk_number
565
- @curr_chunk = nth_chunk(@curr_chunk.chunk_number + 1)
566
- end
567
- buf
568
- end
569
-
570
- def nth_chunk(n)
571
- mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_document
572
- Chunk.new(self, mongo_chunk || {})
573
- end
574
-
575
- def last_chunk_number
576
- (@length / @chunk_size).to_i
577
- end
578
-
579
- end
580
- end