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/lib/bson/array.rb
ADDED
@@ -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
|
data/lib/bson/binary.rb
ADDED
@@ -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
|
data/lib/bson/boolean.rb
ADDED
@@ -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
|
data/lib/bson/code.rb
ADDED
@@ -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
|