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.

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -0
  3. data/CONTRIBUTING.md +0 -0
  4. data/LICENSE.md +13 -0
  5. data/README.md +185 -0
  6. data/Rakefile +83 -0
  7. data/ext/bson/extconf.rb +3 -0
  8. data/lib/bson.rb +105 -94
  9. data/lib/bson/array.rb +74 -0
  10. data/lib/bson/binary.rb +125 -0
  11. data/lib/bson/boolean.rb +35 -0
  12. data/lib/bson/code.rb +96 -0
  13. data/lib/bson/code_with_scope.rb +105 -0
  14. data/lib/bson/document.rb +536 -0
  15. data/lib/bson/encodable.rb +73 -0
  16. data/lib/bson/false_class.rb +48 -0
  17. data/lib/bson/float.rb +69 -0
  18. data/lib/bson/hash.rb +71 -0
  19. data/lib/bson/int32.rb +46 -0
  20. data/lib/bson/int64.rb +46 -0
  21. data/lib/bson/integer.rb +172 -0
  22. data/lib/bson/json.rb +26 -0
  23. data/lib/bson/max_key.rb +57 -0
  24. data/lib/bson/min_key.rb +57 -0
  25. data/lib/bson/nil_class.rb +57 -0
  26. data/lib/bson/object_id.rb +309 -0
  27. data/lib/bson/regexp.rb +111 -0
  28. data/lib/bson/registry.rb +57 -0
  29. data/lib/bson/specialized.rb +61 -0
  30. data/lib/bson/string.rb +173 -0
  31. data/lib/bson/symbol.rb +74 -0
  32. data/lib/bson/time.rb +59 -0
  33. data/lib/bson/timestamp.rb +100 -0
  34. data/lib/bson/true_class.rb +48 -0
  35. data/lib/bson/undefined.rb +61 -0
  36. data/lib/bson/version.rb +4 -0
  37. metadata +52 -40
  38. data/LICENSE +0 -190
  39. data/VERSION +0 -1
  40. data/bin/b2json +0 -63
  41. data/bin/j2bson +0 -64
  42. data/bson.gemspec +0 -34
  43. data/lib/bson/bson_c.rb +0 -37
  44. data/lib/bson/bson_java.rb +0 -49
  45. data/lib/bson/bson_ruby.rb +0 -645
  46. data/lib/bson/byte_buffer.rb +0 -241
  47. data/lib/bson/exceptions.rb +0 -37
  48. data/lib/bson/grow.rb +0 -173
  49. data/lib/bson/ordered_hash.rb +0 -197
  50. data/lib/bson/support/hash_with_indifferent_access.rb +0 -174
  51. data/lib/bson/types/binary.rb +0 -52
  52. data/lib/bson/types/code.rb +0 -55
  53. data/lib/bson/types/dbref.rb +0 -40
  54. data/lib/bson/types/min_max_keys.rb +0 -56
  55. data/lib/bson/types/object_id.rb +0 -226
  56. data/lib/bson/types/regex.rb +0 -116
  57. data/lib/bson/types/timestamp.rb +0 -72
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ module BSON
3
+
4
+ # Injects behaviour for encoding and decoding arrays to
5
+ # and from raw bytes as specified by the BSON spec.
6
+ #
7
+ # @see http://bsonspec.org/#/specification
8
+ #
9
+ # @since 2.0.0
10
+ module Array
11
+ include Encodable
12
+
13
+ # An array is type 0x04 in the BSON spec.
14
+ #
15
+ # @since 2.0.0
16
+ BSON_TYPE = 4.chr.force_encoding(BINARY).freeze
17
+
18
+ # Get the array as encoded BSON.
19
+ #
20
+ # @example Get the array as encoded BSON.
21
+ # [ 1, 2, 3 ].to_bson
22
+ #
23
+ # @note Arrays are encoded as documents, where the index of the value in
24
+ # the array is the actual key.
25
+ #
26
+ # @return [ String ] The encoded string.
27
+ #
28
+ # @see http://bsonspec.org/#/specification
29
+ #
30
+ # @since 2.0.0
31
+ def to_bson(encoded = ''.force_encoding(BINARY))
32
+ encode_with_placeholder_and_null(BSON_ADJUST, encoded) do |encoded|
33
+ each_with_index do |value, index|
34
+ encoded << value.bson_type
35
+ index.to_bson_key(encoded)
36
+ value.to_bson(encoded)
37
+ end
38
+ end
39
+ end
40
+
41
+ module ClassMethods
42
+
43
+ # Deserialize the array from BSON.
44
+ #
45
+ # @param [ BSON ] bson The bson representing an array.
46
+ #
47
+ # @return [ Array ] The decoded array.
48
+ #
49
+ # @see http://bsonspec.org/#/specification
50
+ #
51
+ # @since 2.0.0
52
+ def from_bson(bson)
53
+ array = new
54
+ bson.read(4) # throw away the length
55
+ while (type = bson.readbyte.chr) != NULL_BYTE
56
+ bson.gets(NULL_BYTE)
57
+ array << BSON::Registry.get(type).from_bson(bson)
58
+ end
59
+ array
60
+ end
61
+ end
62
+
63
+ # Register this type when the module is loaded.
64
+ #
65
+ # @since 2.0.0
66
+ Registry.register(BSON_TYPE, ::Array)
67
+ end
68
+
69
+ # Enrich the core Array class with this module.
70
+ #
71
+ # @since 2.0.0
72
+ ::Array.send(:include, Array)
73
+ ::Array.send(:extend, Array::ClassMethods)
74
+ end
@@ -0,0 +1,125 @@
1
+ # encoding: utf-8
2
+ module BSON
3
+
4
+ # Represents binary data.
5
+ #
6
+ # @see http://bsonspec.org/#/specification
7
+ #
8
+ # @since 2.0.0
9
+ class Binary
10
+ include JSON
11
+ include Encodable
12
+
13
+ # A binary is type 0x05 in the BSON spec.
14
+ #
15
+ # @since 2.0.0
16
+ BSON_TYPE = 5.chr.force_encoding(BINARY).freeze
17
+
18
+ # The mappings of subtypes to their single byte identifiers.
19
+ #
20
+ # @since 2.0.0
21
+ SUBTYPES = {
22
+ :generic => 0.chr,
23
+ :function => 1.chr,
24
+ :old => 2.chr,
25
+ :uuid_old => 3.chr,
26
+ :uuid => 4.chr,
27
+ :md5 => 5.chr,
28
+ :user => 128.chr
29
+ }.freeze
30
+
31
+ # The mappings of single byte subtypes to their symbol counterparts.
32
+ #
33
+ # @since 2.0.0
34
+ TYPES = SUBTYPES.invert.freeze
35
+
36
+ # @!attribute data
37
+ # @return [ Object ] The raw binary data.
38
+ # @since 2.0.0
39
+ # @!attribute type
40
+ # @return [ Symbol ] The binary type.
41
+ # @since 2.0.0
42
+ attr_reader :data, :type
43
+
44
+ # Determine if this binary object is equal to another object.
45
+ #
46
+ # @example Check the binary equality.
47
+ # binary == other
48
+ #
49
+ # @param [ Object ] other The object to compare against.
50
+ #
51
+ # @return [ true, false ] If the objects are equal.
52
+ #
53
+ # @since 2.0.0
54
+ def ==(other)
55
+ return false unless other.is_a?(Binary)
56
+ type == other.type && data == other.data
57
+ end
58
+
59
+ # Get the binary as JSON hash data.
60
+ #
61
+ # @example Get the binary as a JSON hash.
62
+ # binary.as_json
63
+ #
64
+ # @return [ Hash ] The binary as a JSON hash.
65
+ #
66
+ # @since 2.0.0
67
+ def as_json(*args)
68
+ { "$binary" => data, "$type" => type }
69
+ end
70
+
71
+ # Instantiate the new binary object.
72
+ #
73
+ # @example Instantiate a binary.
74
+ # BSON::Binary.new(data, :md5)
75
+ #
76
+ # @param [ Object ] data The raw binary data.
77
+ # @param [ Symbol ] type The binary type.
78
+ #
79
+ # @since 2.0.0
80
+ def initialize(data = "", type = :generic)
81
+ @data = data
82
+ @type = type
83
+ end
84
+
85
+ # Encode the binary type
86
+ #
87
+ # @example Encode the binary.
88
+ # binary.to_bson
89
+ #
90
+ # @return [ String ] The encoded binary.
91
+ #
92
+ # @see http://bsonspec.org/#/specification
93
+ #
94
+ # @since 2.0.0
95
+ def to_bson(encoded = ''.force_encoding(BINARY))
96
+ encode_binary_data_with_placeholder(encoded) do |encoded|
97
+ encoded << SUBTYPES.fetch(type)
98
+ encoded << data.bytesize.to_bson if type == :old
99
+ encoded << data
100
+ end
101
+ end
102
+
103
+ # Deserialize the binary data from BSON.
104
+ #
105
+ # @param [ BSON ] bson The bson representing binary data.
106
+ #
107
+ # @return [ Binary ] The decoded binary data.
108
+ #
109
+ # @see http://bsonspec.org/#/specification
110
+ #
111
+ # @since 2.0.0
112
+ def self.from_bson(bson)
113
+ length = Int32.from_bson(bson)
114
+ type = TYPES[bson.read(1)]
115
+ length = Int32.from_bson(bson) if type == :old
116
+ data = bson.read(length)
117
+ new(data, type)
118
+ end
119
+
120
+ # Register this type when the module is loaded.
121
+ #
122
+ # @since 2.0.0
123
+ Registry.register(BSON_TYPE, self)
124
+ end
125
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ module BSON
3
+
4
+ # Represents a $maxKey type, which compares less than any other value in the
5
+ # specification.
6
+ #
7
+ # @see http://bsonspec.org/#/specification
8
+ #
9
+ # @since 2.0.0
10
+ class Boolean
11
+
12
+ # A boolean is type 0x08 in the BSON spec.
13
+ #
14
+ # @since 2.0.0
15
+ BSON_TYPE = 8.chr.force_encoding(BINARY).freeze
16
+
17
+ # Deserialize a boolean from BSON.
18
+ #
19
+ # @param [ BSON ] bson The encoded boolean.
20
+ #
21
+ # @return [ TrueClass, FalseClass ] The decoded boolean.
22
+ #
23
+ # @see http://bsonspec.org/#/specification
24
+ #
25
+ # @since 2.0.0
26
+ def self.from_bson(bson)
27
+ bson.readbyte == 1
28
+ end
29
+
30
+ # Register this type when the module is loaded.
31
+ #
32
+ # @since 2.0.0
33
+ Registry.register(BSON_TYPE, self)
34
+ end
35
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+ module BSON
3
+
4
+ # Represents a code type, which is a wrapper around javascript code.
5
+ #
6
+ # @see http://bsonspec.org/#/specification
7
+ #
8
+ # @since 2.0.0
9
+ class Code
10
+ include JSON
11
+ include Encodable
12
+
13
+ # A code is type 0x0D in the BSON spec.
14
+ #
15
+ # @since 2.0.0
16
+ BSON_TYPE = 13.chr.force_encoding(BINARY).freeze
17
+
18
+ # @!attribute javascript
19
+ # @return [ String ] The javascript code.
20
+ # @since 2.0.0
21
+ attr_reader :javascript
22
+
23
+ # Determine if this code object is equal to another object.
24
+ #
25
+ # @example Check the code equality.
26
+ # code == other
27
+ #
28
+ # @param [ Object ] other The object to compare against.
29
+ #
30
+ # @return [ true, false ] If the objects are equal.
31
+ #
32
+ # @since 2.0.0
33
+ def ==(other)
34
+ return false unless other.is_a?(Code)
35
+ javascript == other.javascript
36
+ end
37
+
38
+ # Get the code as JSON hash data.
39
+ #
40
+ # @example Get the code as a JSON hash.
41
+ # code.as_json
42
+ #
43
+ # @return [ Hash ] The code as a JSON hash.
44
+ #
45
+ # @since 2.0.0
46
+ def as_json(*args)
47
+ { "$code" => javascript }
48
+ end
49
+
50
+ # Instantiate the new code.
51
+ #
52
+ # @example Instantiate the new code.
53
+ # BSON::Code.new("this.value = 5")
54
+ #
55
+ # @param [ String ] javascript The javascript code.
56
+ #
57
+ # @since 2.0.0
58
+ def initialize(javascript = "")
59
+ @javascript = javascript
60
+ end
61
+
62
+ # Encode the javascript code.
63
+ #
64
+ # @example Encode the code.
65
+ # code.to_bson
66
+ #
67
+ # @return [ String ] The encoded string.
68
+ #
69
+ # @see http://bsonspec.org/#/specification
70
+ #
71
+ # @since 2.0.0
72
+ def to_bson(encoded = ''.force_encoding(BINARY))
73
+ encode_with_placeholder_and_null(STRING_ADJUST, encoded) do |encoded|
74
+ javascript.to_bson_string(encoded)
75
+ end
76
+ end
77
+
78
+ # Deserialize code from BSON.
79
+ #
80
+ # @param [ BSON ] bson The encoded code.
81
+ #
82
+ # @return [ TrueClass, FalseClass ] The decoded code.
83
+ #
84
+ # @see http://bsonspec.org/#/specification
85
+ #
86
+ # @since 2.0.0
87
+ def self.from_bson(bson)
88
+ new(bson.read(Int32.from_bson(bson)).from_bson_string.chop!)
89
+ end
90
+
91
+ # Register this type when the module is loaded.
92
+ #
93
+ # @since 2.0.0
94
+ Registry.register(BSON_TYPE, self)
95
+ end
96
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: utf-8
2
+ module BSON
3
+
4
+ # Represents a code with scope, which is a wrapper around javascript code
5
+ # with variable scope provided.
6
+ #
7
+ # @see http://bsonspec.org/#/specification
8
+ #
9
+ # @since 2.0.0
10
+ class CodeWithScope
11
+ include Encodable
12
+ include JSON
13
+
14
+ # A code with scope is type 0x0F in the BSON spec.
15
+ #
16
+ # @since 2.0.0
17
+ BSON_TYPE = 15.chr.force_encoding(BINARY).freeze
18
+
19
+ # @!attribute javascript
20
+ # @return [ String ] The javascript code.
21
+ # @since 2.0.0
22
+ # @!attribute scope
23
+ # @return [ Hash ] The variable scope.
24
+ # @since 2.0.0
25
+ attr_reader :javascript, :scope
26
+
27
+ # Determine if this code with scope object is equal to another object.
28
+ #
29
+ # @example Check the code with scope equality.
30
+ # code_with_scope == other
31
+ #
32
+ # @param [ Object ] other The object to compare against.
33
+ #
34
+ # @return [ true, false ] If the objects are equal.
35
+ #
36
+ # @since 2.0.0
37
+ def ==(other)
38
+ return false unless other.is_a?(CodeWithScope)
39
+ javascript == other.javascript && scope == other.scope
40
+ end
41
+
42
+ # Get the code with scope as JSON hash data.
43
+ #
44
+ # @example Get the code with scope as a JSON hash.
45
+ # code_with_scope.as_json
46
+ #
47
+ # @return [ Hash ] The code with scope as a JSON hash.
48
+ #
49
+ # @since 2.0.0
50
+ def as_json(*args)
51
+ { "$code" => javascript, "$scope" => scope }
52
+ end
53
+
54
+ # Instantiate the new code with scope.
55
+ #
56
+ # @example Instantiate the code with scope.
57
+ # BSON::CodeWithScope.new("this.value = name", name: "sid")
58
+ #
59
+ # @param [ String ] javascript The javascript code.
60
+ # @param [ Hash ] scope The variable scope.
61
+ #
62
+ # @since 2.0.0
63
+ def initialize(javascript = "", scope = {})
64
+ @javascript = javascript
65
+ @scope = scope
66
+ end
67
+
68
+ # Encode the javascript code with scope.
69
+ #
70
+ # @example Encode the code with scope.
71
+ # code_with_scope.to_bson
72
+ #
73
+ # @return [ String ] The encoded string.
74
+ #
75
+ # @see http://bsonspec.org/#/specification
76
+ #
77
+ # @since 2.0.0
78
+ def to_bson(encoded = ''.force_encoding(BINARY))
79
+ encode_with_placeholder_and_null(BSON_ADJUST, encoded) do |encoded|
80
+ javascript.to_bson(encoded)
81
+ scope.to_bson(encoded)
82
+ end
83
+ end
84
+
85
+ # Deserialize a code with scope from BSON.
86
+ #
87
+ # @param [ BSON ] bson The encoded code with scope.
88
+ #
89
+ # @return [ TrueClass, FalseClass ] The decoded code with scope.
90
+ #
91
+ # @see http://bsonspec.org/#/specification
92
+ #
93
+ # @since 2.0.0
94
+ def self.from_bson(bson)
95
+ code_with_scope = StringIO.new(bson.read(Int32.from_bson(bson)))
96
+ length = code_with_scope.read(4).unpack(Int32::PACK).first
97
+ new(code_with_scope.read(length).from_bson_string.chop!)
98
+ end
99
+
100
+ # Register this type when the module is loaded.
101
+ #
102
+ # @since 2.0.0
103
+ Registry.register(BSON_TYPE, self)
104
+ end
105
+ end
@@ -0,0 +1,536 @@
1
+ # encoding: utf-8
2
+ require "yaml"
3
+
4
+ # Since we have a custom bsondoc type for yaml serialization, we need
5
+ # to ensure that it's properly deserialized when parsed.
6
+ #
7
+ # @since 2.0.0
8
+ YAML.add_builtin_type("bsondoc") do |type, value|
9
+ BSON::Document[value.map{ |val| val.to_a.first }]
10
+ end
11
+
12
+ module BSON
13
+
14
+ # This module provides behaviour for serializing and deserializing entire
15
+ # BSON documents, according to the BSON specification.
16
+ #
17
+ # @note The specification is: document ::= int32 e_list "\x00"
18
+ #
19
+ # @see http://bsonspec.org/#/specification
20
+ #
21
+ # @since 2.0.0
22
+ class Document < ::Hash
23
+
24
+ # Get a value from the document for the provided key. Can use string or
25
+ # symbol access, but the fastest will be to always provide a key that is of
26
+ # the same type as the stored keys.
27
+ #
28
+ # @example Get an element for the key.
29
+ # document["field"]
30
+ #
31
+ # @example Get an element for the key by symbol.
32
+ # document[:field]
33
+ #
34
+ # @param [ String, Symbol ] key The key to lookup.
35
+ #
36
+ # @return [ Object ] The found value, or nil if none found.
37
+ #
38
+ # @since 2.0.0
39
+ def [](key)
40
+ super(key) || super(key.to_s)
41
+ end
42
+
43
+ # If we have ordered hashes, the a BSON::Document is simply a hash. If we do
44
+ # not, then we need to import our custom BSON::Document implementation.
45
+ #
46
+ # @since 2.0.0
47
+ unless ordered_hash_support?
48
+
49
+ # Message for argument error when providing bad arguments to [].
50
+ #
51
+ # @since 2.0.0
52
+ ARG_ERROR = "An even number of arguments must be passed to BSON::Document[]."
53
+
54
+ # Sets a value for the provided key.
55
+ #
56
+ # @example Set the value in the document.
57
+ # document[:name] = "Sid"
58
+ #
59
+ # @param [ Object ] key The name of the key.
60
+ # @param [ Object ] value The value for the key.
61
+ #
62
+ # @return [ Object ] The passed in value.
63
+ #
64
+ # @since 2.0.0
65
+ def []=(key, value)
66
+ order.push(key) unless has_key?(key)
67
+ super
68
+ end
69
+
70
+ # Clear out all elements in the document.
71
+ #
72
+ # @example Clear out all elements.
73
+ # document.clear
74
+ #
75
+ # @return [ BSON::Document ] The empty document.
76
+ #
77
+ # @since 2.0.0
78
+ def clear
79
+ super
80
+ order.clear
81
+ self
82
+ end
83
+
84
+ # Delete a value from the document for the provided key.
85
+ #
86
+ # @example Delete a value from the document.
87
+ # document.delete(:name)
88
+ #
89
+ # @param [ Object ] key The key to delete for.
90
+ #
91
+ # @return [ Object ] The deleted value.
92
+ #
93
+ # @since 2.0.0
94
+ def delete(key)
95
+ if has_key?(key)
96
+ order.delete_at(order.index(key))
97
+ end
98
+ super
99
+ end
100
+
101
+ # Delete each key/value pair in the document for which the provided block
102
+ # returns true.
103
+ #
104
+ # @example Delete each for when the block is true.
105
+ # document.delete_if do |key, value|
106
+ # value == 1
107
+ # end
108
+ #
109
+ # @return [ BSON::Document ] The document.
110
+ #
111
+ # @since 2.0.0
112
+ def delete_if
113
+ super
114
+ synchronize!
115
+ self
116
+ end
117
+ alias :reject! :delete_if
118
+
119
+ # Iterate over each element of the document in insertion order and yield
120
+ # the key and value.
121
+ #
122
+ # @example Iterate over the document.
123
+ # document.each do |key, value|
124
+ # #...
125
+ # end
126
+ #
127
+ # @return [ BSON::Document ] The document if a block was given, otherwise
128
+ # an enumerator.
129
+ #
130
+ # @since 2.0.0
131
+ def each
132
+ if block_given?
133
+ order.each{ |key| yield([ key, self[key]]) }
134
+ self
135
+ else
136
+ to_enum(:each)
137
+ end
138
+ end
139
+
140
+ # Iterate over each key in the document in insertion order and yield the
141
+ # key.
142
+ #
143
+ # @example Iterate over the keys.
144
+ # document.each_key do |key|
145
+ # #...
146
+ # end
147
+ #
148
+ # @return [ BSON::Document ] The document if a block was given, otherwise
149
+ # an enumerator.
150
+ #
151
+ # @since 2.0.0
152
+ def each_key
153
+ if block_given?
154
+ order.each{ |key| yield(key) }
155
+ self
156
+ else
157
+ to_enum(:each_key)
158
+ end
159
+ end
160
+
161
+ # Iterate over each value in the document in insertion order and yield the
162
+ # value.
163
+ #
164
+ # @example Iterate over the values.
165
+ # document.each_value do |value|
166
+ # #...
167
+ # end
168
+ #
169
+ # @return [ BSON::Document ] The document if a block was given, otherwise
170
+ # an enumerator.
171
+ #
172
+ # @since 2.0.0
173
+ def each_value
174
+ if block_given?
175
+ order.each{ |key| yield(self[key]) }
176
+ self
177
+ else
178
+ to_enum(:each_value)
179
+ end
180
+ end
181
+
182
+ # Iterate over each element of the document in insertion order and yield
183
+ # the key and value.
184
+ #
185
+ # @example Iterate over the document.
186
+ # document.each_pair do |key, value|
187
+ # #...
188
+ # end
189
+ #
190
+ # @return [ BSON::Document ] The document if a block was given, otherwise
191
+ # an enumerator.
192
+ #
193
+ # @since 2.0.0
194
+ def each_pair
195
+ if block_given?
196
+ order.each{ |key| yield([ key, self[key]]) }
197
+ self
198
+ else
199
+ to_enum(:each_pair)
200
+ end
201
+ end
202
+
203
+ # Encode the document with the provided coder.
204
+ #
205
+ # @example Encode the document with the coder.
206
+ # document.encode_with(coder)
207
+ #
208
+ # @param [ Object ] coder The coder.
209
+ #
210
+ # @return [ String ] The encoded document.
211
+ #
212
+ # @since 2.0.0
213
+ def encode_with(coder)
214
+ coder.represent_seq("!bsondoc", map{ |key, value| { key => value }})
215
+ end
216
+
217
+ # Get all the keys in the document, in order.
218
+ #
219
+ # @example Get all the keys in the document.
220
+ # document.keys
221
+ #
222
+ # @return [ Array<Object> ] The ordered keys.
223
+ #
224
+ # @since 2.0.0
225
+ def keys
226
+ order.dup
227
+ end
228
+
229
+ # Instantiate a new Document.
230
+ #
231
+ # @example Instantiate an empty new document.
232
+ # BSON::Document.new
233
+ #
234
+ # @since 2.0.0
235
+ def initialize(*args, &block)
236
+ super
237
+ @order = []
238
+ end
239
+
240
+ # Inspect the contents of the document.
241
+ #
242
+ # @example Inspect the document.
243
+ # document.inspect
244
+ #
245
+ # @return [ String ] The inspection string.
246
+ #
247
+ # @since 2.0.0
248
+ def inspect
249
+ "#<BSON::Document #{super}>"
250
+ end
251
+
252
+ # Invert the document - reverses the order of all key/value pairs and
253
+ # returns a new document.
254
+ #
255
+ # @example Invert the document.
256
+ # document.invert
257
+ #
258
+ # @return [ BSON::Document ] The inverted document.
259
+ #
260
+ # @since 2.0.0
261
+ def invert
262
+ Document[to_a.map!{ |pair| pair.reverse }]
263
+ end
264
+
265
+ # Merge a document into this document. Will overwrite any existing keys and
266
+ # add potential new ones. This returns a new document instead of merging in
267
+ # place.
268
+ #
269
+ # @example Merge the document into this document.
270
+ # document.merge(other_document)
271
+ #
272
+ # @param [ BSON::Document ] other The document to merge in.
273
+ #
274
+ # @return [ BSON::Document ] A newly merged document.
275
+ #
276
+ # @since 2.0.0
277
+ def merge(other, &block)
278
+ dup.merge!(other, &block)
279
+ end
280
+
281
+ # Merge a document into this document. Will overwrite any existing keys and
282
+ # add potential new ones.
283
+ #
284
+ # @example Merge the document into this document.
285
+ # document.merge!(other_document)
286
+ #
287
+ # @param [ BSON::Document ] other The document to merge in.
288
+ #
289
+ # @return [ BSON::Document ] The document.
290
+ #
291
+ # @since 2.0.0
292
+ def merge!(other)
293
+ if block_given?
294
+ other.each do |key, value|
295
+ self[key] = key?(key) ? yield(key, self[key], value) : value
296
+ end
297
+ else
298
+ other.each{ |key, value| self[key] = value }
299
+ end
300
+ self
301
+ end
302
+ alias :update :merge!
303
+
304
+ # Delete each key/value pair in the document for which the provided block
305
+ # returns true. This returns a new document instead of modifying in place.
306
+ #
307
+ # @example Delete each for when the block is true.
308
+ # document.reject do |key, value|
309
+ # value == 1
310
+ # end
311
+ #
312
+ # @return [ BSON::Document ] The new document.
313
+ #
314
+ # @since 2.0.0
315
+ def reject(&block)
316
+ dup.reject!(&block)
317
+ end
318
+
319
+ # Replace this document with the other document.
320
+ #
321
+ # @example Replace the contents of this document with the other.
322
+ # document.replace(other_document)
323
+ #
324
+ # @param [ BSON::Document ] other The other document.
325
+ #
326
+ # @return [ BSON::Document ] The document replaced.
327
+ #
328
+ # @since 2.0.0
329
+ def replace(other)
330
+ super
331
+ @order = other.keys
332
+ self
333
+ end
334
+
335
+ # Shift the document by popping off the first key/value pair in the
336
+ # document.
337
+ #
338
+ # @example Shift the document.
339
+ # document.shift
340
+ #
341
+ # @return [ Array<Object, Object> ] The first key/value pair.
342
+ #
343
+ # @since 2.0.0
344
+ def shift
345
+ key = order.first
346
+ value = delete(key)
347
+ [ key, value ]
348
+ end
349
+
350
+ alias :select :find_all
351
+
352
+ # Get the document as an array. This returns a multi-dimensional array
353
+ # where each element is a [ key, value ] pair in the insertion order.
354
+ #
355
+ # @example Get the document as an array.
356
+ # document.to_a
357
+ #
358
+ # @return [ Array<Array<Object, Object>> ] The pairs in insertion order.
359
+ #
360
+ # @since 2.0.0
361
+ def to_a
362
+ order.map{ |key| [ key, self[key] ]}
363
+ end
364
+
365
+ # Convert this document to a hash. Since a document is simply an ordered
366
+ # hash we return self.
367
+ #
368
+ # @example Get the document as a hash.
369
+ # document.to_hash
370
+ #
371
+ # @return [ BSON::Document ] The document.
372
+ #
373
+ # @since 2.0.0
374
+ def to_hash
375
+ self
376
+ end
377
+
378
+ # Convert the document to yaml.
379
+ #
380
+ # @example Convert the document to yaml.
381
+ # document.to_yaml
382
+ #
383
+ # @param [ Hash ] options The yaml options.
384
+ #
385
+ # @return [ String ] The document as yaml.
386
+ #
387
+ # @since 2.0.0
388
+ def to_yaml(options = {})
389
+ if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?
390
+ return super
391
+ end
392
+ YAML.quick_emit(self, options) do |out|
393
+ out.seq(taguri) do |seq|
394
+ each{ |key, value| seq.add(key => value) }
395
+ end
396
+ end
397
+ end
398
+
399
+ # Get the custom yaml type for the document.
400
+ #
401
+ # @example Get the yaml type.
402
+ # document.to_yaml_type
403
+ #
404
+ # @return [ String ] "!bsondoc".
405
+ #
406
+ # @since 2.0.0
407
+ def to_yaml_type
408
+ "!bsondoc"
409
+ end
410
+
411
+ # Get all the values in the document, by order of insertion.
412
+ #
413
+ # @example Get all the values in the document.
414
+ # document.values
415
+ #
416
+ # @return [ Array<Object> ] The ordered values.
417
+ #
418
+ # @since 2.0.0
419
+ def values
420
+ order.map{ |key| self[key] }
421
+ end
422
+
423
+ class << self
424
+
425
+ # Create a new document given the provided arguments. The args can either
426
+ # be empty in order to instantiate an empty document, or an array of
427
+ # key/value pairs in the order that they should remain in.
428
+ #
429
+ # @example Create a new empty document.
430
+ # BSON::Document[]
431
+ #
432
+ # @example Create a new document with the provided elements.
433
+ # BSON::Document[1, 2, 3, 4]
434
+ #
435
+ # @example Create a new document with key/value array pairs.
436
+ # BSON::Document[[ 1, 2 ], [ 3, 4 ]]
437
+ #
438
+ # @param [ Array<Object> ] args The key/value pairs.
439
+ #
440
+ # @return [ BSON::Document ] The new document.
441
+ #
442
+ # @since 2.0.0
443
+ def [](*args)
444
+ if (args.length == 1 && args.first.is_a?(Array))
445
+ return document_from_pairs(args)
446
+ end
447
+ raise ArgumentError.new(ARG_ERROR) unless (args.size % 2 == 0)
448
+ document_from_args(args)
449
+ end
450
+
451
+ private
452
+
453
+ # Returns a document that will be generated from an array of [ key, value ]
454
+ # array pairs.
455
+ #
456
+ # @api private
457
+ #
458
+ # @example Initialize a document from array pairs.
459
+ # BSON::Document[[ 1, 2 ], [ 3, 4 ]]
460
+ #
461
+ # @param [ Array ] pairs The key/value pairs.
462
+ #
463
+ # @since 2.0.0
464
+ #
465
+ # @return [ BSON::Document ] The document.
466
+ def document_from_pairs(pairs)
467
+ document = new
468
+ pairs.first.each do |pair|
469
+ next unless (pair.is_a?(Array))
470
+ document[pair[0]] = pair[1]
471
+ end
472
+ return document
473
+ end
474
+
475
+ # Returns a document that will be generated from an even number of
476
+ # individual arguments.
477
+ #
478
+ # @api private
479
+ #
480
+ # @example Initialize a document from args.
481
+ # BSON::Document[1, 2, 3, 4]
482
+ #
483
+ # @param [ Array ] args The arguments.
484
+ #
485
+ # @return [ BSON::Document ] The document.
486
+ #
487
+ # @since 2.0.0
488
+ def document_from_args(args)
489
+ document = new
490
+ args.each_with_index do |val, ind|
491
+ next if (ind % 2 != 0)
492
+ document[val] = args[ind + 1]
493
+ end
494
+ document
495
+ end
496
+ end
497
+
498
+ private
499
+
500
+ # @!attribute order
501
+ # @api private
502
+ # @return [ Array<String> ] The document keys in order.
503
+ # @since 2.0.0
504
+ attr_reader :order
505
+
506
+ # Initialize a copy of the document for use with clone or dup.
507
+ #
508
+ # @api private
509
+ #
510
+ # @example Clone the document.
511
+ # document.clone
512
+ #
513
+ # @param [ Object ] other The original copy.
514
+ #
515
+ # @since 2.0.0
516
+ def initialize_copy(other)
517
+ super
518
+ @order = other.keys
519
+ end
520
+
521
+ # Ensure that the ordered keys are the same entries as the internal keys.
522
+ #
523
+ # @api private
524
+ #
525
+ # @example Synchronize the keys.
526
+ # document.synchronize!
527
+ #
528
+ # @return [ Array<Object> ] The keys.
529
+ #
530
+ # @since 2.0.0
531
+ def synchronize!
532
+ order.reject!{ |key| !has_key?(key) }
533
+ end
534
+ end
535
+ end
536
+ end