mongodb-mongo 0.11.1 → 0.12

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ require 'rbconfig'
12
12
  include Config
13
13
 
14
14
  gem_command = "gem"
15
- gem_command = "gem1.9" if CONFIG["MAJOR"] == "1" && CONFIG["MINOR"] == "9"
15
+ gem_command = "gem1.9" if $0.match(/1\.9$/) # use gem1.9 if we used rake1.9
16
16
 
17
17
  # NOTE: some of the tests assume Mongo is running
18
18
  Rake::TestTask.new do |t|
@@ -80,7 +80,7 @@ benchmark('batch insert (medium, no index)', insert_batch, PER_TRIAL/BATCH_SIZE,
80
80
  benchmark('batch insert (large, no index)', insert_batch, PER_TRIAL/BATCH_SIZE, db, 'large_bulk', LARGE)
81
81
 
82
82
  find_one = Proc.new { |coll, x, i|
83
- coll.find_first('x' => x)
83
+ coll.find_one('x' => x)
84
84
  }
85
85
  benchmark('find_one (small, no index)', find_one, PER_TRIAL, db, 'small_none', PER_TRIAL / 2)
86
86
  benchmark('find_one (medium, no index)', find_one, PER_TRIAL, db, 'medium_none', PER_TRIAL / 2)
@@ -1,5 +1,5 @@
1
1
  require "benchmark"
2
-
2
+
3
3
  $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
4
4
  require 'mongo'
5
5
 
@@ -20,21 +20,21 @@ arr = (0..OBJS_COUNT).collect {|x| { :number => x, :rndm => (rand(5)+1), :msg =>
20
20
 
21
21
  puts "Running benchmark"
22
22
  Benchmark.bmbm do |results|
23
- results.report("single object inserts: ") {
23
+ results.report("single object inserts: ") {
24
24
  TEST_COUNT.times {
25
25
  coll.clear
26
- arr.each {|x| coll.insert(x)}
26
+ arr.each {|x| coll.insert(x)}
27
27
  }
28
28
  }
29
- results.report("multiple object insert: ") {
29
+ results.report("multiple object insert: ") {
30
30
  TEST_COUNT.times {
31
31
  coll.clear
32
32
  coll.insert(arr)
33
33
  }
34
34
  }
35
- results.report("find_first: ") {
35
+ results.report("find_one: ") {
36
36
  TEST_COUNT.times {
37
- coll.find_first(:number => 0)
37
+ coll.find_one(:number => 0)
38
38
  }
39
39
  }
40
40
  end
data/lib/mongo.rb CHANGED
@@ -4,6 +4,7 @@ require 'mongo/types/objectid'
4
4
  require 'mongo/types/regexp_of_holding'
5
5
  require 'mongo/types/undefined'
6
6
 
7
+ require 'mongo/errors'
7
8
  require 'mongo/mongo'
8
9
  require 'mongo/message'
9
10
  require 'mongo/db'
@@ -29,25 +29,36 @@ module XGen
29
29
  case name
30
30
  when Symbol, String
31
31
  else
32
- raise RuntimeError, "new_name must be a string or symbol"
32
+ raise TypeError, "new_name must be a string or symbol"
33
33
  end
34
34
 
35
35
  name = name.to_s
36
36
 
37
37
  if name.empty? or name.include? ".."
38
- raise RuntimeError, "collection names cannot be empty"
38
+ raise InvalidName, "collection names cannot be empty"
39
39
  end
40
40
  if name.include? "$" and not name.match(/^\$cmd/)
41
- raise RuntimeError, "collection names must not contain '$'"
41
+ raise InvalidName, "collection names must not contain '$'"
42
42
  end
43
43
  if name.match(/^\./) or name.match(/\.$/)
44
- raise RuntimeError, "collection names must not start or end with '.'"
44
+ raise InvalidName, "collection names must not start or end with '.'"
45
45
  end
46
46
 
47
47
  @db, @name = db, name
48
48
  @hint = nil
49
49
  end
50
50
 
51
+ # Get a sub-collection of this collection by name.
52
+ #
53
+ # Raises InvalidName if an invalid collection name is used.
54
+ #
55
+ # :name :: the name of the collection to get
56
+ def [](name)
57
+ name = "#{self.name}.#{name}"
58
+ return Collection.new(self, name) if !db.strict? || db.collection_names.include?(name)
59
+ raise "Collection #{name} doesn't exist. Currently in strict mode."
60
+ end
61
+
51
62
  # Set hint fields to use and return +self+. hint may be a single field
52
63
  # name, array of field names, or a hash (preferably an OrderedHash).
53
64
  # May be +nil+.
@@ -89,31 +100,82 @@ module XGen
89
100
  @db.query(self, Query.new(selector, fields, offset, limit, sort, hint, snapshot))
90
101
  end
91
102
 
103
+ # Get a single object from the database.
104
+ #
105
+ # Raises TypeError if the argument is of an improper type. Returns a
106
+ # single document (hash), or nil if no result is found.
107
+ #
108
+ # :spec_or_object_id :: a hash specifying elements which must be
109
+ # present for a document to be included in the result set OR an
110
+ # instance of ObjectID to be used as the value for an _id query.
111
+ # if nil an empty spec, {}, will be used.
112
+ # :options :: options, as passed to Collection#find
113
+ def find_one(spec_or_object_id=nil, options={})
114
+ spec = case spec_or_object_id
115
+ when nil
116
+ {}
117
+ when ObjectID
118
+ {:_id => spec_or_object_id}
119
+ when Hash
120
+ spec_or_object_id
121
+ else
122
+ raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil"
123
+ end
124
+ find(spec, options.merge(:limit => -1)).next_object
125
+ end
126
+
127
+ # DEPRECATED - use find_one instead
128
+ #
92
129
  # Find the first record that matches +selector+. See #find.
93
130
  def find_first(selector={}, options={})
94
- h = options.dup
95
- h[:limit] = 1
96
- cursor = find(selector, h)
97
- cursor.next_object # don't need to explicitly close b/c of limit
131
+ warn "Collection#find_first is deprecated and will be removed. Please use Collection#find_one instead."
132
+ find_one(selector, options)
98
133
  end
99
134
 
100
- # Save an updated +object+ to the collection, or insert it if it doesn't exist already.
101
- def save(object)
102
- if id = object[:_id] || object['_id']
103
- repsert({:_id => id}, object)
135
+ # Save a document in this collection.
136
+ #
137
+ # If +to_save+ already has an '_id' then an update (upsert) operation
138
+ # is performed and any existing document with that _id is overwritten.
139
+ # Otherwise an insert operation is performed. Returns the _id of the
140
+ # saved document.
141
+ #
142
+ # :to_save :: the document (a hash) to be saved
143
+ #
144
+ # Options:
145
+ # :safe :: if true, check that the save succeeded. OperationFailure
146
+ # will be raised on an error. Checking for safety requires an extra
147
+ # round-trip to the database
148
+ def save(to_save, options={})
149
+ if id = to_save[:_id] || to_save['_id']
150
+ update({:_id => id}, to_save, :upsert => true, :safe => options.delete(:safe))
104
151
  id
105
152
  else
106
- insert(object)
153
+ insert(to_save, :safe => options.delete(:safe))
107
154
  end
108
155
  end
109
156
 
110
- # Insert +objects+, which are hashes. "<<" is aliased to this method.
111
- # Returns either the single inserted object or a new array containing
112
- # +objects+. The object(s) may have been modified by the database's PK
113
- # factory, if it has one.
114
- def insert(*objects)
115
- objects = objects.first if objects.size == 1 && objects.first.is_a?(Array)
116
- res = @db.insert_into_db(@name, objects)
157
+ # Insert a document(s) into this collection.
158
+ #
159
+ # "<<" is aliased to this method. Returns the _id of the inserted
160
+ # document or a list of _ids of the inserted documents. The object(s)
161
+ # may have been modified by the database's PK factory, if it has one.
162
+ #
163
+ # :doc_or_docs :: a document (as a hash) or Array of documents to be
164
+ # inserted
165
+ #
166
+ # Options:
167
+ # :safe :: if true, check that the insert succeeded. OperationFailure
168
+ # will be raised on an error. Checking for safety requires an extra
169
+ # round-trip to the database
170
+ def insert(doc_or_docs, options={})
171
+ doc_or_docs = [doc_or_docs] if !doc_or_docs.is_a?(Array)
172
+ res = @db.insert_into_db(@name, doc_or_docs)
173
+ if options.delete(:safe)
174
+ error = @db.error
175
+ if error
176
+ raise OperationFailure, error
177
+ end
178
+ end
117
179
  res.size > 1 ? res : res.first
118
180
  end
119
181
  alias_method :<<, :insert
@@ -128,23 +190,60 @@ module XGen
128
190
  remove({})
129
191
  end
130
192
 
193
+ # DEPRECATED - use update(... :upsert => true) instead
194
+ #
131
195
  # Update records that match +selector+ by applying +obj+ as an update.
132
196
  # If no match, inserts (???).
133
197
  def repsert(selector, obj)
134
- @db.repsert_in_db(@name, selector, obj)
198
+ warn "Collection#repsert is deprecated and will be removed. Please use Collection#update instead."
199
+ update(selector, obj, :upsert => true)
135
200
  end
136
201
 
202
+ # DEPRECATED - use update(... :upsert => false) instead
203
+ #
137
204
  # Update records that match +selector+ by applying +obj+ as an update.
138
205
  def replace(selector, obj)
139
- @db.replace_in_db(@name, selector, obj)
206
+ warn "Collection#replace is deprecated and will be removed. Please use Collection#update instead."
207
+ update(selector, obj)
140
208
  end
141
209
 
210
+ # DEPRECATED - use update(... :upsert => false) instead
211
+ #
142
212
  # Update records that match +selector+ by applying +obj+ as an update.
143
213
  # Both +selector+ and +modifier_obj+ are required.
144
214
  def modify(selector, modifier_obj)
145
- raise "no object" unless modifier_obj
146
- raise "no selector" unless selector
147
- @db.modify_in_db(@name, selector, modifier_obj)
215
+ warn "Collection#modify is deprecated and will be removed. Please use Collection#update instead."
216
+ update(selector, modifier_obj)
217
+ end
218
+
219
+ # Update a document(s) in this collection.
220
+ #
221
+ # :spec :: a hash specifying elements which must be present for
222
+ # a document to be updated
223
+ # :document :: a hash specifying the fields to be changed in the
224
+ # selected document(s), or (in the case of an upsert) the document to
225
+ # be inserted
226
+ #
227
+ # Options:
228
+ # :upsert :: if true, perform an upsert operation
229
+ # :safe :: if true, check that the update succeeded. OperationFailure
230
+ # will be raised on an error. Checking for safety requires an extra
231
+ # round-trip to the database
232
+ def update(spec, document, options={})
233
+ upsert = options.delete(:upsert)
234
+ safe = options.delete(:safe)
235
+
236
+ if upsert
237
+ @db.repsert_in_db(@name, spec, document)
238
+ else
239
+ @db.replace_in_db(@name, spec, document)
240
+ end
241
+ if safe
242
+ error = @db.error
243
+ if error
244
+ raise OperationFailure, error
245
+ end
246
+ end
148
247
  end
149
248
 
150
249
  # Create a new index. +field_or_spec+
@@ -219,7 +318,7 @@ EOS
219
318
  # Rename this collection.
220
319
  #
221
320
  # If operating in auth mode, client must be authorized as an admin to
222
- # perform this operation. Raises an error if +new_name+ is an invalid
321
+ # perform this operation. Raises +InvalidName+ if +new_name+ is an invalid
223
322
  # collection name.
224
323
  #
225
324
  # :new_name :: new name for this collection
@@ -227,19 +326,19 @@ EOS
227
326
  case new_name
228
327
  when Symbol, String
229
328
  else
230
- raise RuntimeError, "new_name must be a string or symbol"
329
+ raise TypeError, "new_name must be a string or symbol"
231
330
  end
232
331
 
233
332
  new_name = new_name.to_s
234
333
 
235
334
  if new_name.empty? or new_name.include? ".."
236
- raise RuntimeError, "collection names cannot be empty"
335
+ raise InvalidName, "collection names cannot be empty"
237
336
  end
238
337
  if new_name.include? "$"
239
- raise RuntimeError, "collection names must not contain '$'"
338
+ raise InvalidName, "collection names must not contain '$'"
240
339
  end
241
340
  if new_name.match(/^\./) or new_name.match(/\.$/)
242
- raise RuntimeError, "collection names must not start or end with '.'"
341
+ raise InvalidName, "collection names must not start or end with '.'"
243
342
  end
244
343
 
245
344
  @db.rename_collection(@name, new_name)
data/lib/mongo/db.rb CHANGED
@@ -114,7 +114,21 @@ module XGen
114
114
  # instance and connect to that one. On socket error or if we recieve a
115
115
  # "not master" error, we again find the master of the pair.
116
116
  def initialize(db_name, nodes, options={})
117
- raise "Invalid DB name \"#{db_name}\" (must be non-nil, non-zero-length, and can not contain \".\")" if !db_name || (db_name && db_name.length > 0 && db_name.include?("."))
117
+ case db_name
118
+ when Symbol, String
119
+ else
120
+ raise TypeError, "db_name must be a string or symbol"
121
+ end
122
+
123
+ [" ", ".", "$", "/", "\\"].each do |invalid_char|
124
+ if db_name.include? invalid_char
125
+ raise InvalidName, "database names cannot contain the character '#{invalid_char}'"
126
+ end
127
+ end
128
+ if db_name.empty?
129
+ raise InvalidName, "database name cannot be the empty string"
130
+ end
131
+
118
132
  @name, @nodes = db_name, nodes
119
133
  @strict = options[:strict]
120
134
  @pk_factory = options[:pk]
@@ -232,6 +246,7 @@ module XGen
232
246
  return Collection.new(self, name) if !strict? || collection_names.include?(name)
233
247
  raise "Collection #{name} doesn't exist. Currently in strict mode."
234
248
  end
249
+ alias_method :[], :collection
235
250
 
236
251
  # Drop collection +name+. Returns +true+ on success or if the
237
252
  # collection does not exist, +false+ otherwise.
@@ -364,8 +379,11 @@ module XGen
364
379
  }
365
380
  end
366
381
 
367
- # Alias for #replace_in_db. Normally called by Collection.modify.
368
- alias_method :modify_in_db, :replace_in_db
382
+ # DEPRECATED - use Collection#update instead
383
+ def modify_in_db(collection_name, selector, obj)
384
+ warn "DB#modify_in_db is deprecated and will be removed. Please use Collection#update instead."
385
+ replace_in_db(collection_name, selector, obj)
386
+ end
369
387
 
370
388
  # Update records in +collection_name+ that match +selector+ by
371
389
  # applying +obj+ as an update. If no match, inserts (???). Normally
@@ -393,7 +411,7 @@ module XGen
393
411
 
394
412
  # Dereference a DBRef, getting the document it points to.
395
413
  def dereference(dbref)
396
- collection(dbref.namespace).find_first("_id" => dbref.object_id)
414
+ collection(dbref.namespace).find_one("_id" => dbref.object_id)
397
415
  end
398
416
 
399
417
  # Evaluate a JavaScript expression on MongoDB.
@@ -474,8 +492,8 @@ module XGen
474
492
  end
475
493
 
476
494
  # Insert +objects+ into +collection_name+. Normally called by
477
- # Collection#insert. Returns a new array containing +objects+,
478
- # possibly modified by @pk_factory.
495
+ # Collection#insert. Returns a new array containing the _ids
496
+ # of the inserted documents.
479
497
  def insert_into_db(collection_name, objects)
480
498
  _synchronize {
481
499
  if @pk_factory
@@ -484,7 +502,7 @@ module XGen
484
502
  }
485
503
  else
486
504
  objects = objects.collect do |o|
487
- o[:_id] || o['_id'] ? o : o.merge(:_id => ObjectID.new)
505
+ o[:_id] || o['_id'] ? o : o.merge!(:_id => ObjectID.new)
488
506
  end
489
507
  end
490
508
  send_to_db(InsertMessage.new(@name, collection_name, true, *objects))
@@ -0,0 +1,27 @@
1
+ # Copyright 2009 10gen, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Exceptions raised by the MongoDB driver.
16
+
17
+ module XGen
18
+ module Mongo
19
+ module Driver
20
+ # Raised when a database operation fails.
21
+ class OperationFailure < RuntimeError; end
22
+
23
+ # Raised when an invalid name is used.
24
+ class InvalidName < RuntimeError; end
25
+ end
26
+ end
27
+ end
@@ -104,10 +104,10 @@ class BSON
104
104
  k = k.to_s
105
105
  if check_keys
106
106
  if k[0] == ?$
107
- raise RuntimeError.new("key #{k} must not start with '$'")
107
+ raise InvalidName.new("key #{k} must not start with '$'")
108
108
  end
109
109
  if k.include? ?.
110
- raise RuntimeError.new("key #{k} must not contain '.'")
110
+ raise InvalidName.new("key #{k} must not contain '.'")
111
111
  end
112
112
  end
113
113
  type = bson_type(v)
@@ -32,65 +32,82 @@ class OrderedHash < Hash
32
32
 
33
33
  # We only need the body of this class if the RUBY_VERSION is before 1.9
34
34
  if RUBY_VERSION < '1.9'
35
+ attr_accessor :ordered_keys
36
+
37
+ def self.[] *args
38
+ oh = OrderedHash.new
39
+ if Hash === args[0]
40
+ oh.merge! args[0]
41
+ elsif (args.size % 2) != 0
42
+ raise ArgumentError, "odd number of elements for Hash"
43
+ else
44
+ 0.step(args.size - 1, 2) do |key|
45
+ value = key + 1
46
+ oh[args[key]] = args[value]
47
+ end
48
+ end
49
+ oh
50
+ end
35
51
 
36
- attr_accessor :ordered_keys
52
+ def initialize(*a, &b)
53
+ super
54
+ @ordered_keys = []
55
+ end
37
56
 
38
- def keys
39
- @ordered_keys || []
40
- end
57
+ def keys
58
+ @ordered_keys || []
59
+ end
41
60
 
42
- def []=(key, value)
43
- @ordered_keys ||= []
44
- @ordered_keys << key unless @ordered_keys.include?(key)
45
- super(key, value)
46
- end
61
+ def []=(key, value)
62
+ @ordered_keys ||= []
63
+ @ordered_keys << key unless @ordered_keys.include?(key)
64
+ super(key, value)
65
+ end
47
66
 
48
- def each
49
- @ordered_keys ||= []
50
- @ordered_keys.each { |k| yield k, self[k] }
51
- end
67
+ def each
68
+ @ordered_keys ||= []
69
+ @ordered_keys.each { |k| yield k, self[k] }
70
+ end
52
71
 
53
- def values
54
- collect { |k, v| v }
55
- end
72
+ def values
73
+ collect { |k, v| v }
74
+ end
56
75
 
57
- def merge(other)
58
- oh = self.dup
59
- oh.merge!(other)
60
- oh
61
- end
76
+ def merge(other)
77
+ oh = self.dup
78
+ oh.merge!(other)
79
+ oh
80
+ end
62
81
 
63
- def merge!(other)
64
- @ordered_keys ||= []
65
- @ordered_keys += other.keys # unordered if not an OrderedHash
66
- @ordered_keys.uniq!
67
- super(other)
68
- end
82
+ def merge!(other)
83
+ @ordered_keys ||= []
84
+ @ordered_keys += other.keys # unordered if not an OrderedHash
85
+ @ordered_keys.uniq!
86
+ super(other)
87
+ end
69
88
 
70
- def inspect
71
- str = '{'
72
- str << (@ordered_keys || []).collect { |k| "\"#{k}\"=>#{self.[](k).inspect}" }.join(", ")
73
- str << '}'
74
- end
89
+ def inspect
90
+ str = '{'
91
+ str << (@ordered_keys || []).collect { |k| "\"#{k}\"=>#{self.[](k).inspect}" }.join(", ")
92
+ str << '}'
93
+ end
75
94
 
76
- def delete(key, &block)
77
- @ordered_keys.delete(key) if @ordered_keys
78
- super
79
- end
95
+ def delete(key, &block)
96
+ @ordered_keys.delete(key) if @ordered_keys
97
+ super
98
+ end
80
99
 
81
- def delete_if(&block)
82
- self.each { |k,v|
83
- if yield k, v
84
- delete(k)
85
- end
86
- }
87
- end
100
+ def delete_if(&block)
101
+ self.each { |k,v|
102
+ if yield k, v
103
+ delete(k)
104
+ end
105
+ }
106
+ end
88
107
 
89
- def clear
90
- super
91
- @ordered_keys = []
108
+ def clear
109
+ super
110
+ @ordered_keys = []
111
+ end
92
112
  end
93
-
94
- end # Ruby before 1.9
95
-
96
113
  end
@@ -24,6 +24,7 @@ PACKAGE_FILES = ['README.rdoc', 'Rakefile', 'mongo-ruby-driver.gemspec',
24
24
  'lib/mongo/gridfs/chunk.rb',
25
25
  'lib/mongo/gridfs/grid_store.rb',
26
26
  'lib/mongo/gridfs.rb',
27
+ 'lib/mongo/errors.rb',
27
28
  'lib/mongo/message/get_more_message.rb',
28
29
  'lib/mongo/message/insert_message.rb',
29
30
  'lib/mongo/message/kill_cursors_message.rb',
@@ -66,6 +67,7 @@ TEST_FILES = ['tests/mongo-qa/_common.rb',
66
67
  'tests/test_bson.rb',
67
68
  'tests/test_byte_buffer.rb',
68
69
  'tests/test_chunk.rb',
70
+ 'tests/test_collection.rb',
69
71
  'tests/test_cursor.rb',
70
72
  'tests/test_db.rb',
71
73
  'tests/test_db_api.rb',
@@ -80,7 +82,7 @@ TEST_FILES = ['tests/mongo-qa/_common.rb',
80
82
 
81
83
  Gem::Specification.new do |s|
82
84
  s.name = 'mongo'
83
- s.version = '0.11.1'
85
+ s.version = '0.12'
84
86
  s.platform = Gem::Platform::RUBY
85
87
  s.summary = 'Ruby driver for the 10gen Mongo DB'
86
88
  s.description = 'A Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
@@ -22,7 +22,7 @@ n1.times { |i|
22
22
  puts
23
23
 
24
24
  n2.times { |i|
25
- x = c.find_first({:id => i})
25
+ x = c.find_one({:id => i})
26
26
  x['subarray'] = "foo#{i}"
27
27
  p x
28
28
  c.modify({:id => i}, x)
@@ -0,0 +1,143 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 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
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
18
+ require 'mongo'
19
+ require 'test/unit'
20
+
21
+ # NOTE: assumes Mongo is running
22
+ class TestCollection < Test::Unit::TestCase
23
+ include XGen::Mongo
24
+ include XGen::Mongo::Driver
25
+
26
+ @@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
27
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
28
+ @@test = @@db.collection("test")
29
+
30
+ def setup
31
+ @@test.drop()
32
+ end
33
+
34
+ def test_collection
35
+ assert_raise InvalidName do
36
+ @@db["te$t"]
37
+ end
38
+
39
+ assert_kind_of Collection, @@db["test"]
40
+ assert_equal @@db["test"].name(), @@db.collection("test").name()
41
+ assert_equal @@db["test"].name(), @@db[:test].name()
42
+
43
+ assert_kind_of Collection, @@db["test"]["foo"]
44
+ assert_equal @@db["test"]["foo"].name(), @@db.collection("test.foo").name()
45
+ assert_equal @@db["test"]["foo"].name(), @@db["test.foo"].name()
46
+ end
47
+
48
+ def test_safe_insert
49
+ a = {"hello" => "world"}
50
+ @@test.insert(a)
51
+ @@test.insert(a)
52
+ assert @@db.error.include? "E11000"
53
+
54
+ assert_raise OperationFailure do
55
+ @@test.insert(a, :safe => true)
56
+ end
57
+ end
58
+
59
+ def test_update
60
+ id1 = @@test.save("x" => 5)
61
+ @@test.update({}, {"$inc" => {"x" => 1}})
62
+ assert_equal 1, @@test.count()
63
+ assert_equal 6, @@test.find_one(:_id => id1)["x"]
64
+
65
+ id2 = @@test.save("x" => 1)
66
+ @@test.update({"x" => 6}, {"$inc" => {"x" => 1}})
67
+ assert_equal 7, @@test.find_one(:_id => id1)["x"]
68
+ assert_equal 1, @@test.find_one(:_id => id2)["x"]
69
+ end
70
+
71
+ def test_upsert
72
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
73
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
74
+
75
+ assert_equal 1, @@test.count()
76
+ assert_equal 2, @@test.find_one()["count"]
77
+ end
78
+
79
+ def test_safe_update
80
+ @@test.create_index("x")
81
+ @@test.insert("x" => 5)
82
+
83
+ @@test.update({}, {"$inc" => {"x" => 1}})
84
+ assert @@db.error?
85
+
86
+ assert_raise OperationFailure do
87
+ @@test.update({}, {"$inc" => {"x" => 1}}, :safe => true)
88
+ end
89
+ end
90
+
91
+ def test_safe_save
92
+ @@test.create_index("hello", true)
93
+
94
+ @@test.save("hello" => "world")
95
+ @@test.save("hello" => "world")
96
+ assert @@db.error.include? "E11000"
97
+
98
+ assert_raise OperationFailure do
99
+ @@test.save({"hello" => "world"}, :safe => true)
100
+ end
101
+ end
102
+
103
+ def test_find_one
104
+ id = @@test.save("hello" => "world", "foo" => "bar")
105
+
106
+ assert_equal "world", @@test.find_one()["hello"]
107
+ assert_equal @@test.find_one(id), @@test.find_one()
108
+ assert_equal @@test.find_one(nil), @@test.find_one()
109
+ assert_equal @@test.find_one({}), @@test.find_one()
110
+ assert_equal @@test.find_one("hello" => "world"), @@test.find_one()
111
+ assert_equal @@test.find_one(OrderedHash["hello", "world"]), @@test.find_one()
112
+
113
+ assert @@test.find_one(nil, :fields => ["hello"]).include?("hello")
114
+ assert !@@test.find_one(nil, :fields => ["foo"]).include?("hello")
115
+
116
+ assert_equal nil, @@test.find_one("hello" => "foo")
117
+ assert_equal nil, @@test.find_one(OrderedHash["hello", "foo"])
118
+ assert_equal nil, @@test.find_one(ObjectID.new)
119
+
120
+ assert_raise TypeError do
121
+ @@test.find_one(6)
122
+ end
123
+ end
124
+
125
+ def test_insert_adds_id
126
+ doc = {"hello" => "world"}
127
+ @@test.insert(doc)
128
+ assert doc.include? :_id
129
+
130
+ docs = [{"hello" => "world"}, {"hello" => "world"}]
131
+ @@test.insert(docs)
132
+ docs.each do |doc|
133
+ assert doc.include? :_id
134
+ end
135
+ end
136
+
137
+ def test_save_adds_id
138
+ doc = {"hello" => "world"}
139
+ @@test.save(doc)
140
+ assert doc.include? :_id
141
+ end
142
+ end
143
+
data/tests/test_db.rb CHANGED
@@ -80,7 +80,7 @@ class DBTest < Test::Unit::TestCase
80
80
 
81
81
  insert_id = coll.insert('name' => 'Fred', 'age' => 42)
82
82
  # new id gets added to returned object
83
- row = coll.find_first({'name' => 'Fred'}, :limit => 1)
83
+ row = coll.find_one({'name' => 'Fred'})
84
84
  oid = row['_id']
85
85
  assert_not_nil oid
86
86
  assert_equal insert_id, oid
@@ -88,7 +88,7 @@ class DBTest < Test::Unit::TestCase
88
88
  oid = XGen::Mongo::Driver::ObjectID.new
89
89
  data = {'_id' => oid, 'name' => 'Barney', 'age' => 41}
90
90
  coll.insert(data)
91
- row = coll.find_first({'name' => data['name']}, :limit => 1)
91
+ row = coll.find_one({'name' => data['name']})
92
92
  db_oid = row['_id']
93
93
  assert_equal oid, db_oid
94
94
  assert_equal data, row
@@ -152,7 +152,7 @@ class DBTest < Test::Unit::TestCase
152
152
  assert_equal 1, prev_error['nPrev']
153
153
  assert_equal prev_error["err"], @@db.error
154
154
 
155
- @@db.collection('test').find_first
155
+ @@db.collection('test').find_one
156
156
  assert_nil @@db.error
157
157
  assert !@@db.error?
158
158
  assert @@db.previous_error
data/tests/test_db_api.rb CHANGED
@@ -52,11 +52,15 @@ class DBAPITest < Test::Unit::TestCase
52
52
  oh['b'] = 'foo'
53
53
 
54
54
  oid = @@coll.save(oh)
55
- assert_equal 'foo', @@coll.find_first(:_id => oid)['b']
55
+ assert_equal 'foo', @@coll.find_one(oid)['b']
56
+
57
+ oh = OrderedHash['a' => 1, 'b' => 'foo']
58
+ oid = @@coll.save(oh)
59
+ assert_equal 'foo', @@coll.find_one(oid)['b']
56
60
  end
57
61
 
58
62
  def test_insert_multiple
59
- ids = @@coll.insert({'a' => 2}, {'b' => 3})
63
+ ids = @@coll.insert([{'a' => 2}, {'b' => 3}])
60
64
 
61
65
  ids.each do |i|
62
66
  assert_kind_of ObjectID, i
@@ -242,9 +246,9 @@ class DBAPITest < Test::Unit::TestCase
242
246
  assert_equal 1, x['a']
243
247
  end
244
248
 
245
- def test_find_first_no_records
249
+ def test_find_one_no_records
246
250
  @@coll.clear
247
- x = @@coll.find_first('a' => 1)
251
+ x = @@coll.find_one('a' => 1)
248
252
  assert_nil x
249
253
  end
250
254
 
@@ -469,28 +473,28 @@ class DBAPITest < Test::Unit::TestCase
469
473
 
470
474
  def test_replace
471
475
  assert_equal @@coll.count, 1
472
- assert_equal @@coll.find_first["a"], 1
476
+ assert_equal @@coll.find_one["a"], 1
473
477
 
474
478
  @@coll.replace({"a" => 1}, {"a" => 2})
475
479
  assert_equal @@coll.count, 1
476
- assert_equal @@coll.find_first["a"], 2
480
+ assert_equal @@coll.find_one["a"], 2
477
481
 
478
482
  @@coll.replace({"b" => 1}, {"a" => 3})
479
483
  assert_equal @@coll.count, 1
480
- assert_equal @@coll.find_first["a"], 2
484
+ assert_equal @@coll.find_one["a"], 2
481
485
  end
482
486
 
483
487
  def test_repsert
484
488
  assert_equal @@coll.count, 1
485
- assert_equal @@coll.find_first["a"], 1
489
+ assert_equal @@coll.find_one["a"], 1
486
490
 
487
491
  @@coll.repsert({"a" => 1}, {"a" => 2})
488
492
  assert_equal @@coll.count, 1
489
- assert_equal @@coll.find_first["a"], 2
493
+ assert_equal @@coll.find_one["a"], 2
490
494
 
491
495
  @@coll.repsert({"b" => 1}, {"a" => 3})
492
496
  assert_equal @@coll.count, 2
493
- assert @@coll.find_first({"a" => 3})
497
+ assert @@coll.find_one({"a" => 3})
494
498
  end
495
499
 
496
500
  def test_to_a
@@ -540,7 +544,7 @@ class DBAPITest < Test::Unit::TestCase
540
544
  assert_equal 3, @@db.eval('function (x) {return x;}', 3)
541
545
 
542
546
  assert_equal nil, @@db.eval("function (x) {db.test_eval.save({y:x});}", 5)
543
- assert_equal 5, @@db.collection('test_eval').find_first['y']
547
+ assert_equal 5, @@db.collection('test_eval').find_one['y']
544
548
 
545
549
  assert_equal 5, @@db.eval("function (x, y) {return x + y;}", 2, 3)
546
550
  assert_equal 5, @@db.eval("function () {return 5;}")
@@ -587,7 +591,7 @@ class DBAPITest < Test::Unit::TestCase
587
591
  val = Hash.new(0)
588
592
  val["x"] = 5
589
593
  @@coll.insert val
590
- id = @@coll.find_first("x" => 5)["_id"]
594
+ id = @@coll.find_one("x" => 5)["_id"]
591
595
  assert id != 0
592
596
  end
593
597
 
@@ -610,7 +614,7 @@ class DBAPITest < Test::Unit::TestCase
610
614
 
611
615
  assert_equal nil, @@db.dereference(DBRef.new("test", ObjectID.new))
612
616
  @@coll.insert({"x" => "hello"})
613
- key = @@coll.find_first()["_id"]
617
+ key = @@coll.find_one()["_id"]
614
618
  assert_equal "hello", @@db.dereference(DBRef.new("test", key))["x"]
615
619
 
616
620
  assert_equal nil, @@db.dereference(DBRef.new("test", 4))
@@ -632,26 +636,25 @@ class DBAPITest < Test::Unit::TestCase
632
636
  assert_kind_of ObjectID, id
633
637
  assert_equal 1, @@coll.count
634
638
 
635
- assert_equal id, @@coll.save(@@coll.find_first)
639
+ assert_equal id, @@coll.save(a)
636
640
  assert_equal 1, @@coll.count
637
641
 
638
- assert_equal "world", @@coll.find_first()["hello"]
642
+ assert_equal "world", @@coll.find_one()["hello"]
639
643
 
640
- doc = @@coll.find_first
641
- doc["hello"] = "mike"
642
- @@coll.save(doc)
644
+ a["hello"] = "mike"
645
+ @@coll.save(a)
643
646
  assert_equal 1, @@coll.count
644
647
 
645
- assert_equal "mike", @@coll.find_first()["hello"]
648
+ assert_equal "mike", @@coll.find_one()["hello"]
646
649
 
647
- @@coll.save(a)
650
+ @@coll.save({"hello" => "world"})
648
651
  assert_equal 2, @@coll.count
649
652
  end
650
653
 
651
654
  def test_save_long
652
655
  @@coll.clear
653
656
  @@coll.insert("x" => 9223372036854775807)
654
- assert_equal 9223372036854775807, @@coll.find_first()["x"]
657
+ assert_equal 9223372036854775807, @@coll.find_one()["x"]
655
658
  end
656
659
 
657
660
  def test_find_by_oid
@@ -661,13 +664,13 @@ class DBAPITest < Test::Unit::TestCase
661
664
  id = @@coll.save("hello" => "world")
662
665
  assert_kind_of ObjectID, id
663
666
 
664
- assert_equal "world", @@coll.find_first(:_id => id)["hello"]
667
+ assert_equal "world", @@coll.find_one(:_id => id)["hello"]
665
668
  @@coll.find(:_id => id).to_a.each do |doc|
666
669
  assert_equal "world", doc["hello"]
667
670
  end
668
671
 
669
672
  id = ObjectID.from_string(id.to_s)
670
- assert_equal "world", @@coll.find_first(:_id => id)["hello"]
673
+ assert_equal "world", @@coll.find_one(:_id => id)["hello"]
671
674
  end
672
675
 
673
676
  def test_save_with_object_that_has_id_but_does_not_actually_exist_in_collection
@@ -676,12 +679,12 @@ class DBAPITest < Test::Unit::TestCase
676
679
  a = {'_id' => '1', 'hello' => 'world'}
677
680
  @@coll.save(a)
678
681
  assert_equal(1, @@coll.count)
679
- assert_equal("world", @@coll.find_first()["hello"])
682
+ assert_equal("world", @@coll.find_one()["hello"])
680
683
 
681
684
  a["hello"] = "mike"
682
685
  @@coll.save(a)
683
686
  assert_equal(1, @@coll.count)
684
- assert_equal("mike", @@coll.find_first()["hello"])
687
+ assert_equal("mike", @@coll.find_one()["hello"])
685
688
  end
686
689
 
687
690
  def test_invalid_key_names
@@ -690,32 +693,32 @@ class DBAPITest < Test::Unit::TestCase
690
693
  @@coll.insert({"hello" => "world"})
691
694
  @@coll.insert({"hello" => {"hello" => "world"}})
692
695
 
693
- assert_raise RuntimeError do
696
+ assert_raise InvalidName do
694
697
  @@coll.insert({"$hello" => "world"})
695
698
  end
696
- assert_raise RuntimeError do
699
+ assert_raise InvalidName do
697
700
  @@coll.insert({"hello" => {"$hello" => "world"}})
698
701
  end
699
702
 
700
703
  @@coll.insert({"he$llo" => "world"})
701
704
  @@coll.insert({"hello" => {"hell$o" => "world"}})
702
705
 
703
- assert_raise RuntimeError do
706
+ assert_raise InvalidName do
704
707
  @@coll.insert({".hello" => "world"})
705
708
  end
706
- assert_raise RuntimeError do
709
+ assert_raise InvalidName do
707
710
  @@coll.insert({"hello" => {".hello" => "world"}})
708
711
  end
709
- assert_raise RuntimeError do
712
+ assert_raise InvalidName do
710
713
  @@coll.insert({"hello." => "world"})
711
714
  end
712
- assert_raise RuntimeError do
715
+ assert_raise InvalidName do
713
716
  @@coll.insert({"hello" => {"hello." => "world"}})
714
717
  end
715
- assert_raise RuntimeError do
718
+ assert_raise InvalidName do
716
719
  @@coll.insert({"hel.lo" => "world"})
717
720
  end
718
- assert_raise RuntimeError do
721
+ assert_raise InvalidName do
719
722
  @@coll.insert({"hello" => {"hel.lo" => "world"}})
720
723
  end
721
724
 
@@ -723,22 +726,22 @@ class DBAPITest < Test::Unit::TestCase
723
726
  end
724
727
 
725
728
  def test_collection_names
726
- assert_raise RuntimeError do
729
+ assert_raise TypeError do
727
730
  @@db.collection(5)
728
731
  end
729
- assert_raise RuntimeError do
732
+ assert_raise InvalidName do
730
733
  @@db.collection("")
731
734
  end
732
- assert_raise RuntimeError do
735
+ assert_raise InvalidName do
733
736
  @@db.collection("te$t")
734
737
  end
735
- assert_raise RuntimeError do
738
+ assert_raise InvalidName do
736
739
  @@db.collection(".test")
737
740
  end
738
- assert_raise RuntimeError do
741
+ assert_raise InvalidName do
739
742
  @@db.collection("test.")
740
743
  end
741
- assert_raise RuntimeError do
744
+ assert_raise InvalidName do
742
745
  @@db.collection("tes..t")
743
746
  end
744
747
  end
@@ -749,22 +752,22 @@ class DBAPITest < Test::Unit::TestCase
749
752
  a = @@db.collection("foo")
750
753
  b = @@db.collection("bar")
751
754
 
752
- assert_raise RuntimeError do
755
+ assert_raise TypeError do
753
756
  a.rename(5)
754
757
  end
755
- assert_raise RuntimeError do
758
+ assert_raise InvalidName do
756
759
  a.rename("")
757
760
  end
758
- assert_raise RuntimeError do
761
+ assert_raise InvalidName do
759
762
  a.rename("te$t")
760
763
  end
761
- assert_raise RuntimeError do
764
+ assert_raise InvalidName do
762
765
  a.rename(".test")
763
766
  end
764
- assert_raise RuntimeError do
767
+ assert_raise InvalidName do
765
768
  a.rename("test.")
766
769
  end
767
- assert_raise RuntimeError do
770
+ assert_raise InvalidName do
768
771
  a.rename("tes..t")
769
772
  end
770
773
 
data/tests/test_mongo.rb CHANGED
@@ -17,6 +17,17 @@ class MongoTest < Test::Unit::TestCase
17
17
  @mongo.db('ruby-mongo-test').error
18
18
  end
19
19
 
20
+ def test_invalid_database_names
21
+ assert_raise TypeError do @mongo.db(4) end
22
+
23
+ assert_raise InvalidName do @mongo.db('') end
24
+ assert_raise InvalidName do @mongo.db('te$t') end
25
+ assert_raise InvalidName do @mongo.db('te.t') end
26
+ assert_raise InvalidName do @mongo.db('te\\t') end
27
+ assert_raise InvalidName do @mongo.db('te/t') end
28
+ assert_raise InvalidName do @mongo.db('te st') end
29
+ end
30
+
20
31
  def test_database_info
21
32
  @mongo.drop_database('ruby-mongo-info-test')
22
33
  @mongo.db('ruby-mongo-info-test').collection('info-test').insert('a' => 1)
@@ -12,6 +12,15 @@ class OrderedHashTest < Test::Unit::TestCase
12
12
  @ordered_keys = %w(c a z)
13
13
  end
14
14
 
15
+ def test_initialize
16
+ a = OrderedHash.new
17
+ a['x'] = 1
18
+ a['y'] = 2
19
+
20
+ b = OrderedHash['x' => 1, 'y' => 2]
21
+ assert_equal a, b
22
+ end
23
+
15
24
  def test_empty
16
25
  assert_equal [], OrderedHash.new.keys
17
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongodb-mongo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.1
4
+ version: "0.12"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Menard
@@ -49,6 +49,7 @@ files:
49
49
  - lib/mongo/gridfs/chunk.rb
50
50
  - lib/mongo/gridfs/grid_store.rb
51
51
  - lib/mongo/gridfs.rb
52
+ - lib/mongo/errors.rb
52
53
  - lib/mongo/message/get_more_message.rb
53
54
  - lib/mongo/message/insert_message.rb
54
55
  - lib/mongo/message/kill_cursors_message.rb
@@ -121,6 +122,7 @@ test_files:
121
122
  - tests/test_bson.rb
122
123
  - tests/test_byte_buffer.rb
123
124
  - tests/test_chunk.rb
125
+ - tests/test_collection.rb
124
126
  - tests/test_cursor.rb
125
127
  - tests/test_db.rb
126
128
  - tests/test_db_api.rb