animehunter-mongo 0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. data/README.rdoc +311 -0
  2. data/Rakefile +62 -0
  3. data/bin/bson_benchmark.rb +59 -0
  4. data/bin/mongo_console +21 -0
  5. data/bin/run_test_script +19 -0
  6. data/bin/standard_benchmark +109 -0
  7. data/examples/admin.rb +41 -0
  8. data/examples/benchmarks.rb +42 -0
  9. data/examples/blog.rb +76 -0
  10. data/examples/capped.rb +23 -0
  11. data/examples/cursor.rb +47 -0
  12. data/examples/gridfs.rb +87 -0
  13. data/examples/index_test.rb +125 -0
  14. data/examples/info.rb +30 -0
  15. data/examples/queries.rb +69 -0
  16. data/examples/simple.rb +23 -0
  17. data/examples/strict.rb +34 -0
  18. data/examples/types.rb +40 -0
  19. data/lib/mongo.rb +19 -0
  20. data/lib/mongo/admin.rb +87 -0
  21. data/lib/mongo/collection.rb +235 -0
  22. data/lib/mongo/cursor.rb +227 -0
  23. data/lib/mongo/db.rb +538 -0
  24. data/lib/mongo/gridfs.rb +16 -0
  25. data/lib/mongo/gridfs/chunk.rb +96 -0
  26. data/lib/mongo/gridfs/grid_store.rb +468 -0
  27. data/lib/mongo/message.rb +20 -0
  28. data/lib/mongo/message/get_more_message.rb +37 -0
  29. data/lib/mongo/message/insert_message.rb +35 -0
  30. data/lib/mongo/message/kill_cursors_message.rb +36 -0
  31. data/lib/mongo/message/message.rb +84 -0
  32. data/lib/mongo/message/message_header.rb +50 -0
  33. data/lib/mongo/message/msg_message.rb +33 -0
  34. data/lib/mongo/message/opcodes.rb +32 -0
  35. data/lib/mongo/message/query_message.rb +77 -0
  36. data/lib/mongo/message/remove_message.rb +36 -0
  37. data/lib/mongo/message/update_message.rb +37 -0
  38. data/lib/mongo/mongo.rb +164 -0
  39. data/lib/mongo/query.rb +119 -0
  40. data/lib/mongo/types/binary.rb +42 -0
  41. data/lib/mongo/types/code.rb +34 -0
  42. data/lib/mongo/types/dbref.rb +37 -0
  43. data/lib/mongo/types/objectid.rb +137 -0
  44. data/lib/mongo/types/regexp_of_holding.rb +44 -0
  45. data/lib/mongo/types/undefined.rb +31 -0
  46. data/lib/mongo/util/bson.rb +534 -0
  47. data/lib/mongo/util/byte_buffer.rb +167 -0
  48. data/lib/mongo/util/ordered_hash.rb +96 -0
  49. data/lib/mongo/util/xml_to_ruby.rb +107 -0
  50. data/mongo-ruby-driver.gemspec +99 -0
  51. data/tests/mongo-qa/_common.rb +8 -0
  52. data/tests/mongo-qa/admin +26 -0
  53. data/tests/mongo-qa/capped +22 -0
  54. data/tests/mongo-qa/count1 +18 -0
  55. data/tests/mongo-qa/dbs +22 -0
  56. data/tests/mongo-qa/find +10 -0
  57. data/tests/mongo-qa/find1 +15 -0
  58. data/tests/mongo-qa/gridfs_in +16 -0
  59. data/tests/mongo-qa/gridfs_out +17 -0
  60. data/tests/mongo-qa/indices +49 -0
  61. data/tests/mongo-qa/remove +25 -0
  62. data/tests/mongo-qa/stress1 +35 -0
  63. data/tests/mongo-qa/test1 +11 -0
  64. data/tests/mongo-qa/update +18 -0
  65. data/tests/test_admin.rb +69 -0
  66. data/tests/test_bson.rb +246 -0
  67. data/tests/test_byte_buffer.rb +69 -0
  68. data/tests/test_chunk.rb +84 -0
  69. data/tests/test_cursor.rb +121 -0
  70. data/tests/test_db.rb +160 -0
  71. data/tests/test_db_api.rb +701 -0
  72. data/tests/test_db_connection.rb +18 -0
  73. data/tests/test_grid_store.rb +284 -0
  74. data/tests/test_message.rb +35 -0
  75. data/tests/test_mongo.rb +78 -0
  76. data/tests/test_objectid.rb +98 -0
  77. data/tests/test_ordered_hash.rb +129 -0
  78. data/tests/test_round_trip.rb +116 -0
  79. metadata +133 -0
@@ -0,0 +1,44 @@
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
+ module XGen
18
+ module Mongo
19
+ module Driver
20
+
21
+ # A Regexp that can hold on to extra options and ignore them. Mongo
22
+ # regexes may contain option characters beyond 'i', 'm', and 'x'. (Note
23
+ # that Mongo only uses those three, but that regexes coming from other
24
+ # languages may store different option characters.)
25
+ #
26
+ # Note that you do not have to use this class at all if you wish to
27
+ # store regular expressions in Mongo. The Mongo and Ruby regex option
28
+ # flags are the same. Storing regexes is discouraged, in any case.
29
+ class RegexpOfHolding < Regexp
30
+
31
+ attr_accessor :extra_options_str
32
+
33
+ # +str+ and +options+ are the same as Regexp. +extra_options_str+
34
+ # contains all the other flags that were in Mongo but we do not use or
35
+ # understand.
36
+ def initialize(str, options, extra_options_str)
37
+ super(str, options)
38
+ @extra_options_str = extra_options_str
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
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
+ module XGen
18
+ module Mongo
19
+ module Driver
20
+
21
+ # A special "undefined" type to match Mongo's storage of UNKNOWN values.
22
+ # "UNKNOWN" comes from JavaScript.
23
+ #
24
+ # NOTE: this class does not attempt to provide ANY of the semantics an
25
+ # "unknown" object might need. It isn't nil, it isn't special in any
26
+ # way, and there isn't any singleton value.
27
+ class Undefined < Object; end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,534 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 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
+ require 'base64'
18
+ require 'mongo/util/byte_buffer'
19
+ require 'mongo/util/ordered_hash'
20
+ require 'mongo/types/binary'
21
+ require 'mongo/types/dbref'
22
+ require 'mongo/types/objectid'
23
+ require 'mongo/types/regexp_of_holding'
24
+ require 'mongo/types/undefined'
25
+
26
+ # A BSON seralizer/deserializer.
27
+ class BSON
28
+
29
+ include XGen::Mongo::Driver
30
+
31
+ MINKEY = -1
32
+ EOO = 0
33
+ NUMBER = 1
34
+ STRING = 2
35
+ OBJECT = 3
36
+ ARRAY = 4
37
+ BINARY = 5
38
+ UNDEFINED = 6
39
+ OID = 7
40
+ BOOLEAN = 8
41
+ DATE = 9
42
+ NULL = 10
43
+ REGEX = 11
44
+ REF = 12
45
+ CODE = 13
46
+ SYMBOL = 14
47
+ CODE_W_SCOPE = 15
48
+ NUMBER_INT = 16
49
+ TIMESTAMP = 17
50
+ MAXKEY = 127
51
+
52
+ if RUBY_VERSION >= '1.9'
53
+ def self.to_utf8(str)
54
+ str.encode("utf-8")
55
+ end
56
+ else
57
+ def self.to_utf8(str)
58
+ str # TODO Ruby 1.8 punt for now
59
+ end
60
+ end
61
+
62
+ def self.serialize_cstr(buf, val)
63
+ buf.put_array(to_utf8(val.to_s).unpack("C*") + [0])
64
+ end
65
+
66
+ def initialize()
67
+ @buf = ByteBuffer.new
68
+ end
69
+
70
+ def to_a
71
+ @buf.to_a
72
+ end
73
+
74
+ begin
75
+ require 'mongo_ext/cbson'
76
+ def serialize(obj, check_keys=false)
77
+ @buf = ByteBuffer.new(CBson.serialize(obj, check_keys))
78
+ end
79
+ rescue LoadError
80
+ def serialize(obj, check_keys=false)
81
+ raise "Document is null" unless obj
82
+
83
+ @buf.rewind
84
+ # put in a placeholder for the total size
85
+ @buf.put_int(0)
86
+
87
+ # Write key/value pairs. Always write _id first if it exists.
88
+ if obj.has_key? '_id'
89
+ serialize_key_value('_id', obj['_id'], check_keys)
90
+ elsif obj.has_key? :_id
91
+ serialize_key_value('_id', obj[:_id], check_keys)
92
+ end
93
+
94
+ obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
95
+
96
+ serialize_eoo_element(@buf)
97
+ @buf.put_int(@buf.size, 0)
98
+ self
99
+ end
100
+ end
101
+
102
+ def serialize_key_value(k, v, check_keys)
103
+ k = k.to_s
104
+ if check_keys
105
+ if k[0] == ?$
106
+ raise RuntimeError.new("key #{k} must not start with '$'")
107
+ end
108
+ if k.include? ?.
109
+ raise RuntimeError.new("key #{k} must not contain '.'")
110
+ end
111
+ end
112
+ type = bson_type(v)
113
+ case type
114
+ when STRING, SYMBOL
115
+ serialize_string_element(@buf, k, v, type)
116
+ when NUMBER, NUMBER_INT
117
+ serialize_number_element(@buf, k, v, type)
118
+ when OBJECT
119
+ serialize_object_element(@buf, k, v, check_keys)
120
+ when OID
121
+ serialize_oid_element(@buf, k, v)
122
+ when ARRAY
123
+ serialize_array_element(@buf, k, v, check_keys)
124
+ when REGEX
125
+ serialize_regex_element(@buf, k, v)
126
+ when BOOLEAN
127
+ serialize_boolean_element(@buf, k, v)
128
+ when DATE
129
+ serialize_date_element(@buf, k, v)
130
+ when NULL
131
+ serialize_null_element(@buf, k)
132
+ when REF
133
+ serialize_dbref_element(@buf, k, v)
134
+ when BINARY
135
+ serialize_binary_element(@buf, k, v)
136
+ when UNDEFINED
137
+ serialize_undefined_element(@buf, k)
138
+ when CODE_W_SCOPE
139
+ serialize_code_w_scope(@buf, k, v)
140
+ else
141
+ raise "unhandled type #{type}"
142
+ end
143
+ end
144
+
145
+ begin
146
+ require 'mongo_ext/cbson'
147
+ def deserialize(buf=nil)
148
+ if buf.is_a? String
149
+ @buf = ByteBuffer.new(buf) if buf
150
+ else
151
+ @buf = ByteBuffer.new(buf.to_a) if buf
152
+ end
153
+ @buf.rewind
154
+ CBson.deserialize(@buf.to_s)
155
+ end
156
+ rescue LoadError
157
+ def deserialize(buf=nil)
158
+ # If buf is nil, use @buf, assumed to contain already-serialized BSON.
159
+ # This is only true during testing.
160
+ if buf.is_a? String
161
+ @buf = ByteBuffer.new(buf) if buf
162
+ else
163
+ @buf = ByteBuffer.new(buf.to_a) if buf
164
+ end
165
+ @buf.rewind
166
+ @buf.get_int # eat message size
167
+ doc = OrderedHash.new
168
+ while @buf.more?
169
+ type = @buf.get
170
+ case type
171
+ when STRING, CODE
172
+ key = deserialize_cstr(@buf)
173
+ doc[key] = deserialize_string_data(@buf)
174
+ when SYMBOL
175
+ key = deserialize_cstr(@buf)
176
+ doc[key] = deserialize_string_data(@buf).intern
177
+ when NUMBER
178
+ key = deserialize_cstr(@buf)
179
+ doc[key] = deserialize_number_data(@buf)
180
+ when NUMBER_INT
181
+ key = deserialize_cstr(@buf)
182
+ doc[key] = deserialize_number_int_data(@buf)
183
+ when OID
184
+ key = deserialize_cstr(@buf)
185
+ doc[key] = deserialize_oid_data(@buf)
186
+ when ARRAY
187
+ key = deserialize_cstr(@buf)
188
+ doc[key] = deserialize_array_data(@buf)
189
+ when REGEX
190
+ key = deserialize_cstr(@buf)
191
+ doc[key] = deserialize_regex_data(@buf)
192
+ when OBJECT
193
+ key = deserialize_cstr(@buf)
194
+ doc[key] = deserialize_object_data(@buf)
195
+ when BOOLEAN
196
+ key = deserialize_cstr(@buf)
197
+ doc[key] = deserialize_boolean_data(@buf)
198
+ when DATE
199
+ key = deserialize_cstr(@buf)
200
+ doc[key] = deserialize_date_data(@buf)
201
+ when NULL
202
+ key = deserialize_cstr(@buf)
203
+ doc[key] = nil
204
+ when UNDEFINED
205
+ key = deserialize_cstr(@buf)
206
+ doc[key] = Undefined.new
207
+ when REF
208
+ key = deserialize_cstr(@buf)
209
+ doc[key] = deserialize_dbref_data(@buf)
210
+ when BINARY
211
+ key = deserialize_cstr(@buf)
212
+ doc[key] = deserialize_binary_data(@buf)
213
+ when CODE_W_SCOPE
214
+ key = deserialize_cstr(@buf)
215
+ doc[key] = deserialize_code_w_scope_data(@buf)
216
+ when TIMESTAMP
217
+ key = deserialize_cstr(@buf)
218
+ doc[key] = [deserialize_number_int_data(@buf),
219
+ deserialize_number_int_data(@buf)]
220
+ when EOO
221
+ break
222
+ else
223
+ raise "Unknown type #{type}, key = #{key}"
224
+ end
225
+ end
226
+ @buf.rewind
227
+ doc
228
+ end
229
+ end
230
+
231
+ # For debugging.
232
+ def hex_dump
233
+ str = ''
234
+ @buf.to_a.each_with_index { |b,i|
235
+ if (i % 8) == 0
236
+ str << "\n" if i > 0
237
+ str << '%4d: ' % i
238
+ else
239
+ str << ' '
240
+ end
241
+ str << '%02X' % b
242
+ }
243
+ str
244
+ end
245
+
246
+ def deserialize_date_data(buf)
247
+ millisecs = buf.get_long()
248
+ Time.at(millisecs.to_f / 1000.0).utc # at() takes fractional seconds
249
+ end
250
+
251
+ def deserialize_boolean_data(buf)
252
+ buf.get == 1
253
+ end
254
+
255
+ def deserialize_number_data(buf)
256
+ buf.get_double
257
+ end
258
+
259
+ def deserialize_number_int_data(buf)
260
+ # sometimes ruby makes me angry... why would the same code pack as signed
261
+ # but unpack as unsigned
262
+ unsigned = buf.get_int
263
+ unsigned >= 2**32 / 2 ? unsigned - 2**32 : unsigned
264
+ end
265
+
266
+ def deserialize_object_data(buf)
267
+ size = buf.get_int
268
+ buf.position -= 4
269
+ object = BSON.new().deserialize(buf.get(size))
270
+ if object.has_key? "$ref"
271
+ DBRef.new(object["$ref"], object["$id"])
272
+ else
273
+ object
274
+ end
275
+ end
276
+
277
+ def deserialize_array_data(buf)
278
+ h = deserialize_object_data(buf)
279
+ a = []
280
+ h.each { |k, v| a[k.to_i] = v }
281
+ a
282
+ end
283
+
284
+ def deserialize_regex_data(buf)
285
+ str = deserialize_cstr(buf)
286
+ options_str = deserialize_cstr(buf)
287
+ options = 0
288
+ options |= Regexp::IGNORECASE if options_str.include?('i')
289
+ options |= Regexp::MULTILINE if options_str.include?('m')
290
+ options |= Regexp::EXTENDED if options_str.include?('x')
291
+ options_str.gsub!(/[imx]/, '') # Now remove the three we understand
292
+ RegexpOfHolding.new(str, options, options_str)
293
+ end
294
+
295
+ def deserialize_string_data(buf)
296
+ len = buf.get_int
297
+ bytes = buf.get(len)
298
+ str = bytes[0..-2]
299
+ if str.respond_to? "pack"
300
+ str = str.pack("C*")
301
+ end
302
+ if RUBY_VERSION >= '1.9'
303
+ str.force_encoding("utf-8")
304
+ end
305
+ str
306
+ end
307
+
308
+ def deserialize_code_w_scope_data(buf)
309
+ buf.get_int
310
+ len = buf.get_int
311
+ code = buf.get(len)[0..-2]
312
+ if code.respond_to? "pack"
313
+ code = code.pack("C*")
314
+ end
315
+ if RUBY_VERSION >= '1.9'
316
+ code.force_encoding("utf-8")
317
+ end
318
+
319
+ scope_size = buf.get_int
320
+ buf.position -= 4
321
+ scope = BSON.new().deserialize(buf.get(scope_size))
322
+
323
+ Code.new(code, scope)
324
+ end
325
+
326
+ def deserialize_oid_data(buf)
327
+ ObjectID.new(buf.get(12))
328
+ end
329
+
330
+ def deserialize_dbref_data(buf)
331
+ ns = deserialize_string_data(buf)
332
+ oid = deserialize_oid_data(buf)
333
+ DBRef.new(ns, oid)
334
+ end
335
+
336
+ def deserialize_binary_data(buf)
337
+ len = buf.get_int
338
+ type = buf.get
339
+ len = buf.get_int if type == Binary::SUBTYPE_BYTES
340
+ Binary.new(buf.get(len), type)
341
+ end
342
+
343
+ def serialize_eoo_element(buf)
344
+ buf.put(EOO)
345
+ end
346
+
347
+ def serialize_null_element(buf, key)
348
+ buf.put(NULL)
349
+ self.class.serialize_cstr(buf, key)
350
+ end
351
+
352
+ def serialize_dbref_element(buf, key, val)
353
+ oh = OrderedHash.new
354
+ oh['$ref'] = val.namespace
355
+ oh['$id'] = val.object_id
356
+ serialize_object_element(buf, key, oh, false)
357
+ end
358
+
359
+ def serialize_binary_element(buf, key, val)
360
+ buf.put(BINARY)
361
+ self.class.serialize_cstr(buf, key)
362
+
363
+ bytes = val.to_a
364
+ num_bytes = bytes.length
365
+ subtype = val.respond_to?(:subtype) ? val.subtype : Binary::SUBTYPE_BYTES
366
+ if subtype == Binary::SUBTYPE_BYTES
367
+ buf.put_int(num_bytes + 4)
368
+ buf.put(subtype)
369
+ buf.put_int(num_bytes)
370
+ buf.put_array(bytes)
371
+ else
372
+ buf.put_int(num_bytes)
373
+ buf.put(subtype)
374
+ buf.put_array(bytes)
375
+ end
376
+ end
377
+
378
+ def serialize_undefined_element(buf, key)
379
+ buf.put(UNDEFINED)
380
+ self.class.serialize_cstr(buf, key)
381
+ end
382
+
383
+ def serialize_boolean_element(buf, key, val)
384
+ buf.put(BOOLEAN)
385
+ self.class.serialize_cstr(buf, key)
386
+ buf.put(val ? 1 : 0)
387
+ end
388
+
389
+ def serialize_date_element(buf, key, val)
390
+ buf.put(DATE)
391
+ self.class.serialize_cstr(buf, key)
392
+ millisecs = (val.to_f * 1000).to_i
393
+ buf.put_long(millisecs)
394
+ end
395
+
396
+ def serialize_number_element(buf, key, val, type)
397
+ buf.put(type)
398
+ self.class.serialize_cstr(buf, key)
399
+ if type == NUMBER
400
+ buf.put_double(val)
401
+ else
402
+ if val > 2**32 / 2 - 1 or val < -2**32 / 2
403
+ raise RangeError.new("MongoDB can only handle 4-byte ints - try converting to a double before saving")
404
+ end
405
+ buf.put_int(val)
406
+ end
407
+ end
408
+
409
+ def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
410
+ buf.put(opcode)
411
+ self.class.serialize_cstr(buf, key)
412
+ buf.put_array(BSON.new.serialize(val, check_keys).to_a)
413
+ end
414
+
415
+ def serialize_array_element(buf, key, val, check_keys)
416
+ # Turn array into hash with integer indices as keys
417
+ h = OrderedHash.new
418
+ i = 0
419
+ val.each { |v| h[i] = v; i += 1 }
420
+ serialize_object_element(buf, key, h, check_keys, ARRAY)
421
+ end
422
+
423
+ def serialize_regex_element(buf, key, val)
424
+ buf.put(REGEX)
425
+ self.class.serialize_cstr(buf, key)
426
+
427
+ str = val.to_s.sub(/.*?:/, '')[0..-2] # Turn "(?xxx:yyy)" into "yyy"
428
+ self.class.serialize_cstr(buf, str)
429
+
430
+ options = val.options
431
+ options_str = ''
432
+ options_str << 'i' if ((options & Regexp::IGNORECASE) != 0)
433
+ options_str << 'm' if ((options & Regexp::MULTILINE) != 0)
434
+ options_str << 'x' if ((options & Regexp::EXTENDED) != 0)
435
+ options_str << val.extra_options_str if val.respond_to?(:extra_options_str)
436
+ # Must store option chars in alphabetical order
437
+ self.class.serialize_cstr(buf, options_str.split(//).sort.uniq.join)
438
+ end
439
+
440
+ def serialize_oid_element(buf, key, val)
441
+ buf.put(OID)
442
+ self.class.serialize_cstr(buf, key)
443
+
444
+ buf.put_array(val.to_a)
445
+ end
446
+
447
+ def serialize_string_element(buf, key, val, type)
448
+ buf.put(type)
449
+ self.class.serialize_cstr(buf, key)
450
+
451
+ # Make a hole for the length
452
+ len_pos = buf.position
453
+ buf.put_int(0)
454
+
455
+ # Save the string
456
+ start_pos = buf.position
457
+ self.class.serialize_cstr(buf, val)
458
+ end_pos = buf.position
459
+
460
+ # Put the string size in front
461
+ buf.put_int(end_pos - start_pos, len_pos)
462
+
463
+ # Go back to where we were
464
+ buf.position = end_pos
465
+ end
466
+
467
+ def serialize_code_w_scope(buf, key, val)
468
+ buf.put(CODE_W_SCOPE)
469
+ self.class.serialize_cstr(buf, key)
470
+
471
+ # Make a hole for the length
472
+ len_pos = buf.position
473
+ buf.put_int(0)
474
+
475
+ buf.put_int(val.length + 1)
476
+ self.class.serialize_cstr(buf, val)
477
+ buf.put_array(BSON.new.serialize(val.scope).to_a)
478
+
479
+ end_pos = buf.position
480
+ buf.put_int(end_pos - len_pos, len_pos)
481
+ buf.position = end_pos
482
+ end
483
+
484
+ def deserialize_cstr(buf)
485
+ chars = ""
486
+ while true
487
+ b = buf.get
488
+ break if b == 0
489
+ chars << b.chr
490
+ end
491
+ if RUBY_VERSION >= '1.9'
492
+ chars.force_encoding("utf-8") # Mongo stores UTF-8
493
+ end
494
+ chars
495
+ end
496
+
497
+ def bson_type(o)
498
+ case o
499
+ when nil
500
+ NULL
501
+ when Integer
502
+ NUMBER_INT
503
+ when Numeric
504
+ NUMBER
505
+ when ByteBuffer
506
+ BINARY
507
+ when Code
508
+ CODE_W_SCOPE
509
+ when String
510
+ STRING
511
+ when Array
512
+ ARRAY
513
+ when Regexp
514
+ REGEX
515
+ when ObjectID
516
+ OID
517
+ when DBRef
518
+ REF
519
+ when true, false
520
+ BOOLEAN
521
+ when Time
522
+ DATE
523
+ when Hash
524
+ OBJECT
525
+ when Symbol
526
+ SYMBOL
527
+ when Undefined
528
+ UNDEFINED
529
+ else
530
+ raise "Unknown type of object: #{o.class.name}"
531
+ end
532
+ end
533
+
534
+ end