bson 4.9.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +15 -6
  4. data/ext/bson/bson-native.h +4 -0
  5. data/ext/bson/init.c +75 -23
  6. data/ext/bson/read.c +63 -11
  7. data/ext/bson/write.c +42 -3
  8. data/lib/bson/active_support.rb +1 -0
  9. data/lib/bson/array.rb +5 -1
  10. data/lib/bson/big_decimal.rb +67 -0
  11. data/lib/bson/binary.rb +8 -5
  12. data/lib/bson/boolean.rb +2 -1
  13. data/lib/bson/code.rb +2 -1
  14. data/lib/bson/code_with_scope.rb +2 -1
  15. data/lib/bson/config.rb +1 -0
  16. data/lib/bson/date.rb +1 -0
  17. data/lib/bson/date_time.rb +2 -1
  18. data/lib/bson/db_pointer.rb +2 -1
  19. data/lib/bson/dbref.rb +152 -0
  20. data/lib/bson/decimal128/builder.rb +27 -20
  21. data/lib/bson/decimal128.rb +39 -14
  22. data/lib/bson/document.rb +61 -18
  23. data/lib/bson/environment.rb +1 -0
  24. data/lib/bson/error.rb +13 -0
  25. data/lib/bson/ext_json.rb +24 -11
  26. data/lib/bson/false_class.rb +2 -1
  27. data/lib/bson/float.rb +21 -32
  28. data/lib/bson/hash.rb +18 -6
  29. data/lib/bson/int32.rb +3 -2
  30. data/lib/bson/int64.rb +3 -2
  31. data/lib/bson/integer.rb +3 -2
  32. data/lib/bson/json.rb +1 -0
  33. data/lib/bson/max_key.rb +3 -2
  34. data/lib/bson/min_key.rb +3 -2
  35. data/lib/bson/nil_class.rb +2 -1
  36. data/lib/bson/object.rb +1 -0
  37. data/lib/bson/object_id.rb +4 -3
  38. data/lib/bson/open_struct.rb +1 -0
  39. data/lib/bson/regexp.rb +24 -7
  40. data/lib/bson/registry.rb +1 -0
  41. data/lib/bson/specialized.rb +1 -0
  42. data/lib/bson/string.rb +3 -2
  43. data/lib/bson/symbol.rb +2 -1
  44. data/lib/bson/time.rb +4 -3
  45. data/lib/bson/time_with_zone.rb +1 -0
  46. data/lib/bson/timestamp.rb +7 -6
  47. data/lib/bson/true_class.rb +2 -1
  48. data/lib/bson/undefined.rb +2 -1
  49. data/lib/bson/version.rb +2 -1
  50. data/lib/bson.rb +8 -5
  51. data/spec/README.md +14 -0
  52. data/spec/bson/array_spec.rb +17 -0
  53. data/spec/bson/big_decimal_spec.rb +316 -0
  54. data/spec/bson/binary_spec.rb +1 -1
  55. data/spec/bson/binary_uuid_spec.rb +12 -0
  56. data/spec/bson/byte_buffer_read_spec.rb +59 -3
  57. data/spec/bson/byte_buffer_spec.rb +129 -6
  58. data/spec/bson/byte_buffer_write_spec.rb +96 -0
  59. data/spec/bson/date_time_spec.rb +53 -0
  60. data/spec/bson/dbref_legacy_spec.rb +169 -0
  61. data/spec/bson/dbref_spec.rb +487 -0
  62. data/spec/bson/decimal128_spec.rb +231 -0
  63. data/spec/bson/document_as_spec.rb +46 -0
  64. data/spec/bson/document_spec.rb +43 -1
  65. data/spec/bson/ext_json_parse_spec.rb +37 -0
  66. data/spec/bson/hash_as_spec.rb +57 -0
  67. data/spec/bson/hash_spec.rb +105 -0
  68. data/spec/bson/int64_spec.rb +4 -24
  69. data/spec/bson/raw_spec.rb +18 -1
  70. data/spec/bson/regexp_spec.rb +52 -0
  71. data/spec/runners/common_driver.rb +1 -1
  72. data/spec/shared/LICENSE +20 -0
  73. data/spec/shared/bin/get-mongodb-download-url +17 -0
  74. data/spec/shared/bin/s3-copy +45 -0
  75. data/spec/shared/bin/s3-upload +69 -0
  76. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  77. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  78. data/spec/shared/lib/mrss/constraints.rb +386 -0
  79. data/spec/shared/lib/mrss/docker_runner.rb +271 -0
  80. data/spec/shared/lib/mrss/event_subscriber.rb +200 -0
  81. data/spec/shared/lib/mrss/lite_constraints.rb +191 -0
  82. data/spec/shared/lib/mrss/server_version_registry.rb +120 -0
  83. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  84. data/spec/shared/lib/mrss/utils.rb +15 -0
  85. data/spec/shared/share/Dockerfile.erb +338 -0
  86. data/spec/shared/share/haproxy-1.conf +16 -0
  87. data/spec/shared/share/haproxy-2.conf +17 -0
  88. data/spec/shared/shlib/distro.sh +74 -0
  89. data/spec/shared/shlib/server.sh +367 -0
  90. data/spec/shared/shlib/set_env.sh +131 -0
  91. data/spec/spec_helper.rb +31 -0
  92. data/spec/spec_tests/common_driver_spec.rb +2 -1
  93. data/spec/spec_tests/data/corpus/binary.json +33 -0
  94. data/spec/spec_tests/data/corpus/dbref.json +21 -1
  95. data/spec/spec_tests/data/corpus/document.json +4 -0
  96. data/spec/spec_tests/data/corpus/regex.json +2 -2
  97. data/spec/spec_tests/data/corpus/timestamp.json +10 -0
  98. data/spec/spec_tests/data/corpus/top.json +23 -12
  99. data/spec/support/spec_config.rb +8 -1
  100. data.tar.gz.sig +0 -0
  101. metadata +168 -93
  102. metadata.gz.sig +1 -0
data/lib/bson/binary.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +28,7 @@ module BSON
27
28
  # A binary is type 0x05 in the BSON spec.
28
29
  #
29
30
  # @since 2.0.0
30
- BSON_TYPE = 5.chr.force_encoding(BINARY).freeze
31
+ BSON_TYPE = ::String.new(5.chr, encoding: BINARY).freeze
31
32
 
32
33
  # The mappings of subtypes to their single byte identifiers.
33
34
  #
@@ -46,7 +47,8 @@ module BSON
46
47
  :uuid => 4.chr,
47
48
  :md5 => 5.chr,
48
49
  :ciphertext => 6.chr,
49
- :user => 128.chr
50
+ :column => 7.chr,
51
+ :user => 128.chr,
50
52
  }.freeze
51
53
 
52
54
  # The mappings of single byte subtypes to their symbol counterparts.
@@ -169,7 +171,7 @@ module BSON
169
171
  #
170
172
  # @since 2.3.0
171
173
  def inspect
172
- "<BSON::Binary:0x#{object_id} type=#{type} data=0x#{data[0, 8].unpack('H*').first}...>"
174
+ "<BSON::Binary:0x#{object_id} type=#{type} data=0x#{data[0, 8].unpack1('H*')}...>"
173
175
  end
174
176
 
175
177
  # Returns a string representation of the UUID stored in this Binary.
@@ -207,7 +209,8 @@ module BSON
207
209
  if representation && representation != :standard
208
210
  raise ArgumentError, "Binary of type :uuid can only be stringified to :standard representation, requested: #{representation.inspect}"
209
211
  end
210
- data.split('').map { |n| '%02x' % n.ord }.join.sub(/(.{8})(.{4})(.{4})(.{12})/, '\1-\2-\3-\4')
212
+
213
+ data.split('').map { |n| '%02x' % n.ord }.join.sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
211
214
  when :uuid_old
212
215
  if representation.nil?
213
216
  raise ArgumentError, 'Representation must be specified for BSON::Binary objects of type :uuid_old'
@@ -229,7 +232,7 @@ module BSON
229
232
  hex
230
233
  else
231
234
  raise ArgumentError, "Invalid representation: #{representation}"
232
- end.sub(/(.{8})(.{4})(.{4})(.{12})/, '\1-\2-\3-\4')
235
+ end.sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
233
236
  else
234
237
  raise TypeError, "The type of Binary must be :uuid or :uuid_old, this object is: #{type.inspect}"
235
238
  end
data/lib/bson/boolean.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +26,7 @@ module BSON
25
26
  # A boolean is type 0x08 in the BSON spec.
26
27
  #
27
28
  # @since 2.0.0
28
- BSON_TYPE = 8.chr.force_encoding(BINARY).freeze
29
+ BSON_TYPE = ::String.new(8.chr, encoding: BINARY).freeze
29
30
 
30
31
  # Deserialize a boolean from BSON.
31
32
  #
data/lib/bson/code.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +26,7 @@ module BSON
25
26
  # A code is type 0x0D in the BSON spec.
26
27
  #
27
28
  # @since 2.0.0
28
- BSON_TYPE = 13.chr.force_encoding(BINARY).freeze
29
+ BSON_TYPE = ::String.new(13.chr, encoding: BINARY).freeze
29
30
 
30
31
  # @!attribute javascript
31
32
  # @return [ String ] The javascript code.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,7 +27,7 @@ module BSON
26
27
  # A code with scope is type 0x0F in the BSON spec.
27
28
  #
28
29
  # @since 2.0.0
29
- BSON_TYPE = 15.chr.force_encoding(BINARY).freeze
30
+ BSON_TYPE = ::String.new(15.chr, encoding: BINARY).freeze
30
31
 
31
32
  # @!attribute javascript
32
33
  # @return [ String ] The javascript code.
data/lib/bson/config.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2016-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
data/lib/bson/date.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,7 +36,7 @@ module BSON
35
36
  #
36
37
  # @since 2.1.0
37
38
  def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
38
- to_time.to_bson(buffer)
39
+ gregorian.to_time.to_bson(buffer)
39
40
  end
40
41
  end
41
42
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +23,7 @@ module BSON
22
23
  include JSON
23
24
 
24
25
  # A DBPointer is type 0x0C in the BSON spec.
25
- BSON_TYPE = 0x0C.chr.force_encoding(BINARY).freeze
26
+ BSON_TYPE = ::String.new(0x0C.chr, encoding: BINARY).freeze
26
27
 
27
28
  # Create a new DBPointer object.
28
29
  #
data/lib/bson/dbref.rb ADDED
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ # Copyright (C) 2015-2021 MongoDB Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the 'License');
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an 'AS IS' BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ module BSON
19
+
20
+ # Represents a DBRef document in the database.
21
+ class DBRef < Document
22
+ include JSON
23
+
24
+ # The constant for the collection reference field.
25
+ #
26
+ # @deprecated
27
+ COLLECTION = '$ref'.freeze
28
+
29
+ # The constant for the id field.
30
+ #
31
+ # @deprecated
32
+ ID = '$id'.freeze
33
+
34
+ # The constant for the database field.
35
+ #
36
+ # @deprecated
37
+ DATABASE = '$db'.freeze
38
+
39
+ # @return [ String ] collection The collection name.
40
+ def collection
41
+ self['$ref']
42
+ end
43
+
44
+ # @return [ BSON::ObjectId ] id The referenced document id.
45
+ def id
46
+ self['$id']
47
+ end
48
+
49
+ # @return [ String ] database The database name.
50
+ def database
51
+ self['$db']
52
+ end
53
+
54
+ # Get the DBRef as a JSON document
55
+ #
56
+ # @example Get the DBRef as a JSON hash.
57
+ # dbref.as_json
58
+ #
59
+ # @return [ Hash ] The max key as a JSON hash.
60
+ def as_json(*args)
61
+ {}.update(self)
62
+ end
63
+
64
+ # Instantiate a new DBRef.
65
+ #
66
+ # @example Create the DBRef - hash API.
67
+ # BSON::DBRef.new({'$ref' => 'users', '$id' => id, '$db' => 'database'})
68
+ #
69
+ # @example Create the DBRef - legacy API.
70
+ # BSON::DBRef.new('users', id, 'database')
71
+ #
72
+ # @param [ Hash | String ] hash_or_collection The DBRef hash, when using
73
+ # the hash API. It must contain $ref and $id. When using the legacy API,
74
+ # this parameter must be a String containing the collection name.
75
+ # @param [ Object ] id The object id, when using the legacy API.
76
+ # @param [ String ] database The database name, when using the legacy API.
77
+ def initialize(hash_or_collection, id = nil, database = nil)
78
+ if hash_or_collection.is_a?(Hash)
79
+ hash = hash_or_collection
80
+
81
+ unless id.nil? && database.nil?
82
+ raise ArgumentError, 'When using the hash API, DBRef constructor accepts only one argument'
83
+ end
84
+ else
85
+ warn("BSON::DBRef constructor called with the legacy API - please use the hash API instead")
86
+
87
+ if id.nil?
88
+ raise ArgumentError, 'When using the legacy constructor API, id must be provided'
89
+ end
90
+
91
+ hash = {
92
+ :$ref => hash_or_collection,
93
+ :$id => id,
94
+ :$db => database,
95
+ }
96
+ end
97
+
98
+ hash = reorder_fields(hash)
99
+ %w($ref $id).each do |key|
100
+ unless hash[key]
101
+ raise ArgumentError, "DBRef must have #{key}: #{hash}"
102
+ end
103
+ end
104
+
105
+ unless hash['$ref'].is_a?(String)
106
+ raise ArgumentError, "The value for key $ref must be a string, got: #{hash['$ref']}"
107
+ end
108
+
109
+ if db = hash['$db']
110
+ unless db.is_a?(String)
111
+ raise ArgumentError, "The value for key $db must be a string, got: #{hash['$db']}"
112
+ end
113
+ end
114
+
115
+ super(hash)
116
+ end
117
+
118
+ # Converts the DBRef to raw BSON.
119
+ #
120
+ # @example Convert the DBRef to raw BSON.
121
+ # dbref.to_bson
122
+ #
123
+ # @param [ BSON::ByteBuffer ] buffer The encoded BSON buffer to append to.
124
+ # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
125
+ #
126
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
127
+ def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
128
+ as_json.to_bson(buffer, validating_keys)
129
+ end
130
+
131
+ private
132
+
133
+ # Reorder the fields of the given Hash to have $ref first, $id second,
134
+ # and $db third. The rest of the fields in the hash can come in any
135
+ # order after that.
136
+ #
137
+ # @param [ Hash ] hash The input hash. Must be a valid dbref.
138
+ #
139
+ # @return [ Hash ] The hash with it's fields reordered.
140
+ def reorder_fields(hash)
141
+ hash = BSON::Document.new(hash)
142
+ reordered = {}
143
+ reordered['$ref'] = hash.delete('$ref')
144
+ reordered['$id'] = hash.delete('$id')
145
+ if db = hash.delete('$db')
146
+ reordered['$db'] = db
147
+ end
148
+
149
+ reordered.update(hash)
150
+ end
151
+ end
152
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2016-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,27 +27,27 @@ module BSON
26
27
  # Infinity mask.
27
28
  #
28
29
  # @since 4.2.0
29
- INFINITY_MASK = 0x7800000000000000.freeze
30
+ INFINITY_MASK = 0x7800000000000000
30
31
 
31
32
  # NaN mask.
32
33
  #
33
34
  # @since 4.2.0
34
- NAN_MASK = 0x7c00000000000000.freeze
35
+ NAN_MASK = 0x7c00000000000000
35
36
 
36
37
  # SNaN mask.
37
38
  #
38
39
  # @since 4.2.0
39
- SNAN_MASK = (1 << 57).freeze
40
+ SNAN_MASK = (1 << 57)
40
41
 
41
42
  # Signed bit mask.
42
43
  #
43
44
  # @since 4.2.0
44
- SIGN_BIT_MASK = (1 << 63).freeze
45
+ SIGN_BIT_MASK = (1 << 63)
45
46
 
46
47
  # The two highest bits of the 64 high order bits.
47
48
  #
48
49
  # @since 4.2.0
49
- TWO_HIGHEST_BITS_SET = (3 << 61).freeze
50
+ TWO_HIGHEST_BITS_SET = (3 << 61)
50
51
 
51
52
  extend self
52
53
 
@@ -84,9 +85,13 @@ module BSON
84
85
  private
85
86
 
86
87
  def validate_range!(exponent, significand)
87
- unless valid_significand?(significand) && valid_exponent?(exponent)
88
+ unless valid_exponent?(exponent)
88
89
  raise Decimal128::InvalidRange.new
89
90
  end
91
+
92
+ unless valid_significand?(significand)
93
+ raise Decimal128::UnrepresentablePrecision.new
94
+ end
90
95
  end
91
96
 
92
97
  def valid_significand?(significand)
@@ -109,14 +114,14 @@ module BSON
109
114
  # @return [ Regex ] A regex matching a NaN string.
110
115
  #
111
116
  # @since 4.2.0
112
- NAN_REGEX = /^(\-)?(S)?NaN$/i.freeze
117
+ NAN_REGEX = /^(\-)?(S)?NaN$/i
113
118
 
114
119
  # Regex matching a string representing positive or negative Infinity.
115
120
  #
116
121
  # @return [ Regex ] A regex matching a positive or negative Infinity string.
117
122
  #
118
123
  # @since 4.2.0
119
- INFINITY_REGEX = /^(\+|\-)?Inf(inity)?$/i.freeze
124
+ INFINITY_REGEX = /^(\+|\-)?Inf(inity)?$/i
120
125
 
121
126
  # Regex for the fraction, including leading zeros.
122
127
  #
@@ -124,33 +129,33 @@ module BSON
124
129
  # including leading zeros.
125
130
  #
126
131
  # @since 4.2.0
127
- SIGNIFICAND_WITH_LEADING_ZEROS_REGEX = /(0*)(\d+)/.freeze
132
+ SIGNIFICAND_WITH_LEADING_ZEROS_REGEX = /(0*)(\d+)/
128
133
 
129
134
  # Regex for separating a negative sign from the significands.
130
135
  #
131
136
  # @return [ Regex ] The regex for separating a sign from significands.
132
137
  #
133
138
  # @since 4.2.0
134
- SIGN_AND_DIGITS_REGEX = /^(\-)?(\S+)/.freeze
139
+ SIGN_AND_DIGITS_REGEX = /^(\-)?(\S+)/
135
140
 
136
141
  # Regex matching a scientific exponent.
137
142
  #
138
143
  # @return [ Regex ] A regex matching E, e, E+, e+.
139
144
  #
140
145
  # @since 4.2.0
141
- SCIENTIFIC_EXPONENT_REGEX = /E\+?/i.freeze
146
+ SCIENTIFIC_EXPONENT_REGEX = /E\+?/i
142
147
 
143
148
  # Regex for capturing trailing zeros.
144
149
  #
145
150
  # @since 4.2.0
146
- TRAILING_ZEROS_REGEX = /[1-9]*(0+)$/.freeze
151
+ TRAILING_ZEROS_REGEX = /[1-9]*(0+)$/
147
152
 
148
153
  # Regex for a valid decimal128 string format.
149
154
  #
150
155
  # @return [ Regex ] The regex for a valid decimal128 string.
151
156
  #
152
157
  # @since 4.2.0
153
- VALID_DECIMAL128_STRING_REGEX = /^[\-\+]?(\d+(\.\d*)?|\.\d+)(E[\-\+]?\d+)?$/i.freeze
158
+ VALID_DECIMAL128_STRING_REGEX = /^[\-\+]?(\d+(\.\d*)?|\.\d+)(E[\-\+]?\d+)?$/i
154
159
 
155
160
  # Initialize the FromString Builder object.
156
161
  #
@@ -301,11 +306,11 @@ module BSON
301
306
 
302
307
  def to_special_bits
303
308
  case @big_decimal.sign
304
- when BigDecimal::SIGN_POSITIVE_INFINITE
309
+ when ::BigDecimal::SIGN_POSITIVE_INFINITE
305
310
  high = INFINITY_MASK
306
- when BigDecimal::SIGN_NEGATIVE_INFINITE
311
+ when ::BigDecimal::SIGN_NEGATIVE_INFINITE
307
312
  high = INFINITY_MASK | SIGN_BIT_MASK
308
- when BigDecimal::SIGN_NaN
313
+ when ::BigDecimal::SIGN_NaN
309
314
  high = NAN_MASK
310
315
  end
311
316
  [ 0, high ]
@@ -314,7 +319,7 @@ module BSON
314
319
  def to_bits
315
320
  sign, significand_str, base, exp = @big_decimal.split
316
321
  exponent = @big_decimal.zero? ? 0 : exp - significand_str.length
317
- is_negative = (sign == BigDecimal::SIGN_NEGATIVE_FINITE || sign == BigDecimal::SIGN_NEGATIVE_ZERO)
322
+ is_negative = (sign == ::BigDecimal::SIGN_NEGATIVE_FINITE || sign == ::BigDecimal::SIGN_NEGATIVE_ZERO)
318
323
  Builder.parts_to_bits(significand_str.to_i,
319
324
  exponent,
320
325
  is_negative)
@@ -337,14 +342,14 @@ module BSON
337
342
  # @return [ String ] The string representing NaN.
338
343
  #
339
344
  # @since 4.2.0
340
- NAN_STRING = 'NaN'.freeze
345
+ NAN_STRING = 'NaN'
341
346
 
342
347
  # String representing an Infinity value.
343
348
  #
344
349
  # @return [ String ] The string representing Infinity.
345
350
  #
346
351
  # @since 4.2.0
347
- INFINITY_STRING = 'Infinity'.freeze
352
+ INFINITY_STRING = 'Infinity'
348
353
 
349
354
  # Initialize the FromBigDecimal Builder object.
350
355
  #
@@ -366,11 +371,13 @@ module BSON
366
371
  #
367
372
  # @return [ String ] The string representing the decimal128 object.
368
373
  #
374
+ # @note The returned string may be frozen.
375
+ #
369
376
  # @since 4.2.0
370
377
  def string
371
378
  return NAN_STRING if nan?
372
379
  str = infinity? ? INFINITY_STRING : create_string
373
- negative? ? '-' << str : str
380
+ negative? ? "-#{str}" : str
374
381
  end
375
382
 
376
383
  private
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2016-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,36 +20,37 @@ module BSON
19
20
 
20
21
  class Decimal128
21
22
  include JSON
23
+ include Comparable
22
24
 
23
25
  # A Decimal128 is type 0x13 in the BSON spec.
24
26
  #
25
27
  # @since 4.2.0
26
- BSON_TYPE = 19.chr.force_encoding(BINARY).freeze
28
+ BSON_TYPE = ::String.new(19.chr, encoding: BINARY).freeze
27
29
 
28
30
  # Exponent offset.
29
31
  #
30
32
  # @since 4.2.0
31
- EXPONENT_OFFSET = 6176.freeze
33
+ EXPONENT_OFFSET = 6176
32
34
 
33
35
  # Minimum exponent.
34
36
  #
35
37
  # @since 4.2.0
36
- MIN_EXPONENT = -6176.freeze
38
+ MIN_EXPONENT = -6176
37
39
 
38
40
  # Maximum exponent.
39
41
  #
40
42
  # @since 4.2.0
41
- MAX_EXPONENT = 6111.freeze
43
+ MAX_EXPONENT = 6111
42
44
 
43
45
  # Maximum digits of precision.
44
46
  #
45
47
  # @since 4.2.0
46
- MAX_DIGITS_OF_PRECISION = 34.freeze
48
+ MAX_DIGITS_OF_PRECISION = 34
47
49
 
48
50
  # Key for this type when converted to extended json.
49
51
  #
50
52
  # @since 4.2.0
51
- EXTENDED_JSON_KEY = "$numberDecimal".freeze
53
+ EXTENDED_JSON_KEY = "$numberDecimal"
52
54
 
53
55
  # The native type to which this object can be converted.
54
56
  #
@@ -96,7 +98,16 @@ module BSON
96
98
  end
97
99
  alias :eql? :==
98
100
 
99
- # Create a new Decimal128 from a BigDecimal.
101
+ def <=>(other)
102
+ to_big_decimal <=> case other
103
+ when Decimal128
104
+ other.to_big_decimal
105
+ else
106
+ other
107
+ end
108
+ end
109
+
110
+ # Create a new Decimal128 from a string or a BigDecimal instance.
100
111
  #
101
112
  # @example Create a Decimal128 from a BigDecimal.
102
113
  # Decimal128.new(big_decimal)
@@ -104,7 +115,7 @@ module BSON
104
115
  # @param [ String, BigDecimal ] object The BigDecimal or String to use for
105
116
  # instantiating a Decimal128.
106
117
  #
107
- # @raise [ InvalidBigDecimal ] Raise error unless object argument is a BigDecimal.
118
+ # @raise [ InvalidArgument ] When argument is not a String or BigDecimal.
108
119
  #
109
120
  # @since 4.2.0
110
121
  def initialize(object)
@@ -262,7 +273,7 @@ module BSON
262
273
  # The custom error message for this error.
263
274
  #
264
275
  # @since 4.2.0
265
- MESSAGE = 'A Decimal128 can only be created from a String or BigDecimal.'.freeze
276
+ MESSAGE = 'A Decimal128 can only be created from a String or BigDecimal.'
266
277
 
267
278
  # Get the custom error message for the exception.
268
279
  #
@@ -288,7 +299,7 @@ module BSON
288
299
  # The custom error message for this error.
289
300
  #
290
301
  # @since 4.2.0
291
- MESSAGE = 'Invalid string format for creating a Decimal128 object.'.freeze
302
+ MESSAGE = 'Invalid string format for creating a Decimal128 object.'
292
303
 
293
304
  # Get the custom error message for the exception.
294
305
  #
@@ -303,9 +314,7 @@ module BSON
303
314
  end
304
315
  end
305
316
 
306
- # Raised when the exponent or significand provided is outside the valid range.
307
- #
308
- # @api private
317
+ # Raised when the exponent is outside the valid range.
309
318
  #
310
319
  # @since 4.2.0
311
320
  class InvalidRange < RuntimeError
@@ -313,7 +322,8 @@ module BSON
313
322
  # The custom error message for this error.
314
323
  #
315
324
  # @since 4.2.0
316
- MESSAGE = 'Value out of range for Decimal128 representation.'.freeze
325
+ # @deprecated
326
+ MESSAGE = 'Value out of range for Decimal128 representation.'
317
327
 
318
328
  # Get the custom error message for the exception.
319
329
  #
@@ -328,6 +338,21 @@ module BSON
328
338
  end
329
339
  end
330
340
 
341
+ # Raised when the significand provided is outside the valid range.
342
+ #
343
+ # @note This class derives from InvalidRange for backwards compatibility,
344
+ # however when RUBY-1806 is implemented it should be changed to derive
345
+ # from the base BSON exception class.
346
+ class UnrepresentablePrecision < InvalidRange
347
+
348
+ # Get the custom error message for the exception.
349
+ #
350
+ # @return [ String ] The error message.
351
+ def message
352
+ 'The value contains too much precision for Decimal128 representation'
353
+ end
354
+ end
355
+
331
356
  Registry.register(BSON_TYPE, self)
332
357
  end
333
358
  end
data/lib/bson/document.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -269,27 +270,69 @@ module BSON
269
270
  end
270
271
  end
271
272
 
272
- if instance_methods.include?(:slice)
273
- # Slices a document to include only the given keys.
274
- # Will normalize symbol keys into strings.
275
- # (this method is backported from ActiveSupport::Hash)
276
- #
277
- # @example Get a document/hash with only the `name` and `age` fields present
278
- # document # => { _id: <ObjectId>, :name => 'John', :age => 30, :location => 'Earth' }
279
- # document.slice(:name, 'age') # => { name: 'John', age: 30 }
280
- # document.slice('name') # => { name: 'John' }
281
- # document.slice(:foo) # => nil
282
- #
283
- # @param [ Array<String, Symbol> ] *keys Keys, that will be kept in the resulting document
284
- #
285
- # @return [ BSON::Document ] The document with only the selected keys
286
- #
287
- # @since 4.3.1
288
- def slice(*keys)
289
- super(*keys.map{|key| convert_key(key)})
273
+ # Slices a document to include only the given keys.
274
+ # Will normalize symbol keys into strings.
275
+ # (this method is backported from ActiveSupport::Hash)
276
+ #
277
+ # @example Get a document/hash with only the `name` and `age` fields present
278
+ # document # => { _id: <ObjectId>, :name => "John", :age => 30, :location => "Earth" }
279
+ # document.slice(:name, 'age') # => { "name": "John", "age" => 30 }
280
+ # document.slice('name') # => { "name" => "John" }
281
+ # document.slice(:foo) # => {}
282
+ #
283
+ # @param [ Array<String, Symbol> ] *keys Keys, that will be kept in the resulting document
284
+ #
285
+ # @return [ BSON::Document ] The document with only the selected keys
286
+ #
287
+ # @since 4.3.1
288
+ def slice(*keys)
289
+ keys.each_with_object(self.class.new) do |key, hash|
290
+ if key?(key)
291
+ hash[key] = self[key]
292
+ end
290
293
  end
291
294
  end
292
295
 
296
+ # Returns a new document consisting of the current document minus the
297
+ # specified keys.
298
+ #
299
+ # The keys to be removed can be specified as either strings or symbols.
300
+ #
301
+ # @example Get a document/hash with only the `name` and `age` fields removed
302
+ # document # => { _id: <ObjectId>, :name => 'John', :age => 30, :location => 'Earth' }
303
+ # document.except(:name, 'age') # => { _id: <ObjectId>, location: 'Earth' }
304
+ #
305
+ # @param [ Array<String, Symbol> ] *keys Keys, that will be removed in the resulting document
306
+ #
307
+ # @return [ BSON::Document ] The document with the specified keys removed.
308
+ #
309
+ # @note This method is always defined, even if Hash already contains a
310
+ # definition of #except, because ActiveSupport unconditionally defines
311
+ # its version of #except which doesn't work for BSON::Document which
312
+ # causes problems if ActiveSupport is loaded after bson-ruby is.
313
+ def except(*keys)
314
+ copy = dup
315
+ keys.each {|key| copy.delete(key)}
316
+ copy
317
+ end
318
+
319
+ def symbolize_keys!
320
+ raise ArgumentError, 'symbolize_keys! is not supported on BSON::Document instances. Please convert the document to hash first (using #to_h), then call #symbolize_keys! on the Hash instance'
321
+ end
322
+
323
+ # Override the Hash implementation of to_bson_normalized_value.
324
+ #
325
+ # BSON::Document is already of the correct type and already provides
326
+ # indifferent access to keys, hence no further conversions are necessary.
327
+ #
328
+ # Attempting to perform Hash's conversion on Document instances converts
329
+ # DBRefs to Documents which is wrong.
330
+ #
331
+ # @return [ BSON::Document ] The normalized hash.
332
+ def to_bson_normalized_value
333
+ self
334
+ end
335
+
293
336
  private
294
337
 
295
338
  def convert_key(key)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");