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.
Files changed (65) hide show
  1. data/README.rdoc +7 -3
  2. data/Rakefile +16 -6
  3. data/bin/bson_benchmark.rb +2 -2
  4. data/examples/gridfs.rb +3 -2
  5. data/lib/mongo.rb +3 -26
  6. data/lib/mongo/collection.rb +69 -50
  7. data/lib/mongo/connection.rb +16 -41
  8. data/lib/mongo/cursor.rb +22 -32
  9. data/lib/mongo/db.rb +13 -5
  10. data/lib/mongo/exceptions.rb +11 -15
  11. data/lib/mongo/gridfs/grid.rb +14 -3
  12. data/lib/mongo/gridfs/grid_file_system.rb +28 -5
  13. data/lib/mongo/gridfs/grid_io.rb +42 -24
  14. data/lib/mongo/util/support.rb +13 -2
  15. data/mongo-ruby-driver.gemspec +3 -1
  16. data/test/collection_test.rb +62 -9
  17. data/test/connection_test.rb +21 -32
  18. data/test/conversions_test.rb +1 -1
  19. data/test/cursor_test.rb +2 -2
  20. data/test/db_api_test.rb +28 -27
  21. data/test/db_connection_test.rb +1 -1
  22. data/test/db_test.rb +23 -13
  23. data/test/grid_file_system_test.rb +30 -4
  24. data/test/grid_io_test.rb +14 -1
  25. data/test/grid_test.rb +59 -3
  26. data/test/test_helper.rb +4 -1
  27. data/test/threading/test_threading_large_pool.rb +1 -1
  28. data/test/threading_test.rb +1 -1
  29. data/test/unit/collection_test.rb +2 -2
  30. data/test/unit/cursor_test.rb +7 -0
  31. data/test/unit/db_test.rb +8 -8
  32. metadata +6 -46
  33. data/bin/gr.rb +0 -14
  34. data/lib/bson.rb +0 -46
  35. data/lib/bson/bson_c.rb +0 -20
  36. data/lib/bson/bson_ruby.rb +0 -601
  37. data/lib/bson/byte_buffer.rb +0 -224
  38. data/lib/bson/exceptions.rb +0 -39
  39. data/lib/bson/ordered_hash.rb +0 -140
  40. data/lib/bson/types/binary.rb +0 -54
  41. data/lib/bson/types/code.rb +0 -36
  42. data/lib/bson/types/dbref.rb +0 -40
  43. data/lib/bson/types/min_max_keys.rb +0 -58
  44. data/lib/bson/types/objectid.rb +0 -180
  45. data/lib/bson/types/regexp_of_holding.rb +0 -45
  46. data/lib/mongo/gridfs.rb +0 -29
  47. data/lib/mongo/gridfs/chunk.rb +0 -91
  48. data/lib/mongo/gridfs/grid_store.rb +0 -580
  49. data/lib/mongo/types/binary.rb +0 -52
  50. data/lib/mongo/types/code.rb +0 -36
  51. data/lib/mongo/types/dbref.rb +0 -40
  52. data/lib/mongo/types/min_max_keys.rb +0 -58
  53. data/lib/mongo/types/objectid.rb +0 -180
  54. data/lib/mongo/types/regexp_of_holding.rb +0 -45
  55. data/lib/mongo/util/bson_c.rb +0 -18
  56. data/lib/mongo/util/bson_ruby.rb +0 -606
  57. data/lib/mongo/util/byte_buffer.rb +0 -222
  58. data/lib/mongo/util/ordered_hash.rb +0 -140
  59. data/test/binary_test.rb +0 -15
  60. data/test/bson_test.rb +0 -459
  61. data/test/byte_buffer_test.rb +0 -81
  62. data/test/chunk_test.rb +0 -82
  63. data/test/grid_store_test.rb +0 -337
  64. data/test/objectid_test.rb +0 -125
  65. data/test/ordered_hash_test.rb +0 -172
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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