bson 4.9.0 → 4.15.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 (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");