bson 0.20
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.
- data/LICENSE.txt +202 -0
- data/Rakefile +158 -0
- data/bson.gemspec +23 -0
- data/lib/bson.rb +45 -0
- data/lib/bson/bson_c.rb +20 -0
- data/lib/bson/bson_ruby.rb +595 -0
- data/lib/bson/byte_buffer.rb +224 -0
- data/lib/bson/exceptions.rb +39 -0
- data/lib/bson/ordered_hash.rb +140 -0
- data/lib/bson/types/binary.rb +54 -0
- data/lib/bson/types/code.rb +36 -0
- data/lib/bson/types/dbref.rb +40 -0
- data/lib/bson/types/min_max_keys.rb +58 -0
- data/lib/bson/types/objectid.rb +180 -0
- data/test/mongo_bson/binary_test.rb +15 -0
- data/test/mongo_bson/bson_test.rb +471 -0
- data/test/mongo_bson/byte_buffer_test.rb +82 -0
- data/test/mongo_bson/objectid_test.rb +126 -0
- data/test/mongo_bson/ordered_hash_test.rb +172 -0
- metadata +74 -0
@@ -0,0 +1,224 @@
|
|
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
|
+
# A byte buffer.
|
18
|
+
module BSON
|
19
|
+
class ByteBuffer
|
20
|
+
|
21
|
+
# Commonly-used integers.
|
22
|
+
INT_LOOKUP = {
|
23
|
+
0 => [0, 0, 0, 0],
|
24
|
+
1 => [1, 0, 0, 0],
|
25
|
+
2 => [2, 0, 0, 0],
|
26
|
+
3 => [3, 0, 0, 0],
|
27
|
+
4 => [4, 0, 0, 0],
|
28
|
+
2001 => [209, 7, 0, 0],
|
29
|
+
2002 => [210, 7, 0, 0],
|
30
|
+
2004 => [212, 7, 0, 0],
|
31
|
+
2005 => [213, 7, 0, 0],
|
32
|
+
2006 => [214, 7, 0, 0]
|
33
|
+
}
|
34
|
+
|
35
|
+
attr_reader :order
|
36
|
+
|
37
|
+
def initialize(initial_data=[])
|
38
|
+
@buf = initial_data
|
39
|
+
@cursor = @buf.length
|
40
|
+
@order = :little_endian
|
41
|
+
@int_pack_order = 'V'
|
42
|
+
@double_pack_order = 'E'
|
43
|
+
end
|
44
|
+
|
45
|
+
if RUBY_VERSION >= '1.9'
|
46
|
+
def self.to_utf8(str)
|
47
|
+
str.encode("utf-8")
|
48
|
+
end
|
49
|
+
else
|
50
|
+
def self.to_utf8(str)
|
51
|
+
begin
|
52
|
+
str.unpack("U*")
|
53
|
+
rescue => ex
|
54
|
+
raise InvalidStringEncoding, "String not valid utf-8: #{str}"
|
55
|
+
end
|
56
|
+
str
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.serialize_cstr(buf, val)
|
61
|
+
buf.put_array(to_utf8(val.to_s).unpack("C*") + [0])
|
62
|
+
end
|
63
|
+
|
64
|
+
# +endianness+ should be :little_endian or :big_endian. Default is :little_endian
|
65
|
+
def order=(endianness)
|
66
|
+
@order = endianness
|
67
|
+
@int_pack_order = endianness == :little_endian ? 'V' : 'N'
|
68
|
+
@double_pack_order = endianness == :little_endian ? 'E' : 'G'
|
69
|
+
end
|
70
|
+
|
71
|
+
def rewind
|
72
|
+
@cursor = 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def position
|
76
|
+
@cursor
|
77
|
+
end
|
78
|
+
|
79
|
+
def position=(val)
|
80
|
+
@cursor = val
|
81
|
+
end
|
82
|
+
|
83
|
+
def clear
|
84
|
+
@buf = []
|
85
|
+
rewind
|
86
|
+
end
|
87
|
+
|
88
|
+
def size
|
89
|
+
@buf.size
|
90
|
+
end
|
91
|
+
alias_method :length, :size
|
92
|
+
|
93
|
+
# Appends a second ByteBuffer object, +buffer+, to the current buffer.
|
94
|
+
def append!(buffer)
|
95
|
+
@buf = @buf + buffer.to_a
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
# Prepends a second ByteBuffer object, +buffer+, to the current buffer.
|
100
|
+
def prepend!(buffer)
|
101
|
+
@buf = buffer.to_a + @buf
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def put(byte, offset=nil)
|
106
|
+
@cursor = offset if offset
|
107
|
+
@buf[@cursor] = byte
|
108
|
+
@cursor += 1
|
109
|
+
end
|
110
|
+
|
111
|
+
def put_array(array, offset=nil)
|
112
|
+
@cursor = offset if offset
|
113
|
+
@buf[@cursor, array.length] = array
|
114
|
+
@cursor += array.length
|
115
|
+
end
|
116
|
+
|
117
|
+
def put_int(i, offset=nil)
|
118
|
+
unless a = INT_LOOKUP[i]
|
119
|
+
a = []
|
120
|
+
[i].pack(@int_pack_order).each_byte { |b| a << b }
|
121
|
+
end
|
122
|
+
put_array(a, offset)
|
123
|
+
end
|
124
|
+
|
125
|
+
def put_long(i, offset=nil)
|
126
|
+
offset = @cursor unless offset
|
127
|
+
if @int_pack_order == 'N'
|
128
|
+
put_int(i >> 32, offset)
|
129
|
+
put_int(i & 0xffffffff, offset + 4)
|
130
|
+
else
|
131
|
+
put_int(i & 0xffffffff, offset)
|
132
|
+
put_int(i >> 32, offset + 4)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def put_double(d, offset=nil)
|
137
|
+
a = []
|
138
|
+
[d].pack(@double_pack_order).each_byte { |b| a << b }
|
139
|
+
put_array(a, offset)
|
140
|
+
end
|
141
|
+
|
142
|
+
# If +size+ == nil, returns one byte. Else returns array of bytes of length
|
143
|
+
# # +size+.
|
144
|
+
def get(len=nil)
|
145
|
+
one_byte = len.nil?
|
146
|
+
len ||= 1
|
147
|
+
check_read_length(len)
|
148
|
+
start = @cursor
|
149
|
+
@cursor += len
|
150
|
+
if one_byte
|
151
|
+
@buf[start]
|
152
|
+
else
|
153
|
+
if @buf.respond_to? "unpack"
|
154
|
+
@buf[start, len].unpack("C*")
|
155
|
+
else
|
156
|
+
@buf[start, len]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def get_int
|
162
|
+
check_read_length(4)
|
163
|
+
vals = ""
|
164
|
+
(@cursor..@cursor+3).each { |i| vals << @buf[i].chr }
|
165
|
+
@cursor += 4
|
166
|
+
vals.unpack(@int_pack_order)[0]
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_long
|
170
|
+
i1 = get_int
|
171
|
+
i2 = get_int
|
172
|
+
if @int_pack_order == 'N'
|
173
|
+
(i1 << 32) + i2
|
174
|
+
else
|
175
|
+
(i2 << 32) + i1
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_double
|
180
|
+
check_read_length(8)
|
181
|
+
vals = ""
|
182
|
+
(@cursor..@cursor+7).each { |i| vals << @buf[i].chr }
|
183
|
+
@cursor += 8
|
184
|
+
vals.unpack(@double_pack_order)[0]
|
185
|
+
end
|
186
|
+
|
187
|
+
def more?
|
188
|
+
@cursor < @buf.size
|
189
|
+
end
|
190
|
+
|
191
|
+
def to_a
|
192
|
+
if @buf.respond_to? "unpack"
|
193
|
+
@buf.unpack("C*")
|
194
|
+
else
|
195
|
+
@buf
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def unpack(args)
|
200
|
+
to_a
|
201
|
+
end
|
202
|
+
|
203
|
+
def to_s
|
204
|
+
if @buf.respond_to? :fast_pack
|
205
|
+
@buf.fast_pack
|
206
|
+
elsif @buf.respond_to? "pack"
|
207
|
+
@buf.pack("C*")
|
208
|
+
else
|
209
|
+
@buf
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def dump
|
214
|
+
@buf.each_with_index { |c, i| $stderr.puts "#{'%04d' % i}: #{'%02x' % c} #{'%03o' % c} #{'%s' % c.chr} #{'%3d' % c}" }
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def check_read_length(len)
|
220
|
+
raise "attempt to read past end of buffer" if @cursor + len > @buf.length
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,39 @@
|
|
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 BSON
|
18
|
+
# Generic Mongo Ruby Driver exception class.
|
19
|
+
class MongoRubyError < StandardError; end
|
20
|
+
|
21
|
+
# Raised when MongoDB itself has returned an error.
|
22
|
+
class MongoDBError < RuntimeError; end
|
23
|
+
|
24
|
+
# This will replace MongoDBError.
|
25
|
+
class BSONError < MongoDBError; end
|
26
|
+
|
27
|
+
# Raised when given a string is not valid utf-8 (Ruby 1.8 only).
|
28
|
+
class InvalidStringEncoding < BSONError; end
|
29
|
+
|
30
|
+
# Raised when attempting to initialize an invalid ObjectID.
|
31
|
+
class InvalidObjectID < BSONError; end
|
32
|
+
|
33
|
+
# Raised when trying to insert a document that exceeds the 4MB limit or
|
34
|
+
# when the document contains objects that can't be serialized as BSON.
|
35
|
+
class InvalidDocument < BSONError; end
|
36
|
+
|
37
|
+
# Raised when an invalid name is used.
|
38
|
+
class InvalidKeyName < BSONError; end
|
39
|
+
end
|
@@ -0,0 +1,140 @@
|
|
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
|
+
# A hash in which the order of keys are preserved.
|
18
|
+
#
|
19
|
+
# Under Ruby 1.9 and greater, this class has no added methods because Ruby's
|
20
|
+
# Hash already keeps its keys ordered by order of insertion.
|
21
|
+
class OrderedHash < Hash
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
begin
|
25
|
+
!other.nil? &&
|
26
|
+
keys == other.keys &&
|
27
|
+
values == other.values
|
28
|
+
rescue
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# We only need the body of this class if the RUBY_VERSION is before 1.9
|
34
|
+
if RUBY_VERSION < '1.9'
|
35
|
+
attr_accessor :ordered_keys
|
36
|
+
|
37
|
+
def self.[] *args
|
38
|
+
oh = OrderedHash.new
|
39
|
+
if Hash === args[0]
|
40
|
+
oh.merge! args[0]
|
41
|
+
elsif (args.size % 2) != 0
|
42
|
+
raise ArgumentError, "odd number of elements for Hash"
|
43
|
+
else
|
44
|
+
0.step(args.size - 1, 2) do |key|
|
45
|
+
value = key + 1
|
46
|
+
oh[args[key]] = args[value]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
oh
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(*a, &b)
|
53
|
+
super
|
54
|
+
@ordered_keys = []
|
55
|
+
end
|
56
|
+
|
57
|
+
def keys
|
58
|
+
@ordered_keys || []
|
59
|
+
end
|
60
|
+
|
61
|
+
def []=(key, value)
|
62
|
+
@ordered_keys ||= []
|
63
|
+
@ordered_keys << key unless @ordered_keys.include?(key)
|
64
|
+
super(key, value)
|
65
|
+
end
|
66
|
+
|
67
|
+
def each
|
68
|
+
@ordered_keys ||= []
|
69
|
+
@ordered_keys.each { |k| yield k, self[k] }
|
70
|
+
self
|
71
|
+
end
|
72
|
+
alias :each_pair :each
|
73
|
+
|
74
|
+
def to_a
|
75
|
+
@ordered_keys ||= []
|
76
|
+
@ordered_keys.map { |k| [k, self[k]] }
|
77
|
+
end
|
78
|
+
|
79
|
+
def values
|
80
|
+
collect { |k, v| v }
|
81
|
+
end
|
82
|
+
|
83
|
+
def merge(other)
|
84
|
+
oh = self.dup
|
85
|
+
oh.merge!(other)
|
86
|
+
oh
|
87
|
+
end
|
88
|
+
|
89
|
+
def merge!(other)
|
90
|
+
@ordered_keys ||= []
|
91
|
+
@ordered_keys += other.keys # unordered if not an OrderedHash
|
92
|
+
@ordered_keys.uniq!
|
93
|
+
super(other)
|
94
|
+
end
|
95
|
+
|
96
|
+
alias :update :merge!
|
97
|
+
|
98
|
+
def inspect
|
99
|
+
str = '{'
|
100
|
+
str << (@ordered_keys || []).collect { |k| "\"#{k}\"=>#{self.[](k).inspect}" }.join(", ")
|
101
|
+
str << '}'
|
102
|
+
end
|
103
|
+
|
104
|
+
def delete(key, &block)
|
105
|
+
@ordered_keys.delete(key) if @ordered_keys
|
106
|
+
super
|
107
|
+
end
|
108
|
+
|
109
|
+
def delete_if(&block)
|
110
|
+
self.each { |k,v|
|
111
|
+
if yield k, v
|
112
|
+
delete(k)
|
113
|
+
end
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
def clear
|
118
|
+
super
|
119
|
+
@ordered_keys = []
|
120
|
+
end
|
121
|
+
|
122
|
+
def hash
|
123
|
+
code = 17
|
124
|
+
each_pair do |key, value|
|
125
|
+
code = 37 * code + key.hash
|
126
|
+
code = 37 * code + value.hash
|
127
|
+
end
|
128
|
+
code & 0x7fffffff
|
129
|
+
end
|
130
|
+
|
131
|
+
def eql?(o)
|
132
|
+
if o.instance_of? OrderedHash
|
133
|
+
self.hash == o.hash
|
134
|
+
else
|
135
|
+
false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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 'bson/byte_buffer'
|
18
|
+
|
19
|
+
module BSON
|
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, String] data to story as BSON binary. If a string is given, the value will be
|
38
|
+
# concerted to an array of bytes using String#unpack("c*").
|
39
|
+
# @param [Fixnum] one of four values specifying a BSON binary subtype. Possible values are
|
40
|
+
# SUBTYPE_BYTES, SUBTYPE_UUID, SUBTYPE_MD5, and SUBTYPE_USER_DEFINED.
|
41
|
+
#
|
42
|
+
# @see http://www.mongodb.org/display/DOCS/BSON#BSON-noteondatabinary BSON binary subtypes.
|
43
|
+
def initialize(data=[], subtype=SUBTYPE_BYTES)
|
44
|
+
data = data.unpack("c*") if data.is_a?(String)
|
45
|
+
super(data)
|
46
|
+
@subtype = subtype
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
"<BSON::Binary:#{object_id}>"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|