mongo 0.19.1 → 0.19.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,12 +21,12 @@ Here's a quick code sample. See the MongoDB Ruby Tutorial
21
21
 
22
22
  = Installation
23
23
 
24
- The driver's gems are hosted on Gemcutter[http://gemcutter.org]. If you haven't
25
- installed a gem from Gemcutter before, you'll need to set up Gemcutter first:
24
+ The driver's gems are hosted at Rubygems.org[http://rubygems.org]. Make sure you're
25
+ using the latest version of rubygems:
26
26
 
27
- $ gem install gemcutter
27
+ $ gem update --system
28
28
 
29
- Once you've installed Gemcutter, install the mongo gem as follows:
29
+ Then you can install the mongo gem as follows:
30
30
 
31
31
  $ gem install mongo
32
32
 
@@ -192,19 +192,19 @@ generate _id values. If you want to control _id values or even their types,
192
192
  using a PK factory lets you do so.
193
193
 
194
194
  You can tell the Ruby Mongo driver how to create primary keys by passing in
195
- the :pk_factory option to the Connection#db method.
195
+ the :pk option to the Connection#db method.
196
196
 
197
- db = Mongo::Connection.new.db('dbname', :pk_factory => MyPKFactory.new)
197
+ db = Mongo::Connection.new.db('dbname', :pk => MyPKFactory.new)
198
198
 
199
- A primary key factory object must respond to :create_pk, which should take a
200
- hash and return a hash which merges the original hash with any primary key
201
- fields the factory wishes to inject.
199
+ A primary key factory object must respond to :create_pk, which should
200
+ take a hash and return a hash which merges the original hash with any
201
+ primary key fields the factory wishes to inject.
202
202
 
203
- NOTE: if the object already has a primary key, the factory should not inject
204
- a new key; this means that the object may already exist in the database.
205
- The idea here is that whenever a record is inserted,
206
- the :pk_factory object's +create_pk+ method will be called and
207
- the new hash returned will be inserted.
203
+ NOTE: if the object already has a primary key, the factory should not
204
+ inject a new key; this means that the object may already exist in the
205
+ database. The idea here is that whenever a record is inserted, the
206
+ :pk object's +create_pk+ method will be called and the new hash
207
+ returned will be inserted.
208
208
 
209
209
  Here is a sample primary key factory, taken from the tests:
210
210
 
data/Rakefile CHANGED
@@ -86,6 +86,11 @@ namespace :test do
86
86
  t.verbose = true
87
87
  end
88
88
 
89
+ Rake::TestTask.new(:new_features) do |t|
90
+ t.test_files = FileList['test/auxillary/1.4_features.rb']
91
+ t.verbose = true
92
+ end
93
+
89
94
  task :drop_databases do |t|
90
95
  puts "Dropping test database..."
91
96
  require File.join(File.dirname(__FILE__), 'lib', 'mongo')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'mongo'
4
+
5
+ include Mongo
6
+ include GridFS
7
+
8
+ db = Connection.new['benchmark-gridfs']
9
+ sample_data = File.open(File.join(File.dirname(__FILE__), 'sample_file.pdf'), 'r').read
10
+ db['fs.files'].remove
11
+ db['fs.chunks'].remove
12
+
13
+ @grid = Grid.new(db)
14
+ id = @grid.put(sample_data, "mongodb-new.pdf")
@@ -0,0 +1,46 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ module BSON
4
+ VERSION = "0.20"
5
+ def self.serialize(obj, check_keys=false, move_id=false)
6
+ BSON_CODER.serialize(obj, check_keys, move_id)
7
+ end
8
+
9
+
10
+ def self.deserialize(buf=nil)
11
+ BSON_CODER.deserialize(buf)
12
+ end
13
+ end
14
+
15
+ begin
16
+ # Need this for running test with and without c ext in Ruby 1.9.
17
+ raise LoadError if ENV['TEST_MODE'] && !ENV['C_EXT']
18
+ require 'bson_ext/cbson'
19
+ raise LoadError unless defined?(CBson::VERSION)# && CBson::VERSION == Mongo::BSON::VERSION
20
+ require 'bson/bson_c'
21
+ module BSON
22
+ BSON_CODER = BSON_C
23
+ end
24
+ rescue LoadError
25
+ require 'bson/bson_ruby'
26
+ module BSON
27
+ BSON_CODER = BSON_RUBY
28
+ end
29
+ warn "\n**Notice: C extension not loaded. This is required for optimum MongoDB Ruby driver performance."
30
+ warn " You can install the extension as follows:\n gem install mongo_ext\n"
31
+ warn " If you continue to receive this message after installing, make sure that the"
32
+ warn " mongo_ext gem is in your load path and that the mongo_ext and mongo gems are of the same version.\n"
33
+ end
34
+
35
+ require 'bson/types/binary'
36
+ require 'bson/types/code'
37
+ require 'bson/types/dbref'
38
+ require 'bson/types/objectid'
39
+ require 'bson/types/regexp_of_holding'
40
+ require 'bson/types/min_max_keys'
41
+
42
+ require 'base64'
43
+ require 'bson/ordered_hash'
44
+ require 'bson/byte_buffer'
45
+ require 'bson/bson_ruby'
46
+ require 'bson/exceptions'
@@ -0,0 +1,20 @@
1
+ # A thin wrapper for the CBson class
2
+ module BSON
3
+ class BSON_C
4
+
5
+ def self.serialize(obj, check_keys=false, move_id=false)
6
+ ByteBuffer.new(CBson.serialize(obj, check_keys, move_id))
7
+ end
8
+
9
+ def self.deserialize(buf=nil)
10
+ if buf.is_a? String
11
+ to_deserialize = ByteBuffer.new(buf) if buf
12
+ else
13
+ buf = ByteBuffer.new(buf.to_a) if buf
14
+ end
15
+ buf.rewind
16
+ CBson.deserialize(buf.to_s)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,601 @@
1
+ # --
2
+ # Copyright (C) 2008-2010 10gen Inc.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify it
5
+ # under the terms of the GNU Affero General Public License, version 3, as
6
+ # published by the Free Software Foundation.
7
+ #
8
+ # This program is distributed in the hope that it will be useful, but WITHOUT
9
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
11
+ # for more details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+ # ++
16
+
17
+ module BSON
18
+ # A BSON seralizer/deserializer in pure Ruby.
19
+ class BSON_RUBY
20
+
21
+ # why was this necessary?
22
+ #include Mongo
23
+
24
+ MINKEY = -1
25
+ EOO = 0
26
+ NUMBER = 1
27
+ STRING = 2
28
+ OBJECT = 3
29
+ ARRAY = 4
30
+ BINARY = 5
31
+ UNDEFINED = 6
32
+ OID = 7
33
+ BOOLEAN = 8
34
+ DATE = 9
35
+ NULL = 10
36
+ REGEX = 11
37
+ REF = 12
38
+ CODE = 13
39
+ SYMBOL = 14
40
+ CODE_W_SCOPE = 15
41
+ NUMBER_INT = 16
42
+ TIMESTAMP = 17
43
+ NUMBER_LONG = 18
44
+ MAXKEY = 127
45
+
46
+ def initialize
47
+ @buf = ByteBuffer.new
48
+ end
49
+
50
+ if RUBY_VERSION >= '1.9'
51
+ def self.to_utf8(str)
52
+ str.encode("utf-8")
53
+ end
54
+ else
55
+ def self.to_utf8(str)
56
+ begin
57
+ str.unpack("U*")
58
+ rescue => ex
59
+ raise InvalidStringEncoding, "String not valid utf-8: #{str}"
60
+ end
61
+ str
62
+ end
63
+ end
64
+
65
+ def self.serialize_cstr(buf, val)
66
+ buf.put_array(to_utf8(val.to_s).unpack("C*") << 0)
67
+ end
68
+
69
+ def self.serialize_key(buf, key)
70
+ raise InvalidDocument, "Key names / regex patterns must not contain the NULL byte" if key.include? "\x00"
71
+ self.serialize_cstr(buf, key)
72
+ end
73
+
74
+ def to_a
75
+ @buf.to_a
76
+ end
77
+
78
+ def to_s
79
+ @buf.to_s
80
+ end
81
+
82
+ # Serializes an object.
83
+ # Implemented to ensure an API compatible with BSON extension.
84
+ def self.serialize(obj, check_keys=false, move_id=false)
85
+ new.serialize(obj, check_keys, move_id)
86
+ end
87
+
88
+ def self.deserialize(buf=nil)
89
+ new.deserialize(buf)
90
+ end
91
+
92
+ def serialize(obj, check_keys=false, move_id=false)
93
+ raise "Document is null" unless obj
94
+
95
+ @buf.rewind
96
+ # put in a placeholder for the total size
97
+ @buf.put_int(0)
98
+
99
+ # Write key/value pairs. Always write _id first if it exists.
100
+ if move_id
101
+ if obj.has_key? '_id'
102
+ serialize_key_value('_id', obj['_id'], false)
103
+ elsif obj.has_key? :_id
104
+ serialize_key_value('_id', obj[:_id], false)
105
+ end
106
+ obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
107
+ else
108
+ if obj.has_key?('_id') && obj.has_key?(:_id)
109
+ obj['_id'] = obj.delete(:_id)
110
+ end
111
+ obj.each {|k, v| serialize_key_value(k, v, check_keys) }
112
+ end
113
+
114
+ serialize_eoo_element(@buf)
115
+ if @buf.size > 4 * 1024 * 1024
116
+ raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
117
+ end
118
+ @buf.put_int(@buf.size, 0)
119
+ self
120
+ end
121
+
122
+ # Returns the array stored in the buffer.
123
+ # Implemented to ensure an API compatible with BSON extension.
124
+ def unpack(arg)
125
+ @buf.to_a
126
+ end
127
+
128
+ def serialize_key_value(k, v, check_keys)
129
+ k = k.to_s
130
+ if check_keys
131
+ if k[0] == ?$
132
+ raise InvalidName.new("key #{k} must not start with '$'")
133
+ end
134
+ if k.include? ?.
135
+ raise InvalidName.new("key #{k} must not contain '.'")
136
+ end
137
+ end
138
+ type = bson_type(v)
139
+ case type
140
+ when STRING, SYMBOL
141
+ serialize_string_element(@buf, k, v, type)
142
+ when NUMBER, NUMBER_INT
143
+ serialize_number_element(@buf, k, v, type)
144
+ when OBJECT
145
+ serialize_object_element(@buf, k, v, check_keys)
146
+ when OID
147
+ serialize_oid_element(@buf, k, v)
148
+ when ARRAY
149
+ serialize_array_element(@buf, k, v, check_keys)
150
+ when REGEX
151
+ serialize_regex_element(@buf, k, v)
152
+ when BOOLEAN
153
+ serialize_boolean_element(@buf, k, v)
154
+ when DATE
155
+ serialize_date_element(@buf, k, v)
156
+ when NULL
157
+ serialize_null_element(@buf, k)
158
+ when REF
159
+ serialize_dbref_element(@buf, k, v)
160
+ when BINARY
161
+ serialize_binary_element(@buf, k, v)
162
+ when UNDEFINED
163
+ serialize_null_element(@buf, k)
164
+ when CODE_W_SCOPE
165
+ serialize_code_w_scope(@buf, k, v)
166
+ when MAXKEY
167
+ serialize_max_key_element(@buf, k)
168
+ when MINKEY
169
+ serialize_min_key_element(@buf, k)
170
+ else
171
+ raise "unhandled type #{type}"
172
+ end
173
+ end
174
+
175
+ def deserialize(buf=nil)
176
+ # If buf is nil, use @buf, assumed to contain already-serialized BSON.
177
+ # This is only true during testing.
178
+ if buf.is_a? String
179
+ @buf = ByteBuffer.new(buf) if buf
180
+ else
181
+ @buf = ByteBuffer.new(buf.to_a) if buf
182
+ end
183
+ @buf.rewind
184
+ @buf.get_int # eat message size
185
+ doc = OrderedHash.new
186
+ while @buf.more?
187
+ type = @buf.get
188
+ case type
189
+ when STRING, CODE
190
+ key = deserialize_cstr(@buf)
191
+ doc[key] = deserialize_string_data(@buf)
192
+ when SYMBOL
193
+ key = deserialize_cstr(@buf)
194
+ doc[key] = deserialize_string_data(@buf).intern
195
+ when NUMBER
196
+ key = deserialize_cstr(@buf)
197
+ doc[key] = deserialize_number_data(@buf)
198
+ when NUMBER_INT
199
+ key = deserialize_cstr(@buf)
200
+ doc[key] = deserialize_number_int_data(@buf)
201
+ when NUMBER_LONG
202
+ key = deserialize_cstr(@buf)
203
+ doc[key] = deserialize_number_long_data(@buf)
204
+ when OID
205
+ key = deserialize_cstr(@buf)
206
+ doc[key] = deserialize_oid_data(@buf)
207
+ when ARRAY
208
+ key = deserialize_cstr(@buf)
209
+ doc[key] = deserialize_array_data(@buf)
210
+ when REGEX
211
+ key = deserialize_cstr(@buf)
212
+ doc[key] = deserialize_regex_data(@buf)
213
+ when OBJECT
214
+ key = deserialize_cstr(@buf)
215
+ doc[key] = deserialize_object_data(@buf)
216
+ when BOOLEAN
217
+ key = deserialize_cstr(@buf)
218
+ doc[key] = deserialize_boolean_data(@buf)
219
+ when DATE
220
+ key = deserialize_cstr(@buf)
221
+ doc[key] = deserialize_date_data(@buf)
222
+ when NULL
223
+ key = deserialize_cstr(@buf)
224
+ doc[key] = nil
225
+ when UNDEFINED
226
+ key = deserialize_cstr(@buf)
227
+ doc[key] = nil
228
+ when REF
229
+ key = deserialize_cstr(@buf)
230
+ doc[key] = deserialize_dbref_data(@buf)
231
+ when BINARY
232
+ key = deserialize_cstr(@buf)
233
+ doc[key] = deserialize_binary_data(@buf)
234
+ when CODE_W_SCOPE
235
+ key = deserialize_cstr(@buf)
236
+ doc[key] = deserialize_code_w_scope_data(@buf)
237
+ when TIMESTAMP
238
+ key = deserialize_cstr(@buf)
239
+ doc[key] = [deserialize_number_int_data(@buf),
240
+ deserialize_number_int_data(@buf)]
241
+ when MAXKEY
242
+ key = deserialize_cstr(@buf)
243
+ doc[key] = MaxKey.new
244
+ when MINKEY, 255 # This is currently easier than unpack the type byte as an unsigned char.
245
+ key = deserialize_cstr(@buf)
246
+ doc[key] = MinKey.new
247
+ when EOO
248
+ break
249
+ else
250
+ raise "Unknown type #{type}, key = #{key}"
251
+ end
252
+ end
253
+ @buf.rewind
254
+ doc
255
+ end
256
+
257
+ # For debugging.
258
+ def hex_dump
259
+ str = ''
260
+ @buf.to_a.each_with_index { |b,i|
261
+ if (i % 8) == 0
262
+ str << "\n" if i > 0
263
+ str << '%4d: ' % i
264
+ else
265
+ str << ' '
266
+ end
267
+ str << '%02X' % b
268
+ }
269
+ str
270
+ end
271
+
272
+ def deserialize_date_data(buf)
273
+ unsigned = buf.get_long()
274
+ # see note for deserialize_number_long_data below
275
+ milliseconds = unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
276
+ Time.at(milliseconds.to_f / 1000.0).utc # at() takes fractional seconds
277
+ end
278
+
279
+ def deserialize_boolean_data(buf)
280
+ buf.get == 1
281
+ end
282
+
283
+ def deserialize_number_data(buf)
284
+ buf.get_double
285
+ end
286
+
287
+ def deserialize_number_int_data(buf)
288
+ # sometimes ruby makes me angry... why would the same code pack as signed
289
+ # but unpack as unsigned
290
+ unsigned = buf.get_int
291
+ unsigned >= 2**32 / 2 ? unsigned - 2**32 : unsigned
292
+ end
293
+
294
+ def deserialize_number_long_data(buf)
295
+ # same note as above applies here...
296
+ unsigned = buf.get_long
297
+ unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
298
+ end
299
+
300
+ def deserialize_object_data(buf)
301
+ size = buf.get_int
302
+ buf.position -= 4
303
+ object = BSON_CODER.new().deserialize(buf.get(size))
304
+ if object.has_key? "$ref"
305
+ DBRef.new(object["$ref"], object["$id"])
306
+ else
307
+ object
308
+ end
309
+ end
310
+
311
+ def deserialize_array_data(buf)
312
+ h = deserialize_object_data(buf)
313
+ a = []
314
+ h.each { |k, v| a[k.to_i] = v }
315
+ a
316
+ end
317
+
318
+ def deserialize_regex_data(buf)
319
+ str = deserialize_cstr(buf)
320
+ options_str = deserialize_cstr(buf)
321
+ options = 0
322
+ options |= Regexp::IGNORECASE if options_str.include?('i')
323
+ options |= Regexp::MULTILINE if options_str.include?('m')
324
+ options |= Regexp::EXTENDED if options_str.include?('x')
325
+ options_str.gsub!(/[imx]/, '') # Now remove the three we understand
326
+ if options_str == ''
327
+ Regexp.new(str, options)
328
+ else
329
+ warn("Using deprecated Regexp options #{options_str}; future versions of this MongoDB driver will support only i, m, and x. See deprecated class RegexpOfHolding for more info.")
330
+ RegexpOfHolding.new(str, options, options_str)
331
+ end
332
+ end
333
+
334
+ def deserialize_string_data(buf)
335
+ len = buf.get_int
336
+ bytes = buf.get(len)
337
+ str = bytes[0..-2]
338
+ if str.respond_to? "pack"
339
+ str = str.pack("C*")
340
+ end
341
+ if RUBY_VERSION >= '1.9'
342
+ str.force_encoding("utf-8")
343
+ end
344
+ str
345
+ end
346
+
347
+ def deserialize_code_w_scope_data(buf)
348
+ buf.get_int
349
+ len = buf.get_int
350
+ code = buf.get(len)[0..-2]
351
+ if code.respond_to? "pack"
352
+ code = code.pack("C*")
353
+ end
354
+ if RUBY_VERSION >= '1.9'
355
+ code.force_encoding("utf-8")
356
+ end
357
+
358
+ scope_size = buf.get_int
359
+ buf.position -= 4
360
+ scope = BSON_CODER.new().deserialize(buf.get(scope_size))
361
+
362
+ Code.new(code, scope)
363
+ end
364
+
365
+ def deserialize_oid_data(buf)
366
+ ObjectID.new(buf.get(12))
367
+ end
368
+
369
+ def deserialize_dbref_data(buf)
370
+ ns = deserialize_string_data(buf)
371
+ oid = deserialize_oid_data(buf)
372
+ DBRef.new(ns, oid)
373
+ end
374
+
375
+ def deserialize_binary_data(buf)
376
+ len = buf.get_int
377
+ type = buf.get
378
+ len = buf.get_int if type == Binary::SUBTYPE_BYTES
379
+ Binary.new(buf.get(len), type)
380
+ end
381
+
382
+ def serialize_eoo_element(buf)
383
+ buf.put(EOO)
384
+ end
385
+
386
+ def serialize_null_element(buf, key)
387
+ buf.put(NULL)
388
+ self.class.serialize_key(buf, key)
389
+ end
390
+
391
+ def serialize_dbref_element(buf, key, val)
392
+ oh = OrderedHash.new
393
+ oh['$ref'] = val.namespace
394
+ oh['$id'] = val.object_id
395
+ serialize_object_element(buf, key, oh, false)
396
+ end
397
+
398
+ def serialize_binary_element(buf, key, val)
399
+ buf.put(BINARY)
400
+ self.class.serialize_key(buf, key)
401
+
402
+ bytes = val.to_a
403
+ num_bytes = bytes.length
404
+ subtype = val.respond_to?(:subtype) ? val.subtype : Binary::SUBTYPE_BYTES
405
+ if subtype == Binary::SUBTYPE_BYTES
406
+ buf.put_int(num_bytes + 4)
407
+ buf.put(subtype)
408
+ buf.put_int(num_bytes)
409
+ buf.put_array(bytes)
410
+ else
411
+ buf.put_int(num_bytes)
412
+ buf.put(subtype)
413
+ buf.put_array(bytes)
414
+ end
415
+ end
416
+
417
+ def serialize_boolean_element(buf, key, val)
418
+ buf.put(BOOLEAN)
419
+ self.class.serialize_key(buf, key)
420
+ buf.put(val ? 1 : 0)
421
+ end
422
+
423
+ def serialize_date_element(buf, key, val)
424
+ buf.put(DATE)
425
+ self.class.serialize_key(buf, key)
426
+ millisecs = (val.to_f * 1000).to_i
427
+ buf.put_long(millisecs)
428
+ end
429
+
430
+ def serialize_number_element(buf, key, val, type)
431
+ if type == NUMBER
432
+ buf.put(type)
433
+ self.class.serialize_key(buf, key)
434
+ buf.put_double(val)
435
+ else
436
+ if val > 2**64 / 2 - 1 or val < -2**64 / 2
437
+ raise RangeError.new("MongoDB can only handle 8-byte ints")
438
+ end
439
+ if val > 2**32 / 2 - 1 or val < -2**32 / 2
440
+ buf.put(NUMBER_LONG)
441
+ self.class.serialize_key(buf, key)
442
+ buf.put_long(val)
443
+ else
444
+ buf.put(type)
445
+ self.class.serialize_key(buf, key)
446
+ buf.put_int(val)
447
+ end
448
+ end
449
+ end
450
+
451
+ def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
452
+ buf.put(opcode)
453
+ self.class.serialize_key(buf, key)
454
+ buf.put_array(BSON_CODER.new.serialize(val, check_keys).to_a)
455
+ end
456
+
457
+ def serialize_array_element(buf, key, val, check_keys)
458
+ # Turn array into hash with integer indices as keys
459
+ h = OrderedHash.new
460
+ i = 0
461
+ val.each { |v| h[i] = v; i += 1 }
462
+ serialize_object_element(buf, key, h, check_keys, ARRAY)
463
+ end
464
+
465
+ def serialize_regex_element(buf, key, val)
466
+ buf.put(REGEX)
467
+ self.class.serialize_key(buf, key)
468
+
469
+ str = val.source
470
+ # We use serialize_key here since regex patterns aren't prefixed with
471
+ # length (can't contain the NULL byte).
472
+ self.class.serialize_key(buf, str)
473
+
474
+ options = val.options
475
+ options_str = ''
476
+ options_str << 'i' if ((options & Regexp::IGNORECASE) != 0)
477
+ options_str << 'm' if ((options & Regexp::MULTILINE) != 0)
478
+ options_str << 'x' if ((options & Regexp::EXTENDED) != 0)
479
+ options_str << val.extra_options_str if val.respond_to?(:extra_options_str)
480
+ # Must store option chars in alphabetical order
481
+ self.class.serialize_cstr(buf, options_str.split(//).sort.uniq.join)
482
+ end
483
+
484
+ def serialize_max_key_element(buf, key)
485
+ buf.put(MAXKEY)
486
+ self.class.serialize_key(buf, key)
487
+ end
488
+
489
+ def serialize_min_key_element(buf, key)
490
+ buf.put(MINKEY)
491
+ self.class.serialize_key(buf, key)
492
+ end
493
+
494
+ def serialize_oid_element(buf, key, val)
495
+ buf.put(OID)
496
+ self.class.serialize_key(buf, key)
497
+
498
+ buf.put_array(val.to_a)
499
+ end
500
+
501
+ def serialize_string_element(buf, key, val, type)
502
+ buf.put(type)
503
+ self.class.serialize_key(buf, key)
504
+
505
+ # Make a hole for the length
506
+ len_pos = buf.position
507
+ buf.put_int(0)
508
+
509
+ # Save the string
510
+ start_pos = buf.position
511
+ self.class.serialize_cstr(buf, val)
512
+ end_pos = buf.position
513
+
514
+ # Put the string size in front
515
+ buf.put_int(end_pos - start_pos, len_pos)
516
+
517
+ # Go back to where we were
518
+ buf.position = end_pos
519
+ end
520
+
521
+ def serialize_code_w_scope(buf, key, val)
522
+ buf.put(CODE_W_SCOPE)
523
+ self.class.serialize_key(buf, key)
524
+
525
+ # Make a hole for the length
526
+ len_pos = buf.position
527
+ buf.put_int(0)
528
+
529
+ buf.put_int(val.length + 1)
530
+ self.class.serialize_cstr(buf, val)
531
+ buf.put_array(BSON_CODER.new.serialize(val.scope).to_a)
532
+
533
+ end_pos = buf.position
534
+ buf.put_int(end_pos - len_pos, len_pos)
535
+ buf.position = end_pos
536
+ end
537
+
538
+ def deserialize_cstr(buf)
539
+ chars = ""
540
+ while true
541
+ b = buf.get
542
+ break if b == 0
543
+ chars << b.chr
544
+ end
545
+ if RUBY_VERSION >= '1.9'
546
+ chars.force_encoding("utf-8") # Mongo stores UTF-8
547
+ end
548
+ chars
549
+ end
550
+
551
+ def bson_type(o)
552
+ case o
553
+ when nil
554
+ NULL
555
+ when Integer
556
+ NUMBER_INT
557
+ when Float
558
+ NUMBER
559
+ when ByteBuffer
560
+ BINARY
561
+ when Code
562
+ CODE_W_SCOPE
563
+ when String
564
+ STRING
565
+ when Array
566
+ ARRAY
567
+ when Regexp
568
+ REGEX
569
+ when ObjectID
570
+ OID
571
+ when DBRef
572
+ REF
573
+ when true, false
574
+ BOOLEAN
575
+ when Time
576
+ DATE
577
+ when Hash
578
+ OBJECT
579
+ when Symbol
580
+ SYMBOL
581
+ when MaxKey
582
+ MAXKEY
583
+ when MinKey
584
+ MINKEY
585
+ when Numeric
586
+ raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
587
+ when Date, DateTime
588
+ raise InvalidDocument, "#{o.class} is not currently supported; " +
589
+ "use a UTC Time instance instead."
590
+ else
591
+ if defined?(ActiveSupport::TimeWithZone) && o.is_a?(ActiveSupport::TimeWithZone)
592
+ raise InvalidDocument, "ActiveSupport::TimeWithZone is not currently supported; " +
593
+ "use a UTC Time instance instead."
594
+ else
595
+ raise InvalidDocument, "Cannot serialize #{o.class} as a BSON type; it either isn't supported or won't translate to BSON."
596
+ end
597
+ end
598
+ end
599
+
600
+ end
601
+ end