bson 4.1.1 → 4.2.0.rc0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +18 -3
  5. data/ext/bson/{native.c → bson_native.c} +48 -8
  6. data/ext/bson/extconf.rb +1 -1
  7. data/ext/bson/native-endian.h +1 -1
  8. data/lib/bson.rb +3 -1
  9. data/lib/bson/config.rb +1 -1
  10. data/lib/bson/decimal128.rb +318 -0
  11. data/lib/bson/decimal128/builder.rb +448 -0
  12. data/lib/bson/document.rb +2 -2
  13. data/lib/bson/environment.rb +13 -1
  14. data/lib/bson/int32.rb +46 -0
  15. data/lib/bson/int64.rb +46 -0
  16. data/lib/bson/max_key.rb +1 -1
  17. data/lib/bson/min_key.rb +1 -1
  18. data/lib/bson/object_id.rb +2 -1
  19. data/lib/bson/open_struct.rb +57 -0
  20. data/lib/bson/regexp.rb +1 -1
  21. data/lib/bson/registry.rb +1 -1
  22. data/lib/bson/version.rb +2 -2
  23. data/spec/bson/decimal128_spec.rb +1583 -0
  24. data/spec/bson/document_spec.rb +1 -1
  25. data/spec/bson/driver_bson_spec.rb +77 -0
  26. data/spec/bson/int32_spec.rb +58 -0
  27. data/spec/bson/int64_spec.rb +58 -0
  28. data/spec/bson/open_struct_spec.rb +144 -0
  29. data/spec/spec_helper.rb +4 -0
  30. data/spec/support/common_driver.rb +347 -0
  31. data/spec/support/driver-spec-tests/decimal128/decimal128-1.json +363 -0
  32. data/spec/support/driver-spec-tests/decimal128/decimal128-2.json +793 -0
  33. data/spec/support/driver-spec-tests/decimal128/decimal128-3.json +1771 -0
  34. data/spec/support/driver-spec-tests/decimal128/decimal128-4.json +165 -0
  35. data/spec/support/driver-spec-tests/decimal128/decimal128-5.json +402 -0
  36. data/spec/support/driver-spec-tests/decimal128/decimal128-6.json +131 -0
  37. data/spec/support/driver-spec-tests/decimal128/decimal128-7.json +327 -0
  38. metadata +29 -4
  39. metadata.gz.sig +0 -0
@@ -887,7 +887,7 @@ describe BSON::Document do
887
887
  end
888
888
  end
889
889
 
890
- context "when binary strings with utf-8 values exist", if: BSON::Environment.jruby? && (JRUBY_VERSION !~ /9.0/) do
890
+ context "when binary strings with utf-8 values exist", if: BSON::Environment.jruby? && (JRUBY_VERSION < '9') do
891
891
 
892
892
  let(:string) { "europäisch" }
893
893
  let(:document) do
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Driver common bson tests' do
4
+
5
+ specs = DRIVER_COMMON_BSON_TESTS.map { |file| BSON::CommonDriver::Spec.new(file) }
6
+
7
+ specs.each do |spec|
8
+
9
+ context(spec.description) do
10
+
11
+ spec.valid_tests.each do |test|
12
+
13
+ context(test.description << ' - ' << test.string) do
14
+
15
+ it 'decodes the subject and displays as the correct string' do
16
+ expect(test.object.to_s).to eq(test.expected_to_string)
17
+ end
18
+
19
+ it 'encodes the decoded object correctly (roundtrips)' do
20
+ expect(test.reencoded_hex).to eq(test.subject.upcase)
21
+ end
22
+
23
+ it 'creates the correct object from extended json', if: test.from_ext_json? do
24
+ expect(test.from_json_string).to eq(test.object)
25
+ end
26
+
27
+ it 'creates the correct extended json document from the decoded object', if: test.to_ext_json? do
28
+ expect(test.document_as_json).to eq(test.ext_json)
29
+ end
30
+
31
+ it 'parses the string value to the same value as the decoded document', if: test.from_string? do
32
+ expect(BSON::Decimal128.new(test.string)).to eq(test.object)
33
+ end
34
+
35
+ it 'parses the #to_s (match_string) value to the same value as the decoded document', if: test.match_string do
36
+ expect(BSON::Decimal128.new(test.match_string)).to eq(test.object)
37
+ end
38
+
39
+ it 'creates the correct object from a non canonical string and then prints to the correct string', if: test.match_string do
40
+ expect(BSON::Decimal128.new(test.string).to_s).to eq(test.match_string)
41
+ end
42
+
43
+ it 'can be converted to a native type' do
44
+ expect(test.native_type_conversion).to be_a(test.native_type)
45
+ end
46
+ end
47
+ end
48
+
49
+ spec.invalid_tests.each do |test|
50
+
51
+ context(test.description << " - " << test.subject ) do
52
+
53
+ let(:error) do
54
+ ex = nil
55
+ begin
56
+ test.parse_invalid_string
57
+ rescue => e
58
+ ex = e
59
+ end
60
+ ex
61
+ end
62
+
63
+ let(:valid_errors) do
64
+ [
65
+ BSON::Decimal128::InvalidString,
66
+ BSON::Decimal128::InvalidRange
67
+ ]
68
+ end
69
+
70
+ it 'raises an exception when parsing' do
71
+ expect(error.class).to satisfy { |e| valid_errors.include?(e) }
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -16,6 +16,42 @@ require "spec_helper"
16
16
 
17
17
  describe BSON::Int32 do
18
18
 
19
+ describe "#intiialize" do
20
+
21
+ let(:obj) { described_class.new(integer) }
22
+
23
+ context "when the integer is 32-bit" do
24
+
25
+ let(:integer) { Integer::MAX_32BIT }
26
+
27
+ it "wraps the integer" do
28
+ expect(obj.instance_variable_get(:@integer)).to be(integer)
29
+ end
30
+ end
31
+
32
+ context "when the integer is too large" do
33
+
34
+ let(:integer) { Integer::MAX_32BIT + 1 }
35
+
36
+ it "raises an out of range error" do
37
+ expect {
38
+ obj
39
+ }.to raise_error(RangeError)
40
+ end
41
+ end
42
+
43
+ context "when the integer is too small" do
44
+
45
+ let(:integer) { Integer::MIN_32BIT - 1 }
46
+
47
+ it "raises an out of range error" do
48
+ expect {
49
+ obj
50
+ }.to raise_error(RangeError)
51
+ end
52
+ end
53
+ end
54
+
19
55
  describe "#from_bson" do
20
56
 
21
57
  let(:type) { 16.chr }
@@ -41,4 +77,26 @@ describe BSON::Int32 do
41
77
  expect(BSON::Int32.from_bson(encoded_2)).to eq(decoded_2)
42
78
  end
43
79
  end
80
+
81
+ describe "#to_bson" do
82
+
83
+ context "when the integer is 32 bit" do
84
+
85
+ let(:type) { 16.chr }
86
+ let(:obj) { BSON::Int32.new(Integer::MAX_32BIT - 1) }
87
+ let(:bson) { [ Integer::MAX_32BIT - 1 ].pack(BSON::Int32::PACK) }
88
+
89
+ it_behaves_like "a serializable bson element"
90
+ end
91
+ end
92
+
93
+ describe "#to_bson_key" do
94
+
95
+ let(:obj) { BSON::Int32.new(Integer::MAX_32BIT - 1) }
96
+ let(:encoded) { (Integer::MAX_32BIT - 1).to_s }
97
+
98
+ it "returns the key as a string" do
99
+ expect(obj.to_bson_key).to eq(encoded)
100
+ end
101
+ end
44
102
  end
@@ -16,6 +16,42 @@ require "spec_helper"
16
16
 
17
17
  describe BSON::Int64 do
18
18
 
19
+ describe "#intiialize" do
20
+
21
+ let(:obj) { described_class.new(integer) }
22
+
23
+ context "when the integer is 64-bit" do
24
+
25
+ let(:integer) { Integer::MAX_64BIT - 1 }
26
+
27
+ it "wraps the integer" do
28
+ expect(obj.instance_variable_get(:@integer)).to be(integer)
29
+ end
30
+ end
31
+
32
+ context "when the integer is too large" do
33
+
34
+ let(:integer) { Integer::MAX_64BIT + 1 }
35
+
36
+ it "raises an out of range error" do
37
+ expect {
38
+ obj
39
+ }.to raise_error(RangeError)
40
+ end
41
+ end
42
+
43
+ context "when the integer is too small" do
44
+
45
+ let(:integer) { Integer::MIN_64BIT - 1 }
46
+
47
+ it "raises an out of range error" do
48
+ expect {
49
+ obj
50
+ }.to raise_error(RangeError)
51
+ end
52
+ end
53
+ end
54
+
19
55
  describe "#from_bson" do
20
56
 
21
57
  let(:type) { 18.chr }
@@ -25,4 +61,26 @@ describe BSON::Int64 do
25
61
  it_behaves_like "a bson element"
26
62
  it_behaves_like "a deserializable bson element"
27
63
  end
64
+
65
+ describe "#to_bson" do
66
+
67
+ context "when the integer is 64 bit" do
68
+
69
+ let(:type) { 18.chr }
70
+ let(:obj) { BSON::Int64.new(Integer::MAX_64BIT - 1) }
71
+ let(:bson) { [ Integer::MAX_64BIT - 1 ].pack(BSON::Int64::PACK) }
72
+
73
+ it_behaves_like "a serializable bson element"
74
+ end
75
+ end
76
+
77
+ describe "#to_bson_key" do
78
+
79
+ let(:obj) { BSON::Int64.new(Integer::MAX_64BIT - 1) }
80
+ let(:encoded) { (Integer::MAX_64BIT - 1).to_s }
81
+
82
+ it "returns the key as a string" do
83
+ expect(obj.to_bson_key).to eq(encoded)
84
+ end
85
+ end
28
86
  end
@@ -0,0 +1,144 @@
1
+ # Copyright (C) 2016 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 OpenStruct do
18
+
19
+ describe "#to_bson" do
20
+
21
+ let(:type) { 3.chr }
22
+
23
+ it_behaves_like "a bson element"
24
+
25
+ context "when the struct is a single level" do
26
+
27
+ let(:obj) do
28
+ described_class.new({"key" => "value" })
29
+ end
30
+
31
+ let(:bson) do
32
+ "#{20.to_bson.to_s}#{String::BSON_TYPE}key#{BSON::NULL_BYTE}" +
33
+ "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}"
34
+ end
35
+
36
+ it_behaves_like "a serializable bson element"
37
+ end
38
+
39
+ context "when the struct has invalid keys" do
40
+
41
+ let(:obj) do
42
+ described_class.new({ "$testing" => "value" })
43
+ end
44
+
45
+ context "when validating keys" do
46
+
47
+ context "when validating globally" do
48
+
49
+ before do
50
+ BSON::Config.validating_keys = true
51
+ end
52
+
53
+ after do
54
+ BSON::Config.validating_keys = false
55
+ end
56
+
57
+ it "raises an error" do
58
+ expect {
59
+ obj.to_bson
60
+ }.to raise_error(BSON::String::IllegalKey)
61
+ end
62
+
63
+ context "when the struct contains an array of documents containing invalid keys" do
64
+
65
+ let(:obj) do
66
+ described_class.new({ "array" => [{ "$testing" => "value" }] })
67
+ end
68
+
69
+ it "raises an error" do
70
+ expect {
71
+ obj.to_bson
72
+ }.to raise_error(BSON::String::IllegalKey)
73
+ end
74
+ end
75
+ end
76
+
77
+ context "when validating locally" do
78
+
79
+ it "raises an error" do
80
+ expect {
81
+ obj.to_bson(BSON::ByteBuffer.new, true)
82
+ }.to raise_error(BSON::String::IllegalKey)
83
+ end
84
+
85
+ context "when the struct contains an array of documents containing invalid keys" do
86
+
87
+ let(:obj) do
88
+ described_class.new({ "array" => [{ "$testing" => "value" }] })
89
+ end
90
+
91
+ it "raises an error" do
92
+ expect {
93
+ obj.to_bson(BSON::ByteBuffer.new, true)
94
+ }.to raise_error(BSON::String::IllegalKey)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ context "when not validating keys" do
101
+
102
+ let(:bson) do
103
+ "#{25.to_bson.to_s}#{String::BSON_TYPE}$testing#{BSON::NULL_BYTE}" +
104
+ "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}"
105
+ end
106
+
107
+ it "serializes the struct" do
108
+ expect(obj.to_bson.to_s).to eq(bson)
109
+ end
110
+
111
+ context "when the struct contains an array of documents containing invalid keys" do
112
+
113
+ let(:obj) do
114
+ described_class.new({ "array" => [{ "$testing" => "value" }] })
115
+ end
116
+
117
+ let(:bson) do
118
+ "#{45.to_bson.to_s}#{Array::BSON_TYPE}array#{BSON::NULL_BYTE}" +
119
+ "#{[{ "$testing" => "value" }].to_bson.to_s}#{BSON::NULL_BYTE}"
120
+ end
121
+
122
+ it "serializes the struct" do
123
+ expect(obj.to_bson.to_s).to eq(bson)
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ context "when the struct is embedded" do
130
+
131
+ let(:obj) do
132
+ described_class.new({ "field" => OpenStruct.new({ "key" => "value" })})
133
+ end
134
+
135
+ let(:bson) do
136
+ "#{32.to_bson.to_s}#{Hash::BSON_TYPE}field#{BSON::NULL_BYTE}" +
137
+ "#{20.to_bson.to_s}#{String::BSON_TYPE}key#{BSON::NULL_BYTE}" +
138
+ "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}"
139
+ end
140
+
141
+ it_behaves_like "a serializable bson element"
142
+ end
143
+ end
144
+ end
@@ -12,9 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
16
+ DRIVER_COMMON_BSON_TESTS = Dir.glob("#{CURRENT_PATH}/support/driver-spec-tests/**/*.json")
17
+
15
18
  $LOAD_PATH.unshift(File.dirname(__FILE__))
16
19
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
17
20
 
21
+ require "ostruct"
18
22
  require "bson"
19
23
  require "json"
20
24
  require "rspec"
@@ -0,0 +1,347 @@
1
+ # Copyright (C) 2016 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 'json'
16
+ require 'bigdecimal'
17
+
18
+ module BSON
19
+ module CommonDriver
20
+
21
+ # Represents a Common Driver specification test.
22
+ #
23
+ # @since 4.2.0
24
+ class Spec
25
+
26
+ # The spec description.
27
+ #
28
+ # @return [ String ] The spec description.
29
+ #
30
+ # @since 4.2.0
31
+ attr_reader :description
32
+
33
+ # The document key of the object to test.
34
+ #
35
+ # @return [ String ] The document key.
36
+ #
37
+ # @since 4.2.0
38
+ attr_reader :test_key
39
+
40
+ # Instantiate the new spec.
41
+ #
42
+ # @example Create the spec.
43
+ # Spec.new(file)
44
+ #
45
+ # @param [ String ] file The name of the yaml file.
46
+ #
47
+ # @since 4.2.0
48
+ def initialize(file)
49
+ @spec = ::JSON.parse(File.read(file))
50
+ @valid = @spec['valid'] || []
51
+ @invalid = @spec['parseErrors'] || []
52
+ @description = @spec['description']
53
+ @test_key = @spec['test_key']
54
+ end
55
+
56
+ # Get a list of tests that don't raise exceptions.
57
+ #
58
+ # @example Get the list of valid tests.
59
+ # spec.valid_tests
60
+ #
61
+ # @return [ Array<BSON::CommonDriver::Test> ] The list of valid Tests.
62
+ #
63
+ # @since 4.2.0
64
+ def valid_tests
65
+ @valid_tests ||=
66
+ @valid.collect do |test|
67
+ BSON::CommonDriver::Test.new(self, test)
68
+ end
69
+ end
70
+
71
+ # Get a list of tests that raise exceptions.
72
+ #
73
+ # @example Get the list of invalid tests.
74
+ # spec.invalid_tests
75
+ #
76
+ # @return [ Array<BSON::CommonDriver::Test> ] The list of invalid Tests.
77
+ #
78
+ # @since 4.2.0
79
+ def invalid_tests
80
+ @invalid_tests ||=
81
+ @invalid.collect do |test|
82
+ BSON::CommonDriver::Test.new(self, test)
83
+ end
84
+ end
85
+
86
+ # The class of the bson object to test.
87
+ #
88
+ # @example Get the class of the object to test.
89
+ # spec.klass
90
+ #
91
+ # @return [ Class ] The object class.
92
+ #
93
+ # @since 4.2.0
94
+ def klass
95
+ @klass ||= BSON.const_get(description)
96
+ end
97
+ end
98
+
99
+ # Represents a single CommonDriver test.
100
+ #
101
+ # @since 4.2.0
102
+ class Test
103
+
104
+ # The test description.
105
+ #
106
+ # @return [ String ] The test description.
107
+ #
108
+ # @since 4.2.0
109
+ attr_reader :description
110
+
111
+ # The test subject.
112
+ #
113
+ # @return [ String ] The test subject.
114
+ #
115
+ # @since 4.2.0
116
+ attr_reader :subject
117
+
118
+ # The string to use to create a Decimal128.
119
+ #
120
+ # @return [ String ] The string to use in creating a Decimal128 object.
121
+ #
122
+ # @since 4.2.0
123
+ attr_reader :string
124
+
125
+ # The expected string representation of the Decimal128 object.
126
+ #
127
+ # @return [ String ] The object as a string.
128
+ #
129
+ # @since 4.2.0
130
+ attr_reader :match_string
131
+
132
+ # The json representation of the object.
133
+ #
134
+ # @return [ Hash ] The json representation of the object.
135
+ #
136
+ # @since 4.2.0
137
+ attr_reader :ext_json
138
+
139
+ # Instantiate the new Test.
140
+ #
141
+ # @example Create the test.
142
+ # Test.new(test)
143
+ #
144
+ # @param [ CommonDriver::Spec ] spec The test specification.
145
+ # @param [ Hash ] test The test specification.
146
+ #
147
+ # @since 4.2.0
148
+ def initialize(spec, test)
149
+ @spec = spec
150
+ @description = test['description']
151
+ @string = test['string']
152
+ @match_string = test['match_string']
153
+ @ext_json = ::JSON.parse(test['extjson']) if test['extjson']
154
+ @from_ext_json = test['from_extjson'].nil? ? true : test['from_extjson']
155
+ @to_ext_json = test['to_extjson'].nil? ? true : test['to_extjson']
156
+ @subject = test['subject']
157
+ @test_key = spec.test_key
158
+ end
159
+
160
+ # Get the reencoded document in hex format.
161
+ #
162
+ # @example Get the reencoded document as hex.
163
+ # test.reencoded_hex
164
+ #
165
+ # @return [ String ] The reencoded document in hex format.
166
+ #
167
+ # @since 4.2.0
168
+ def reencoded_hex
169
+ decoded_document.to_bson.to_s.unpack("H*").first.upcase
170
+ end
171
+
172
+ # The object tested.
173
+ #
174
+ # @example Get the object for this test.
175
+ # test.object
176
+ #
177
+ # @return [ BSON::Object ] The object.
178
+ #
179
+ # @since 4.2.0
180
+ def object
181
+ @object ||= decoded_document[@test_key]
182
+ end
183
+
184
+ # The object as json, in a document with the test key.
185
+ #
186
+ # @example Get a document with the object at the test key.
187
+ # test.document_as_json
188
+ #
189
+ # @return [ BSON::Document ] The json document.
190
+ #
191
+ # @since 4.2.0
192
+ def document_as_json
193
+ { @test_key => object.as_json }
194
+ end
195
+
196
+ # Use the string in the extended json to instantiate the bson object.
197
+ #
198
+ # @example Get a bson object from the string in the extended json.
199
+ # test.from_json
200
+ #
201
+ # @return [ BSON::Object ] The BSON object.
202
+ #
203
+ # @since 4.2.0
204
+ def from_json_string
205
+ klass.from_string(@ext_json[@test_key][klass::EXTENDED_JSON_KEY])
206
+ end
207
+
208
+ # Create an object from the given test string.
209
+ #
210
+ # @example
211
+ # test.parse_string
212
+ #
213
+ # @return [ BSON::Object ] The object.
214
+ #
215
+ # @since 4.2.0
216
+ def parse_string
217
+ klass.from_string(string)
218
+ end
219
+
220
+ # Attempt to create an object from an invalid string.
221
+ #
222
+ # @example
223
+ # test.parse_invalid_string
224
+ #
225
+ # @raise [ Error ] Parsing an invalid string will raise an error.
226
+ #
227
+ # @since 4.2.0
228
+ def parse_invalid_string
229
+ klass.from_string(subject)
230
+ end
231
+
232
+ # The class of the object being tested.
233
+ #
234
+ # @example
235
+ # test.klass
236
+ #
237
+ # @return [ Class ] The object class.
238
+ #
239
+ # @since 4.2.0
240
+ def klass
241
+ @spec.klass
242
+ end
243
+
244
+ # The error class of a parse error.
245
+ #
246
+ # @example
247
+ # test.parse_error
248
+ #
249
+ # @return [ Class ] The parse error class.
250
+ #
251
+ # @since 4.2.0
252
+ def parse_error
253
+ klass::InvalidRange
254
+ end
255
+
256
+ # Whether the object can be instantiated from extended json.
257
+ #
258
+ # @example Check if an object can be instantiated from the extended json.
259
+ # test.from_ex_json?
260
+ #
261
+ # @return [ true, false ] If the object can be instantiated from
262
+ # the provided extended json.
263
+ #
264
+ # @since 4.2.0
265
+ def from_ext_json?
266
+ @ext_json && @from_ext_json
267
+ end
268
+
269
+ # Whether the object can be represented as extended json.
270
+ #
271
+ # @example Check if an object can be represented as extended json.
272
+ # test.to_ext_json?
273
+ #
274
+ # @return [ true, false ] If the object can be represented as
275
+ # extended json.
276
+ #
277
+ # @since 4.2.0
278
+ def to_ext_json?
279
+ @ext_json && @to_ext_json
280
+ end
281
+
282
+ # Whether the object can be instantiated from a string.
283
+ #
284
+ # @example Check if an object can be instantiated from a string.
285
+ # test.from_string?
286
+ #
287
+ # @return [ true, false ] If the object can be instantiated from a string.
288
+ #
289
+ # @since 4.2.0
290
+ def from_string?
291
+ @string && @from_ext_json
292
+ end
293
+
294
+ # The expected string representation of the test object.
295
+ #
296
+ # @example Get the expected String representation of the test object.
297
+ # test.expected_to_string
298
+ #
299
+ # @return [ String ] The expected string representation.
300
+ #
301
+ # @since 4.2.0
302
+ def expected_to_string
303
+ match_string || string
304
+ end
305
+
306
+ # The Ruby class to which this bson object can be converted via a helper.
307
+ #
308
+ # @example Get the native type to which this object can be converted.
309
+ # test.native_type
310
+ #
311
+ # @return [ Class ] The Ruby native type.
312
+ #
313
+ # @since 4.2.0
314
+ def native_type
315
+ klass::NATIVE_TYPE
316
+ end
317
+
318
+ # Get the object converted to an instance of the native Ruby type.
319
+ #
320
+ # @example Get a native Ruby instance.
321
+ # test.native_type_conversion
322
+ #
323
+ # @return [ Object ] An instance of the Ruby native type.
324
+ #
325
+ # @since 4.2.0
326
+ def native_type_conversion
327
+ object.send("to_#{to_snake_case(native_type)}")
328
+ end
329
+
330
+ private
331
+
332
+ def to_snake_case(string)
333
+ string.to_s.gsub(/::/, '/').
334
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
335
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
336
+ tr("-", "_").
337
+ downcase
338
+ end
339
+
340
+ def decoded_document
341
+ @document ||= (data = [ @subject ].pack('H*')
342
+ buffer = BSON::ByteBuffer.new(data)
343
+ BSON::Document.from_bson(buffer))
344
+ end
345
+ end
346
+ end
347
+ end