mongo 0.19.3 → 0.20
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.
- data/README.rdoc +7 -3
- data/Rakefile +16 -6
- data/bin/bson_benchmark.rb +2 -2
- data/examples/gridfs.rb +3 -2
- data/lib/mongo.rb +3 -26
- data/lib/mongo/collection.rb +69 -50
- data/lib/mongo/connection.rb +16 -41
- data/lib/mongo/cursor.rb +22 -32
- data/lib/mongo/db.rb +13 -5
- data/lib/mongo/exceptions.rb +11 -15
- data/lib/mongo/gridfs/grid.rb +14 -3
- data/lib/mongo/gridfs/grid_file_system.rb +28 -5
- data/lib/mongo/gridfs/grid_io.rb +42 -24
- data/lib/mongo/util/support.rb +13 -2
- data/mongo-ruby-driver.gemspec +3 -1
- data/test/collection_test.rb +62 -9
- data/test/connection_test.rb +21 -32
- data/test/conversions_test.rb +1 -1
- data/test/cursor_test.rb +2 -2
- data/test/db_api_test.rb +28 -27
- data/test/db_connection_test.rb +1 -1
- data/test/db_test.rb +23 -13
- data/test/grid_file_system_test.rb +30 -4
- data/test/grid_io_test.rb +14 -1
- data/test/grid_test.rb +59 -3
- data/test/test_helper.rb +4 -1
- data/test/threading/test_threading_large_pool.rb +1 -1
- data/test/threading_test.rb +1 -1
- data/test/unit/collection_test.rb +2 -2
- data/test/unit/cursor_test.rb +7 -0
- data/test/unit/db_test.rb +8 -8
- metadata +6 -46
- data/bin/gr.rb +0 -14
- data/lib/bson.rb +0 -46
- data/lib/bson/bson_c.rb +0 -20
- data/lib/bson/bson_ruby.rb +0 -601
- data/lib/bson/byte_buffer.rb +0 -224
- data/lib/bson/exceptions.rb +0 -39
- data/lib/bson/ordered_hash.rb +0 -140
- data/lib/bson/types/binary.rb +0 -54
- data/lib/bson/types/code.rb +0 -36
- data/lib/bson/types/dbref.rb +0 -40
- data/lib/bson/types/min_max_keys.rb +0 -58
- data/lib/bson/types/objectid.rb +0 -180
- data/lib/bson/types/regexp_of_holding.rb +0 -45
- data/lib/mongo/gridfs.rb +0 -29
- data/lib/mongo/gridfs/chunk.rb +0 -91
- data/lib/mongo/gridfs/grid_store.rb +0 -580
- data/lib/mongo/types/binary.rb +0 -52
- data/lib/mongo/types/code.rb +0 -36
- data/lib/mongo/types/dbref.rb +0 -40
- data/lib/mongo/types/min_max_keys.rb +0 -58
- data/lib/mongo/types/objectid.rb +0 -180
- data/lib/mongo/types/regexp_of_holding.rb +0 -45
- data/lib/mongo/util/bson_c.rb +0 -18
- data/lib/mongo/util/bson_ruby.rb +0 -606
- data/lib/mongo/util/byte_buffer.rb +0 -222
- data/lib/mongo/util/ordered_hash.rb +0 -140
- data/test/binary_test.rb +0 -15
- data/test/bson_test.rb +0 -459
- data/test/byte_buffer_test.rb +0 -81
- data/test/chunk_test.rb +0 -82
- data/test/grid_store_test.rb +0 -337
- data/test/objectid_test.rb +0 -125
- data/test/ordered_hash_test.rb +0 -172
data/lib/mongo/types/binary.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# Copyright (C) 2008-2010 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
|
-
require 'mongo/util/byte_buffer'
|
18
|
-
|
19
|
-
module Mongo
|
20
|
-
|
21
|
-
# An array of binary bytes with a MongoDB subtype. See the subtype
|
22
|
-
# constants for reference.
|
23
|
-
#
|
24
|
-
# Use this class when storing binary data in documents.
|
25
|
-
class Binary < ByteBuffer
|
26
|
-
|
27
|
-
SUBTYPE_BYTES = 0x02
|
28
|
-
SUBTYPE_UUID = 0x03
|
29
|
-
SUBTYPE_MD5 = 0x05
|
30
|
-
SUBTYPE_USER_DEFINED = 0x80
|
31
|
-
|
32
|
-
# One of the SUBTYPE_* constants. Default is SUBTYPE_BYTES.
|
33
|
-
attr_accessor :subtype
|
34
|
-
|
35
|
-
# Create a buffer for storing binary data in MongoDB.
|
36
|
-
#
|
37
|
-
# @param [Array] initia_data
|
38
|
-
# @param [Fixnum] one of four values specifying a BSON binary subtype. Possible values are
|
39
|
-
# SUBTYPE_BYTES, SUBTYPE_UUID, SUBTYPE_MD5, and SUBTYPE_USER_DEFINED.
|
40
|
-
#
|
41
|
-
# @see http://www.mongodb.org/display/DOCS/BSON#BSON-noteondatabinary BSON binary subtypes.
|
42
|
-
def initialize(initial_data=[], subtype=SUBTYPE_BYTES)
|
43
|
-
super(initial_data)
|
44
|
-
@subtype = subtype
|
45
|
-
end
|
46
|
-
|
47
|
-
def inspect
|
48
|
-
"<Mongo::Binary:#{object_id}>"
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
data/lib/mongo/types/code.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# Copyright (C) 2008-2010 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 Mongo
|
18
|
-
|
19
|
-
# JavaScript code to be evaluated by MongoDB.
|
20
|
-
class Code < String
|
21
|
-
|
22
|
-
# Hash mapping identifiers to their values
|
23
|
-
attr_accessor :scope
|
24
|
-
|
25
|
-
# Wrap code to be evaluated by MongoDB.
|
26
|
-
#
|
27
|
-
# @param [String] code the JavaScript code.
|
28
|
-
# @param [Hash] a document mapping identifiers to values, which
|
29
|
-
# represent the scope in which the code is to be executed.
|
30
|
-
def initialize(code, scope={})
|
31
|
-
super(code)
|
32
|
-
@scope = scope
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
data/lib/mongo/types/dbref.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# Copyright (C) 2008-2010 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 Mongo
|
18
|
-
|
19
|
-
# A reference to another object in a MongoDB database.
|
20
|
-
class DBRef
|
21
|
-
|
22
|
-
attr_reader :namespace, :object_id
|
23
|
-
|
24
|
-
# Create a DBRef. Use this class in conjunction with DB#dereference.
|
25
|
-
#
|
26
|
-
# @param [String] a collection name
|
27
|
-
# @param [ObjectID] an object id
|
28
|
-
#
|
29
|
-
# @core dbrefs constructor_details
|
30
|
-
def initialize(namespace, object_id)
|
31
|
-
@namespace = namespace
|
32
|
-
@object_id = object_id
|
33
|
-
end
|
34
|
-
|
35
|
-
def to_s
|
36
|
-
"ns: #{namespace}, id: #{object_id}"
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# Copyright (C) 2008-2010 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 Mongo
|
18
|
-
|
19
|
-
# A class representing the BSON MaxKey type. MaxKey will always compare greater than
|
20
|
-
# all other BSON types and values.
|
21
|
-
#
|
22
|
-
# @example Sorting (assume @numbers is a collection):
|
23
|
-
#
|
24
|
-
# >> @numbers.save({"n" => Mongo::MaxKey.new})
|
25
|
-
# >> @numbers.save({"n" => 0})
|
26
|
-
# >> @numbers.save({"n" => 5_000_000})
|
27
|
-
# >> @numbers.find.sort("n").to_a
|
28
|
-
# => [{"_id"=>4b5a050c238d3bace2000004, "n"=>0},
|
29
|
-
# {"_id"=>4b5a04e6238d3bace2000002, "n"=>5_000_000},
|
30
|
-
# {"_id"=>4b5a04ea238d3bace2000003, "n"=>#<Mongo::MaxKey:0x1014ef410>},
|
31
|
-
# ]
|
32
|
-
class MaxKey
|
33
|
-
|
34
|
-
def ==(obj)
|
35
|
-
obj.class == MaxKey
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# A class representing the BSON MinKey type. MinKey will always compare less than
|
40
|
-
# all other BSON types and values.
|
41
|
-
#
|
42
|
-
# @example Sorting (assume @numbers is a collection):
|
43
|
-
#
|
44
|
-
# >> @numbers.save({"n" => Mongo::MinKey.new})
|
45
|
-
# >> @numbers.save({"n" => -1_000_000})
|
46
|
-
# >> @numbers.save({"n" => 1_000_000})
|
47
|
-
# >> @numbers.find.sort("n").to_a
|
48
|
-
# => [{"_id"=>4b5a050c238d3bace2000004, "n"=>#<Mongo::MinKey:0x1014ef410>},
|
49
|
-
# {"_id"=>4b5a04e6238d3bace2000002, "n"=>-1_000_000},
|
50
|
-
# {"_id"=>4b5a04ea238d3bace2000003, "n"=>1_000_000},
|
51
|
-
# ]
|
52
|
-
class MinKey
|
53
|
-
|
54
|
-
def ==(obj)
|
55
|
-
obj.class == MinKey
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
data/lib/mongo/types/objectid.rb
DELETED
@@ -1,180 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# Copyright (C) 2008-2010 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
|
-
require 'thread'
|
18
|
-
require 'socket'
|
19
|
-
require 'digest/md5'
|
20
|
-
|
21
|
-
module Mongo
|
22
|
-
|
23
|
-
# Generates MongoDB object ids.
|
24
|
-
#
|
25
|
-
# @core objectids
|
26
|
-
class ObjectID
|
27
|
-
@@lock = Mutex.new
|
28
|
-
@@index = 0
|
29
|
-
|
30
|
-
# Create a new object id. If no parameter is given, an id corresponding
|
31
|
-
# to the ObjectID BSON data type will be created. This is a 12-byte value
|
32
|
-
# consisting of a 4-byte timestamp, a 3-byte machine id, a 2-byte process id,
|
33
|
-
# and a 3-byte counter.
|
34
|
-
#
|
35
|
-
# @param [Array] data should be an array of bytes. If you want
|
36
|
-
# to generate a standard MongoDB object id, leave this argument blank.
|
37
|
-
def initialize(data=nil)
|
38
|
-
@data = data || generate
|
39
|
-
end
|
40
|
-
|
41
|
-
# Determine if the supplied string is legal. Legal strings will
|
42
|
-
# consist of 24 hexadecimal characters.
|
43
|
-
#
|
44
|
-
# @param [String] str
|
45
|
-
#
|
46
|
-
# @return [Boolean]
|
47
|
-
def self.legal?(str)
|
48
|
-
len = 24
|
49
|
-
str =~ /([0-9a-f]+)/i
|
50
|
-
match = $1
|
51
|
-
str && str.length == len && match == str
|
52
|
-
end
|
53
|
-
|
54
|
-
# Create an object id from the given time. This is useful for doing range
|
55
|
-
# queries; it works because MongoDB's object ids begin
|
56
|
-
# with a timestamp.
|
57
|
-
#
|
58
|
-
# @param [Time] time a utc time to encode as an object id.
|
59
|
-
#
|
60
|
-
# @return [Mongo::ObjectID]
|
61
|
-
#
|
62
|
-
# @example Return all document created before Jan 1, 2010.
|
63
|
-
# time = Time.utc(2010, 1, 1)
|
64
|
-
# time_id = ObjectID.from_time(time)
|
65
|
-
# collection.find({'_id' => {'$lt' => time_id}})
|
66
|
-
def self.from_time(time)
|
67
|
-
self.new([time.to_i,0,0].pack("NNN").unpack("C12"))
|
68
|
-
end
|
69
|
-
|
70
|
-
# Adds a primary key to the given document if needed.
|
71
|
-
#
|
72
|
-
# @param [Hash] doc a document requiring an _id.
|
73
|
-
#
|
74
|
-
# @return [Mongo::ObjectID, Object] returns a newly-created or
|
75
|
-
# current _id for the given document.
|
76
|
-
def self.create_pk(doc)
|
77
|
-
doc.has_key?(:_id) || doc.has_key?('_id') ? doc : doc.merge!(:_id => self.new)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Check equality of this object id with another.
|
81
|
-
#
|
82
|
-
# @param [Mongo::ObjectID] object_id
|
83
|
-
def eql?(object_id)
|
84
|
-
@data == object_id.instance_variable_get("@data")
|
85
|
-
end
|
86
|
-
alias_method :==, :eql?
|
87
|
-
|
88
|
-
# Get a unique hashcode for this object.
|
89
|
-
# This is required since we've defined an #eql? method.
|
90
|
-
#
|
91
|
-
# @return [Integer]
|
92
|
-
def hash
|
93
|
-
@data.hash
|
94
|
-
end
|
95
|
-
|
96
|
-
# Get an array representation of the object id.
|
97
|
-
#
|
98
|
-
# @return [Array]
|
99
|
-
def to_a
|
100
|
-
@data.dup
|
101
|
-
end
|
102
|
-
|
103
|
-
# Given a string representation of an ObjectID, return a new ObjectID
|
104
|
-
# with that value.
|
105
|
-
#
|
106
|
-
# @param [String] str
|
107
|
-
#
|
108
|
-
# @return [Mongo::ObjectID]
|
109
|
-
def self.from_string(str)
|
110
|
-
raise InvalidObjectID, "illegal ObjectID format" unless legal?(str)
|
111
|
-
data = []
|
112
|
-
12.times do |i|
|
113
|
-
data[i] = str[i * 2, 2].to_i(16)
|
114
|
-
end
|
115
|
-
self.new(data)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Get a string representation of this object id.
|
119
|
-
#
|
120
|
-
# @return [String]
|
121
|
-
def to_s
|
122
|
-
str = ' ' * 24
|
123
|
-
12.times do |i|
|
124
|
-
str[i * 2, 2] = '%02x' % @data[i]
|
125
|
-
end
|
126
|
-
str
|
127
|
-
end
|
128
|
-
|
129
|
-
def inspect
|
130
|
-
"ObjectID('#{to_s}')"
|
131
|
-
end
|
132
|
-
|
133
|
-
# Convert to MongoDB extended JSON format. Since JSON includes type information,
|
134
|
-
# but lacks an ObjectID type, this JSON format encodes the type using an $id key.
|
135
|
-
#
|
136
|
-
# @return [String] the object id represented as MongoDB extended JSON.
|
137
|
-
def to_json(escaped=false)
|
138
|
-
"{\"$oid\": \"#{to_s}\"}"
|
139
|
-
end
|
140
|
-
|
141
|
-
# Return the UTC time at which this ObjectID was generated. This may
|
142
|
-
# be used in lieu of a created_at timestamp since this information
|
143
|
-
# is always encoded in the object id.
|
144
|
-
#
|
145
|
-
# @return [Time] the time at which this object was created.
|
146
|
-
def generation_time
|
147
|
-
Time.at(@data.pack("C4").unpack("N")[0]).utc
|
148
|
-
end
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
|
-
# We need to define this method only if CBson isn't loaded.
|
153
|
-
unless defined? CBson
|
154
|
-
def generate
|
155
|
-
oid = ''
|
156
|
-
|
157
|
-
# 4 bytes current time
|
158
|
-
time = Time.new.to_i
|
159
|
-
oid += [time].pack("N")
|
160
|
-
|
161
|
-
# 3 bytes machine
|
162
|
-
oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
|
163
|
-
|
164
|
-
# 2 bytes pid
|
165
|
-
oid += [Process.pid % 0xFFFF].pack("n")
|
166
|
-
|
167
|
-
# 3 bytes inc
|
168
|
-
oid += [get_inc].pack("N")[1, 3]
|
169
|
-
|
170
|
-
oid.unpack("C12")
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def get_inc
|
175
|
-
@@lock.synchronize do
|
176
|
-
@@index = (@@index + 1) % 0xFFFFFF
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# Copyright (C) 2008-2010 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 Mongo
|
18
|
-
|
19
|
-
# A Regexp that can hold on to extra options and ignore them. Mongo
|
20
|
-
# regexes may contain option characters beyond 'i', 'm', and 'x'. (Note
|
21
|
-
# that Mongo only uses those three, but that regexes coming from other
|
22
|
-
# languages may store different option characters.)
|
23
|
-
#
|
24
|
-
# Note that you do not have to use this class at all if you wish to
|
25
|
-
# store regular expressions in Mongo. The Mongo and Ruby regex option
|
26
|
-
# flags are the same. Storing regexes is discouraged, in any case.
|
27
|
-
#
|
28
|
-
# @deprecated
|
29
|
-
class RegexpOfHolding < Regexp
|
30
|
-
|
31
|
-
attr_accessor :extra_options_str
|
32
|
-
|
33
|
-
# @deprecated we're no longer supporting this.
|
34
|
-
# +str+ and +options+ are the same as Regexp. +extra_options_str+
|
35
|
-
# contains all the other flags that were in Mongo but we do not use or
|
36
|
-
# understand.
|
37
|
-
def initialize(str, options, extra_options_str)
|
38
|
-
warn "RegexpOfHolding is deprecated; the modifiers i, m, and x will be stored automatically as BSON." +
|
39
|
-
"If you're only storing the options i, m, and x, you can safely ignore this message."
|
40
|
-
super(str, options)
|
41
|
-
@extra_options_str = extra_options_str
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
data/lib/mongo/util/bson_c.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# A thin wrapper for the CBson class
|
2
|
-
class BSON_C
|
3
|
-
|
4
|
-
def self.serialize(obj, check_keys=false, move_id=false)
|
5
|
-
ByteBuffer.new(CBson.serialize(obj, check_keys, move_id))
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.deserialize(buf=nil)
|
9
|
-
if buf.is_a? String
|
10
|
-
to_deserialize = ByteBuffer.new(buf) if buf
|
11
|
-
else
|
12
|
-
buf = ByteBuffer.new(buf.to_a) if buf
|
13
|
-
end
|
14
|
-
buf.rewind
|
15
|
-
CBson.deserialize(buf.to_s)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
data/lib/mongo/util/bson_ruby.rb
DELETED
@@ -1,606 +0,0 @@
|
|
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
|
-
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
|
-
|
25
|
-
# A BSON seralizer/deserializer in pure Ruby.
|
26
|
-
class BSON_RUBY
|
27
|
-
|
28
|
-
include Mongo
|
29
|
-
|
30
|
-
MINKEY = -1
|
31
|
-
EOO = 0
|
32
|
-
NUMBER = 1
|
33
|
-
STRING = 2
|
34
|
-
OBJECT = 3
|
35
|
-
ARRAY = 4
|
36
|
-
BINARY = 5
|
37
|
-
UNDEFINED = 6
|
38
|
-
OID = 7
|
39
|
-
BOOLEAN = 8
|
40
|
-
DATE = 9
|
41
|
-
NULL = 10
|
42
|
-
REGEX = 11
|
43
|
-
REF = 12
|
44
|
-
CODE = 13
|
45
|
-
SYMBOL = 14
|
46
|
-
CODE_W_SCOPE = 15
|
47
|
-
NUMBER_INT = 16
|
48
|
-
TIMESTAMP = 17
|
49
|
-
NUMBER_LONG = 18
|
50
|
-
MAXKEY = 127
|
51
|
-
|
52
|
-
def initialize
|
53
|
-
@buf = ByteBuffer.new
|
54
|
-
end
|
55
|
-
|
56
|
-
if RUBY_VERSION >= '1.9'
|
57
|
-
def self.to_utf8(str)
|
58
|
-
str.encode("utf-8")
|
59
|
-
end
|
60
|
-
else
|
61
|
-
def self.to_utf8(str)
|
62
|
-
begin
|
63
|
-
str.unpack("U*")
|
64
|
-
rescue => ex
|
65
|
-
raise InvalidStringEncoding, "String not valid utf-8: #{str}"
|
66
|
-
end
|
67
|
-
str
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.serialize_cstr(buf, val)
|
72
|
-
buf.put_array(to_utf8(val.to_s).unpack("C*") << 0)
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.serialize_key(buf, key)
|
76
|
-
raise InvalidDocument, "Key names / regex patterns must not contain the NULL byte" if key.include? "\x00"
|
77
|
-
self.serialize_cstr(buf, key)
|
78
|
-
end
|
79
|
-
|
80
|
-
def to_a
|
81
|
-
@buf.to_a
|
82
|
-
end
|
83
|
-
|
84
|
-
def to_s
|
85
|
-
@buf.to_s
|
86
|
-
end
|
87
|
-
|
88
|
-
# Serializes an object.
|
89
|
-
# Implemented to ensure an API compatible with BSON extension.
|
90
|
-
def self.serialize(obj, check_keys=false, move_id=false)
|
91
|
-
new.serialize(obj, check_keys, move_id)
|
92
|
-
end
|
93
|
-
|
94
|
-
def self.deserialize(buf=nil)
|
95
|
-
new.deserialize(buf)
|
96
|
-
end
|
97
|
-
|
98
|
-
def serialize(obj, check_keys=false, move_id=false)
|
99
|
-
raise "Document is null" unless obj
|
100
|
-
|
101
|
-
@buf.rewind
|
102
|
-
# put in a placeholder for the total size
|
103
|
-
@buf.put_int(0)
|
104
|
-
|
105
|
-
# Write key/value pairs. Always write _id first if it exists.
|
106
|
-
if move_id
|
107
|
-
if obj.has_key? '_id'
|
108
|
-
serialize_key_value('_id', obj['_id'], false)
|
109
|
-
elsif obj.has_key? :_id
|
110
|
-
serialize_key_value('_id', obj[:_id], false)
|
111
|
-
end
|
112
|
-
obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
|
113
|
-
else
|
114
|
-
if obj.has_key?('_id') && obj.has_key?(:_id)
|
115
|
-
obj['_id'] = obj.delete(:_id)
|
116
|
-
end
|
117
|
-
obj.each {|k, v| serialize_key_value(k, v, check_keys) }
|
118
|
-
end
|
119
|
-
|
120
|
-
serialize_eoo_element(@buf)
|
121
|
-
if @buf.size > 4 * 1024 * 1024
|
122
|
-
raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
|
123
|
-
end
|
124
|
-
@buf.put_int(@buf.size, 0)
|
125
|
-
self
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns the array stored in the buffer.
|
129
|
-
# Implemented to ensure an API compatible with BSON extension.
|
130
|
-
def unpack(arg)
|
131
|
-
@buf.to_a
|
132
|
-
end
|
133
|
-
|
134
|
-
def serialize_key_value(k, v, check_keys)
|
135
|
-
k = k.to_s
|
136
|
-
if check_keys
|
137
|
-
if k[0] == ?$
|
138
|
-
raise InvalidName.new("key #{k} must not start with '$'")
|
139
|
-
end
|
140
|
-
if k.include? ?.
|
141
|
-
raise InvalidName.new("key #{k} must not contain '.'")
|
142
|
-
end
|
143
|
-
end
|
144
|
-
type = bson_type(v)
|
145
|
-
case type
|
146
|
-
when STRING, SYMBOL
|
147
|
-
serialize_string_element(@buf, k, v, type)
|
148
|
-
when NUMBER, NUMBER_INT
|
149
|
-
serialize_number_element(@buf, k, v, type)
|
150
|
-
when OBJECT
|
151
|
-
serialize_object_element(@buf, k, v, check_keys)
|
152
|
-
when OID
|
153
|
-
serialize_oid_element(@buf, k, v)
|
154
|
-
when ARRAY
|
155
|
-
serialize_array_element(@buf, k, v, check_keys)
|
156
|
-
when REGEX
|
157
|
-
serialize_regex_element(@buf, k, v)
|
158
|
-
when BOOLEAN
|
159
|
-
serialize_boolean_element(@buf, k, v)
|
160
|
-
when DATE
|
161
|
-
serialize_date_element(@buf, k, v)
|
162
|
-
when NULL
|
163
|
-
serialize_null_element(@buf, k)
|
164
|
-
when REF
|
165
|
-
serialize_dbref_element(@buf, k, v)
|
166
|
-
when BINARY
|
167
|
-
serialize_binary_element(@buf, k, v)
|
168
|
-
when UNDEFINED
|
169
|
-
serialize_null_element(@buf, k)
|
170
|
-
when CODE_W_SCOPE
|
171
|
-
serialize_code_w_scope(@buf, k, v)
|
172
|
-
when MAXKEY
|
173
|
-
serialize_max_key_element(@buf, k)
|
174
|
-
when MINKEY
|
175
|
-
serialize_min_key_element(@buf, k)
|
176
|
-
else
|
177
|
-
raise "unhandled type #{type}"
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def deserialize(buf=nil)
|
182
|
-
# If buf is nil, use @buf, assumed to contain already-serialized BSON.
|
183
|
-
# This is only true during testing.
|
184
|
-
if buf.is_a? String
|
185
|
-
@buf = ByteBuffer.new(buf) if buf
|
186
|
-
else
|
187
|
-
@buf = ByteBuffer.new(buf.to_a) if buf
|
188
|
-
end
|
189
|
-
@buf.rewind
|
190
|
-
@buf.get_int # eat message size
|
191
|
-
doc = OrderedHash.new
|
192
|
-
while @buf.more?
|
193
|
-
type = @buf.get
|
194
|
-
case type
|
195
|
-
when STRING, CODE
|
196
|
-
key = deserialize_cstr(@buf)
|
197
|
-
doc[key] = deserialize_string_data(@buf)
|
198
|
-
when SYMBOL
|
199
|
-
key = deserialize_cstr(@buf)
|
200
|
-
doc[key] = deserialize_string_data(@buf).intern
|
201
|
-
when NUMBER
|
202
|
-
key = deserialize_cstr(@buf)
|
203
|
-
doc[key] = deserialize_number_data(@buf)
|
204
|
-
when NUMBER_INT
|
205
|
-
key = deserialize_cstr(@buf)
|
206
|
-
doc[key] = deserialize_number_int_data(@buf)
|
207
|
-
when NUMBER_LONG
|
208
|
-
key = deserialize_cstr(@buf)
|
209
|
-
doc[key] = deserialize_number_long_data(@buf)
|
210
|
-
when OID
|
211
|
-
key = deserialize_cstr(@buf)
|
212
|
-
doc[key] = deserialize_oid_data(@buf)
|
213
|
-
when ARRAY
|
214
|
-
key = deserialize_cstr(@buf)
|
215
|
-
doc[key] = deserialize_array_data(@buf)
|
216
|
-
when REGEX
|
217
|
-
key = deserialize_cstr(@buf)
|
218
|
-
doc[key] = deserialize_regex_data(@buf)
|
219
|
-
when OBJECT
|
220
|
-
key = deserialize_cstr(@buf)
|
221
|
-
doc[key] = deserialize_object_data(@buf)
|
222
|
-
when BOOLEAN
|
223
|
-
key = deserialize_cstr(@buf)
|
224
|
-
doc[key] = deserialize_boolean_data(@buf)
|
225
|
-
when DATE
|
226
|
-
key = deserialize_cstr(@buf)
|
227
|
-
doc[key] = deserialize_date_data(@buf)
|
228
|
-
when NULL
|
229
|
-
key = deserialize_cstr(@buf)
|
230
|
-
doc[key] = nil
|
231
|
-
when UNDEFINED
|
232
|
-
key = deserialize_cstr(@buf)
|
233
|
-
doc[key] = nil
|
234
|
-
when REF
|
235
|
-
key = deserialize_cstr(@buf)
|
236
|
-
doc[key] = deserialize_dbref_data(@buf)
|
237
|
-
when BINARY
|
238
|
-
key = deserialize_cstr(@buf)
|
239
|
-
doc[key] = deserialize_binary_data(@buf)
|
240
|
-
when CODE_W_SCOPE
|
241
|
-
key = deserialize_cstr(@buf)
|
242
|
-
doc[key] = deserialize_code_w_scope_data(@buf)
|
243
|
-
when TIMESTAMP
|
244
|
-
key = deserialize_cstr(@buf)
|
245
|
-
doc[key] = [deserialize_number_int_data(@buf),
|
246
|
-
deserialize_number_int_data(@buf)]
|
247
|
-
when MAXKEY
|
248
|
-
key = deserialize_cstr(@buf)
|
249
|
-
doc[key] = MaxKey.new
|
250
|
-
when MINKEY, 255 # This is currently easier than unpack the type byte as an unsigned char.
|
251
|
-
key = deserialize_cstr(@buf)
|
252
|
-
doc[key] = MinKey.new
|
253
|
-
when EOO
|
254
|
-
break
|
255
|
-
else
|
256
|
-
raise "Unknown type #{type}, key = #{key}"
|
257
|
-
end
|
258
|
-
end
|
259
|
-
@buf.rewind
|
260
|
-
doc
|
261
|
-
end
|
262
|
-
|
263
|
-
# For debugging.
|
264
|
-
def hex_dump
|
265
|
-
str = ''
|
266
|
-
@buf.to_a.each_with_index { |b,i|
|
267
|
-
if (i % 8) == 0
|
268
|
-
str << "\n" if i > 0
|
269
|
-
str << '%4d: ' % i
|
270
|
-
else
|
271
|
-
str << ' '
|
272
|
-
end
|
273
|
-
str << '%02X' % b
|
274
|
-
}
|
275
|
-
str
|
276
|
-
end
|
277
|
-
|
278
|
-
def deserialize_date_data(buf)
|
279
|
-
unsigned = buf.get_long()
|
280
|
-
# see note for deserialize_number_long_data below
|
281
|
-
milliseconds = unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
|
282
|
-
Time.at(milliseconds.to_f / 1000.0).utc # at() takes fractional seconds
|
283
|
-
end
|
284
|
-
|
285
|
-
def deserialize_boolean_data(buf)
|
286
|
-
buf.get == 1
|
287
|
-
end
|
288
|
-
|
289
|
-
def deserialize_number_data(buf)
|
290
|
-
buf.get_double
|
291
|
-
end
|
292
|
-
|
293
|
-
def deserialize_number_int_data(buf)
|
294
|
-
# sometimes ruby makes me angry... why would the same code pack as signed
|
295
|
-
# but unpack as unsigned
|
296
|
-
unsigned = buf.get_int
|
297
|
-
unsigned >= 2**32 / 2 ? unsigned - 2**32 : unsigned
|
298
|
-
end
|
299
|
-
|
300
|
-
def deserialize_number_long_data(buf)
|
301
|
-
# same note as above applies here...
|
302
|
-
unsigned = buf.get_long
|
303
|
-
unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
|
304
|
-
end
|
305
|
-
|
306
|
-
def deserialize_object_data(buf)
|
307
|
-
size = buf.get_int
|
308
|
-
buf.position -= 4
|
309
|
-
object = BSON.new().deserialize(buf.get(size))
|
310
|
-
if object.has_key? "$ref"
|
311
|
-
DBRef.new(object["$ref"], object["$id"])
|
312
|
-
else
|
313
|
-
object
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
def deserialize_array_data(buf)
|
318
|
-
h = deserialize_object_data(buf)
|
319
|
-
a = []
|
320
|
-
h.each { |k, v| a[k.to_i] = v }
|
321
|
-
a
|
322
|
-
end
|
323
|
-
|
324
|
-
def deserialize_regex_data(buf)
|
325
|
-
str = deserialize_cstr(buf)
|
326
|
-
options_str = deserialize_cstr(buf)
|
327
|
-
options = 0
|
328
|
-
options |= Regexp::IGNORECASE if options_str.include?('i')
|
329
|
-
options |= Regexp::MULTILINE if options_str.include?('m')
|
330
|
-
options |= Regexp::EXTENDED if options_str.include?('x')
|
331
|
-
options_str.gsub!(/[imx]/, '') # Now remove the three we understand
|
332
|
-
if options_str == ''
|
333
|
-
Regexp.new(str, options)
|
334
|
-
else
|
335
|
-
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.")
|
336
|
-
RegexpOfHolding.new(str, options, options_str)
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def deserialize_string_data(buf)
|
341
|
-
len = buf.get_int
|
342
|
-
bytes = buf.get(len)
|
343
|
-
str = bytes[0..-2]
|
344
|
-
if str.respond_to? "pack"
|
345
|
-
str = str.pack("C*")
|
346
|
-
end
|
347
|
-
if RUBY_VERSION >= '1.9'
|
348
|
-
str.force_encoding("utf-8")
|
349
|
-
end
|
350
|
-
str
|
351
|
-
end
|
352
|
-
|
353
|
-
def deserialize_code_w_scope_data(buf)
|
354
|
-
buf.get_int
|
355
|
-
len = buf.get_int
|
356
|
-
code = buf.get(len)[0..-2]
|
357
|
-
if code.respond_to? "pack"
|
358
|
-
code = code.pack("C*")
|
359
|
-
end
|
360
|
-
if RUBY_VERSION >= '1.9'
|
361
|
-
code.force_encoding("utf-8")
|
362
|
-
end
|
363
|
-
|
364
|
-
scope_size = buf.get_int
|
365
|
-
buf.position -= 4
|
366
|
-
scope = BSON.new().deserialize(buf.get(scope_size))
|
367
|
-
|
368
|
-
Code.new(code, scope)
|
369
|
-
end
|
370
|
-
|
371
|
-
def deserialize_oid_data(buf)
|
372
|
-
ObjectID.new(buf.get(12))
|
373
|
-
end
|
374
|
-
|
375
|
-
def deserialize_dbref_data(buf)
|
376
|
-
ns = deserialize_string_data(buf)
|
377
|
-
oid = deserialize_oid_data(buf)
|
378
|
-
DBRef.new(ns, oid)
|
379
|
-
end
|
380
|
-
|
381
|
-
def deserialize_binary_data(buf)
|
382
|
-
len = buf.get_int
|
383
|
-
type = buf.get
|
384
|
-
len = buf.get_int if type == Binary::SUBTYPE_BYTES
|
385
|
-
Binary.new(buf.get(len), type)
|
386
|
-
end
|
387
|
-
|
388
|
-
def serialize_eoo_element(buf)
|
389
|
-
buf.put(EOO)
|
390
|
-
end
|
391
|
-
|
392
|
-
def serialize_null_element(buf, key)
|
393
|
-
buf.put(NULL)
|
394
|
-
self.class.serialize_key(buf, key)
|
395
|
-
end
|
396
|
-
|
397
|
-
def serialize_dbref_element(buf, key, val)
|
398
|
-
oh = OrderedHash.new
|
399
|
-
oh['$ref'] = val.namespace
|
400
|
-
oh['$id'] = val.object_id
|
401
|
-
serialize_object_element(buf, key, oh, false)
|
402
|
-
end
|
403
|
-
|
404
|
-
def serialize_binary_element(buf, key, val)
|
405
|
-
buf.put(BINARY)
|
406
|
-
self.class.serialize_key(buf, key)
|
407
|
-
|
408
|
-
bytes = val.to_a
|
409
|
-
num_bytes = bytes.length
|
410
|
-
subtype = val.respond_to?(:subtype) ? val.subtype : Binary::SUBTYPE_BYTES
|
411
|
-
if subtype == Binary::SUBTYPE_BYTES
|
412
|
-
buf.put_int(num_bytes + 4)
|
413
|
-
buf.put(subtype)
|
414
|
-
buf.put_int(num_bytes)
|
415
|
-
buf.put_array(bytes)
|
416
|
-
else
|
417
|
-
buf.put_int(num_bytes)
|
418
|
-
buf.put(subtype)
|
419
|
-
buf.put_array(bytes)
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
def serialize_boolean_element(buf, key, val)
|
424
|
-
buf.put(BOOLEAN)
|
425
|
-
self.class.serialize_key(buf, key)
|
426
|
-
buf.put(val ? 1 : 0)
|
427
|
-
end
|
428
|
-
|
429
|
-
def serialize_date_element(buf, key, val)
|
430
|
-
buf.put(DATE)
|
431
|
-
self.class.serialize_key(buf, key)
|
432
|
-
millisecs = (val.to_f * 1000).to_i
|
433
|
-
buf.put_long(millisecs)
|
434
|
-
end
|
435
|
-
|
436
|
-
def serialize_number_element(buf, key, val, type)
|
437
|
-
if type == NUMBER
|
438
|
-
buf.put(type)
|
439
|
-
self.class.serialize_key(buf, key)
|
440
|
-
buf.put_double(val)
|
441
|
-
else
|
442
|
-
if val > 2**64 / 2 - 1 or val < -2**64 / 2
|
443
|
-
raise RangeError.new("MongoDB can only handle 8-byte ints")
|
444
|
-
end
|
445
|
-
if val > 2**32 / 2 - 1 or val < -2**32 / 2
|
446
|
-
buf.put(NUMBER_LONG)
|
447
|
-
self.class.serialize_key(buf, key)
|
448
|
-
buf.put_long(val)
|
449
|
-
else
|
450
|
-
buf.put(type)
|
451
|
-
self.class.serialize_key(buf, key)
|
452
|
-
buf.put_int(val)
|
453
|
-
end
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
|
458
|
-
buf.put(opcode)
|
459
|
-
self.class.serialize_key(buf, key)
|
460
|
-
buf.put_array(BSON.new.serialize(val, check_keys).to_a)
|
461
|
-
end
|
462
|
-
|
463
|
-
def serialize_array_element(buf, key, val, check_keys)
|
464
|
-
# Turn array into hash with integer indices as keys
|
465
|
-
h = OrderedHash.new
|
466
|
-
i = 0
|
467
|
-
val.each { |v| h[i] = v; i += 1 }
|
468
|
-
serialize_object_element(buf, key, h, check_keys, ARRAY)
|
469
|
-
end
|
470
|
-
|
471
|
-
def serialize_regex_element(buf, key, val)
|
472
|
-
buf.put(REGEX)
|
473
|
-
self.class.serialize_key(buf, key)
|
474
|
-
|
475
|
-
str = val.source
|
476
|
-
# We use serialize_key here since regex patterns aren't prefixed with
|
477
|
-
# length (can't contain the NULL byte).
|
478
|
-
self.class.serialize_key(buf, str)
|
479
|
-
|
480
|
-
options = val.options
|
481
|
-
options_str = ''
|
482
|
-
options_str << 'i' if ((options & Regexp::IGNORECASE) != 0)
|
483
|
-
options_str << 'm' if ((options & Regexp::MULTILINE) != 0)
|
484
|
-
options_str << 'x' if ((options & Regexp::EXTENDED) != 0)
|
485
|
-
options_str << val.extra_options_str if val.respond_to?(:extra_options_str)
|
486
|
-
# Must store option chars in alphabetical order
|
487
|
-
self.class.serialize_cstr(buf, options_str.split(//).sort.uniq.join)
|
488
|
-
end
|
489
|
-
|
490
|
-
def serialize_max_key_element(buf, key)
|
491
|
-
buf.put(MAXKEY)
|
492
|
-
self.class.serialize_key(buf, key)
|
493
|
-
end
|
494
|
-
|
495
|
-
def serialize_min_key_element(buf, key)
|
496
|
-
buf.put(MINKEY)
|
497
|
-
self.class.serialize_key(buf, key)
|
498
|
-
end
|
499
|
-
|
500
|
-
def serialize_oid_element(buf, key, val)
|
501
|
-
buf.put(OID)
|
502
|
-
self.class.serialize_key(buf, key)
|
503
|
-
|
504
|
-
buf.put_array(val.to_a)
|
505
|
-
end
|
506
|
-
|
507
|
-
def serialize_string_element(buf, key, val, type)
|
508
|
-
buf.put(type)
|
509
|
-
self.class.serialize_key(buf, key)
|
510
|
-
|
511
|
-
# Make a hole for the length
|
512
|
-
len_pos = buf.position
|
513
|
-
buf.put_int(0)
|
514
|
-
|
515
|
-
# Save the string
|
516
|
-
start_pos = buf.position
|
517
|
-
self.class.serialize_cstr(buf, val)
|
518
|
-
end_pos = buf.position
|
519
|
-
|
520
|
-
# Put the string size in front
|
521
|
-
buf.put_int(end_pos - start_pos, len_pos)
|
522
|
-
|
523
|
-
# Go back to where we were
|
524
|
-
buf.position = end_pos
|
525
|
-
end
|
526
|
-
|
527
|
-
def serialize_code_w_scope(buf, key, val)
|
528
|
-
buf.put(CODE_W_SCOPE)
|
529
|
-
self.class.serialize_key(buf, key)
|
530
|
-
|
531
|
-
# Make a hole for the length
|
532
|
-
len_pos = buf.position
|
533
|
-
buf.put_int(0)
|
534
|
-
|
535
|
-
buf.put_int(val.length + 1)
|
536
|
-
self.class.serialize_cstr(buf, val)
|
537
|
-
buf.put_array(BSON.new.serialize(val.scope).to_a)
|
538
|
-
|
539
|
-
end_pos = buf.position
|
540
|
-
buf.put_int(end_pos - len_pos, len_pos)
|
541
|
-
buf.position = end_pos
|
542
|
-
end
|
543
|
-
|
544
|
-
def deserialize_cstr(buf)
|
545
|
-
chars = ""
|
546
|
-
while true
|
547
|
-
b = buf.get
|
548
|
-
break if b == 0
|
549
|
-
chars << b.chr
|
550
|
-
end
|
551
|
-
if RUBY_VERSION >= '1.9'
|
552
|
-
chars.force_encoding("utf-8") # Mongo stores UTF-8
|
553
|
-
end
|
554
|
-
chars
|
555
|
-
end
|
556
|
-
|
557
|
-
def bson_type(o)
|
558
|
-
case o
|
559
|
-
when nil
|
560
|
-
NULL
|
561
|
-
when Integer
|
562
|
-
NUMBER_INT
|
563
|
-
when Float
|
564
|
-
NUMBER
|
565
|
-
when ByteBuffer
|
566
|
-
BINARY
|
567
|
-
when Code
|
568
|
-
CODE_W_SCOPE
|
569
|
-
when String
|
570
|
-
STRING
|
571
|
-
when Array
|
572
|
-
ARRAY
|
573
|
-
when Regexp
|
574
|
-
REGEX
|
575
|
-
when ObjectID
|
576
|
-
OID
|
577
|
-
when DBRef
|
578
|
-
REF
|
579
|
-
when true, false
|
580
|
-
BOOLEAN
|
581
|
-
when Time
|
582
|
-
DATE
|
583
|
-
when Hash
|
584
|
-
OBJECT
|
585
|
-
when Symbol
|
586
|
-
SYMBOL
|
587
|
-
when MaxKey
|
588
|
-
MAXKEY
|
589
|
-
when MinKey
|
590
|
-
MINKEY
|
591
|
-
when Numeric
|
592
|
-
raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
|
593
|
-
when Date, DateTime
|
594
|
-
raise InvalidDocument, "#{o.class} is not currently supported; " +
|
595
|
-
"use a UTC Time instance instead."
|
596
|
-
else
|
597
|
-
if defined?(ActiveSupport::TimeWithZone) && o.is_a?(ActiveSupport::TimeWithZone)
|
598
|
-
raise InvalidDocument, "ActiveSupport::TimeWithZone is not currently supported; " +
|
599
|
-
"use a UTC Time instance instead."
|
600
|
-
else
|
601
|
-
raise InvalidDocument, "Cannot serialize #{o.class} as a BSON type; it either isn't supported or won't translate to BSON."
|
602
|
-
end
|
603
|
-
end
|
604
|
-
end
|
605
|
-
|
606
|
-
end
|