kbaum-mongo 0.18.3p

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/LICENSE.txt +202 -0
  2. data/README.rdoc +339 -0
  3. data/Rakefile +138 -0
  4. data/bin/bson_benchmark.rb +59 -0
  5. data/bin/fail_if_no_c.rb +11 -0
  6. data/examples/admin.rb +42 -0
  7. data/examples/capped.rb +22 -0
  8. data/examples/cursor.rb +48 -0
  9. data/examples/gridfs.rb +88 -0
  10. data/examples/index_test.rb +126 -0
  11. data/examples/info.rb +31 -0
  12. data/examples/queries.rb +70 -0
  13. data/examples/simple.rb +24 -0
  14. data/examples/strict.rb +35 -0
  15. data/examples/types.rb +36 -0
  16. data/lib/mongo/collection.rb +609 -0
  17. data/lib/mongo/connection.rb +672 -0
  18. data/lib/mongo/cursor.rb +403 -0
  19. data/lib/mongo/db.rb +555 -0
  20. data/lib/mongo/exceptions.rb +66 -0
  21. data/lib/mongo/gridfs/chunk.rb +91 -0
  22. data/lib/mongo/gridfs/grid.rb +79 -0
  23. data/lib/mongo/gridfs/grid_file_system.rb +101 -0
  24. data/lib/mongo/gridfs/grid_io.rb +338 -0
  25. data/lib/mongo/gridfs/grid_store.rb +580 -0
  26. data/lib/mongo/gridfs.rb +25 -0
  27. data/lib/mongo/types/binary.rb +52 -0
  28. data/lib/mongo/types/code.rb +36 -0
  29. data/lib/mongo/types/dbref.rb +40 -0
  30. data/lib/mongo/types/min_max_keys.rb +58 -0
  31. data/lib/mongo/types/objectid.rb +180 -0
  32. data/lib/mongo/types/regexp_of_holding.rb +45 -0
  33. data/lib/mongo/util/bson_c.rb +18 -0
  34. data/lib/mongo/util/bson_ruby.rb +606 -0
  35. data/lib/mongo/util/byte_buffer.rb +222 -0
  36. data/lib/mongo/util/conversions.rb +87 -0
  37. data/lib/mongo/util/ordered_hash.rb +140 -0
  38. data/lib/mongo/util/server_version.rb +69 -0
  39. data/lib/mongo/util/support.rb +26 -0
  40. data/lib/mongo.rb +63 -0
  41. data/mongo-ruby-driver.gemspec +28 -0
  42. data/test/auxillary/autoreconnect_test.rb +42 -0
  43. data/test/binary_test.rb +15 -0
  44. data/test/bson_test.rb +427 -0
  45. data/test/byte_buffer_test.rb +81 -0
  46. data/test/chunk_test.rb +82 -0
  47. data/test/collection_test.rb +515 -0
  48. data/test/connection_test.rb +160 -0
  49. data/test/conversions_test.rb +120 -0
  50. data/test/cursor_test.rb +379 -0
  51. data/test/db_api_test.rb +780 -0
  52. data/test/db_connection_test.rb +16 -0
  53. data/test/db_test.rb +272 -0
  54. data/test/grid_file_system_test.rb +210 -0
  55. data/test/grid_io_test.rb +78 -0
  56. data/test/grid_store_test.rb +334 -0
  57. data/test/grid_test.rb +87 -0
  58. data/test/objectid_test.rb +125 -0
  59. data/test/ordered_hash_test.rb +172 -0
  60. data/test/replica/count_test.rb +34 -0
  61. data/test/replica/insert_test.rb +50 -0
  62. data/test/replica/pooled_insert_test.rb +54 -0
  63. data/test/replica/query_test.rb +39 -0
  64. data/test/slave_connection_test.rb +36 -0
  65. data/test/test_helper.rb +42 -0
  66. data/test/threading/test_threading_large_pool.rb +90 -0
  67. data/test/threading_test.rb +87 -0
  68. data/test/unit/collection_test.rb +61 -0
  69. data/test/unit/connection_test.rb +117 -0
  70. data/test/unit/cursor_test.rb +93 -0
  71. data/test/unit/db_test.rb +98 -0
  72. metadata +127 -0
data/lib/mongo/db.rb ADDED
@@ -0,0 +1,555 @@
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 'socket'
18
+ require 'timeout'
19
+ require 'digest/md5'
20
+ require 'thread'
21
+
22
+ module Mongo
23
+
24
+ # A MongoDB database.
25
+ class DB
26
+
27
+ SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
28
+ SYSTEM_INDEX_COLLECTION = "system.indexes"
29
+ SYSTEM_PROFILE_COLLECTION = "system.profile"
30
+ SYSTEM_USER_COLLECTION = "system.users"
31
+ SYSTEM_COMMAND_COLLECTION = "$cmd"
32
+
33
+ # Counter for generating unique request ids.
34
+ @@current_request_id = 0
35
+
36
+ # Strict mode enforces collection existence checks. When +true+,
37
+ # asking for a collection that does not exist, or trying to create a
38
+ # collection that already exists, raises an error.
39
+ #
40
+ # Strict mode is disabled by default, but enabled (+true+) at any time.
41
+ attr_writer :strict
42
+
43
+ # Returns the value of the +strict+ flag.
44
+ def strict?; @strict; end
45
+
46
+ # The name of the database.
47
+ attr_reader :name
48
+
49
+ # The Mongo::Connection instance connecting to the MongoDB server.
50
+ attr_reader :connection
51
+
52
+ # Instances of DB are normally obtained by calling Mongo#db.
53
+ #
54
+ # @param [String] db_name the database name.
55
+ # @param [Mongo::Connection] connection a connection object pointing to MongoDB. Note
56
+ # that databases are usually instantiated via the Connection class. See the examples below.
57
+ #
58
+ # @option options [Boolean] strict (False) If true, collections must exist to be accessed and must
59
+ # not exist to be created. See DB#collection and DB#create_collection.
60
+ #
61
+ # @option options [Object, #create_pk(doc)] pk (Mongo::ObjectID) A primary key factory object,
62
+ # which should take a hash and return a hash which merges the original hash with any primary key
63
+ # fields the factory wishes to inject. (NOTE: if the object already has a primary key,
64
+ # the factory should not inject a new key).
65
+ #
66
+ # @core databases constructor_details
67
+ def initialize(db_name, connection, options={})
68
+ @name = validate_db_name(db_name)
69
+ @connection = connection
70
+ @strict = options[:strict]
71
+ @pk_factory = options[:pk]
72
+ end
73
+
74
+ # Authenticate with the given username and password. Note that mongod
75
+ # must be started with the --auth option for authentication to be enabled.
76
+ #
77
+ # @param [String] username
78
+ # @param [String] password
79
+ #
80
+ # @return [Boolean]
81
+ #
82
+ # @raise [AuthenticationError]
83
+ #
84
+ # @core authenticate authenticate-instance_method
85
+ def authenticate(username, password)
86
+ doc = command(:getnonce => 1)
87
+ raise "error retrieving nonce: #{doc}" unless ok?(doc)
88
+ nonce = doc['nonce']
89
+
90
+ auth = OrderedHash.new
91
+ auth['authenticate'] = 1
92
+ auth['user'] = username
93
+ auth['nonce'] = nonce
94
+ auth['key'] = Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
95
+ ok?(command(auth)) ||
96
+ raise(MongoDBError::AuthenticationError, "Failed to authenticate user '#{username}' on db '#{self.name}'")
97
+ end
98
+
99
+ # Adds a user to this database for use with authentication. If the user already
100
+ # exists in the system, the password will be updated.
101
+ #
102
+ # @param [String] username
103
+ # @param [String] password
104
+ #
105
+ # @return [Hash] an object representing the user.
106
+ def add_user(username, password)
107
+ users = self[SYSTEM_USER_COLLECTION]
108
+ user = users.find_one({:user => username}) || {:user => username}
109
+ user['pwd'] = hash_password(username, password)
110
+ users.save(user)
111
+ return user
112
+ end
113
+
114
+ # Remove the given user from this database. Returns false if the user
115
+ # doesn't exist in the system.
116
+ #
117
+ # @param [String] username
118
+ #
119
+ # @return [Boolean]
120
+ def remove_user(username)
121
+ if self[SYSTEM_USER_COLLECTION].find_one({:user => username})
122
+ self[SYSTEM_USER_COLLECTION].remove({:user => username}, :safe => true)
123
+ else
124
+ return false
125
+ end
126
+ end
127
+
128
+ # Deauthorizes use for this database for this connection.
129
+ #
130
+ # @raise [MongoDBError] if logging out fails.
131
+ #
132
+ # @return [Boolean]
133
+ def logout
134
+ doc = command(:logout => 1)
135
+ return true if ok?(doc)
136
+ raise MongoDBError, "error logging out: #{doc.inspect}"
137
+ end
138
+
139
+ # Get an array of collection names in this database.
140
+ #
141
+ # @return [Array]
142
+ def collection_names
143
+ names = collections_info.collect { |doc| doc['name'] || '' }
144
+ names = names.delete_if {|name| name.index(@name).nil? || name.index('$')}
145
+ names.map {|name| name.sub(@name + '.', '')}
146
+ end
147
+
148
+ # Get an array of Collection instances, one for each collection in this database.
149
+ #
150
+ # @return [Array<Mongo::Collection>]
151
+ def collections
152
+ collection_names.map do |collection_name|
153
+ Collection.new(self, collection_name)
154
+ end
155
+ end
156
+
157
+ # Get info on system namespaces (collections). This method returns
158
+ # a cursor which can be iterated over. For each collection, a hash
159
+ # will be yielded containing a 'name' string and, optionally, an 'options' hash.
160
+ #
161
+ # @param [String] coll_name return info for the specifed collection only.
162
+ #
163
+ # @return [Mongo::Cursor]
164
+ def collections_info(coll_name=nil)
165
+ selector = {}
166
+ selector[:name] = full_collection_name(coll_name) if coll_name
167
+ Cursor.new(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), :selector => selector)
168
+ end
169
+
170
+ # Create a collection.
171
+ #
172
+ # new collection. If +strict+ is true, will raise an error if
173
+ # collection +name+ already exists.
174
+ #
175
+ # @param [String] name the name of the new collection.
176
+ #
177
+ # @option options [Boolean] :capped (False) created a capped collection.
178
+ #
179
+ # @option options [Integer] :size (Nil) If +capped+ is +true+, specifies the maximum number of
180
+ # bytes for the capped collection. If +false+, specifies the number of bytes allocated
181
+ # for the initial extent of the collection.
182
+ #
183
+ # @option options [Integer] :max (Nil) If +capped+ is +true+, indicates the maximum number of records
184
+ # in a capped collection.
185
+ #
186
+ # @raise [MongoDBError] raised under two conditions: either we're in +strict+ mode and the collection
187
+ # already exists or collection creation fails on the server.
188
+ #
189
+ # @return [Mongo::Collection]
190
+ def create_collection(name, options={})
191
+ # Does the collection already exist?
192
+ if collection_names.include?(name)
193
+ if strict?
194
+ raise MongoDBError, "Collection #{name} already exists. Currently in strict mode."
195
+ else
196
+ return Collection.new(self, name)
197
+ end
198
+ end
199
+
200
+ # Create a new collection.
201
+ oh = OrderedHash.new
202
+ oh[:create] = name
203
+ doc = command(oh.merge(options || {}))
204
+ ok = doc['ok']
205
+ return Collection.new(self, name, @pk_factory) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0)
206
+ raise MongoDBError, "Error creating collection: #{doc.inspect}"
207
+ end
208
+
209
+ # Get a collection by name.
210
+ #
211
+ # @param [String] name the collection name.
212
+ #
213
+ # @raise [MongoDBError] if collection does not already exist and we're in +strict+ mode.
214
+ #
215
+ # @return [Mongo::Collection]
216
+ def collection(name)
217
+ return Collection.new(self, name, @pk_factory) if !strict? || collection_names.include?(name)
218
+ raise MongoDBError, "Collection #{name} doesn't exist. Currently in strict mode."
219
+ end
220
+ alias_method :[], :collection
221
+
222
+ # Drop a collection by +name+.
223
+ #
224
+ # @param [String] name
225
+ #
226
+ # @return [Boolean] True on success or if the collection names doesn't exist.
227
+ def drop_collection(name)
228
+ return true unless collection_names.include?(name)
229
+
230
+ ok?(command(:drop => name))
231
+ end
232
+
233
+ # Get the error message from the most recently executed database
234
+ # operation for this connection.
235
+ #
236
+ # @return [String, Nil] either the text describing the error or nil if no
237
+ # error has occurred.
238
+ def error
239
+ doc = command(:getlasterror => 1)
240
+ raise MongoDBError, "error retrieving last error: #{doc}" unless ok?(doc)
241
+ doc['err']
242
+ end
243
+
244
+ # Get status information from the last operation on this connection.
245
+ #
246
+ # @return [Hash] a hash representing the status of the last db op.
247
+ def last_status
248
+ command(:getlasterror => 1)
249
+ end
250
+
251
+ # Return +true+ if an error was caused by the most recently executed
252
+ # database operation.
253
+ #
254
+ # @return [Boolean]
255
+ def error?
256
+ error != nil
257
+ end
258
+
259
+ # Get the most recent error to have occured on this database.
260
+ #
261
+ # This command only returns errors that have occured since the last call to
262
+ # DB#reset_error_history - returns +nil+ if there is no such error.
263
+ #
264
+ # @return [String, Nil] the text of the error or +nil+ if no error has occurred.
265
+ def previous_error
266
+ error = command(:getpreverror => 1)
267
+ if error["err"]
268
+ error
269
+ else
270
+ nil
271
+ end
272
+ end
273
+
274
+ # Reset the error history of this database
275
+ #
276
+ # Calls to DB#previous_error will only return errors that have occurred
277
+ # since the most recent call to this method.
278
+ #
279
+ # @return [Hash]
280
+ def reset_error_history
281
+ command(:reseterror => 1)
282
+ end
283
+
284
+ # @deprecated please use Collection#find to create queries.
285
+ #
286
+ # Returns a Cursor over the query results.
287
+ #
288
+ # Note that the query gets sent lazily; the cursor calls
289
+ # Connection#send_message when needed. If the caller never requests an
290
+ # object from the cursor, the query never gets sent.
291
+ def query(collection, query, admin=false)
292
+ Cursor.new(self, collection, query, admin)
293
+ end
294
+
295
+ # Dereference a DBRef, returning the document it points to.
296
+ #
297
+ # @param [Mongo::DBRef] dbref
298
+ #
299
+ # @return [Hash] the document indicated by the db reference.
300
+ #
301
+ # @see http://www.mongodb.org/display/DOCS/DB+Ref MongoDB DBRef spec.
302
+ def dereference(dbref)
303
+ collection(dbref.namespace).find_one("_id" => dbref.object_id)
304
+ end
305
+
306
+ # Evaluate a JavaScript expression in MongoDB.
307
+ #
308
+ # @param [String, Code] code a JavaScript expression to evaluate server-side.
309
+ # @param [Integer, Hash] args any additional argument to be passed to the +code+ expression when
310
+ # it's run on the server.
311
+ #
312
+ # @return [String] the return value of the function.
313
+ def eval(code, *args)
314
+ if not code.is_a? Code
315
+ code = Code.new(code)
316
+ end
317
+
318
+ oh = OrderedHash.new
319
+ oh[:$eval] = code
320
+ oh[:args] = args
321
+ doc = command(oh)
322
+ return doc['retval'] if ok?(doc)
323
+ raise OperationFailure, "eval failed: #{doc['errmsg']}"
324
+ end
325
+
326
+ # Rename a collection.
327
+ #
328
+ # @param [String] from original collection name.
329
+ # @param [String] to new collection name.
330
+ #
331
+ # @return [True] returns +true+ on success.
332
+ #
333
+ # @raise MongoDBError if there's an error renaming the collection.
334
+ def rename_collection(from, to)
335
+ oh = OrderedHash.new
336
+ oh[:renameCollection] = "#{@name}.#{from}"
337
+ oh[:to] = "#{@name}.#{to}"
338
+ doc = command(oh, true)
339
+ ok?(doc) || raise(MongoDBError, "Error renaming collection: #{doc.inspect}")
340
+ end
341
+
342
+ # Drop an index from a given collection. Normally called from
343
+ # Collection#drop_index or Collection#drop_indexes.
344
+ #
345
+ # @param [String] collection_name
346
+ # @param [String] index_name
347
+ #
348
+ # @return [True] returns +true+ on success.
349
+ #
350
+ # @raise MongoDBError if there's an error renaming the collection.
351
+ def drop_index(collection_name, index_name)
352
+ oh = OrderedHash.new
353
+ oh[:deleteIndexes] = collection_name
354
+ oh[:index] = index_name
355
+ doc = command(oh)
356
+ ok?(doc) || raise(MongoDBError, "Error with drop_index command: #{doc.inspect}")
357
+ end
358
+
359
+ # Get information on the indexes for the given collection.
360
+ # Normally called by Collection#index_information.
361
+ #
362
+ # @param [String] collection_name
363
+ #
364
+ # @return [Hash] keys are index names and the values are lists of [key, direction] pairs
365
+ # defining the index.
366
+ def index_information(collection_name)
367
+ sel = {:ns => full_collection_name(collection_name)}
368
+ info = {}
369
+ Cursor.new(Collection.new(self, SYSTEM_INDEX_COLLECTION), :selector => sel).each { |index|
370
+ info[index['name']] = index['key'].map {|k| k}
371
+ }
372
+ info
373
+ end
374
+
375
+ # Create a new index on the given collection.
376
+ # Normally called by Collection#create_index.
377
+ #
378
+ # @param [String] collection_name
379
+ # @param [String, Array] field_or_spec either either a single field name
380
+ # or an array of [field name, direction] pairs. Directions should be specified as
381
+ # Mongo::ASCENDING or Mongo::DESCENDING.
382
+ # @param [Boolean] unique if +true+, the created index will enforce a uniqueness constraint.
383
+ #
384
+ # @return [String] the name of the index created.
385
+ def create_index(collection_name, field_or_spec, unique=false)
386
+ self.collection(collection_name).create_index(field_or_spec, unique)
387
+ end
388
+
389
+ # Return +true+ if the supplied +doc+ contains an 'ok' field with the value 1.
390
+ #
391
+ # @param [Hash] doc
392
+ #
393
+ # @return [Boolean]
394
+ def ok?(doc)
395
+ ok = doc['ok']
396
+ ok.kind_of?(Numeric) && ok.to_i == 1
397
+ end
398
+
399
+ # Send a command to the database.
400
+ #
401
+ # Note: DB commands must start with the "command" key. For this reason,
402
+ # any selector containing more than one key must be an OrderedHash.
403
+ #
404
+ # It may be of interest hat a command in MongoDB is technically a kind of query
405
+ # that occurs on the system command collection ($cmd).
406
+ #
407
+ # @param [OrderedHash, Hash] selector an OrderedHash, or a standard Hash with just one
408
+ # key, specifying the command to be performed.
409
+ #
410
+ # @param [Boolean] admin If +true+, the command will be executed on the admin
411
+ # collection.
412
+ #
413
+ # @param [Boolean] check_response If +true+, will raise an exception if the
414
+ # command fails.
415
+ #
416
+ # @param [Socket] sock a socket to use. This is mainly for internal use.
417
+ #
418
+ # @return [Hash]
419
+ #
420
+ # @core commands command_instance-method
421
+ def command(selector, admin=false, check_response=false, sock=nil)
422
+ raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
423
+ if selector.class.eql?(Hash) && selector.keys.length > 1
424
+ raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
425
+ end
426
+
427
+ result = Cursor.new(system_command_collection, :admin => admin,
428
+ :limit => -1, :selector => selector, :socket => sock).next_document
429
+
430
+ if check_response && !ok?(result)
431
+ raise OperationFailure, "Database command '#{selector.keys.first}' failed."
432
+ else
433
+ result
434
+ end
435
+ end
436
+
437
+ # A shortcut returning db plus dot plus collection name.
438
+ #
439
+ # @param [String] collection_name
440
+ #
441
+ # @return [String]
442
+ def full_collection_name(collection_name)
443
+ "#{@name}.#{collection_name}"
444
+ end
445
+
446
+ # The primary key factory object (or +nil+).
447
+ #
448
+ # @return [Object, Nil]
449
+ def pk_factory
450
+ @pk_factory
451
+ end
452
+
453
+ # Specify a primary key factory if not already set.
454
+ #
455
+ # @raise [MongoArgumentError] if the primary key factory has already been set.
456
+ def pk_factory=(pk_factory)
457
+ if @pk_factory
458
+ raise MongoArgumentError, "Cannot change primary key factory once it's been set"
459
+ end
460
+
461
+ @pk_factory = pk_factory
462
+ end
463
+
464
+ # Return the current database profiling level. If profiling is enabled, you can
465
+ # get the results using DB#profiling_info.
466
+ #
467
+ # @return [Symbol] :off, :slow_only, or :all
468
+ #
469
+ # @core profiling profiling_level-instance_method
470
+ def profiling_level
471
+ oh = OrderedHash.new
472
+ oh[:profile] = -1
473
+ doc = command(oh)
474
+ raise "Error with profile command: #{doc.inspect}" unless ok?(doc) && doc['was'].kind_of?(Numeric)
475
+ case doc['was'].to_i
476
+ when 0
477
+ :off
478
+ when 1
479
+ :slow_only
480
+ when 2
481
+ :all
482
+ else
483
+ raise "Error: illegal profiling level value #{doc['was']}"
484
+ end
485
+ end
486
+
487
+ # Set this database's profiling level. If profiling is enabled, you can
488
+ # get the results using DB#profiling_info.
489
+ #
490
+ # @param [Symbol] level acceptable options are +:off+, +:slow_only+, or +:all+.
491
+ def profiling_level=(level)
492
+ oh = OrderedHash.new
493
+ oh[:profile] = case level
494
+ when :off
495
+ 0
496
+ when :slow_only
497
+ 1
498
+ when :all
499
+ 2
500
+ else
501
+ raise "Error: illegal profiling level value #{level}"
502
+ end
503
+ doc = command(oh)
504
+ ok?(doc) || raise(MongoDBError, "Error with profile command: #{doc.inspect}")
505
+ end
506
+
507
+ # Get the current profiling information.
508
+ #
509
+ # @return [Array] a list of documents containing profiling information.
510
+ def profiling_info
511
+ Cursor.new(Collection.new(self, DB::SYSTEM_PROFILE_COLLECTION), :selector => {}).to_a
512
+ end
513
+
514
+ # Validate a named collection.
515
+ #
516
+ # @param [String] name the collection name.
517
+ #
518
+ # @return [Hash] validation information.
519
+ #
520
+ # @raise [MongoDBError] if the command fails or there's a problem with the validation
521
+ # data, or if the collection is invalid.
522
+ def validate_collection(name)
523
+ doc = command(:validate => name)
524
+ raise MongoDBError, "Error with validate command: #{doc.inspect}" unless ok?(doc)
525
+ result = doc['result']
526
+ raise MongoDBError, "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
527
+ raise MongoDBError, "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
528
+ doc
529
+ end
530
+
531
+ private
532
+
533
+ def hash_password(username, plaintext)
534
+ Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
535
+ end
536
+
537
+ def system_command_collection
538
+ Collection.new(self, SYSTEM_COMMAND_COLLECTION)
539
+ end
540
+
541
+ def validate_db_name(db_name)
542
+ unless [String, Symbol].include?(db_name.class)
543
+ raise TypeError, "db_name must be a string or symbol"
544
+ end
545
+
546
+ [" ", ".", "$", "/", "\\"].each do |invalid_char|
547
+ if db_name.include? invalid_char
548
+ raise InvalidName, "database names cannot contain the character '#{invalid_char}'"
549
+ end
550
+ end
551
+ raise InvalidName, "database name cannot be the empty string" if db_name.empty?
552
+ db_name
553
+ end
554
+ end
555
+ end
@@ -0,0 +1,66 @@
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
+ module Mongo
18
+ # Generic Mongo Ruby Driver exception class.
19
+ class MongoRubyError < StandardError; end
20
+
21
+ # Raised when MongoDB itself has returned an error.
22
+ class MongoDBError < RuntimeError; end
23
+
24
+ # Raised when configuration options cause connections, queries, etc., to fail.
25
+ class ConfigurationError < MongoRubyError; end
26
+
27
+ # Raised with fatal errors to GridFS.
28
+ class GridError < MongoRubyError; end
29
+
30
+ # Raised when invalid arguments are sent to Mongo Ruby methods.
31
+ class MongoArgumentError < MongoRubyError; end
32
+
33
+ # Raised when given a string is not valid utf-8 (Ruby 1.8 only).
34
+ class InvalidStringEncoding < MongoRubyError; end
35
+
36
+ # Raised when attempting to initialize an invalid ObjectID.
37
+ class InvalidObjectID < MongoRubyError; end
38
+
39
+ # Raised on failures in connection to the database server.
40
+ class ConnectionError < MongoRubyError; end
41
+
42
+ # Raised on failures in connection to the database server.
43
+ class ConnectionTimeoutError < MongoRubyError; end
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
48
+
49
+ # Raised when authentication fails.
50
+ class AuthenticationError < MongoDBError; end
51
+
52
+ # Raised when a database operation fails.
53
+ class OperationFailure < MongoDBError; end
54
+
55
+ # Raised when a connection operation fails.
56
+ class ConnectionFailure < MongoDBError; end
57
+
58
+ # Raised when a client attempts to perform an invalid operation.
59
+ class InvalidOperation < MongoDBError; end
60
+
61
+ # Raised when an invalid name is used.
62
+ class InvalidName < RuntimeError; end
63
+
64
+ # Raised when the client supplies an invalid value to sort by.
65
+ class InvalidSortValueError < MongoRubyError; end
66
+ end
@@ -0,0 +1,91 @@
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/byte_buffer'
19
+ require 'mongo/util/ordered_hash'
20
+
21
+ module GridFS
22
+
23
+ # A chunk stores a portion of GridStore data.
24
+ # @deprecated
25
+ class Chunk
26
+
27
+ DEFAULT_CHUNK_SIZE = 1024 * 256
28
+
29
+ attr_reader :object_id, :chunk_number
30
+ attr_accessor :data
31
+
32
+ def initialize(file, mongo_object={})
33
+ @file = file
34
+ @object_id = mongo_object['_id'] || Mongo::ObjectID.new
35
+ @chunk_number = mongo_object['n'] || 0
36
+
37
+ @data = ByteBuffer.new
38
+ case mongo_object['data']
39
+ when String
40
+ mongo_object['data'].each_byte { |b| @data.put(b) }
41
+ when ByteBuffer
42
+ @data.put_array(mongo_object['data'].to_a)
43
+ when Array
44
+ @data.put_array(mongo_object['data'])
45
+ when nil
46
+ else
47
+ raise "illegal chunk format; data is #{mongo_object['data'] ? (' ' + mongo_object['data'].class.name) : 'nil'}"
48
+ end
49
+ @data.rewind
50
+ end
51
+
52
+ def pos; @data.position; end
53
+ def pos=(pos); @data.position = pos; end
54
+ def eof?; !@data.more?; end
55
+
56
+ def size; @data.size; end
57
+ alias_method :length, :size
58
+
59
+ def truncate
60
+ if @data.position < @data.length
61
+ curr_data = @data
62
+ @data = ByteBuffer.new
63
+ @data.put_array(curr_data.to_a[0...curr_data.position])
64
+ end
65
+ end
66
+
67
+ def getc
68
+ @data.more? ? @data.get : nil
69
+ end
70
+
71
+ def putc(byte)
72
+ @data.put(byte)
73
+ end
74
+
75
+ def save
76
+ coll = @file.chunk_collection
77
+ coll.remove({'_id' => @object_id})
78
+ coll.insert(to_mongo_object)
79
+ end
80
+
81
+ def to_mongo_object
82
+ h = OrderedHash.new
83
+ h['_id'] = @object_id
84
+ h['files_id'] = @file.files_id
85
+ h['n'] = @chunk_number
86
+ h['data'] = data
87
+ h
88
+ end
89
+
90
+ end
91
+ end