mongo 0.19.1 → 0.19.2

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.
@@ -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