bson 4.7.1 → 4.8.0

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 (116) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -2
  3. data.tar.gz.sig +0 -0
  4. data/README.md +5 -1
  5. data/ext/bson/bson-native.h +14 -4
  6. data/ext/bson/init.c +25 -2
  7. data/ext/bson/read.c +61 -15
  8. data/ext/bson/util.c +40 -0
  9. data/ext/bson/write.c +17 -13
  10. data/lib/bson.rb +3 -0
  11. data/lib/bson/array.rb +21 -3
  12. data/lib/bson/binary.rb +41 -3
  13. data/lib/bson/boolean.rb +3 -1
  14. data/lib/bson/code.rb +15 -1
  15. data/lib/bson/code_with_scope.rb +17 -3
  16. data/lib/bson/db_pointer.rb +104 -0
  17. data/lib/bson/decimal128.rb +15 -1
  18. data/lib/bson/error.rb +17 -0
  19. data/lib/bson/ext_json.rb +374 -0
  20. data/lib/bson/float.rb +47 -1
  21. data/lib/bson/hash.rb +23 -3
  22. data/lib/bson/int32.rb +21 -1
  23. data/lib/bson/int64.rb +28 -3
  24. data/lib/bson/integer.rb +34 -0
  25. data/lib/bson/max_key.rb +12 -0
  26. data/lib/bson/min_key.rb +12 -0
  27. data/lib/bson/nil_class.rb +3 -1
  28. data/lib/bson/object.rb +27 -0
  29. data/lib/bson/object_id.rb +15 -1
  30. data/lib/bson/regexp.rb +19 -2
  31. data/lib/bson/specialized.rb +3 -1
  32. data/lib/bson/string.rb +3 -1
  33. data/lib/bson/symbol.rb +92 -3
  34. data/lib/bson/time.rb +28 -3
  35. data/lib/bson/timestamp.rb +15 -1
  36. data/lib/bson/undefined.rb +11 -0
  37. data/lib/bson/version.rb +1 -1
  38. data/spec/bson/binary_spec.rb +33 -3
  39. data/spec/bson/ext_json_parse_spec.rb +276 -0
  40. data/spec/bson/float_spec.rb +36 -0
  41. data/spec/bson/hash_spec.rb +70 -0
  42. data/spec/bson/int32_spec.rb +20 -0
  43. data/spec/bson/int64_spec.rb +38 -0
  44. data/spec/bson/integer_spec.rb +26 -0
  45. data/spec/bson/raw_spec.rb +22 -1
  46. data/spec/bson/symbol_raw_spec.rb +45 -0
  47. data/spec/bson/symbol_spec.rb +60 -0
  48. data/spec/{support → runners}/common_driver.rb +0 -0
  49. data/spec/runners/corpus.rb +182 -0
  50. data/spec/{support/corpus.rb → runners/corpus_legacy.rb} +40 -58
  51. data/spec/spec_helper.rb +9 -2
  52. data/spec/{bson/driver_bson_spec.rb → spec_tests/common_driver_spec.rb} +1 -0
  53. data/spec/{bson/corpus_spec.rb → spec_tests/corpus_legacy_spec.rb} +4 -4
  54. data/spec/spec_tests/corpus_spec.rb +124 -0
  55. data/spec/spec_tests/data/corpus/README.md +15 -0
  56. data/spec/spec_tests/data/corpus/array.json +49 -0
  57. data/spec/spec_tests/data/corpus/binary.json +85 -0
  58. data/spec/spec_tests/data/corpus/boolean.json +27 -0
  59. data/spec/spec_tests/data/corpus/code.json +67 -0
  60. data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
  61. data/spec/spec_tests/data/corpus/datetime.json +42 -0
  62. data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
  63. data/spec/spec_tests/data/corpus/dbref.json +31 -0
  64. data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
  65. data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
  66. data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
  67. data/spec/spec_tests/data/corpus/decimal128-4.json +117 -0
  68. data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
  69. data/spec/spec_tests/data/corpus/decimal128-6.json +119 -0
  70. data/spec/spec_tests/data/corpus/decimal128-7.json +323 -0
  71. data/spec/spec_tests/data/corpus/document.json +36 -0
  72. data/spec/spec_tests/data/corpus/double.json +87 -0
  73. data/spec/spec_tests/data/corpus/int32.json +43 -0
  74. data/spec/spec_tests/data/corpus/int64.json +43 -0
  75. data/spec/spec_tests/data/corpus/maxkey.json +12 -0
  76. data/spec/spec_tests/data/corpus/minkey.json +12 -0
  77. data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
  78. data/spec/spec_tests/data/corpus/multi-type.json +11 -0
  79. data/spec/spec_tests/data/corpus/null.json +12 -0
  80. data/spec/spec_tests/data/corpus/oid.json +28 -0
  81. data/spec/spec_tests/data/corpus/regex.json +65 -0
  82. data/spec/spec_tests/data/corpus/string.json +72 -0
  83. data/spec/spec_tests/data/corpus/symbol.json +80 -0
  84. data/spec/spec_tests/data/corpus/timestamp.json +24 -0
  85. data/spec/spec_tests/data/corpus/top.json +240 -0
  86. data/spec/spec_tests/data/corpus/undefined.json +15 -0
  87. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/array.json +0 -0
  88. data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/binary.json +0 -0
  89. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/boolean.json +0 -0
  90. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code.json +1 -1
  91. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code_w_scope.json +1 -1
  92. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/document.json +1 -1
  93. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/double.json +1 -1
  94. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/datetime.json +0 -0
  95. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/dbpointer.json +0 -0
  96. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/int64.json +0 -0
  97. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/symbol.json +0 -0
  98. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/int32.json +1 -1
  99. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/maxkey.json +1 -1
  100. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/minkey.json +1 -1
  101. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/null.json +1 -1
  102. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/oid.json +0 -0
  103. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/regex.json +1 -1
  104. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/string.json +0 -0
  105. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/timestamp.json +1 -1
  106. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/top.json +0 -0
  107. data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/undefined.json +0 -0
  108. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-1.json +0 -0
  109. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-2.json +0 -0
  110. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-3.json +0 -0
  111. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-4.json +0 -0
  112. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-5.json +0 -0
  113. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-6.json +0 -0
  114. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-7.json +0 -0
  115. metadata +170 -95
  116. metadata.gz.sig +2 -4
@@ -89,19 +89,108 @@ module BSON
89
89
  to_s
90
90
  end
91
91
 
92
+ # Converts this object to a representation directly serializable to
93
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
94
+ #
95
+ # @option options [ true | false ] :relaxed Whether to produce relaxed
96
+ # extended JSON representation.
97
+ #
98
+ # @return [ Hash ] The extended json representation.
99
+ def as_extended_json(**options)
100
+ { "$symbol" => to_s }
101
+ end
102
+
103
+ class Raw
104
+ # Create a BSON Symbol
105
+ #
106
+ # @param [ String | Symbol ] str_or_sym The symbol represented by this
107
+ # object. Can be specified as a Symbol or a String.
108
+ #
109
+ # @see http://bsonspec.org/#/specification
110
+ def initialize(str_or_sym)
111
+ unless str_or_sym.is_a?(String) || str_or_sym.is_a?(Symbol)
112
+ raise ArgumentError, "BSON::Symbol::Raw must be given a symbol or a string, not #{str_or_sym}"
113
+ end
114
+
115
+ @symbol = str_or_sym.to_sym
116
+ end
117
+
118
+ # Get the underlying symbol as a Ruby symbol.
119
+ #
120
+ # @return [ Symbol ] The symbol represented by this BSON object.
121
+ def to_sym
122
+ @symbol
123
+ end
124
+
125
+ # Get the underlying symbol as a Ruby string.
126
+ #
127
+ # @return [ String ] The symbol as a string.
128
+ def to_s
129
+ @symbol.to_s
130
+ end
131
+
132
+ # Check equality of the raw bson symbol against another.
133
+ #
134
+ # @param [ Object ] other The object to check against.
135
+ #
136
+ # @return [ true, false ] If the objects are equal.
137
+ def ==(other)
138
+ return false unless other.is_a?(Raw)
139
+ to_sym == other.to_sym
140
+ end
141
+ alias :eql? :==
142
+
143
+ # Get the symbol as encoded BSON.
144
+ #
145
+ # @raise [ EncodingError ] If the symbol is not UTF-8.
146
+ #
147
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
148
+ #
149
+ # @see http://bsonspec.org/#/specification
150
+ def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
151
+ buffer.put_string(to_s)
152
+ end
153
+
154
+ def bson_type
155
+ Symbol::BSON_TYPE
156
+ end
157
+
158
+ # Converts this object to a representation directly serializable to
159
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
160
+ #
161
+ # This method returns the integer value if relaxed representation is
162
+ # requested, otherwise a $numberLong hash.
163
+ #
164
+ # @option options [ true | false ] :relaxed Whether to produce relaxed
165
+ # extended JSON representation.
166
+ #
167
+ # @return [ Hash | Integer ] The extended json representation.
168
+ def as_extended_json(**options)
169
+ {'$symbol' => to_s}
170
+ end
171
+ end
172
+
92
173
  module ClassMethods
93
174
 
94
175
  # Deserialize a symbol from BSON.
95
176
  #
96
177
  # @param [ ByteBuffer ] buffer The byte buffer.
97
178
  #
98
- # @return [ Regexp ] The decoded symbol.
179
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
180
+ #
181
+ # @return [ Symbol | BSON::Symbol::Raw ] The decoded symbol.
99
182
  #
100
183
  # @see http://bsonspec.org/#/specification
101
184
  #
102
185
  # @since 2.0.0
103
- def from_bson(buffer)
104
- buffer.get_string.intern
186
+ def from_bson(buffer, **options)
187
+ sym = buffer.get_string.intern
188
+
189
+ if options[:mode] == :bson
190
+ Raw.new(sym)
191
+ else
192
+ sym
193
+ end
105
194
  end
106
195
  end
107
196
 
@@ -38,7 +38,30 @@ module BSON
38
38
  #
39
39
  # @since 2.0.0
40
40
  def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
41
- buffer.put_int64((to_i * 1000) + (usec / 1000))
41
+ # A previous version of this method used the following implementation:
42
+ # buffer.put_int64((to_i * 1000) + (usec / 1000))
43
+ # Turns out, usec returned incorrect value - 999 for 1 millisecond.
44
+ buffer.put_int64((to_f * 1000).round)
45
+ end
46
+
47
+ # Converts this object to a representation directly serializable to
48
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
49
+ #
50
+ # @option options [ true | false ] :relaxed Whether to produce relaxed
51
+ # extended JSON representation.
52
+ #
53
+ # @return [ Hash ] The extended json representation.
54
+ def as_extended_json(**options)
55
+ utc_time = utc
56
+ if options[:mode] == :relaxed && (1970..9999).include?(utc_time.year)
57
+ if utc_time.usec != 0
58
+ {'$date' => utc_time.strftime('%Y-%m-%dT%H:%M:%S.%LZ')}
59
+ else
60
+ {'$date' => utc_time.strftime('%Y-%m-%dT%H:%M:%SZ')}
61
+ end
62
+ else
63
+ {'$date' => {'$numberLong' => (utc_time.to_f * 1000).round.to_s}}
64
+ end
42
65
  end
43
66
 
44
67
  module ClassMethods
@@ -47,13 +70,15 @@ module BSON
47
70
  #
48
71
  # @param [ ByteBuffer ] buffer The byte buffer.
49
72
  #
73
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
74
+ #
50
75
  # @return [ Time ] The decoded UTC datetime.
51
76
  #
52
77
  # @see http://bsonspec.org/#/specification
53
78
  #
54
79
  # @since 2.0.0
55
- def from_bson(buffer)
56
- seconds, fragment = Int64.from_bson(buffer).divmod(1000)
80
+ def from_bson(buffer, **options)
81
+ seconds, fragment = Int64.from_bson(buffer, mode: nil).divmod(1000)
57
82
  at(seconds, fragment * 1000).utc
58
83
  end
59
84
  end
@@ -84,7 +84,19 @@ module BSON
84
84
  # @return [ Hash ] The timestamp as a JSON hash.
85
85
  #
86
86
  # @since 2.0.0
87
+ # @deprecated Use as_extended_json instead.
87
88
  def as_json(*args)
89
+ as_extended_json
90
+ end
91
+
92
+ # Converts this object to a representation directly serializable to
93
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
94
+ #
95
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
96
+ # (default is canonical extended JSON)
97
+ #
98
+ # @return [ Hash ] The extended json representation.
99
+ def as_extended_json(**options)
88
100
  { "$timestamp" => { "t" => seconds, "i" => increment } }
89
101
  end
90
102
 
@@ -120,12 +132,14 @@ module BSON
120
132
  #
121
133
  # @param [ ByteBuffer ] buffer The byte buffer.
122
134
  #
135
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
136
+ #
123
137
  # @return [ Timestamp ] The decoded timestamp.
124
138
  #
125
139
  # @see http://bsonspec.org/#/specification
126
140
  #
127
141
  # @since 2.0.0
128
- def self.from_bson(buffer)
142
+ def self.from_bson(buffer, **options)
129
143
  increment = buffer.get_int32
130
144
  seconds = buffer.get_int32
131
145
  new(seconds, increment)
@@ -41,6 +41,17 @@ module BSON
41
41
  self.class == other.class
42
42
  end
43
43
 
44
+ # Converts this object to a representation directly serializable to
45
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
46
+ #
47
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
48
+ # (default is canonical extended JSON)
49
+ #
50
+ # @return [ Hash ] The extended json representation.
51
+ def as_extended_json(**options)
52
+ { "$undefined" => true }
53
+ end
54
+
44
55
  # Register this type when the module is loaded.
45
56
  #
46
57
  # @since 2.0.0
@@ -13,5 +13,5 @@
13
13
  # limitations under the License.
14
14
 
15
15
  module BSON
16
- VERSION = "4.7.1".freeze
16
+ VERSION = "4.8.0".freeze
17
17
  end
@@ -63,7 +63,7 @@ describe BSON::Binary do
63
63
 
64
64
  it "returns the binary data plus type" do
65
65
  expect(object.as_json).to eq(
66
- { "$binary" => Base64.encode64("testing"), "$type" => :user }
66
+ { "$binary" => {'base64' => Base64.encode64("testing").strip, "subType" => '80' }}
67
67
  )
68
68
  end
69
69
 
@@ -126,12 +126,34 @@ describe BSON::Binary do
126
126
  end
127
127
 
128
128
  describe '#from_bson' do
129
- let(:bson) { BSON::ByteBuffer.new("#{5.to_bson}#{0.chr}hello".force_encoding('BINARY')) }
130
- let(:obj) { described_class.from_bson(bson) }
129
+ let(:buffer) { BSON::ByteBuffer.new(bson) }
130
+ let(:obj) { described_class.from_bson(buffer) }
131
+
132
+ let(:bson) { "#{5.to_bson}#{0.chr}hello".force_encoding('BINARY') }
131
133
 
132
134
  it 'sets data encoding to binary' do
133
135
  expect(obj.data.encoding).to eq(Encoding.find('BINARY'))
134
136
  end
137
+
138
+ context 'when binary subtype is supported' do
139
+ let(:bson) { [3, 0, 0, 0, 1].map(&:chr).join.force_encoding('BINARY') + 'foo' }
140
+
141
+ it 'works' do
142
+ obj.should be_a(described_class)
143
+ obj.type.should be :function
144
+ end
145
+ end
146
+
147
+ context 'when binary subtype is not supported' do
148
+ let(:bson) { [3, 0, 0, 0, 16].map(&:chr).join.force_encoding('BINARY') + 'foo' }
149
+
150
+ it 'raises an exception' do
151
+ lambda do
152
+ obj
153
+ end.should raise_error(BSON::Error::UnsupportedBinarySubtype,
154
+ /BSON data contains unsupported binary subtype 0x10/)
155
+ end
156
+ end
135
157
  end
136
158
 
137
159
  describe "#to_bson/#from_bson" do
@@ -203,6 +225,14 @@ describe BSON::Binary do
203
225
  it_behaves_like "a deserializable bson element"
204
226
  end
205
227
 
228
+ context "when the type is :cyphertext" do
229
+ let(:obj) { described_class.new("testing", :ciphertext) }
230
+ let(:bson) { "#{7.to_bson}#{6.chr}testing" }
231
+
232
+ it_behaves_like "a serializable bson element"
233
+ it_behaves_like "a deserializable bson element"
234
+ end
235
+
206
236
  context 'when given binary string' do
207
237
  let(:obj) { described_class.new("\x00\xfe\xff".force_encoding('BINARY')) }
208
238
  let(:bson) { "#{3.to_bson}#{0.chr}\x00\xfe\xff".force_encoding('BINARY') }
@@ -0,0 +1,276 @@
1
+ # Copyright (C) 2020 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "spec_helper"
16
+
17
+ describe "BSON::ExtJSON.parse" do
18
+
19
+ let(:parsed) { BSON::ExtJSON.parse_obj(input) }
20
+
21
+ context 'when input is true' do
22
+ let(:input) { true }
23
+
24
+ it 'returns true' do
25
+ parsed.should == true
26
+ end
27
+ end
28
+
29
+ context 'when input is false' do
30
+ let(:input) { false }
31
+
32
+ it 'returns false' do
33
+ parsed.should == false
34
+ end
35
+ end
36
+
37
+ context 'when input is nil' do
38
+ let(:input) { nil }
39
+
40
+ it 'returns nil' do
41
+ parsed.should be nil
42
+ end
43
+ end
44
+
45
+ context 'when input is a string' do
46
+ let(:input) { 'hello' }
47
+
48
+ it 'returns the string' do
49
+ parsed.should == 'hello'
50
+ end
51
+ end
52
+
53
+ context 'when input is a timestamp' do
54
+ let(:input) { {'$timestamp' => {'t' => 12345, 'i' => 42}} }
55
+
56
+ it 'returns a timestamp object' do
57
+ parsed.should == BSON::Timestamp.new(12345, 42)
58
+ end
59
+ end
60
+
61
+ context 'when input is an int32' do
62
+ let(:input) do
63
+ {'$numberInt' => '42'}
64
+ end
65
+
66
+ let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
67
+
68
+ context 'when :mode is nil' do
69
+ let(:mode) { nil }
70
+
71
+ it 'returns Integer instance' do
72
+ parsed.should be_a(Integer)
73
+ parsed.should == 42
74
+ end
75
+ end
76
+
77
+ context 'when :mode is :bson' do
78
+ let(:mode) { :bson }
79
+
80
+ it 'returns Integer instance' do
81
+ parsed.should be_a(Integer)
82
+ parsed.should == 42
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'when input is an int64' do
88
+ let(:input) do
89
+ {'$numberLong' => '42'}
90
+ end
91
+
92
+ let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
93
+
94
+ context 'when :mode is nil' do
95
+ let(:mode) { nil }
96
+
97
+ it 'returns Integer instance' do
98
+ parsed.should be_a(Integer)
99
+ parsed.should == 42
100
+ end
101
+ end
102
+
103
+ context 'when :mode is :bson' do
104
+ let(:mode) { :bson }
105
+
106
+ it 'returns Int64 instance' do
107
+ parsed.should be_a(BSON::Int64)
108
+ parsed.value.should == 42
109
+ end
110
+ end
111
+ end
112
+
113
+ context 'when input is a hash' do
114
+ let(:input) do
115
+ {}
116
+ end
117
+
118
+ let(:parsed) { BSON::ExtJSON.parse_obj(input, mode: mode) }
119
+
120
+ context 'when mode is invalid' do
121
+ let(:mode) { :foo }
122
+
123
+ it 'raises an exception' do
124
+ lambda do
125
+ parsed
126
+ end.should raise_error(ArgumentError, /Invalid value for :mode option/)
127
+ end
128
+ end
129
+ end
130
+
131
+ context 'when input is a binary' do
132
+ let(:data) do
133
+ Base64.decode64("//8=")
134
+ end
135
+
136
+ context 'in current format' do
137
+ let(:input) do
138
+ { "$binary" => { "base64"=>"//8=", "subType"=>"00" } }
139
+ end
140
+
141
+ context 'when :mode is nil' do
142
+ let(:mode) { nil }
143
+
144
+ it 'returns BSON::Binary instance' do
145
+ parsed.should be_a(BSON::Binary)
146
+ parsed.data.should == data
147
+ end
148
+ end
149
+
150
+ context 'when mode is :bson' do
151
+ let(:mode) { :bson }
152
+
153
+ it 'returns BSON::Binary instance' do
154
+ parsed.should be_a(BSON::Binary)
155
+ parsed.data.should == data
156
+ end
157
+ end
158
+ end
159
+
160
+ context 'in legacy format' do
161
+ let(:input) do
162
+ { "$binary"=>"//8=", "$type"=>"00" }
163
+ end
164
+
165
+ context 'when :mode is nil' do
166
+ let(:mode) { nil }
167
+
168
+ it 'returns BSON::Binary instance' do
169
+ parsed.should be_a(BSON::Binary)
170
+ parsed.data.should == data
171
+ end
172
+ end
173
+
174
+ context 'when mode is :bson' do
175
+ let(:mode) { :bson }
176
+
177
+ it 'returns BSON::Binary instance' do
178
+ parsed.should be_a(BSON::Binary)
179
+ parsed.data.should == data
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ context 'when input is a regex' do
186
+ let(:pattern) { 'abc' }
187
+ let(:options) { 'im' }
188
+
189
+ context 'in current format' do
190
+ let(:input) do
191
+ { "$regularExpression" => { "pattern" => pattern, "options" => options } }
192
+ end
193
+
194
+ context 'when :mode is nil' do
195
+ let(:mode) { nil }
196
+
197
+ it 'returns a BSON::Regexp::Raw instance' do
198
+ parsed.should be_a(BSON::Regexp::Raw)
199
+ parsed.pattern.should == pattern
200
+ parsed.options.should == options
201
+ end
202
+ end
203
+
204
+ context 'when :mode is :bson' do
205
+ let(:mode) { :bson }
206
+
207
+ it 'returns a BSON::Regexp::Raw instance' do
208
+ parsed.should be_a(BSON::Regexp::Raw)
209
+ parsed.pattern.should == pattern
210
+ parsed.options.should == options
211
+ end
212
+ end
213
+ end
214
+
215
+ context 'in legacy format' do
216
+ let(:input) do
217
+ { "$regex" => pattern, "$options" => options }
218
+ end
219
+
220
+ context 'when :mode is nil' do
221
+ let(:mode) { nil }
222
+
223
+ it 'returns a BSON::Regexp::Raw instance' do
224
+ parsed.should be_a(BSON::Regexp::Raw)
225
+ parsed.pattern.should == pattern
226
+ parsed.options.should == options
227
+ end
228
+ end
229
+
230
+ context 'when :mode is :bson' do
231
+ let(:mode) { :bson }
232
+
233
+ it 'returns a BSON::Regexp::Raw instance' do
234
+ parsed.should be_a(BSON::Regexp::Raw)
235
+ parsed.pattern.should == pattern
236
+ parsed.options.should == options
237
+ end
238
+ end
239
+ end
240
+
241
+ context 'when $regularExpression is nested in $regex' do
242
+ context 'with options' do
243
+ let(:input) do
244
+ {
245
+ "$regex" => {
246
+ "$regularExpression" => { "pattern" => "foo*", "options" => "" },
247
+ },
248
+ "$options" => "ix",
249
+ }
250
+ end
251
+
252
+ it 'parses' do
253
+ parsed.should == {
254
+ '$regex' => BSON::Regexp::Raw.new('foo*'), '$options' => 'ix'
255
+ }
256
+ end
257
+ end
258
+
259
+ context 'without options' do
260
+ let(:input) do
261
+ {
262
+ "$regex" => {
263
+ "$regularExpression" => { "pattern" => "foo*", "options" => "" },
264
+ },
265
+ }
266
+ end
267
+
268
+ it 'parses' do
269
+ parsed.should == {
270
+ '$regex' => BSON::Regexp::Raw.new('foo*'),
271
+ }
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end