mongo 1.2.4 → 1.3.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/README.md +13 -25
  2. data/Rakefile +9 -1
  3. data/docs/HISTORY.md +19 -0
  4. data/docs/RELEASES.md +33 -0
  5. data/docs/REPLICA_SETS.md +4 -3
  6. data/lib/mongo.rb +20 -2
  7. data/lib/mongo/collection.rb +15 -2
  8. data/lib/mongo/connection.rb +75 -14
  9. data/lib/mongo/cursor.rb +12 -4
  10. data/lib/mongo/db.rb +3 -3
  11. data/lib/mongo/exceptions.rb +3 -0
  12. data/lib/mongo/gridfs/grid_io.rb +88 -7
  13. data/lib/mongo/repl_set_connection.rb +29 -11
  14. data/lib/mongo/util/pool.rb +15 -6
  15. data/lib/mongo/util/timeout.rb +42 -0
  16. data/lib/mongo/util/uri_parser.rb +5 -1
  17. data/test/auxillary/fork_test.rb +30 -0
  18. data/test/bson/bson_test.rb +68 -27
  19. data/test/bson/byte_buffer_test.rb +11 -0
  20. data/test/bson/object_id_test.rb +14 -1
  21. data/test/bson/ordered_hash_test.rb +7 -0
  22. data/test/bson/timestamp_test.rb +24 -0
  23. data/test/collection_test.rb +41 -24
  24. data/test/connection_test.rb +33 -2
  25. data/test/conversions_test.rb +10 -11
  26. data/test/cursor_fail_test.rb +1 -1
  27. data/test/cursor_message_test.rb +1 -1
  28. data/test/cursor_test.rb +33 -4
  29. data/test/db_api_test.rb +13 -2
  30. data/test/db_test.rb +3 -3
  31. data/test/grid_file_system_test.rb +0 -1
  32. data/test/grid_io_test.rb +72 -1
  33. data/test/grid_test.rb +16 -16
  34. data/test/replica_sets/connect_test.rb +8 -0
  35. data/test/replica_sets/query_test.rb +10 -0
  36. data/test/support/hash_with_indifferent_access.rb +0 -13
  37. data/test/support_test.rb +0 -1
  38. data/test/test_helper.rb +27 -8
  39. data/test/timeout_test.rb +14 -0
  40. data/test/unit/collection_test.rb +1 -1
  41. data/test/unit/connection_test.rb +0 -13
  42. data/test/unit/cursor_test.rb +16 -6
  43. data/test/unit/db_test.rb +9 -11
  44. data/test/unit/repl_set_connection_test.rb +0 -13
  45. data/test/unit/safe_test.rb +1 -1
  46. metadata +15 -23
@@ -69,7 +69,7 @@ module Mongo
69
69
  # the factory should not inject a new key).
70
70
  #
71
71
  # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
72
- # propogated to Collection objects instantiated off of this DB. If no
72
+ # propagated to Collection objects instantiated off of this DB. If no
73
73
  # value is provided, the default value set on this instance's Connection object will be used. This
74
74
  # default can be overridden upon instantiation of any collection by explicity setting a :safe value
75
75
  # on initialization
@@ -272,7 +272,7 @@ module Mongo
272
272
  if strict?
273
273
  raise MongoDBError, "Collection #{name} already exists. Currently in strict mode."
274
274
  else
275
- return Collection.new(name, self)
275
+ return Collection.new(name, self, opts)
276
276
  end
277
277
  end
278
278
 
@@ -287,7 +287,7 @@ module Mongo
287
287
  # Get a collection by name.
288
288
  #
289
289
  # @param [String] name the collection name.
290
- # @param [Hash] opts any valid options that can me passed to Collection#new.
290
+ # @param [Hash] opts any valid options that can be passed to Collection#new.
291
291
  #
292
292
  # @raise [MongoDBError] if collection does not already exist and we're in +strict+ mode.
293
293
  #
@@ -57,6 +57,9 @@ module Mongo
57
57
  # Raised when a database operation fails.
58
58
  class OperationFailure < MongoDBError; end
59
59
 
60
+ # Raised when a socket read operation times out.
61
+ class OperationTimeout < ::Timeout::Error; end
62
+
60
63
  # Raised when a client attempts to perform an invalid operation.
61
64
  class InvalidOperation < MongoDBError; end
62
65
 
@@ -17,10 +17,6 @@
17
17
  # ++
18
18
 
19
19
  require 'digest/md5'
20
- begin
21
- require 'mime/types'
22
- rescue LoadError
23
- end
24
20
 
25
21
  module Mongo
26
22
 
@@ -172,6 +168,91 @@ module Mongo
172
168
  def tell
173
169
  @file_position
174
170
  end
171
+ alias :pos :tell
172
+
173
+ # Rewind the file. This is equivalent to seeking to the zeroth position.
174
+ #
175
+ # @return [Integer] the position of the file after rewinding (always zero).
176
+ def rewind
177
+ raise GridError, "file not opened for read" unless @mode[0] == ?r
178
+ seek(0)
179
+ end
180
+
181
+ # Return a boolean indicating whether the position pointer is
182
+ # at the end of the file.
183
+ #
184
+ # @return [Boolean]
185
+ def eof
186
+ raise GridError, "file not opened for read #{@mode}" unless @mode[0] == ?r
187
+ @file_position >= @file_length
188
+ end
189
+ alias :eof? :eof
190
+
191
+ # Return the next line from a GridFS file. This probably
192
+ # makes sense only if you're storing plain text. This method
193
+ # has a somewhat tricky API, which it inherits from Ruby's
194
+ # StringIO#gets.
195
+ #
196
+ # @param [String, Integer] separator or length. If a separator,
197
+ # read up to the separator. If a length, read the +length+ number
198
+ # of bytes. If nil, read the entire file.
199
+ # @param [Integer] length If a separator is provided, then
200
+ # read until either finding the separator or
201
+ # passing over the +length+ number of bytes.
202
+ #
203
+ # @return [String]
204
+ def gets(separator="\n", length=nil)
205
+ if separator.nil?
206
+ read_all
207
+ elsif separator.is_a?(Integer)
208
+ read_length(separator)
209
+ elsif separator.length > 1
210
+ result = ''
211
+ len = 0
212
+ match_idx = 0
213
+ match_num = separator.length - 1
214
+ to_match = separator[match_idx].chr
215
+ if length
216
+ matcher = lambda {|idx, num| idx < num && len < length }
217
+ else
218
+ matcher = lambda {|idx, num| idx < num}
219
+ end
220
+ while matcher.call(match_idx, match_num) && char = getc
221
+ result << char
222
+ len += 1
223
+ if char == to_match
224
+ while match_idx < match_num do
225
+ match_idx += 1
226
+ to_match = separator[match_idx].chr
227
+ char = getc
228
+ result << char
229
+ if char != to_match
230
+ match_idx = 0
231
+ to_match = separator[match_idx].chr
232
+ break
233
+ end
234
+ end
235
+ end
236
+ end
237
+ result
238
+ else
239
+ result = ''
240
+ len = 0
241
+ while char = getc
242
+ result << char
243
+ len += 1
244
+ break if char == separator || (length ? len >= length : false)
245
+ end
246
+ result
247
+ end
248
+ end
249
+
250
+ # Return the next byte from the GridFS file.
251
+ #
252
+ # @return [String]
253
+ def getc
254
+ read_length(1)
255
+ end
175
256
 
176
257
  # Creates or updates the document from the files collection that
177
258
  # stores the chunks' metadata. The file becomes available only after
@@ -260,7 +341,7 @@ module Mongo
260
341
  if length.nil?
261
342
  to_read = remaining
262
343
  else
263
- to_read = length > remaining ? remaining : length
344
+ to_read = length > remaining ? remaining : length
264
345
  end
265
346
  return nil unless remaining > 0
266
347
 
@@ -335,8 +416,8 @@ module Mongo
335
416
  @files_id = opts.delete(:_id) || BSON::ObjectId.new
336
417
  @content_type = opts.delete(:content_type) || (defined? MIME) && get_content_type || DEFAULT_CONTENT_TYPE
337
418
  @chunk_size = opts.delete(:chunk_size) || DEFAULT_CHUNK_SIZE
338
- @metadata = opts.delete(:metadata) if opts[:metadata]
339
- @aliases = opts.delete(:aliases) if opts[:aliases]
419
+ @metadata = opts.delete(:metadata)
420
+ @aliases = opts.delete(:aliases)
340
421
  @file_length = 0
341
422
  opts.each {|k, v| self[k] = v}
342
423
  check_existing_file if @safe
@@ -29,8 +29,8 @@ module Mongo
29
29
  # Connection#arbiters. This is useful if your application needs to connect manually to nodes other
30
30
  # than the primary.
31
31
  #
32
- # @param [Array] args A list of host-port pairs ending with a hash containing any options. See
33
- # the examples below for exactly how to use the constructor.
32
+ # @param [Array] args A list of host-port pairs to be used as seed nodes followed by a
33
+ # hash containing any options. See the examples below for exactly how to use the constructor.
34
34
  #
35
35
  # @option options [String] :rs_name (nil) The name of the replica set to connect to. You
36
36
  # can use this option to verify that you're connecting to the right replica set.
@@ -47,7 +47,9 @@ module Mongo
47
47
  # this is the number of seconds to wait for a new connection to be released before throwing an exception.
48
48
  # Note: this setting is relevant only for multi-threaded applications.
49
49
  #
50
- # @example Connect to a replica set and provide two seed nodes:
50
+ # @example Connect to a replica set and provide two seed nodes. Note that the number of seed nodes does
51
+ # not have to be equal to the number of replica set members. The purpose of seed nodes is to permit
52
+ # the driver to find at least one replica set member even if a member is down.
51
53
  # ReplSetConnection.new(['localhost', 30000], ['localhost', 30001])
52
54
  #
53
55
  # @example Connect to a replica set providing two seed nodes and ensuring a connection to the replica set named 'prod':
@@ -99,7 +101,7 @@ module Mongo
99
101
  #
100
102
  # @raise [ConnectionFailure] if unable to connect to any host or port.
101
103
  def connect
102
- reset_connection
104
+ close
103
105
  @nodes_to_try = @nodes.clone
104
106
 
105
107
  while connecting?
@@ -133,6 +135,20 @@ module Mongo
133
135
  @nodes_to_try.length > 0
134
136
  end
135
137
 
138
+ # The replica set primary's host name.
139
+ #
140
+ # @return [String]
141
+ def host
142
+ super
143
+ end
144
+
145
+ # The replica set primary's port.
146
+ #
147
+ # @return [Integer]
148
+ def port
149
+ super
150
+ end
151
+
136
152
  # Determine whether we're reading from a primary node. If false,
137
153
  # this connection connects to a secondary node and @read_secondaries is true.
138
154
  #
@@ -149,13 +165,6 @@ module Mongo
149
165
  @secondary_pools.each do |pool|
150
166
  pool.close
151
167
  end
152
- end
153
-
154
- # If a ConnectionFailure is raised, this method will be called
155
- # to close the connection and reset connection values.
156
- # TODO: what's the point of this method?
157
- def reset_connection
158
- super
159
168
  @secondaries = []
160
169
  @secondary_pools = []
161
170
  @arbiters = []
@@ -163,6 +172,15 @@ module Mongo
163
172
  @nodes_to_try = []
164
173
  end
165
174
 
175
+ # If a ConnectionFailure is raised, this method will be called
176
+ # to close the connection and reset connection values.
177
+ # @deprecated
178
+ def reset_connection
179
+ close
180
+ warn "ReplSetConnection#reset_connection is now deprecated. " +
181
+ "Use ReplSetConnection#close instead."
182
+ end
183
+
166
184
  # Is it okay to connect to a slave?
167
185
  #
168
186
  # @return [Boolean]
@@ -41,6 +41,7 @@ module Mongo
41
41
  @socket_ops = Hash.new { |h, k| h[k] = [] }
42
42
 
43
43
  @sockets = []
44
+ @pids = {}
44
45
  @checked_out = []
45
46
  end
46
47
 
@@ -54,6 +55,7 @@ module Mongo
54
55
  end
55
56
  @host = @port = nil
56
57
  @sockets.clear
58
+ @pids.clear
57
59
  @checked_out.clear
58
60
  end
59
61
 
@@ -83,6 +85,7 @@ module Mongo
83
85
  @connection.apply_saved_authentication(:socket => socket)
84
86
 
85
87
  @sockets << socket
88
+ @pids[socket] = Process.pid
86
89
  @checked_out << socket
87
90
  socket
88
91
  end
@@ -115,12 +118,22 @@ module Mongo
115
118
 
116
119
  # Checks out the first available socket from the pool.
117
120
  #
121
+ # If the pid has changed, remove the socket and check out
122
+ # new one.
123
+ #
118
124
  # This method is called exclusively from #checkout;
119
125
  # therefore, it runs within a mutex.
120
126
  def checkout_existing_socket
121
127
  socket = (@sockets - @checked_out).first
122
- @checked_out << socket
123
- socket
128
+ if @pids[socket] != Process.pid
129
+ @pids[socket] = nil
130
+ @sockets.delete(socket)
131
+ socket.close
132
+ checkout_new_socket
133
+ else
134
+ @checked_out << socket
135
+ socket
136
+ end
124
137
  end
125
138
 
126
139
  # Check out an existing socket or create a new socket if the maximum
@@ -155,10 +168,6 @@ module Mongo
155
168
  return socket
156
169
  else
157
170
  # Otherwise, wait
158
- if @logger
159
- @logger.warn "MONGODB Waiting for available connection; " +
160
- "#{@checked_out.size} of #{@size} connections checked out."
161
- end
162
171
  @queue.wait(@connection_mutex)
163
172
  end
164
173
  end
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+ module Mongo #:nodoc:
19
+ module TimeoutWrapper
20
+ extend self
21
+
22
+ def timeout_lib=(lib)
23
+ @@timeout_lib = lib
24
+ end
25
+
26
+ def timeout_lib
27
+ @@timeout_lib
28
+ end
29
+
30
+ def timeout(delay, &block)
31
+ if timeout_lib
32
+ begin
33
+ timeout_lib.timeout(delay, &block)
34
+ rescue ::Timeout::Error
35
+ raise Mongo::OperationTimeout
36
+ end
37
+ else
38
+ yield
39
+ end
40
+ end
41
+ end
42
+ end
@@ -20,7 +20,7 @@ module Mongo
20
20
  class URIParser
21
21
 
22
22
  DEFAULT_PORT = 27017
23
- MONGODB_URI_MATCHER = /(([-_.\w\d]+):([^@]+)@)?([-.\w\d]+)(:([\w\d]+))?(\/([-\d\w]+))?/
23
+ MONGODB_URI_MATCHER = /(([-.\w]+):([^@]+)@)?([-.\w]+)(:([\w]+))?(\/([-\w]+))?/
24
24
  MONGODB_URI_SPEC = "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]"
25
25
  SPEC_ATTRS = [:nodes, :auths]
26
26
  OPT_ATTRS = [:connect, :replicaset, :slaveok, :safe, :w, :wtimeout, :fsync]
@@ -142,7 +142,11 @@ module Mongo
142
142
  # This method uses the lambdas defined in OPT_VALID and OPT_CONV to validate
143
143
  # and convert the given options.
144
144
  def parse_options(opts)
145
+ # initialize instance variables for available options
146
+ OPT_VALID.keys.each { |k| instance_variable_set("@#{k}", nil) }
147
+
145
148
  return unless opts
149
+
146
150
  separator = opts.include?('&') ? '&' : ';'
147
151
  opts.split(separator).each do |attr|
148
152
  key, value = attr.split('=')
@@ -0,0 +1,30 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'mongo'
3
+ require 'test/unit'
4
+ require './test/test_helper'
5
+
6
+ class ForkTest < Test::Unit::TestCase
7
+ include Mongo
8
+
9
+ def setup
10
+ @conn = standard_connection
11
+ end
12
+
13
+ def test_fork
14
+ # Now insert some data
15
+ 10.times do |n|
16
+ @conn[MONGO_TEST_DB]['nums'].insert({:a => n})
17
+ end
18
+
19
+ # Now fork. You'll almost always see an exception here.
20
+ if !Kernel.fork
21
+ 10.times do
22
+ assert @conn[MONGO_TEST_DB]['nums'].find_one
23
+ end
24
+ else
25
+ 10.times do
26
+ assert @conn[MONGO_TEST_DB]['nums'].find_one
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,10 +1,14 @@
1
1
  # encoding:utf-8
2
2
  require './test/test_helper'
3
- require 'complex'
3
+
4
+ if RUBY_VERSION < '1.9'
5
+ require 'complex'
6
+ require 'rational'
7
+ end
4
8
  require 'bigdecimal'
5
- require 'rational'
6
9
 
7
10
  begin
11
+ require 'tzinfo'
8
12
  require 'active_support/core_ext'
9
13
  Time.zone = "Pacific Time (US & Canada)"
10
14
  Zone = Time.zone.now
@@ -116,15 +120,23 @@ class BSONTest < Test::Unit::TestCase
116
120
  end
117
121
  else
118
122
  def test_non_utf8_string
119
- bson = BSON::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
120
- result = BSON::BSON_CODER.deserialize(bson)['str']
121
- assert_equal 'aé', result
122
- assert_equal 'UTF-8', result.encoding.name
123
+ assert_raise BSON::InvalidStringEncoding do
124
+ BSON::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
125
+ end
126
+ end
127
+
128
+ def test_invalid_utf8_string
129
+ str = "123\xD9"
130
+ assert !str.valid_encoding?
131
+ assert_raise BSON::InvalidStringEncoding do
132
+ BSON::BSON_CODER.serialize({'str' => str})
133
+ end
123
134
  end
124
135
 
125
136
  def test_non_utf8_key
126
- bson = BSON::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
127
- assert_equal 'hello', BSON::BSON_CODER.deserialize(bson)['aé']
137
+ assert_raise BSON::InvalidStringEncoding do
138
+ BSON::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
139
+ end
128
140
  end
129
141
 
130
142
  # Based on a test from sqlite3-ruby
@@ -134,14 +146,14 @@ class BSONTest < Test::Unit::TestCase
134
146
  str = "壁に耳あり、障子に目あり"
135
147
  bson = BSON::BSON_CODER.serialize("x" => str)
136
148
 
137
- Encoding.default_internal = 'EUC-JP'
149
+ silently { Encoding.default_internal = 'EUC-JP' }
138
150
  out = BSON::BSON_CODER.deserialize(bson)["x"]
139
151
 
140
152
  assert_equal Encoding.default_internal, out.encoding
141
153
  assert_equal str.encode('EUC-JP'), out
142
154
  assert_equal str, out.encode(str.encoding)
143
155
  ensure
144
- Encoding.default_internal = before_enc
156
+ silently { Encoding.default_internal = before_enc }
145
157
  end
146
158
  end
147
159
 
@@ -161,12 +173,12 @@ class BSONTest < Test::Unit::TestCase
161
173
  assert_doc_pass(doc)
162
174
  end
163
175
 
164
- def test_double
165
- doc = {'doc' => 41.25}
166
- assert_doc_pass(doc)
167
- end
176
+ def test_double
177
+ doc = {'doc' => 41.25}
178
+ assert_doc_pass(doc)
179
+ end
168
180
 
169
- def test_int
181
+ def test_int
170
182
  doc = {'doc' => 42}
171
183
  assert_doc_pass(doc)
172
184
 
@@ -246,8 +258,14 @@ class BSONTest < Test::Unit::TestCase
246
258
  assert_in_delta doc['date'], doc2['date'], 0.001
247
259
  end
248
260
 
261
+ def test_date_in_array
262
+ doc = {'date' => [Time.now.utc]}
263
+ bson = @encoder.serialize(doc)
264
+ doc2 = @encoder.deserialize(bson)
265
+ end
266
+
249
267
  def test_date_returns_as_utc
250
- doc = {'date' => Time.now}
268
+ doc = {'date' => Time.now.utc}
251
269
  bson = @encoder.serialize(doc)
252
270
  doc2 = @encoder.deserialize(bson)
253
271
  assert doc2['date'].utc?
@@ -277,7 +295,7 @@ class BSONTest < Test::Unit::TestCase
277
295
  ensure
278
296
  if !invalid_date.is_a? Time
279
297
  assert_equal InvalidDocument, e.class
280
- assert_match /UTC Time/, e.message
298
+ assert_match(/UTC Time/, e.message)
281
299
  end
282
300
  end
283
301
  end
@@ -311,6 +329,20 @@ class BSONTest < Test::Unit::TestCase
311
329
  bin = Binary.new
312
330
  'binstring'.each_byte { |b| bin.put(b) }
313
331
 
332
+ doc = {'bin' => bin}
333
+ bson = @encoder.serialize(doc)
334
+ doc2 = @encoder.deserialize(bson)
335
+ bin2 = doc2['bin']
336
+ assert_kind_of Binary, bin2
337
+ assert_equal 'binstring', bin2.to_s
338
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
339
+ end
340
+
341
+ def test_binary_with_deprecated_subtype
342
+ bin = Binary.new
343
+ 'binstring'.each_byte { |b| bin.put(b) }
344
+ bin.subtype = Binary::SUBTYPE_BYTES
345
+
314
346
  doc = {'bin' => bin}
315
347
  bson = @encoder.serialize(doc)
316
348
  doc2 = @encoder.deserialize(bson)
@@ -328,7 +360,7 @@ class BSONTest < Test::Unit::TestCase
328
360
  bin2 = doc2['bin']
329
361
  assert_kind_of Binary, bin2
330
362
  assert_equal 'somebinarystring', bin2.to_s
331
- assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
363
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
332
364
  end
333
365
 
334
366
  def test_binary_type
@@ -368,7 +400,7 @@ class BSONTest < Test::Unit::TestCase
368
400
  bin2 = doc2['bin']
369
401
  assert_kind_of Binary, bin2
370
402
  assert_equal [1, 2, 3, 4, 5], bin2.to_a
371
- assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
403
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
372
404
  end
373
405
 
374
406
  def test_put_id_first
@@ -393,15 +425,24 @@ class BSONTest < Test::Unit::TestCase
393
425
  if !(RUBY_PLATFORM =~ /java/)
394
426
  def test_timestamp
395
427
  val = {"test" => [4, 20]}
396
- assert_equal val, @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
397
- 0x11, 0x74, 0x65, 0x73,
398
- 0x74, 0x00, 0x04, 0x00,
399
- 0x00, 0x00, 0x14, 0x00,
400
- 0x00, 0x00, 0x00])
428
+ result = @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
429
+ 0x11, 0x74, 0x65, 0x73,
430
+ 0x74, 0x00, 0x04, 0x00,
431
+ 0x00, 0x00, 0x14, 0x00,
432
+ 0x00, 0x00, 0x00])
401
433
 
434
+ assert_equal 4, result["test"][0]
435
+ assert_equal 20, result["test"][1]
402
436
  end
403
437
  end
404
438
 
439
+ def test_timestamp_type
440
+ ts = Timestamp.new(5000, 100)
441
+ doc = {:ts => ts}
442
+ bson = @encoder.serialize(doc)
443
+ assert_equal ts, @encoder.deserialize(bson)["ts"]
444
+ end
445
+
405
446
  def test_overflow
406
447
  doc = {"x" => 2**75}
407
448
  assert_raise RangeError do
@@ -439,7 +480,7 @@ class BSONTest < Test::Unit::TestCase
439
480
  rescue => e
440
481
  ensure
441
482
  assert_equal InvalidDocument, e.class
442
- assert_match /Cannot serialize/, e.message
483
+ assert_match(/Cannot serialize/, e.message)
443
484
  end
444
485
  end
445
486
  end
@@ -573,7 +614,7 @@ class BSONTest < Test::Unit::TestCase
573
614
  @encoder.serialize({"he\0llo" => "world"}, true)
574
615
  end
575
616
 
576
- assert_raise_error BSON::InvalidKeyName, "$hello" do
617
+ assert_raise BSON::InvalidKeyName do
577
618
  @encoder.serialize({"$hello" => "world"}, true)
578
619
  end
579
620
 
@@ -581,7 +622,7 @@ class BSONTest < Test::Unit::TestCase
581
622
  @encoder.serialize({"hello" => {"$hello" => "world"}}, true)
582
623
  end
583
624
 
584
- assert_raise_error BSON::InvalidKeyName, ".hello" do
625
+ assert_raise BSON::InvalidKeyName do
585
626
  @encoder.serialize({".hello" => "world"}, true)
586
627
  end
587
628