bson 4.2.2 → 4.12.1
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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +25 -7
- data/Rakefile +16 -9
- data/ext/bson/{native-endian.h → bson-endian.h} +5 -99
- data/ext/bson/bson-native.h +125 -0
- data/ext/bson/bytebuf.c +133 -0
- data/ext/bson/endian.c +117 -0
- data/ext/bson/init.c +355 -0
- data/ext/bson/libbson-utf8.c +230 -0
- data/ext/bson/read.c +411 -0
- data/ext/bson/util.c +95 -0
- data/ext/bson/write.c +680 -0
- data/lib/bson.rb +6 -3
- data/lib/bson/active_support.rb +17 -0
- data/lib/bson/array.rb +57 -17
- data/lib/bson/binary.rb +185 -13
- data/lib/bson/boolean.rb +12 -3
- data/lib/bson/code.rb +16 -2
- data/lib/bson/code_with_scope.rb +32 -5
- data/lib/bson/config.rb +1 -1
- data/lib/bson/date.rb +12 -2
- data/lib/bson/date_time.rb +2 -2
- data/lib/bson/db_pointer.rb +110 -0
- data/lib/bson/decimal128.rb +17 -3
- data/lib/bson/decimal128/builder.rb +1 -1
- data/lib/bson/document.rb +152 -5
- data/lib/bson/environment.rb +2 -1
- data/lib/bson/error.rb +27 -0
- data/lib/bson/ext_json.rb +383 -0
- data/lib/bson/false_class.rb +1 -1
- data/lib/bson/float.rb +48 -2
- data/lib/bson/hash.rb +68 -17
- data/lib/bson/int32.rb +52 -13
- data/lib/bson/int64.rb +59 -15
- data/lib/bson/integer.rb +36 -2
- data/lib/bson/json.rb +1 -1
- data/lib/bson/max_key.rb +13 -1
- data/lib/bson/min_key.rb +13 -1
- data/lib/bson/nil_class.rb +4 -2
- data/lib/bson/object.rb +28 -1
- data/lib/bson/object_id.rb +16 -2
- data/lib/bson/open_struct.rb +1 -1
- data/lib/bson/regexp.rb +27 -4
- data/lib/bson/registry.rb +3 -3
- data/lib/bson/specialized.rb +4 -2
- data/lib/bson/string.rb +5 -3
- data/lib/bson/symbol.rb +99 -7
- data/lib/bson/time.rb +63 -4
- data/lib/bson/time_with_zone.rb +54 -0
- data/lib/bson/timestamp.rb +44 -6
- data/lib/bson/true_class.rb +1 -1
- data/lib/bson/undefined.rb +12 -1
- data/lib/bson/version.rb +2 -2
- data/spec/bson/array_spec.rb +18 -1
- data/spec/bson/binary_spec.rb +100 -3
- data/spec/bson/binary_uuid_spec.rb +189 -0
- data/spec/bson/boolean_spec.rb +1 -1
- data/spec/bson/byte_buffer_read_spec.rb +197 -0
- data/spec/bson/byte_buffer_spec.rb +121 -381
- data/spec/bson/byte_buffer_write_spec.rb +854 -0
- data/spec/bson/code_spec.rb +1 -1
- data/spec/bson/code_with_scope_spec.rb +1 -1
- data/spec/bson/date_spec.rb +1 -1
- data/spec/bson/date_time_spec.rb +54 -1
- data/spec/bson/decimal128_spec.rb +35 -35
- data/spec/bson/document_as_spec.rb +46 -0
- data/spec/bson/document_spec.rb +197 -30
- data/spec/bson/ext_json_parse_spec.rb +308 -0
- data/spec/bson/false_class_spec.rb +1 -1
- data/spec/bson/float_spec.rb +37 -1
- data/spec/bson/hash_as_spec.rb +57 -0
- data/spec/bson/hash_spec.rb +209 -1
- data/spec/bson/int32_spec.rb +180 -6
- data/spec/bson/int64_spec.rb +199 -6
- data/spec/bson/integer_spec.rb +29 -3
- data/spec/bson/json_spec.rb +1 -1
- data/spec/bson/max_key_spec.rb +1 -1
- data/spec/bson/min_key_spec.rb +1 -1
- data/spec/bson/nil_class_spec.rb +1 -1
- data/spec/bson/object_id_spec.rb +1 -1
- data/spec/bson/object_spec.rb +1 -1
- data/spec/bson/open_struct_spec.rb +1 -1
- data/spec/bson/raw_spec.rb +34 -2
- data/spec/bson/regexp_spec.rb +1 -1
- data/spec/bson/registry_spec.rb +1 -1
- data/spec/bson/string_spec.rb +19 -1
- data/spec/bson/symbol_raw_spec.rb +45 -0
- data/spec/bson/symbol_spec.rb +63 -3
- data/spec/bson/time_spec.rb +205 -2
- data/spec/bson/time_with_zone_spec.rb +68 -0
- data/spec/bson/timestamp_spec.rb +56 -1
- data/spec/bson/true_class_spec.rb +1 -1
- data/spec/bson/undefined_spec.rb +1 -1
- data/spec/bson_spec.rb +1 -1
- data/spec/{support → runners}/common_driver.rb +1 -1
- data/spec/runners/corpus.rb +185 -0
- data/spec/{support/corpus.rb → runners/corpus_legacy.rb} +41 -59
- data/spec/spec_helper.rb +40 -3
- data/spec/{bson/driver_bson_spec.rb → spec_tests/common_driver_spec.rb} +1 -0
- data/spec/{bson/corpus_spec.rb → spec_tests/corpus_legacy_spec.rb} +10 -7
- data/spec/spec_tests/corpus_spec.rb +124 -0
- data/spec/spec_tests/data/corpus/README.md +15 -0
- data/spec/spec_tests/data/corpus/array.json +49 -0
- data/spec/spec_tests/data/corpus/binary.json +113 -0
- data/spec/spec_tests/data/corpus/boolean.json +27 -0
- data/spec/spec_tests/data/corpus/code.json +67 -0
- data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
- data/spec/spec_tests/data/corpus/datetime.json +42 -0
- data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
- data/spec/spec_tests/data/corpus/dbref.json +31 -0
- data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
- data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
- data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
- data/spec/spec_tests/data/corpus/decimal128-4.json +117 -0
- data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
- data/spec/spec_tests/data/corpus/decimal128-6.json +119 -0
- data/spec/spec_tests/data/corpus/decimal128-7.json +323 -0
- data/spec/spec_tests/data/corpus/document.json +36 -0
- data/spec/spec_tests/data/corpus/double.json +87 -0
- data/spec/spec_tests/data/corpus/int32.json +43 -0
- data/spec/spec_tests/data/corpus/int64.json +43 -0
- data/spec/spec_tests/data/corpus/maxkey.json +12 -0
- data/spec/spec_tests/data/corpus/minkey.json +12 -0
- data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
- data/spec/spec_tests/data/corpus/multi-type.json +11 -0
- data/spec/spec_tests/data/corpus/null.json +12 -0
- data/spec/spec_tests/data/corpus/oid.json +28 -0
- data/spec/spec_tests/data/corpus/regex.json +65 -0
- data/spec/spec_tests/data/corpus/string.json +72 -0
- data/spec/spec_tests/data/corpus/symbol.json +80 -0
- data/spec/spec_tests/data/corpus/timestamp.json +34 -0
- data/spec/spec_tests/data/corpus/top.json +236 -0
- data/spec/spec_tests/data/corpus/undefined.json +15 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/array.json +8 -2
- data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/binary.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/boolean.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code_w_scope.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/document.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/double.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/datetime.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/dbpointer.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/int64.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/symbol.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/int32.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/maxkey.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/minkey.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/null.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/oid.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/regex.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/string.json +0 -0
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/timestamp.json +1 -1
- data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/top.json +0 -0
- data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/undefined.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-1.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-2.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-3.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-4.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-5.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-6.json +0 -0
- data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-7.json +0 -0
- data/spec/support/shared_examples.rb +3 -5
- data/spec/support/spec_config.rb +16 -0
- data/spec/support/utils.rb +10 -0
- metadata +227 -124
- metadata.gz.sig +0 -0
- data/ext/bson/bson_native.c +0 -762
data/lib/bson/environment.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2009-
|
1
|
+
# Copyright (C) 2009-2020 MongoDB Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -41,6 +41,7 @@ module BSON
|
|
41
41
|
# @return [ true, false ] If the Ruby version is 1.9.
|
42
42
|
#
|
43
43
|
# @since 4.2.0
|
44
|
+
# @deprecated
|
44
45
|
def ruby_1_9?
|
45
46
|
@ruby_1_9 ||= RUBY_VERSION < '2.0.0'
|
46
47
|
end
|
data/lib/bson/error.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module BSON
|
2
|
+
# Base exception class for all BSON-related errors.
|
3
|
+
#
|
4
|
+
# @note Many existing exceptions raised by bson-ruby do not derive from
|
5
|
+
# this base class. This will change in the next major version (5.0).
|
6
|
+
class Error < StandardError
|
7
|
+
|
8
|
+
# Exception raised when Extended JSON parsing fails.
|
9
|
+
class ExtJSONParseError < Error
|
10
|
+
end
|
11
|
+
|
12
|
+
# Exception raised when decoding BSON and the data contains an
|
13
|
+
# unsupported binary subtype.
|
14
|
+
class UnsupportedBinarySubtype < Error
|
15
|
+
end
|
16
|
+
|
17
|
+
# Exception raised when BSON decoding fails.
|
18
|
+
class BSONDecodeError < Error
|
19
|
+
end
|
20
|
+
|
21
|
+
# Exception raised when serializing an Array or Hash to BSON and an
|
22
|
+
# array or hash element is of a class that does not define how to serialize
|
23
|
+
# itself to BSON.
|
24
|
+
class UnserializableClass < Error
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,383 @@
|
|
1
|
+
# Copyright (C) 2019-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 'json'
|
16
|
+
|
17
|
+
module BSON
|
18
|
+
|
19
|
+
# This module contains methods for parsing Extended JSON 2.0.
|
20
|
+
# https://github.com/mongodb/specifications/blob/master/source/extended-json.rst
|
21
|
+
module ExtJSON
|
22
|
+
|
23
|
+
# Parses JSON in a string into a Ruby object tree.
|
24
|
+
#
|
25
|
+
# There are two strategies that this method can follow. If the canonical
|
26
|
+
# strategy is used which is the default, this method returns BSON types
|
27
|
+
# as much as possible. This allows the resulting object tree to be
|
28
|
+
# serialized back to extended JSON or to BSON while preserving the types.
|
29
|
+
# The relaxed strategy, enabled by passing {emit_relaxed: true} option,
|
30
|
+
# returns native Ruby types as much as possible which makes the resulting
|
31
|
+
# object tree easier to work with but may lose type information.
|
32
|
+
#
|
33
|
+
# Please note the following aspects of this method when emitting relaxed
|
34
|
+
# object trees:
|
35
|
+
#
|
36
|
+
# 1. $numberInt and $numberLong inputs produce Integer instances.
|
37
|
+
# 2. $regularExpression inputs produce BSON Regexp instances. This may
|
38
|
+
# change in a future version of bson-ruby to produce Ruby Regexp
|
39
|
+
# instances, potentially depending on regular expression options.
|
40
|
+
# 3. $numberDecimal inputs produce BSON Decimal128 instances. This may
|
41
|
+
# change in a future version of bson-ruby to produce Ruby BigDecimal
|
42
|
+
# instances instead.
|
43
|
+
#
|
44
|
+
# This method accepts canonical extended JSON, relaxed extended JSON and
|
45
|
+
# JSON without type information as well as a mix of the above.
|
46
|
+
#
|
47
|
+
# @note This method uses Ruby standard library's JSON.parse method to
|
48
|
+
# perform JSON parsing. As the JSON.parse method accepts inputs other
|
49
|
+
# than hashes, so does this method and therefore this method can return
|
50
|
+
# objects of any type.
|
51
|
+
#
|
52
|
+
# @param [ String ] str The string to parse.
|
53
|
+
#
|
54
|
+
# @option options [ nil | :bson ] :mode Which types to emit
|
55
|
+
#
|
56
|
+
# @return [ Object ] Parsed object tree.
|
57
|
+
module_function def parse(str, **options)
|
58
|
+
parse_obj(::JSON.parse(str), **options)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Transforms a Ruby object tree containing extended JSON type hashes
|
62
|
+
# into a Ruby object tree with said hashes replaced by BSON or Ruby native
|
63
|
+
# types.
|
64
|
+
#
|
65
|
+
# @example Convert extended JSON type hashes:
|
66
|
+
# BSON::ExtJSON.parse_obj('foo' => {'$numberLong' => '42'})
|
67
|
+
# => {"foo"=>#<BSON::Int64:0x000055e55f4d40f0 @value=42>}
|
68
|
+
#
|
69
|
+
# @example Convert a non-hash value:
|
70
|
+
# BSON::ExtJSON.parse_obj('$numberLong' => '42')
|
71
|
+
# => #<BSON::Int64:0x000055e55f4e6ed0 @value=42>
|
72
|
+
#
|
73
|
+
# There are two strategies that this method can follow. If the canonical
|
74
|
+
# strategy is used which is the default, this method returns BSON types
|
75
|
+
# as much as possible. This allows the resulting object tree to be
|
76
|
+
# serialized back to extended JSON or to BSON while preserving the types.
|
77
|
+
# The relaxed strategy, enabled by passing {emit_relaxed: true} option,
|
78
|
+
# returns native Ruby types as much as possible which makes the resulting
|
79
|
+
# object tree easier to work with but may lose type information.
|
80
|
+
#
|
81
|
+
# Please note the following aspects of this method when emitting relaxed
|
82
|
+
# object trees:
|
83
|
+
#
|
84
|
+
# 1. $numberInt and $numberLong inputs produce Integer instances.
|
85
|
+
# 2. $regularExpression inputs produce BSON Regexp instances. This may
|
86
|
+
# change in a future version of bson-ruby to produce Ruby Regexp
|
87
|
+
# instances, potentially depending on regular expression options.
|
88
|
+
# 3. $numberDecimal inputs produce BSON Decimal128 instances. This may
|
89
|
+
# change in a future version of bson-ruby to produce Ruby BigDecimal
|
90
|
+
# instances instead.
|
91
|
+
#
|
92
|
+
# This method accepts object trees resulting from parsing canonical
|
93
|
+
# extended JSON, relaxed extended JSON and JSON without type information
|
94
|
+
# as well as a mix of the above.
|
95
|
+
#
|
96
|
+
# @note This method accepts any types as input, not just Hash instances.
|
97
|
+
# Consequently, it can return values of any type.
|
98
|
+
#
|
99
|
+
# @param [ Object ] value The object tree to convert.
|
100
|
+
#
|
101
|
+
# @option options [ nil | :bson ] :mode Which types to emit
|
102
|
+
#
|
103
|
+
# @return [ Object ] Converted object tree.
|
104
|
+
module_function def parse_obj(value, **options)
|
105
|
+
# TODO implement :ruby and :ruby! modes
|
106
|
+
unless [nil, :bson].include?(options[:mode])
|
107
|
+
raise ArgumentError, "Invalid value for :mode option: #{options[:mode].inspect}"
|
108
|
+
end
|
109
|
+
|
110
|
+
case value
|
111
|
+
when String, TrueClass, FalseClass, NilClass, Numeric
|
112
|
+
value
|
113
|
+
when Hash
|
114
|
+
parse_hash(value, **options)
|
115
|
+
when Array
|
116
|
+
value.map do |item|
|
117
|
+
parse_obj(item, **options)
|
118
|
+
end
|
119
|
+
else
|
120
|
+
raise Error::ExtJSONParseError, "Unknown value type: #{value}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
RESERVED_KEYS = %w(
|
127
|
+
$oid $symbol $numberInt $numberLong $numberDouble $numberDecimal
|
128
|
+
$binary $code $scope $timestamp $regularExpression $dbPointer
|
129
|
+
$date $minKey $maxKey $undefined
|
130
|
+
).freeze
|
131
|
+
|
132
|
+
RESERVED_KEYS_HASH = Hash[RESERVED_KEYS.map do |key|
|
133
|
+
[key, true]
|
134
|
+
end].freeze
|
135
|
+
|
136
|
+
module_function def parse_hash(hash, **options)
|
137
|
+
if hash.empty?
|
138
|
+
return {}
|
139
|
+
end
|
140
|
+
|
141
|
+
if hash.key?('$ref')
|
142
|
+
# Legacy dbref handling.
|
143
|
+
# Note that according to extended json spec, only hash values (but
|
144
|
+
# not the top-level BSON document itself) may be of type "dbref".
|
145
|
+
# This code applies to both hash values and the hash overall; however,
|
146
|
+
# since we do not have DBRef as a distinct type, applying the below
|
147
|
+
# logic to top level hashes doesn't cause harm.
|
148
|
+
hash = hash.dup
|
149
|
+
ref = hash.delete('$ref')
|
150
|
+
# $ref can be a string value or an ObjectId
|
151
|
+
unless ref.is_a?(String)
|
152
|
+
raise Error::ExtJSONParseError, "Invalid $ref value: #{ref}"
|
153
|
+
end
|
154
|
+
# $id, if present, can be anything
|
155
|
+
id = hash.delete('$id')
|
156
|
+
if id.is_a?(Hash)
|
157
|
+
id = parse_hash(id)
|
158
|
+
end
|
159
|
+
# Preserve $id value as it was, do not convert either to ObjectId
|
160
|
+
# or to a string. But if the value was in {'$oid' => ...} format,
|
161
|
+
# the value is converted to an ObjectId instance so that
|
162
|
+
# serialization to BSON later on works correctly.
|
163
|
+
out = {'$ref' => ref, '$id' => id}
|
164
|
+
if hash.key?('$db')
|
165
|
+
# $db must always be a string, if provided
|
166
|
+
db = hash.delete('$db')
|
167
|
+
unless db.is_a?(String)
|
168
|
+
raise Error::ExtJSONParseError, "Invalid $db value: #{db}"
|
169
|
+
end
|
170
|
+
out['$db'] = db
|
171
|
+
end
|
172
|
+
return out.update(hash)
|
173
|
+
end
|
174
|
+
|
175
|
+
if hash.length == 1
|
176
|
+
key, value = hash.first
|
177
|
+
return case key
|
178
|
+
when '$oid'
|
179
|
+
ObjectId.from_string(value)
|
180
|
+
when '$symbol'
|
181
|
+
Symbol::Raw.new(value)
|
182
|
+
when '$numberInt'
|
183
|
+
unless value.is_a?(String)
|
184
|
+
raise Error::ExtJSONParseError, "$numberInt value is of an incorrect type: #{value}"
|
185
|
+
end
|
186
|
+
value.to_i
|
187
|
+
when '$numberLong'
|
188
|
+
unless value.is_a?(String)
|
189
|
+
raise Error::ExtJSONParseError, "$numberLong value is of an incorrect type: #{value}"
|
190
|
+
end
|
191
|
+
value = value.to_i
|
192
|
+
if options[:mode] != :bson
|
193
|
+
value
|
194
|
+
else
|
195
|
+
Int64.new(value)
|
196
|
+
end
|
197
|
+
when '$numberDouble'
|
198
|
+
# This handles string to double conversion as well as inf/-inf/nan
|
199
|
+
unless value.is_a?(String)
|
200
|
+
raise Error::ExtJSONParseError, "Invalid $numberDouble value: #{value}"
|
201
|
+
end
|
202
|
+
BigDecimal(value).to_f
|
203
|
+
when '$numberDecimal'
|
204
|
+
# TODO consider returning BigDecimal here instead of Decimal128
|
205
|
+
Decimal128.new(value)
|
206
|
+
when '$binary'
|
207
|
+
unless value.is_a?(Hash)
|
208
|
+
raise Error::ExtJSONParseError, "Invalid $binary value: #{value}"
|
209
|
+
end
|
210
|
+
unless value.keys.sort == %w(base64 subType)
|
211
|
+
raise Error::ExtJSONParseError, "Invalid $binary value: #{value}"
|
212
|
+
end
|
213
|
+
encoded_value = value['base64']
|
214
|
+
unless encoded_value.is_a?(String)
|
215
|
+
raise Error::ExtJSONParseError, "Invalid base64 value in $binary: #{value}"
|
216
|
+
end
|
217
|
+
subtype = value['subType']
|
218
|
+
unless subtype.is_a?(String)
|
219
|
+
raise Error::ExtJSONParseError, "Invalid subType value in $binary: #{value}"
|
220
|
+
end
|
221
|
+
create_binary(encoded_value, subtype)
|
222
|
+
|
223
|
+
when '$uuid'
|
224
|
+
unless /\A[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\z/.match(value)
|
225
|
+
raise Error::ExtJSONParseError, "Invalid $uuid value: #{value}"
|
226
|
+
end
|
227
|
+
|
228
|
+
return Binary.from_uuid(value)
|
229
|
+
|
230
|
+
when '$code'
|
231
|
+
unless value.is_a?(String)
|
232
|
+
raise Error::ExtJSONParseError, "Invalid $code value: #{value}"
|
233
|
+
end
|
234
|
+
Code.new(value)
|
235
|
+
when '$timestamp'
|
236
|
+
unless value.keys.sort == %w(i t)
|
237
|
+
raise Error::ExtJSONParseError, "Invalid $timestamp value: #{value}"
|
238
|
+
end
|
239
|
+
t = value['t']
|
240
|
+
unless t.is_a?(Integer)
|
241
|
+
raise Error::ExtJSONParseError, "Invalid t value: #{value}"
|
242
|
+
end
|
243
|
+
i = value['i']
|
244
|
+
unless i.is_a?(Integer)
|
245
|
+
raise Error::ExtJSONParseError, "Invalid i value: #{value}"
|
246
|
+
end
|
247
|
+
Timestamp.new(t, i)
|
248
|
+
when '$regularExpression'
|
249
|
+
unless value.keys.sort == %w(options pattern)
|
250
|
+
raise Error::ExtJSONParseError, "Invalid $regularExpression value: #{value}"
|
251
|
+
end
|
252
|
+
# TODO consider returning Ruby regular expression object here
|
253
|
+
create_regexp(value['pattern'], value['options'])
|
254
|
+
when '$dbPointer'
|
255
|
+
unless value.keys.sort == %w($id $ref)
|
256
|
+
raise Error::ExtJSONParseError, "Invalid $dbPointer value: #{value}"
|
257
|
+
end
|
258
|
+
DbPointer.new(value['$ref'], parse_hash(value['$id']))
|
259
|
+
when '$date'
|
260
|
+
case value
|
261
|
+
when String
|
262
|
+
::Time.parse(value).utc
|
263
|
+
when Hash
|
264
|
+
unless value.keys.sort == %w($numberLong)
|
265
|
+
raise Error::ExtJSONParseError, "Invalid value for $date: #{value}"
|
266
|
+
end
|
267
|
+
sec, msec = value.values.first.to_i.divmod(1000)
|
268
|
+
::Time.at(sec, msec*1000).utc
|
269
|
+
else
|
270
|
+
raise Error::ExtJSONParseError, "Invalid value for $date: #{value}"
|
271
|
+
end
|
272
|
+
when '$minKey'
|
273
|
+
unless value == 1
|
274
|
+
raise Error::ExtJSONParseError, "Invalid $minKey value: #{value}"
|
275
|
+
end
|
276
|
+
MinKey.new
|
277
|
+
when '$maxKey'
|
278
|
+
unless value == 1
|
279
|
+
raise Error::ExtJSONParseError, "Invalid $maxKey value: #{value}"
|
280
|
+
end
|
281
|
+
MaxKey.new
|
282
|
+
when '$undefined'
|
283
|
+
unless value == true
|
284
|
+
raise Error::ExtJSONParseError, "Invalid $undefined value: #{value}"
|
285
|
+
end
|
286
|
+
Undefined.new
|
287
|
+
else
|
288
|
+
map_hash(hash, **options)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
if hash.length == 2
|
293
|
+
sorted_keys = hash.keys.sort
|
294
|
+
first_key = sorted_keys.first
|
295
|
+
last_key = sorted_keys.last
|
296
|
+
|
297
|
+
if first_key == '$code'
|
298
|
+
unless sorted_keys == %w($code $scope)
|
299
|
+
raise Error::ExtJSONParseError, "Invalid $code value: #{hash}"
|
300
|
+
end
|
301
|
+
unless hash['$code'].is_a?(String)
|
302
|
+
raise Error::ExtJSONParseError, "Invalid $code value: #{value}"
|
303
|
+
end
|
304
|
+
|
305
|
+
return CodeWithScope.new(hash['$code'], map_hash(hash['$scope']))
|
306
|
+
end
|
307
|
+
|
308
|
+
if first_key == '$binary'
|
309
|
+
unless sorted_keys == %w($binary $type)
|
310
|
+
raise Error::ExtJSONParseError, "Invalid $binary value: #{hash}"
|
311
|
+
end
|
312
|
+
unless hash['$binary'].is_a?(String)
|
313
|
+
raise Error::ExtJSONParseError, "Invalid $binary value: #{value}"
|
314
|
+
end
|
315
|
+
unless hash['$type'].is_a?(String)
|
316
|
+
raise Error::ExtJSONParseError, "Invalid $binary subtype: #{hash['$type']}"
|
317
|
+
end
|
318
|
+
|
319
|
+
return create_binary(hash['$binary'], hash['$type'])
|
320
|
+
end
|
321
|
+
|
322
|
+
if last_key == '$regex'
|
323
|
+
unless sorted_keys == %w($options $regex)
|
324
|
+
raise Error::ExtJSONParseError, "Invalid $regex value: #{hash}"
|
325
|
+
end
|
326
|
+
|
327
|
+
if hash['$regex'].is_a?(Hash)
|
328
|
+
return {
|
329
|
+
'$regex' => parse_hash(hash['$regex']),
|
330
|
+
'$options' => hash['$options']
|
331
|
+
}
|
332
|
+
end
|
333
|
+
|
334
|
+
unless hash['$regex'].is_a?(String)
|
335
|
+
raise Error::ExtJSONParseError, "Invalid $regex pattern: #{hash['$regex']}"
|
336
|
+
end
|
337
|
+
unless hash['$options'].is_a?(String)
|
338
|
+
raise Error::ExtJSONParseError, "Invalid $regex options: #{hash['$options']}"
|
339
|
+
end
|
340
|
+
|
341
|
+
return create_regexp(hash['$regex'], hash['$options'])
|
342
|
+
end
|
343
|
+
|
344
|
+
verify_no_reserved_keys(hash, **options)
|
345
|
+
end
|
346
|
+
|
347
|
+
verify_no_reserved_keys(hash, **options)
|
348
|
+
end
|
349
|
+
|
350
|
+
module_function def verify_no_reserved_keys(hash, **options)
|
351
|
+
if hash.length > RESERVED_KEYS.length
|
352
|
+
if RESERVED_KEYS.any? { |key| hash.key?(key) }
|
353
|
+
raise Error::ExtJSONParseError, "Hash uses reserved keys but does not match a known type: #{hash}"
|
354
|
+
end
|
355
|
+
else
|
356
|
+
if hash.keys.any? { |key| RESERVED_KEYS_HASH.key?(key) }
|
357
|
+
raise Error::ExtJSONParseError, "Hash uses reserved keys but does not match a known type: #{hash}"
|
358
|
+
end
|
359
|
+
end
|
360
|
+
map_hash(hash, **options)
|
361
|
+
end
|
362
|
+
|
363
|
+
module_function def map_hash(hash, **options)
|
364
|
+
::Hash[hash.map do |key, value|
|
365
|
+
[key, parse_obj(value, **options)]
|
366
|
+
end]
|
367
|
+
end
|
368
|
+
|
369
|
+
module_function def create_binary(encoded_value, encoded_subtype)
|
370
|
+
subtype = encoded_subtype.hex
|
371
|
+
type = Binary::TYPES[subtype.chr]
|
372
|
+
unless type
|
373
|
+
# Requires https://jira.mongodb.org/browse/RUBY-2056
|
374
|
+
raise NotImplementedError, "Binary subtype #{encoded_subtype} is not currently supported"
|
375
|
+
end
|
376
|
+
Binary.new(Base64.decode64(encoded_value), type)
|
377
|
+
end
|
378
|
+
|
379
|
+
module_function def create_regexp(pattern, options)
|
380
|
+
Regexp::Raw.new(pattern, options)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
data/lib/bson/false_class.rb
CHANGED
data/lib/bson/float.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2009-
|
1
|
+
# Copyright (C) 2009-2020 MongoDB Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -46,18 +46,64 @@ module BSON
|
|
46
46
|
buffer.put_double(self)
|
47
47
|
end
|
48
48
|
|
49
|
+
# Converts this object to a representation directly serializable to
|
50
|
+
# Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
|
51
|
+
#
|
52
|
+
# This method returns the float itself if relaxed representation is
|
53
|
+
# requested and the value is finite, otherwise a $numberDouble hash.
|
54
|
+
#
|
55
|
+
# @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
|
56
|
+
# (default is canonical extended JSON)
|
57
|
+
#
|
58
|
+
# @return [ Hash | Float ] The extended json representation.
|
59
|
+
def as_extended_json(**options)
|
60
|
+
case infinite?
|
61
|
+
when 1
|
62
|
+
{'$numberDouble' => 'Infinity'}
|
63
|
+
when -1
|
64
|
+
{'$numberDouble' => '-Infinity'}
|
65
|
+
else
|
66
|
+
if nan?
|
67
|
+
{'$numberDouble' => 'NaN'}
|
68
|
+
else
|
69
|
+
if options[:mode] == :relaxed || options[:mode] == :legacy
|
70
|
+
self
|
71
|
+
else
|
72
|
+
value = if BSON::Environment.jruby?
|
73
|
+
# Hack to make bson corpus spec tests pass.
|
74
|
+
# JRuby serializes -1.2345678901234568e+18 as
|
75
|
+
# -1234567890123456770.0, which is valid but differs from MRI
|
76
|
+
# serialization. Extended JSON spec does not define precise
|
77
|
+
# stringification of floats.
|
78
|
+
# https://jira.mongodb.org/browse/SPEC-1536
|
79
|
+
if abs > 1e15
|
80
|
+
'%.17g' % to_s
|
81
|
+
else
|
82
|
+
to_s
|
83
|
+
end
|
84
|
+
else
|
85
|
+
to_s
|
86
|
+
end
|
87
|
+
{'$numberDouble' => value.upcase}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
49
93
|
module ClassMethods
|
50
94
|
|
51
95
|
# Deserialize an instance of a Float from a BSON double.
|
52
96
|
#
|
53
97
|
# @param [ ByteBuffer ] buffer The byte buffer.
|
54
98
|
#
|
99
|
+
# @option options [ nil | :bson ] :mode Decoding mode to use.
|
100
|
+
#
|
55
101
|
# @return [ Float ] The decoded Float.
|
56
102
|
#
|
57
103
|
# @see http://bsonspec.org/#/specification
|
58
104
|
#
|
59
105
|
# @since 2.0.0
|
60
|
-
def from_bson(buffer)
|
106
|
+
def from_bson(buffer, **options)
|
61
107
|
buffer.get_double
|
62
108
|
end
|
63
109
|
end
|