bson 1.12.5 → 2.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bson might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +0 -0
- data/CONTRIBUTING.md +0 -0
- data/LICENSE.md +13 -0
- data/README.md +185 -0
- data/Rakefile +83 -0
- data/ext/bson/extconf.rb +3 -0
- data/lib/bson.rb +105 -94
- data/lib/bson/array.rb +74 -0
- data/lib/bson/binary.rb +125 -0
- data/lib/bson/boolean.rb +35 -0
- data/lib/bson/code.rb +96 -0
- data/lib/bson/code_with_scope.rb +105 -0
- data/lib/bson/document.rb +536 -0
- data/lib/bson/encodable.rb +73 -0
- data/lib/bson/false_class.rb +48 -0
- data/lib/bson/float.rb +69 -0
- data/lib/bson/hash.rb +71 -0
- data/lib/bson/int32.rb +46 -0
- data/lib/bson/int64.rb +46 -0
- data/lib/bson/integer.rb +172 -0
- data/lib/bson/json.rb +26 -0
- data/lib/bson/max_key.rb +57 -0
- data/lib/bson/min_key.rb +57 -0
- data/lib/bson/nil_class.rb +57 -0
- data/lib/bson/object_id.rb +309 -0
- data/lib/bson/regexp.rb +111 -0
- data/lib/bson/registry.rb +57 -0
- data/lib/bson/specialized.rb +61 -0
- data/lib/bson/string.rb +173 -0
- data/lib/bson/symbol.rb +74 -0
- data/lib/bson/time.rb +59 -0
- data/lib/bson/timestamp.rb +100 -0
- data/lib/bson/true_class.rb +48 -0
- data/lib/bson/undefined.rb +61 -0
- data/lib/bson/version.rb +4 -0
- metadata +52 -40
- data/LICENSE +0 -190
- data/VERSION +0 -1
- data/bin/b2json +0 -63
- data/bin/j2bson +0 -64
- data/bson.gemspec +0 -34
- data/lib/bson/bson_c.rb +0 -37
- data/lib/bson/bson_java.rb +0 -49
- data/lib/bson/bson_ruby.rb +0 -645
- data/lib/bson/byte_buffer.rb +0 -241
- data/lib/bson/exceptions.rb +0 -37
- data/lib/bson/grow.rb +0 -173
- data/lib/bson/ordered_hash.rb +0 -197
- data/lib/bson/support/hash_with_indifferent_access.rb +0 -174
- data/lib/bson/types/binary.rb +0 -52
- data/lib/bson/types/code.rb +0 -55
- data/lib/bson/types/dbref.rb +0 -40
- data/lib/bson/types/min_max_keys.rb +0 -56
- data/lib/bson/types/object_id.rb +0 -226
- data/lib/bson/types/regex.rb +0 -116
- data/lib/bson/types/timestamp.rb +0 -72
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.12.5
|
data/bin/b2json
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# encoding: UTF-8
|
3
|
-
|
4
|
-
# Copyright (C) 2009-2013 MongoDB, Inc.
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
|
18
|
-
require 'rubygems'
|
19
|
-
require 'bson'
|
20
|
-
|
21
|
-
# Note that this will not properly round-trip in all cases
|
22
|
-
# from the output generated by j2bson.
|
23
|
-
begin
|
24
|
-
require 'yajl'
|
25
|
-
rescue LoadError
|
26
|
-
puts "This script requires yajl. Please install as follows:"
|
27
|
-
puts " gem install yajl-ruby"
|
28
|
-
Process.exit
|
29
|
-
end
|
30
|
-
|
31
|
-
# Convert all documents in an IO into JSON.
|
32
|
-
def print_b2json(io)
|
33
|
-
while not io.eof? do
|
34
|
-
bsonobj = BSON.read_bson_document(io)
|
35
|
-
Yajl::Encoder.encode(bsonobj, STDOUT)
|
36
|
-
STDOUT << "\n"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# print usage
|
41
|
-
def usage()
|
42
|
-
STDERR << <<END_OF_USAGE
|
43
|
-
usage: b2json [-h] [file1 [file2]]
|
44
|
-
|
45
|
-
Converts a BSON file to JSON on STDOUT.
|
46
|
-
You can pass multiple filenames.
|
47
|
-
If no filenames are passed, then STDIN is consumed.
|
48
|
-
|
49
|
-
END_OF_USAGE
|
50
|
-
exit
|
51
|
-
end
|
52
|
-
|
53
|
-
# no arg, use STDIN
|
54
|
-
# -h, print usage and exit
|
55
|
-
# otherwise loop of filenames
|
56
|
-
if ARGV.empty? then
|
57
|
-
print_b2json(STDIN)
|
58
|
-
exit
|
59
|
-
elsif ARGV[0] == "-h" then
|
60
|
-
usage()
|
61
|
-
else
|
62
|
-
ARGV.each { |fname| print_b2json(File.new(fname)) }
|
63
|
-
end
|
data/bin/j2bson
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# encoding: UTF-8
|
3
|
-
|
4
|
-
# Copyright (C) 2009-2013 MongoDB, Inc.
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
|
18
|
-
require 'rubygems'
|
19
|
-
require 'bson'
|
20
|
-
|
21
|
-
# Note that, at the moment, this will not properly round-trip
|
22
|
-
# in all cases from the output generated by b2json.
|
23
|
-
begin
|
24
|
-
require 'json/pure' # broken with 'json/ext'
|
25
|
-
rescue LoadError
|
26
|
-
puts "This script requires json/pure. Please install one of the following:"
|
27
|
-
puts " gem install json_pure"
|
28
|
-
puts " gem install json"
|
29
|
-
Process.exit
|
30
|
-
end
|
31
|
-
|
32
|
-
# Convert all JSON objects in an IO into BSON.
|
33
|
-
def print_j2bson(io)
|
34
|
-
io.each_line do |line|
|
35
|
-
jsonobj = JSON.parse(line, { :object_class => BSON::OrderedHash } )
|
36
|
-
bsonobj = BSON.serialize(jsonobj)
|
37
|
-
STDOUT << bsonobj.to_s
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# print usage
|
42
|
-
def usage()
|
43
|
-
STDERR << <<END_OF_USAGE
|
44
|
-
usage: j2bson [-h] [file1 [file2]]
|
45
|
-
|
46
|
-
Converts a JSON file to BSON on STDOUT.
|
47
|
-
You can pass multiple filenames.
|
48
|
-
If no filenames are passed, then STDIN is consumed.
|
49
|
-
|
50
|
-
END_OF_USAGE
|
51
|
-
exit
|
52
|
-
end
|
53
|
-
|
54
|
-
# no arg, use STDIN
|
55
|
-
# -h, print usage and exit
|
56
|
-
# otherwise loop of filenames
|
57
|
-
if ARGV.empty? then
|
58
|
-
print_j2bson(STDIN)
|
59
|
-
exit
|
60
|
-
elsif ARGV[0] == "-h" then
|
61
|
-
usage()
|
62
|
-
else
|
63
|
-
ARGV.each { |fname| print_j2bson(File.new(fname)) }
|
64
|
-
end
|
data/bson.gemspec
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = 'bson'
|
3
|
-
|
4
|
-
s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION'))
|
5
|
-
s.authors = ['Emily Stolfo', 'Durran Jordan', 'Gary Murakami', 'Tyler Brock', 'Brandon Black']
|
6
|
-
s.email = 'mongodb-dev@googlegroups.com'
|
7
|
-
s.homepage = 'http://www.mongodb.org'
|
8
|
-
s.summary = 'Ruby implementation of BSON'
|
9
|
-
s.description = 'A Ruby BSON implementation for MongoDB. For more information about Mongo, see http://www.mongodb.org. For more information on BSON, see http://www.bsonspec.org.'
|
10
|
-
s.rubyforge_project = 'bson'
|
11
|
-
s.license = 'Apache License Version 2.0'
|
12
|
-
|
13
|
-
if File.exists?('gem-private_key.pem')
|
14
|
-
s.signing_key = 'gem-private_key.pem'
|
15
|
-
s.cert_chain = ['gem-public_cert.pem']
|
16
|
-
else
|
17
|
-
warn 'Warning: No private key present, creating unsigned gem.'
|
18
|
-
end
|
19
|
-
|
20
|
-
s.files = ['bson.gemspec', 'LICENSE', 'VERSION']
|
21
|
-
s.files += ['bin/b2json', 'bin/j2bson', 'lib/bson.rb']
|
22
|
-
s.files += Dir['lib/bson/**/*.rb']
|
23
|
-
|
24
|
-
if RUBY_PLATFORM =~ /java/
|
25
|
-
s.platform = 'java'
|
26
|
-
s.files += ['ext/jbson/target/jbson.jar', 'ext/jbson/lib/java-bson.jar']
|
27
|
-
else
|
28
|
-
s.platform = Gem::Platform::RUBY
|
29
|
-
end
|
30
|
-
|
31
|
-
s.executables = ['b2json', 'j2bson']
|
32
|
-
s.require_paths = ['lib']
|
33
|
-
s.has_rdoc = 'yard'
|
34
|
-
end
|
data/lib/bson/bson_c.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
|
15
|
-
# A thin wrapper for the BSON C-Extension
|
16
|
-
module BSON
|
17
|
-
class BSON_C
|
18
|
-
|
19
|
-
def self.serialize(obj, check_keys=false, move_id=false, max_bson_size=DEFAULT_MAX_BSON_SIZE)
|
20
|
-
ByteBuffer.new(CBson.serialize(obj, check_keys, move_id, max_bson_size))
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.deserialize(buf=nil, opts={})
|
24
|
-
CBson.deserialize(ByteBuffer.new(buf).to_s, opts)
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.max_bson_size
|
28
|
-
warn "BSON::BSON_CODER.max_bson_size is deprecated and will be removed in v2.0."
|
29
|
-
CBson.max_bson_size
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.update_max_bson_size(connection)
|
33
|
-
warn "BSON::BSON_CODER.update_max_bson_size is deprecated and now a no-op. It will be removed in v2.0."
|
34
|
-
CBson.update_max_bson_size(connection)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/bson/bson_java.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
|
15
|
-
require 'jruby'
|
16
|
-
|
17
|
-
include Java
|
18
|
-
|
19
|
-
jar_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../ext/jbson'))
|
20
|
-
require File.join(jar_dir, 'lib/java-bson.jar')
|
21
|
-
require File.join(jar_dir, 'target/jbson.jar')
|
22
|
-
|
23
|
-
module BSON
|
24
|
-
class BSON_JAVA
|
25
|
-
def self.serialize(obj, check_keys=false, move_id=false, max_bson_size=DEFAULT_MAX_BSON_SIZE)
|
26
|
-
raise InvalidDocument, "BSON_JAVA.serialize takes a Hash" unless obj.is_a?(Hash)
|
27
|
-
enc = Java::OrgJbson::RubyBSONEncoder.new(JRuby.runtime, check_keys, move_id, max_bson_size)
|
28
|
-
ByteBuffer.new(enc.encode(obj))
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.deserialize(buf, opts={})
|
32
|
-
dec = Java::OrgJbson::RubyBSONDecoder.new
|
33
|
-
callback = Java::OrgJbson::RubyBSONCallback.new(JRuby.runtime)
|
34
|
-
callback.set_opts(opts);
|
35
|
-
dec.decode(buf.to_s.to_java_bytes, callback)
|
36
|
-
callback.get
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.max_bson_size
|
40
|
-
warn "BSON::BSON_CODER.max_bson_size is deprecated and will be removed in v2.0."
|
41
|
-
Java::OrgJbson::RubyBSONEncoder.max_bson_size(self)
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.update_max_bson_size(connection)
|
45
|
-
warn "BSON::BSON_CODER.update_max_bson_size is deprecated and now a no-op. It will be removed in v2.0."
|
46
|
-
Java::OrgJbson::RubyBSONEncoder.update_max_bson_size(self, connection)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/lib/bson/bson_ruby.rb
DELETED
@@ -1,645 +0,0 @@
|
|
1
|
-
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
|
15
|
-
module BSON
|
16
|
-
NULL_BYTE = "\x00"
|
17
|
-
|
18
|
-
# A BSON seralizer/deserializer in pure Ruby.
|
19
|
-
class BSON_RUBY
|
20
|
-
@@max_bson_size = DEFAULT_MAX_BSON_SIZE
|
21
|
-
|
22
|
-
MINKEY = -1
|
23
|
-
EOO = 0
|
24
|
-
NUMBER = 1
|
25
|
-
STRING = 2
|
26
|
-
OBJECT = 3
|
27
|
-
ARRAY = 4
|
28
|
-
BINARY = 5
|
29
|
-
UNDEFINED = 6
|
30
|
-
OID = 7
|
31
|
-
BOOLEAN = 8
|
32
|
-
DATE = 9
|
33
|
-
NULL = 10
|
34
|
-
REGEX = 11
|
35
|
-
REF = 12
|
36
|
-
CODE = 13
|
37
|
-
SYMBOL = 14
|
38
|
-
CODE_W_SCOPE = 15
|
39
|
-
NUMBER_INT = 16
|
40
|
-
TIMESTAMP = 17
|
41
|
-
NUMBER_LONG = 18
|
42
|
-
MAXKEY = 127
|
43
|
-
|
44
|
-
INT32_MIN = -(1 << 31) + 1
|
45
|
-
INT32_MAX = (1 << 31) - 1
|
46
|
-
INT64_MIN = -2**64 / 2
|
47
|
-
INT64_MAX = 2**64 / 2 - 1
|
48
|
-
|
49
|
-
def initialize(max_bson_size=DEFAULT_MAX_BSON_SIZE)
|
50
|
-
@buf = ByteBuffer.new('', max_bson_size)
|
51
|
-
@encoder = BSON_RUBY
|
52
|
-
end
|
53
|
-
|
54
|
-
if RUBY_VERSION >= '1.9'
|
55
|
-
UTF8_ENCODING = Encoding.find('utf-8')
|
56
|
-
BINARY_ENCODING = Encoding.find('binary')
|
57
|
-
|
58
|
-
def self.to_utf8_binary(str)
|
59
|
-
begin
|
60
|
-
str.unpack("U*")
|
61
|
-
rescue
|
62
|
-
raise InvalidStringEncoding, "String not valid utf-8: #{str.inspect}"
|
63
|
-
end
|
64
|
-
str.dup.force_encoding(BINARY_ENCODING)
|
65
|
-
end
|
66
|
-
else
|
67
|
-
def self.to_utf8_binary(str)
|
68
|
-
begin
|
69
|
-
str.unpack("U*")
|
70
|
-
rescue
|
71
|
-
raise InvalidStringEncoding, "String not valid utf-8: #{str.inspect}"
|
72
|
-
end
|
73
|
-
str
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.update_max_bson_size(connection)
|
78
|
-
warn "BSON::BSON_CODER.update_max_bson_size is deprecated and now a no-op. It will be removed in v2.0."
|
79
|
-
@@max_bson_size = connection.max_bson_size
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.max_bson_size
|
83
|
-
warn "BSON::BSON_CODER.max_bson_size is deprecated and will be removed in v2.0."
|
84
|
-
@@max_bson_size
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.serialize_cstr(buf, val)
|
88
|
-
buf.put_binary(to_utf8_binary(val.to_s))
|
89
|
-
buf.put_binary(NULL_BYTE)
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.serialize_key(buf, key)
|
93
|
-
raise InvalidDocument, "Key names / regex patterns must not contain the NULL byte" if key.include? "\x00"
|
94
|
-
self.serialize_cstr(buf, key)
|
95
|
-
end
|
96
|
-
|
97
|
-
def to_a
|
98
|
-
@buf.to_a
|
99
|
-
end
|
100
|
-
|
101
|
-
def to_s
|
102
|
-
@buf.to_s
|
103
|
-
end
|
104
|
-
|
105
|
-
# Serializes an object.
|
106
|
-
# Implemented to ensure an API compatible with BSON extension.
|
107
|
-
def self.serialize(obj, check_keys=false, move_id=false, max_bson_size=DEFAULT_MAX_BSON_SIZE)
|
108
|
-
new(max_bson_size).serialize(obj, check_keys, move_id)
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.deserialize(buf=nil, opts={})
|
112
|
-
new.deserialize(buf, opts)
|
113
|
-
end
|
114
|
-
|
115
|
-
def serialize(obj, check_keys=false, move_id=false)
|
116
|
-
raise(InvalidDocument, "BSON.serialize takes a Hash but got a #{obj.class}") unless obj.is_a?(Hash)
|
117
|
-
raise "Document is null" unless obj
|
118
|
-
|
119
|
-
@buf.rewind
|
120
|
-
# put in a placeholder for the total size
|
121
|
-
@buf.put_int(0)
|
122
|
-
|
123
|
-
# Write key/value pairs. Always write _id first if it exists.
|
124
|
-
if move_id
|
125
|
-
if obj.has_key? '_id'
|
126
|
-
serialize_key_value('_id', obj['_id'], false)
|
127
|
-
elsif obj.has_key? :_id
|
128
|
-
serialize_key_value('_id', obj[:_id], false)
|
129
|
-
end
|
130
|
-
obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
|
131
|
-
else
|
132
|
-
if obj.has_key?('_id') && obj.has_key?(:_id)
|
133
|
-
obj['_id'] = obj.delete(:_id)
|
134
|
-
end
|
135
|
-
obj.each {|k, v| serialize_key_value(k, v, check_keys) }
|
136
|
-
end
|
137
|
-
|
138
|
-
serialize_eoo_element(@buf)
|
139
|
-
if @buf.size > @buf.max_size
|
140
|
-
raise InvalidDocument, "Document is too large (#{@buf.size}). " +
|
141
|
-
"This BSON document is limited to #{@buf.max_size} bytes."
|
142
|
-
end
|
143
|
-
@buf.put_int(@buf.size, 0)
|
144
|
-
@buf
|
145
|
-
end
|
146
|
-
|
147
|
-
# Returns the array stored in the buffer.
|
148
|
-
# Implemented to ensure an API compatible with BSON extension.
|
149
|
-
def unpack
|
150
|
-
@buf.to_a
|
151
|
-
end
|
152
|
-
|
153
|
-
def serialize_key_value(k, v, check_keys)
|
154
|
-
k = k.to_s
|
155
|
-
if check_keys
|
156
|
-
if k[0] == ?$
|
157
|
-
raise InvalidKeyName.new("key #{k} must not start with '$'")
|
158
|
-
end
|
159
|
-
if k.include? ?.
|
160
|
-
raise InvalidKeyName.new("key #{k} must not contain '.'")
|
161
|
-
end
|
162
|
-
end
|
163
|
-
type = bson_type(v)
|
164
|
-
case type
|
165
|
-
when STRING, SYMBOL
|
166
|
-
serialize_string_element(@buf, k, v, type)
|
167
|
-
when NUMBER, NUMBER_INT
|
168
|
-
serialize_number_element(@buf, k, v, type)
|
169
|
-
when OBJECT
|
170
|
-
serialize_object_element(@buf, k, v, check_keys)
|
171
|
-
when OID
|
172
|
-
serialize_oid_element(@buf, k, v)
|
173
|
-
when ARRAY
|
174
|
-
serialize_array_element(@buf, k, v, check_keys)
|
175
|
-
when REGEX
|
176
|
-
serialize_regex_element(@buf, k, v)
|
177
|
-
when BOOLEAN
|
178
|
-
serialize_boolean_element(@buf, k, v)
|
179
|
-
when DATE
|
180
|
-
serialize_date_element(@buf, k, v)
|
181
|
-
when NULL
|
182
|
-
serialize_null_element(@buf, k)
|
183
|
-
when REF
|
184
|
-
serialize_dbref_element(@buf, k, v)
|
185
|
-
when BINARY
|
186
|
-
serialize_binary_element(@buf, k, v)
|
187
|
-
when UNDEFINED
|
188
|
-
serialize_null_element(@buf, k)
|
189
|
-
when CODE_W_SCOPE
|
190
|
-
serialize_code_w_scope(@buf, k, v)
|
191
|
-
when MAXKEY
|
192
|
-
serialize_max_key_element(@buf, k)
|
193
|
-
when MINKEY
|
194
|
-
serialize_min_key_element(@buf, k)
|
195
|
-
when TIMESTAMP
|
196
|
-
serialize_timestamp_element(@buf, k, v)
|
197
|
-
else
|
198
|
-
raise "unhandled type #{type}"
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def deserialize(buf=nil, opts={})
|
203
|
-
# If buf is nil, use @buf, assumed to contain already-serialized BSON.
|
204
|
-
# This is only true during testing.
|
205
|
-
if buf.is_a? String
|
206
|
-
@buf = ByteBuffer.new(buf.unpack("C*")) if buf
|
207
|
-
else
|
208
|
-
@buf = ByteBuffer.new(buf.to_a) if buf
|
209
|
-
end
|
210
|
-
@buf.rewind
|
211
|
-
@buf.get_int # eat message size
|
212
|
-
doc = BSON::OrderedHash.new
|
213
|
-
while @buf.more?
|
214
|
-
type = @buf.get
|
215
|
-
case type
|
216
|
-
when STRING, CODE
|
217
|
-
key = deserialize_cstr(@buf)
|
218
|
-
doc[key] = deserialize_string_data(@buf)
|
219
|
-
when SYMBOL
|
220
|
-
key = deserialize_cstr(@buf)
|
221
|
-
doc[key] = deserialize_string_data(@buf).intern
|
222
|
-
when NUMBER
|
223
|
-
key = deserialize_cstr(@buf)
|
224
|
-
doc[key] = deserialize_number_data(@buf)
|
225
|
-
when NUMBER_INT
|
226
|
-
key = deserialize_cstr(@buf)
|
227
|
-
doc[key] = deserialize_number_int_data(@buf)
|
228
|
-
when NUMBER_LONG
|
229
|
-
key = deserialize_cstr(@buf)
|
230
|
-
doc[key] = deserialize_number_long_data(@buf)
|
231
|
-
when OID
|
232
|
-
key = deserialize_cstr(@buf)
|
233
|
-
doc[key] = deserialize_oid_data(@buf)
|
234
|
-
when ARRAY
|
235
|
-
key = deserialize_cstr(@buf)
|
236
|
-
doc[key] = deserialize_array_data(@buf, opts)
|
237
|
-
when REGEX
|
238
|
-
key = deserialize_cstr(@buf)
|
239
|
-
doc[key] = deserialize_regex_data(@buf, opts)
|
240
|
-
when OBJECT
|
241
|
-
key = deserialize_cstr(@buf)
|
242
|
-
doc[key] = deserialize_object_data(@buf, opts)
|
243
|
-
when BOOLEAN
|
244
|
-
key = deserialize_cstr(@buf)
|
245
|
-
doc[key] = deserialize_boolean_data(@buf)
|
246
|
-
when DATE
|
247
|
-
key = deserialize_cstr(@buf)
|
248
|
-
doc[key] = deserialize_date_data(@buf)
|
249
|
-
when NULL
|
250
|
-
key = deserialize_cstr(@buf)
|
251
|
-
doc[key] = nil
|
252
|
-
when UNDEFINED
|
253
|
-
key = deserialize_cstr(@buf)
|
254
|
-
doc[key] = nil
|
255
|
-
when REF
|
256
|
-
key = deserialize_cstr(@buf)
|
257
|
-
doc[key] = deserialize_dbref_data(@buf)
|
258
|
-
when BINARY
|
259
|
-
key = deserialize_cstr(@buf)
|
260
|
-
doc[key] = deserialize_binary_data(@buf)
|
261
|
-
when CODE_W_SCOPE
|
262
|
-
key = deserialize_cstr(@buf)
|
263
|
-
doc[key] = deserialize_code_w_scope_data(@buf)
|
264
|
-
when TIMESTAMP
|
265
|
-
key = deserialize_cstr(@buf)
|
266
|
-
doc[key] = deserialize_timestamp_data(@buf)
|
267
|
-
when MAXKEY
|
268
|
-
key = deserialize_cstr(@buf)
|
269
|
-
doc[key] = MaxKey.new
|
270
|
-
when MINKEY, 255 # This is currently easier than unpack the type byte as an unsigned char.
|
271
|
-
key = deserialize_cstr(@buf)
|
272
|
-
doc[key] = MinKey.new
|
273
|
-
when EOO
|
274
|
-
break
|
275
|
-
else
|
276
|
-
raise "Unknown type #{type}, key = #{key}"
|
277
|
-
end
|
278
|
-
end
|
279
|
-
@buf.rewind
|
280
|
-
doc
|
281
|
-
end
|
282
|
-
|
283
|
-
# For debugging.
|
284
|
-
def hex_dump
|
285
|
-
str = ''
|
286
|
-
@buf.to_a.each_with_index { |b,i|
|
287
|
-
if (i % 8) == 0
|
288
|
-
str << "\n" if i > 0
|
289
|
-
str << '%4d: ' % i
|
290
|
-
else
|
291
|
-
str << ' '
|
292
|
-
end
|
293
|
-
str << '%02X' % b
|
294
|
-
}
|
295
|
-
str
|
296
|
-
end
|
297
|
-
|
298
|
-
def deserialize_date_data(buf)
|
299
|
-
milliseconds = buf.get_long
|
300
|
-
Time.at(milliseconds.to_f / 1000.0).utc # at() takes fractional seconds
|
301
|
-
end
|
302
|
-
|
303
|
-
def deserialize_boolean_data(buf)
|
304
|
-
buf.get == 1
|
305
|
-
end
|
306
|
-
|
307
|
-
def deserialize_number_data(buf)
|
308
|
-
buf.get_double
|
309
|
-
end
|
310
|
-
|
311
|
-
def deserialize_number_int_data(buf)
|
312
|
-
buf.get_int
|
313
|
-
end
|
314
|
-
|
315
|
-
def deserialize_number_long_data(buf)
|
316
|
-
buf.get_long
|
317
|
-
end
|
318
|
-
|
319
|
-
def deserialize_object_data(buf, opts={})
|
320
|
-
size = buf.get_int
|
321
|
-
buf.position -= 4
|
322
|
-
object = @encoder.new.deserialize(buf.get(size), opts)
|
323
|
-
if object.has_key? "$ref"
|
324
|
-
DBRef.new(object["$ref"], object["$id"])
|
325
|
-
else
|
326
|
-
object
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
def deserialize_array_data(buf, opts={})
|
331
|
-
h = deserialize_object_data(buf, opts)
|
332
|
-
a = []
|
333
|
-
h.each { |k, v| a[k.to_i] = v }
|
334
|
-
a
|
335
|
-
end
|
336
|
-
|
337
|
-
def deserialize_regex_data(buf, opts={})
|
338
|
-
compile = opts.key?(:compile_regex) ? opts[:compile_regex] : true
|
339
|
-
compile = true if compile.nil?
|
340
|
-
str = deserialize_cstr(buf)
|
341
|
-
options_str = deserialize_cstr(buf)
|
342
|
-
bson_regex = BSON::Regex.new(str, options_str)
|
343
|
-
compile ? bson_regex.try_compile : bson_regex
|
344
|
-
end
|
345
|
-
|
346
|
-
def deserialize_timestamp_data(buf)
|
347
|
-
increment = buf.get_int
|
348
|
-
seconds = buf.get_int
|
349
|
-
Timestamp.new(seconds, increment)
|
350
|
-
end
|
351
|
-
|
352
|
-
def encoded_str(str)
|
353
|
-
if RUBY_VERSION >= '1.9'
|
354
|
-
str.force_encoding("utf-8")
|
355
|
-
if Encoding.default_internal
|
356
|
-
str.encode!(Encoding.default_internal)
|
357
|
-
end
|
358
|
-
end
|
359
|
-
str
|
360
|
-
end
|
361
|
-
|
362
|
-
def deserialize_string_data(buf)
|
363
|
-
len = buf.get_int
|
364
|
-
bytes = buf.get(len)
|
365
|
-
str = bytes[0..-2]
|
366
|
-
if str.respond_to? "pack"
|
367
|
-
str = str.pack("C*")
|
368
|
-
end
|
369
|
-
encoded_str(str)
|
370
|
-
end
|
371
|
-
|
372
|
-
def deserialize_code_w_scope_data(buf, opts={})
|
373
|
-
buf.get_int
|
374
|
-
len = buf.get_int
|
375
|
-
code = buf.get(len)[0..-2]
|
376
|
-
if code.respond_to? "pack"
|
377
|
-
code = code.pack("C*")
|
378
|
-
end
|
379
|
-
|
380
|
-
scope_size = buf.get_int
|
381
|
-
buf.position -= 4
|
382
|
-
scope = @encoder.new.deserialize(buf.get(scope_size), opts)
|
383
|
-
|
384
|
-
Code.new(encoded_str(code), scope)
|
385
|
-
end
|
386
|
-
|
387
|
-
def deserialize_oid_data(buf)
|
388
|
-
ObjectId.new(buf.get(12))
|
389
|
-
end
|
390
|
-
|
391
|
-
def deserialize_dbref_data(buf)
|
392
|
-
ns = deserialize_string_data(buf)
|
393
|
-
oid = deserialize_oid_data(buf)
|
394
|
-
DBRef.new(ns, oid)
|
395
|
-
end
|
396
|
-
|
397
|
-
def deserialize_binary_data(buf)
|
398
|
-
len = buf.get_int
|
399
|
-
type = buf.get
|
400
|
-
len = buf.get_int if type == Binary::SUBTYPE_BYTES
|
401
|
-
Binary.new(buf.get(len), type)
|
402
|
-
end
|
403
|
-
|
404
|
-
def serialize_eoo_element(buf)
|
405
|
-
buf.put(EOO)
|
406
|
-
end
|
407
|
-
|
408
|
-
def serialize_null_element(buf, key)
|
409
|
-
buf.put(NULL)
|
410
|
-
self.class.serialize_key(buf, key)
|
411
|
-
end
|
412
|
-
|
413
|
-
def serialize_dbref_element(buf, key, val) # this does NOT use the BSON "\x0C" DBPointer type
|
414
|
-
oh = BSON::OrderedHash.new
|
415
|
-
oh['$ref'] = val.namespace
|
416
|
-
oh['$id'] = val.object_id
|
417
|
-
serialize_object_element(buf, key, oh, false)
|
418
|
-
end
|
419
|
-
|
420
|
-
def serialize_binary_element(buf, key, val)
|
421
|
-
buf.put(BINARY)
|
422
|
-
self.class.serialize_key(buf, key)
|
423
|
-
|
424
|
-
bytes = val.to_a
|
425
|
-
num_bytes = bytes.length
|
426
|
-
subtype = val.respond_to?(:subtype) ? val.subtype : Binary::SUBTYPE_BYTES
|
427
|
-
if subtype == Binary::SUBTYPE_BYTES
|
428
|
-
buf.put_int(num_bytes + 4)
|
429
|
-
buf.put(subtype)
|
430
|
-
buf.put_int(num_bytes)
|
431
|
-
buf.put_array(bytes)
|
432
|
-
else
|
433
|
-
buf.put_int(num_bytes)
|
434
|
-
buf.put(subtype)
|
435
|
-
buf.put_array(bytes)
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
|
-
def serialize_boolean_element(buf, key, val)
|
440
|
-
buf.put(BOOLEAN)
|
441
|
-
self.class.serialize_key(buf, key)
|
442
|
-
buf.put(val ? 1 : 0)
|
443
|
-
end
|
444
|
-
|
445
|
-
def serialize_date_element(buf, key, val)
|
446
|
-
buf.put(DATE)
|
447
|
-
self.class.serialize_key(buf, key)
|
448
|
-
millisecs = (val.to_f * 1000).to_i
|
449
|
-
buf.put_long(millisecs)
|
450
|
-
end
|
451
|
-
|
452
|
-
def serialize_number_element(buf, key, val, type)
|
453
|
-
if type == NUMBER
|
454
|
-
buf.put(type)
|
455
|
-
self.class.serialize_key(buf, key)
|
456
|
-
buf.put_double(val)
|
457
|
-
else
|
458
|
-
if val > INT64_MAX or val < INT64_MIN
|
459
|
-
raise RangeError.new("MongoDB can only handle 8-byte ints")
|
460
|
-
end
|
461
|
-
if val > INT32_MAX or val < INT32_MIN
|
462
|
-
buf.put(NUMBER_LONG)
|
463
|
-
self.class.serialize_key(buf, key)
|
464
|
-
buf.put_long(val)
|
465
|
-
else
|
466
|
-
buf.put(type)
|
467
|
-
self.class.serialize_key(buf, key)
|
468
|
-
buf.put_int(val)
|
469
|
-
end
|
470
|
-
end
|
471
|
-
end
|
472
|
-
|
473
|
-
def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
|
474
|
-
buf.put(opcode)
|
475
|
-
self.class.serialize_key(buf, key)
|
476
|
-
buf.put_array(@encoder.new.serialize(val, check_keys).to_a)
|
477
|
-
end
|
478
|
-
|
479
|
-
def serialize_array_element(buf, key, val, check_keys)
|
480
|
-
# Turn array into hash with integer indices as keys
|
481
|
-
h = BSON::OrderedHash.new
|
482
|
-
i = 0
|
483
|
-
val.each { |v| h[i] = v; i += 1 }
|
484
|
-
serialize_object_element(buf, key, h, check_keys, ARRAY)
|
485
|
-
end
|
486
|
-
|
487
|
-
def serialize_regex_element(buf, key, val)
|
488
|
-
buf.put(REGEX)
|
489
|
-
self.class.serialize_key(buf, key)
|
490
|
-
|
491
|
-
str = val.source
|
492
|
-
# We use serialize_key here since regex patterns aren't prefixed with
|
493
|
-
# length (can't contain the NULL byte).
|
494
|
-
self.class.serialize_key(buf, str)
|
495
|
-
|
496
|
-
options = val.options
|
497
|
-
options_str = ''
|
498
|
-
|
499
|
-
if val.is_a?(BSON::Regex)
|
500
|
-
options_str << 'i' if ((options & BSON::Regex::IGNORECASE) != 0)
|
501
|
-
options_str << 'l' if ((options & BSON::Regex::LOCALE_DEPENDENT) != 0)
|
502
|
-
options_str << 'm' if ((options & BSON::Regex::MULTILINE) != 0)
|
503
|
-
options_str << 's' if ((options & BSON::Regex::DOTALL) != 0)
|
504
|
-
options_str << 'u' if ((options & BSON::Regex::UNICODE) != 0)
|
505
|
-
options_str << 'x' if ((options & BSON::Regex::EXTENDED) != 0)
|
506
|
-
else
|
507
|
-
options_str << 'm' # Ruby regular expressions always use multiline mode
|
508
|
-
options_str << 'i' if ((options & Regexp::IGNORECASE) != 0)
|
509
|
-
# dotall on the server is multiline in Ruby
|
510
|
-
options_str << 's' if ((options & Regexp::MULTILINE) != 0)
|
511
|
-
options_str << 'x' if ((options & Regexp::EXTENDED) != 0)
|
512
|
-
end
|
513
|
-
|
514
|
-
options_str << val.extra_options_str if val.respond_to?(:extra_options_str)
|
515
|
-
# Must store option chars in alphabetical order
|
516
|
-
self.class.serialize_cstr(buf, options_str.split(//).sort.uniq.join)
|
517
|
-
end
|
518
|
-
|
519
|
-
def serialize_max_key_element(buf, key)
|
520
|
-
buf.put(MAXKEY)
|
521
|
-
self.class.serialize_key(buf, key)
|
522
|
-
end
|
523
|
-
|
524
|
-
def serialize_min_key_element(buf, key)
|
525
|
-
buf.put(MINKEY)
|
526
|
-
self.class.serialize_key(buf, key)
|
527
|
-
end
|
528
|
-
|
529
|
-
def serialize_timestamp_element(buf, key, val)
|
530
|
-
buf.put(TIMESTAMP)
|
531
|
-
self.class.serialize_key(buf, key)
|
532
|
-
|
533
|
-
buf.put_int(val.increment)
|
534
|
-
buf.put_int(val.seconds)
|
535
|
-
end
|
536
|
-
|
537
|
-
def serialize_oid_element(buf, key, val)
|
538
|
-
buf.put(OID)
|
539
|
-
self.class.serialize_key(buf, key)
|
540
|
-
|
541
|
-
buf.put_array(val.to_a)
|
542
|
-
end
|
543
|
-
|
544
|
-
def serialize_string_element(buf, key, val, type)
|
545
|
-
buf.put(type)
|
546
|
-
self.class.serialize_key(buf, key)
|
547
|
-
|
548
|
-
# Make a hole for the length
|
549
|
-
len_pos = buf.position
|
550
|
-
buf.put_int(0)
|
551
|
-
|
552
|
-
# Save the string
|
553
|
-
start_pos = buf.position
|
554
|
-
self.class.serialize_cstr(buf, val)
|
555
|
-
end_pos = buf.position
|
556
|
-
|
557
|
-
# Put the string size in front
|
558
|
-
buf.put_int(end_pos - start_pos, len_pos)
|
559
|
-
|
560
|
-
# Go back to where we were
|
561
|
-
buf.position = end_pos
|
562
|
-
end
|
563
|
-
|
564
|
-
def serialize_code_w_scope(buf, key, val)
|
565
|
-
buf.put(CODE_W_SCOPE)
|
566
|
-
self.class.serialize_key(buf, key)
|
567
|
-
|
568
|
-
# Make a hole for the length
|
569
|
-
len_pos = buf.position
|
570
|
-
buf.put_int(0)
|
571
|
-
|
572
|
-
buf.put_int(val.code.length + 1)
|
573
|
-
self.class.serialize_cstr(buf, val.code)
|
574
|
-
buf.put_array(@encoder.new.serialize(val.scope).to_a)
|
575
|
-
|
576
|
-
end_pos = buf.position
|
577
|
-
buf.put_int(end_pos - len_pos, len_pos)
|
578
|
-
buf.position = end_pos
|
579
|
-
end
|
580
|
-
|
581
|
-
def deserialize_cstr(buf)
|
582
|
-
chars = ""
|
583
|
-
while true
|
584
|
-
b = buf.get
|
585
|
-
break if b == 0
|
586
|
-
chars << b.chr
|
587
|
-
end
|
588
|
-
encoded_str(chars)
|
589
|
-
end
|
590
|
-
|
591
|
-
def bson_type(o)
|
592
|
-
case o
|
593
|
-
when nil
|
594
|
-
NULL
|
595
|
-
when Integer
|
596
|
-
NUMBER_INT
|
597
|
-
when Float
|
598
|
-
NUMBER
|
599
|
-
when ByteBuffer
|
600
|
-
BINARY
|
601
|
-
when Code
|
602
|
-
CODE_W_SCOPE
|
603
|
-
when String
|
604
|
-
STRING
|
605
|
-
when Array
|
606
|
-
ARRAY
|
607
|
-
when Regexp, BSON::Regex
|
608
|
-
REGEX
|
609
|
-
when ObjectId
|
610
|
-
OID
|
611
|
-
when DBRef
|
612
|
-
REF
|
613
|
-
when true, false
|
614
|
-
BOOLEAN
|
615
|
-
when Time
|
616
|
-
DATE
|
617
|
-
when Hash
|
618
|
-
OBJECT
|
619
|
-
when Symbol
|
620
|
-
SYMBOL
|
621
|
-
when MaxKey
|
622
|
-
MAXKEY
|
623
|
-
when MinKey
|
624
|
-
MINKEY
|
625
|
-
when Timestamp
|
626
|
-
TIMESTAMP
|
627
|
-
when Numeric
|
628
|
-
raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
|
629
|
-
when Date, DateTime
|
630
|
-
raise InvalidDocument, "#{o.class} is not currently supported; " +
|
631
|
-
"use a UTC Time instance instead."
|
632
|
-
else
|
633
|
-
if defined?(ActiveSupport::Multibyte::Chars) && o.is_a?(ActiveSupport::Multibyte::Chars)
|
634
|
-
STRING
|
635
|
-
elsif defined?(ActiveSupport::TimeWithZone) && o.is_a?(ActiveSupport::TimeWithZone)
|
636
|
-
raise InvalidDocument, "ActiveSupport::TimeWithZone is not currently supported; " +
|
637
|
-
"use a UTC Time instance instead."
|
638
|
-
else
|
639
|
-
raise InvalidDocument, "Cannot serialize #{o.class} as a BSON type; it either isn't supported or won't translate to BSON."
|
640
|
-
end
|
641
|
-
end
|
642
|
-
end
|
643
|
-
|
644
|
-
end
|
645
|
-
end
|