bson 4.15.0 → 5.0.2

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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +94 -10
  3. data/Rakefile +68 -39
  4. data/ext/bson/bson-native.h +12 -4
  5. data/ext/bson/extconf.rb +8 -3
  6. data/ext/bson/init.c +11 -11
  7. data/ext/bson/read.c +39 -9
  8. data/ext/bson/util.c +171 -16
  9. data/ext/bson/write.c +34 -39
  10. data/lib/bson/active_support.rb +1 -0
  11. data/lib/bson/array.rb +58 -32
  12. data/lib/bson/big_decimal.rb +16 -6
  13. data/lib/bson/binary.rb +271 -129
  14. data/lib/bson/boolean.rb +1 -0
  15. data/lib/bson/code.rb +10 -12
  16. data/lib/bson/code_with_scope.rb +9 -11
  17. data/lib/bson/config.rb +1 -27
  18. data/lib/bson/date.rb +2 -1
  19. data/lib/bson/date_time.rb +2 -1
  20. data/lib/bson/db_pointer.rb +12 -13
  21. data/lib/bson/dbref.rb +11 -9
  22. data/lib/bson/decimal128/builder.rb +10 -9
  23. data/lib/bson/decimal128.rb +25 -111
  24. data/lib/bson/document.rb +1 -0
  25. data/lib/bson/environment.rb +1 -0
  26. data/lib/bson/error/bson_decode_error.rb +11 -0
  27. data/lib/bson/error/ext_json_parse_error.rb +11 -0
  28. data/lib/bson/error/illegal_key.rb +23 -0
  29. data/lib/bson/error/invalid_binary_type.rb +37 -0
  30. data/lib/bson/error/invalid_dbref_argument.rb +12 -0
  31. data/lib/bson/error/invalid_decimal128_argument.rb +25 -0
  32. data/lib/bson/error/invalid_decimal128_range.rb +27 -0
  33. data/lib/bson/error/invalid_decimal128_string.rb +26 -0
  34. data/lib/bson/error/invalid_key.rb +24 -0
  35. data/lib/bson/error/invalid_object_id.rb +11 -0
  36. data/lib/bson/error/invalid_regexp_pattern.rb +13 -0
  37. data/lib/bson/error/unrepresentable_precision.rb +19 -0
  38. data/lib/bson/error/unserializable_class.rb +13 -0
  39. data/lib/bson/error/unsupported_binary_subtype.rb +12 -0
  40. data/lib/bson/error/unsupported_type.rb +11 -0
  41. data/lib/bson/error.rb +16 -28
  42. data/lib/bson/ext_json.rb +2 -1
  43. data/lib/bson/false_class.rb +2 -1
  44. data/lib/bson/float.rb +3 -2
  45. data/lib/bson/hash.rb +128 -73
  46. data/lib/bson/int32.rb +17 -5
  47. data/lib/bson/int64.rb +17 -5
  48. data/lib/bson/integer.rb +4 -5
  49. data/lib/bson/json.rb +1 -0
  50. data/lib/bson/max_key.rb +8 -10
  51. data/lib/bson/min_key.rb +8 -10
  52. data/lib/bson/nil_class.rb +1 -0
  53. data/lib/bson/object.rb +7 -27
  54. data/lib/bson/object_id.rb +84 -120
  55. data/lib/bson/open_struct.rb +3 -2
  56. data/lib/bson/regexp.rb +36 -65
  57. data/lib/bson/registry.rb +2 -6
  58. data/lib/bson/specialized.rb +2 -1
  59. data/lib/bson/string.rb +4 -27
  60. data/lib/bson/symbol.rb +23 -20
  61. data/lib/bson/time.rb +3 -2
  62. data/lib/bson/time_with_zone.rb +13 -1
  63. data/lib/bson/timestamp.rb +3 -2
  64. data/lib/bson/true_class.rb +2 -1
  65. data/lib/bson/undefined.rb +15 -1
  66. data/lib/bson/version.rb +3 -1
  67. data/lib/bson.rb +3 -2
  68. data/spec/bson/array_spec.rb +19 -60
  69. data/spec/bson/big_decimal_spec.rb +16 -4
  70. data/spec/bson/binary_spec.rb +129 -81
  71. data/spec/bson/binary_uuid_spec.rb +1 -0
  72. data/spec/bson/boolean_spec.rb +1 -0
  73. data/spec/bson/byte_buffer_read_spec.rb +1 -0
  74. data/spec/bson/byte_buffer_spec.rb +1 -0
  75. data/spec/bson/byte_buffer_write_spec.rb +1 -0
  76. data/spec/bson/code_spec.rb +5 -3
  77. data/spec/bson/code_with_scope_spec.rb +5 -3
  78. data/spec/bson/config_spec.rb +1 -35
  79. data/spec/bson/date_spec.rb +1 -0
  80. data/spec/bson/date_time_spec.rb +1 -0
  81. data/spec/bson/dbref_legacy_spec.rb +20 -3
  82. data/spec/bson/dbref_spec.rb +9 -9
  83. data/spec/bson/decimal128_spec.rb +40 -20
  84. data/spec/bson/document_as_spec.rb +1 -0
  85. data/spec/bson/document_spec.rb +1 -1
  86. data/spec/bson/ext_json_parse_spec.rb +1 -0
  87. data/spec/bson/false_class_spec.rb +8 -0
  88. data/spec/bson/float_spec.rb +8 -3
  89. data/spec/bson/hash_as_spec.rb +1 -0
  90. data/spec/bson/hash_spec.rb +87 -75
  91. data/spec/bson/int32_spec.rb +21 -6
  92. data/spec/bson/int64_spec.rb +21 -6
  93. data/spec/bson/integer_spec.rb +45 -13
  94. data/spec/bson/json_spec.rb +1 -0
  95. data/spec/bson/max_key_spec.rb +5 -3
  96. data/spec/bson/min_key_spec.rb +5 -3
  97. data/spec/bson/nil_class_spec.rb +1 -0
  98. data/spec/bson/object_id_spec.rb +57 -4
  99. data/spec/bson/object_spec.rb +2 -1
  100. data/spec/bson/open_struct_spec.rb +14 -71
  101. data/spec/bson/raw_spec.rb +9 -15
  102. data/spec/bson/regexp_spec.rb +4 -3
  103. data/spec/bson/registry_spec.rb +2 -1
  104. data/spec/bson/string_spec.rb +13 -38
  105. data/spec/bson/symbol_raw_spec.rb +25 -0
  106. data/spec/bson/symbol_spec.rb +15 -18
  107. data/spec/bson/time_spec.rb +1 -0
  108. data/spec/bson/time_with_zone_spec.rb +1 -0
  109. data/spec/bson/timestamp_spec.rb +1 -0
  110. data/spec/bson/true_class_spec.rb +8 -0
  111. data/spec/bson/undefined_spec.rb +27 -0
  112. data/spec/bson_spec.rb +1 -0
  113. data/spec/runners/common_driver.rb +6 -5
  114. data/spec/runners/corpus.rb +6 -0
  115. data/spec/runners/corpus_legacy.rb +1 -0
  116. data/spec/spec_helper.rb +1 -0
  117. data/spec/spec_tests/common_driver_spec.rb +9 -4
  118. data/spec/spec_tests/corpus_legacy_spec.rb +1 -0
  119. data/spec/spec_tests/corpus_spec.rb +13 -3
  120. data/spec/spec_tests/data/corpus/binary.json +5 -0
  121. data/spec/spec_tests/data/corpus/code.json +13 -13
  122. data/spec/spec_tests/data/corpus/decimal128-4.json +48 -0
  123. data/spec/spec_tests/data/corpus/decimal128-6.json +12 -0
  124. data/spec/spec_tests/data/corpus/decimal128-7.json +4 -0
  125. data/spec/spec_tests/data/corpus/document.json +20 -0
  126. data/spec/spec_tests/data/corpus/symbol.json +7 -7
  127. data/spec/spec_tests/data/corpus/top.json +18 -3
  128. data/spec/support/shared_examples.rb +28 -5
  129. data/spec/support/spec_config.rb +1 -0
  130. data/spec/support/utils.rb +49 -1
  131. metadata +114 -164
  132. checksums.yaml.gz.sig +0 -0
  133. data/spec/shared/LICENSE +0 -20
  134. data/spec/shared/bin/get-mongodb-download-url +0 -17
  135. data/spec/shared/bin/s3-copy +0 -45
  136. data/spec/shared/bin/s3-upload +0 -69
  137. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  138. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  139. data/spec/shared/lib/mrss/constraints.rb +0 -386
  140. data/spec/shared/lib/mrss/docker_runner.rb +0 -271
  141. data/spec/shared/lib/mrss/event_subscriber.rb +0 -200
  142. data/spec/shared/lib/mrss/lite_constraints.rb +0 -191
  143. data/spec/shared/lib/mrss/server_version_registry.rb +0 -120
  144. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  145. data/spec/shared/lib/mrss/utils.rb +0 -15
  146. data/spec/shared/share/Dockerfile.erb +0 -338
  147. data/spec/shared/share/haproxy-1.conf +0 -16
  148. data/spec/shared/share/haproxy-2.conf +0 -17
  149. data/spec/shared/shlib/distro.sh +0 -74
  150. data/spec/shared/shlib/server.sh +0 -367
  151. data/spec/shared/shlib/set_env.sh +0 -131
  152. data.tar.gz.sig +0 -0
  153. metadata.gz.sig +0 -1
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # rubocop:todo all
2
3
  # Copyright (C) 2009-2021 MongoDB Inc.
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +22,7 @@ module BSON
21
22
  # @see http://bsonspec.org/#/specification
22
23
  module BigDecimal
23
24
 
24
- # BigDecimals are serialized as Decimal128s under the hood. A Decimal128
25
+ # BigDecimals are serialized as Decimal128s under the hood. A Decimal128
25
26
  # is type 0x13 in the BSON spec.
26
27
  BSON_TYPE = ::String.new(19.chr, encoding: BINARY).freeze
27
28
 
@@ -33,8 +34,8 @@ module BSON
33
34
  # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
34
35
  #
35
36
  # @see http://bsonspec.org/#/specification
36
- def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
37
- BSON::Decimal128.new(to_s).to_bson(buffer, validating_keys)
37
+ def to_bson(buffer = ByteBuffer.new)
38
+ BSON::Decimal128.new(to_s).to_bson(buffer)
38
39
  end
39
40
 
40
41
  # Get the BSON type for BigDecimal. This is the same BSON type as
@@ -45,7 +46,8 @@ module BSON
45
46
 
46
47
  module ClassMethods
47
48
 
48
- # Deserialize the BigDecimal from raw BSON bytes.
49
+ # Deserialize the BigDecimal from raw BSON bytes. If the :mode option
50
+ # is set to BSON, this will return a BSON::Decimal128
49
51
  #
50
52
  # @example Get the BigDecimal from BSON.
51
53
  # BigDecimal.from_bson(bson)
@@ -54,11 +56,19 @@ module BSON
54
56
  #
55
57
  # @option options [ nil | :bson ] :mode Decoding mode to use.
56
58
  #
57
- # @return [ BigDecimal ] The decimal object.
59
+ # @return [ BigDecimal | BSON::Decimal128 ] The decimal object.
58
60
  def from_bson(buffer, **options)
59
- Decimal128.from_bson(buffer, **options).to_big_decimal
61
+ dec128 = Decimal128.from_bson(buffer, **options)
62
+ if options[:mode] == :bson
63
+ dec128
64
+ else
65
+ dec128.to_d
66
+ end
60
67
  end
61
68
  end
69
+
70
+ # Register this type when the module is loaded.
71
+ Registry.register(BSON_TYPE, ::BigDecimal)
62
72
  end
63
73
 
64
74
  # Enrich the core BigDecimal class with this module.
data/lib/bson/binary.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Copyright (C) 2009-2020 MongoDB Inc.
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +17,6 @@
16
17
  require 'base64'
17
18
 
18
19
  module BSON
19
-
20
20
  # Represents binary data.
21
21
  #
22
22
  # @see http://bsonspec.org/#/specification
@@ -24,6 +24,7 @@ module BSON
24
24
  # @since 2.0.0
25
25
  class Binary
26
26
  include JSON
27
+ include Comparable
27
28
 
28
29
  # A binary is type 0x05 in the BSON spec.
29
30
  #
@@ -40,17 +41,21 @@ module BSON
40
41
  #
41
42
  # @since 2.0.0
42
43
  SUBTYPES = {
43
- :generic => 0.chr,
44
- :function => 1.chr,
45
- :old => 2.chr,
46
- :uuid_old => 3.chr,
47
- :uuid => 4.chr,
48
- :md5 => 5.chr,
49
- :ciphertext => 6.chr,
50
- :column => 7.chr,
51
- :user => 128.chr,
44
+ generic: 0.chr,
45
+ function: 1.chr,
46
+ old: 2.chr,
47
+ uuid_old: 3.chr,
48
+ uuid: 4.chr,
49
+ md5: 5.chr,
50
+ ciphertext: 6.chr,
51
+ column: 7.chr,
52
+ sensitive: 8.chr,
53
+ user: 128.chr,
52
54
  }.freeze
53
55
 
56
+ # The starting point of the user-defined subtype range.
57
+ USER_SUBTYPE = 0x80
58
+
54
59
  # The mappings of single byte subtypes to their symbol counterparts.
55
60
  #
56
61
  # @since 2.0.0
@@ -64,10 +69,11 @@ module BSON
64
69
  attr_reader :data
65
70
 
66
71
  # @return [ Symbol ] The binary type.
67
- #
68
- # @since 2.0.0
69
72
  attr_reader :type
70
73
 
74
+ # @return [ String ] The raw type value, as an encoded integer.
75
+ attr_reader :raw_type
76
+
71
77
  # Determine if this binary object is equal to another object.
72
78
  #
73
79
  # @example Check the binary equality.
@@ -80,10 +86,25 @@ module BSON
80
86
  # @since 2.0.0
81
87
  def ==(other)
82
88
  return false unless other.is_a?(Binary)
89
+
83
90
  type == other.type && data == other.data
84
91
  end
85
92
  alias eql? ==
86
93
 
94
+ # Compare this binary object to another object. The two objects must have
95
+ # the same type for any meaningful comparison.
96
+ #
97
+ # @param [ Object ] other The object to compare against.
98
+ #
99
+ # @return [ Integer | nil ] If the objects have the same type, the result
100
+ # is -1 if self < other, 0 if self == other, and 1 if self > other. If
101
+ # other is not a Binary, or is a Binary of a different type, returns nil.
102
+ def <=>(other)
103
+ return nil unless other.is_a?(Binary) && type == other.type
104
+
105
+ data <=> other.data
106
+ end
107
+
87
108
  # Generates a Fixnum hash value for this object.
88
109
  #
89
110
  # Allows using Binary as hash keys.
@@ -92,41 +113,36 @@ module BSON
92
113
  #
93
114
  # @since 2.3.1
94
115
  def hash
95
- data.hash + type.hash
116
+ [ data, type ].hash
96
117
  end
97
118
 
98
- # Get the binary as JSON hash data.
99
- #
100
- # @example Get the binary as a JSON hash.
101
- # binary.as_json
119
+ # Return a representation of the object for use in
120
+ # application-level JSON serialization. Since BSON::Binary
121
+ # is used exclusively in BSON-related contexts, this
122
+ # method returns the canonical Extended JSON representation.
102
123
  #
103
- # @return [ Hash ] The binary as a JSON hash.
104
- #
105
- # @since 2.0.0
106
- # @deprecated Use as_extended_json instead.
107
- def as_json(*args)
124
+ # @return [ Hash ] The extended json representation.
125
+ def as_json(*_args)
108
126
  as_extended_json
109
127
  end
110
128
 
111
129
  # Converts this object to a representation directly serializable to
112
- # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
130
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
113
131
  #
114
132
  # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
115
133
  # (default is canonical extended JSON)
116
134
  #
117
135
  # @return [ Hash ] The extended json representation.
118
136
  def as_extended_json(**options)
119
- subtype = SUBTYPES[type].each_byte.map { |c| c.to_s(16) }.join
120
- if subtype.length == 1
121
- subtype = "0#{subtype}"
122
- end
137
+ subtype = @raw_type.each_byte.map { |c| c.to_s(16) }.join
138
+ subtype = "0#{subtype}" if subtype.length == 1
123
139
 
124
140
  value = Base64.encode64(data).strip
125
141
 
126
142
  if options[:mode] == :legacy
127
- { "$binary" => value, "$type" => subtype }
143
+ { '$binary' => value, '$type' => subtype }
128
144
  else
129
- { "$binary" => {'base64' => value, "subType" => subtype }}
145
+ { '$binary' => { 'base64' => value, 'subType' => subtype } }
130
146
  end
131
147
  end
132
148
 
@@ -146,20 +162,17 @@ module BSON
146
162
  # @param [ Symbol ] type The binary type.
147
163
  #
148
164
  # @since 2.0.0
149
- def initialize(data = "", type = :generic)
150
- validate_type!(type)
151
-
152
- # The Binary class used to force encoding to BINARY when serializing to
153
- # BSON. Instead of doing that during serialization, perform this
154
- # operation during Binary construction to make it clear that once
155
- # the string is given to the Binary, the data is treated as a binary
156
- # string and not a text string in any encoding.
157
- unless data.encoding == Encoding.find('BINARY')
158
- data = data.dup.force_encoding('BINARY')
159
- end
165
+ def initialize(data = '', type = :generic)
166
+ initialize_instance(data, type)
167
+ end
160
168
 
161
- @data = data
162
- @type = type
169
+ # For legacy deserialization support where BSON::Binary objects are
170
+ # expected to have a specific internal representation (with only
171
+ # @type and @data instance variables).
172
+ #
173
+ # @api private
174
+ def init_with(coder)
175
+ initialize_instance(coder['data'], coder['type'])
163
176
  end
164
177
 
165
178
  # Get a nice string for use with object inspection.
@@ -202,37 +215,15 @@ module BSON
202
215
  # @api experimental
203
216
  def to_uuid(representation = nil)
204
217
  if representation.is_a?(String)
205
- raise ArgumentError, "Representation must be given as a symbol: #{representation}"
218
+ raise ArgumentError,
219
+ "Representation must be given as a symbol: #{representation.inspect}"
206
220
  end
221
+
207
222
  case type
208
223
  when :uuid
209
- if representation && representation != :standard
210
- raise ArgumentError, "Binary of type :uuid can only be stringified to :standard representation, requested: #{representation.inspect}"
211
- end
212
-
213
- data.split('').map { |n| '%02x' % n.ord }.join.sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
224
+ from_uuid_to_uuid(representation || :standard)
214
225
  when :uuid_old
215
- if representation.nil?
216
- raise ArgumentError, 'Representation must be specified for BSON::Binary objects of type :uuid_old'
217
- end
218
-
219
- hex = data.split('').map { |n| '%02x' % n.ord }.join
220
-
221
- case representation
222
- when :standard
223
- raise ArgumentError, 'BSON::Binary objects of type :uuid_old cannot be stringified to :standard representation'
224
- when :csharp_legacy
225
- hex.sub(/\A(..)(..)(..)(..)(..)(..)(..)(..)(.{16})\z/, '\4\3\2\1\6\5\8\7\9')
226
- when :java_legacy
227
- hex.sub(/\A(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)\z/) do |m|
228
- "#{$8}#{$7}#{$6}#{$5}#{$4}#{$3}#{$2}#{$1}" +
229
- "#{$16}#{$15}#{$14}#{$13}#{$12}#{$11}#{$10}#{$9}"
230
- end
231
- when :python_legacy
232
- hex
233
- else
234
- raise ArgumentError, "Invalid representation: #{representation}"
235
- end.sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
226
+ from_uuid_old_to_uuid(representation)
236
227
  else
237
228
  raise TypeError, "The type of Binary must be :uuid or :uuid_old, this object is: #{type.inspect}"
238
229
  end
@@ -248,10 +239,10 @@ module BSON
248
239
  # @see http://bsonspec.org/#/specification
249
240
  #
250
241
  # @since 2.0.0
251
- def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
242
+ def to_bson(buffer = ByteBuffer.new)
252
243
  position = buffer.length
253
244
  buffer.put_int32(0)
254
- buffer.put_byte(SUBTYPES[type])
245
+ buffer.put_byte(@raw_type)
255
246
  buffer.put_int32(data.bytesize) if type == :old
256
247
  buffer.put_bytes(data)
257
248
  buffer.replace_int32(position, buffer.length - position - 5)
@@ -268,13 +259,19 @@ module BSON
268
259
  # @see http://bsonspec.org/#/specification
269
260
  #
270
261
  # @since 2.0.0
271
- def self.from_bson(buffer, **options)
262
+ def self.from_bson(buffer, **_options)
272
263
  length = buffer.get_int32
273
264
  type_byte = buffer.get_byte
274
- type = TYPES[type_byte]
275
- if type.nil?
276
- raise Error::UnsupportedBinarySubtype,
277
- "BSON data contains unsupported binary subtype #{'0x%02x' % type_byte.ord}"
265
+
266
+ if type_byte.bytes.first < USER_SUBTYPE
267
+ type = TYPES[type_byte]
268
+
269
+ if type.nil?
270
+ raise Error::UnsupportedBinarySubtype,
271
+ "BSON data contains unsupported binary subtype #{'0x%02x' % type_byte.ord}"
272
+ end
273
+ else
274
+ type = type_byte
278
275
  end
279
276
 
280
277
  length = buffer.get_int32 if type == :old
@@ -308,66 +305,166 @@ module BSON
308
305
  #
309
306
  # @api experimental
310
307
  def self.from_uuid(uuid, representation = nil)
311
- if representation.is_a?(String)
312
- raise ArgumentError, "Representation must be given as a symbol: #{representation}"
308
+ raise ArgumentError, "Representation must be given as a symbol: #{representation}" if representation.is_a?(String)
309
+
310
+ uuid_binary = uuid.delete('-').scan(/../).map(&:hex).map(&:chr).join
311
+ representation ||= :standard
312
+
313
+ handler = :"from_#{representation}_uuid"
314
+ raise ArgumentError, "Invalid representation: #{representation}" unless respond_to?(handler)
315
+
316
+ send(handler, uuid_binary)
317
+ end
318
+
319
+ # Constructs a new binary object from a standard-format binary UUID
320
+ # representation.
321
+ #
322
+ # @param [ String ] uuid_binary the UUID data
323
+ #
324
+ # @return [ BSON::Binary ] the Binary object
325
+ #
326
+ # @api private
327
+ def self.from_standard_uuid(uuid_binary)
328
+ new(uuid_binary, :uuid)
329
+ end
330
+
331
+ # Constructs a new binary object from a csharp legacy-format binary UUID
332
+ # representation.
333
+ #
334
+ # @param [ String ] uuid_binary the UUID data
335
+ #
336
+ # @return [ BSON::Binary ] the Binary object
337
+ #
338
+ # @api private
339
+ def self.from_csharp_legacy_uuid(uuid_binary)
340
+ uuid_binary.sub!(/\A(.)(.)(.)(.)(.)(.)(.)(.)(.{8})\z/, '\4\3\2\1\6\5\8\7\9')
341
+ new(uuid_binary, :uuid_old)
342
+ end
343
+
344
+ # Constructs a new binary object from a java legacy-format binary UUID
345
+ # representation.
346
+ #
347
+ # @param [ String ] uuid_binary the UUID data
348
+ #
349
+ # @return [ BSON::Binary ] the Binary object
350
+ #
351
+ # @api private
352
+ def self.from_java_legacy_uuid(uuid_binary)
353
+ uuid_binary.sub!(/\A(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\z/) do
354
+ (::Regexp.last_match[1..8].reverse + ::Regexp.last_match[9..16].reverse).join
313
355
  end
314
- uuid_binary = uuid.gsub('-', '').scan(/../).map(&:hex).map(&:chr).join
315
- case representation && representation
316
- when nil, :standard
317
- new(uuid_binary, :uuid)
318
- when :csharp_legacy
319
- uuid_binary.sub!(/\A(.)(.)(.)(.)(.)(.)(.)(.)(.{8})\z/, '\4\3\2\1\6\5\8\7\9')
320
- new(uuid_binary, :uuid_old)
321
- when :java_legacy
322
- uuid_binary.sub!(/\A(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\z/) do |m|
323
- "#{$8}#{$7}#{$6}#{$5}#{$4}#{$3}#{$2}#{$1}" +
324
- "#{$16}#{$15}#{$14}#{$13}#{$12}#{$11}#{$10}#{$9}"
325
- end
326
- new(uuid_binary, :uuid_old)
327
- when :python_legacy
328
- new(uuid_binary, :uuid_old)
329
- else
330
- raise ArgumentError, "Invalid representation: #{representation}"
356
+ new(uuid_binary, :uuid_old)
357
+ end
358
+
359
+ # Constructs a new binary object from a python legacy-format binary UUID
360
+ # representation.
361
+ #
362
+ # @param [ String ] uuid_binary the UUID data
363
+ #
364
+ # @return [ BSON::Binary ] the Binary object
365
+ #
366
+ # @api private
367
+ def self.from_python_legacy_uuid(uuid_binary)
368
+ new(uuid_binary, :uuid_old)
369
+ end
370
+
371
+ private
372
+
373
+ # initializes an instance of BSON::Binary.
374
+ #
375
+ # @param [ String ] data the data to initialize the object with
376
+ # @param [ Symbol ] type the type to assign the binary object
377
+ def initialize_instance(data, type)
378
+ @type = validate_type!(type)
379
+
380
+ # The Binary class used to force encoding to BINARY when serializing to
381
+ # BSON. Instead of doing that during serialization, perform this
382
+ # operation during Binary construction to make it clear that once
383
+ # the string is given to the Binary, the data is treated as a binary
384
+ # string and not a text string in any encoding.
385
+ data = data.dup.force_encoding('BINARY') unless data.encoding == Encoding.find('BINARY')
386
+
387
+ @data = data
388
+ end
389
+
390
+ # Converts the Binary UUID object to a UUID of the given representation.
391
+ # Currently, only :standard representation is supported.
392
+ #
393
+ # @param [ Symbol ] representation The representation to target (must be
394
+ # :standard)
395
+ #
396
+ # @return [ String ] the UUID as a string
397
+ def from_uuid_to_uuid(representation)
398
+ if representation != :standard
399
+ raise ArgumentError,
400
+ 'Binary of type :uuid can only be stringified to :standard representation, ' \
401
+ "requested: #{representation.inspect}"
331
402
  end
403
+
404
+ data
405
+ .chars
406
+ .map { |n| '%02x' % n.ord }
407
+ .join
408
+ .sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
332
409
  end
333
410
 
334
- # Raised when providing an invalid type to the Binary.
411
+ # Converts the UUID-old object to a UUID of the given representation.
335
412
  #
336
- # @since 2.0.0
337
- class InvalidType < RuntimeError
338
-
339
- # @!attribute type
340
- # @return [ Object ] The invalid type.
341
- # @since 2.0.0
342
- attr_reader :type
343
-
344
- # Instantiate the new error.
345
- #
346
- # @example Instantiate the error.
347
- # InvalidType.new(:error)
348
- #
349
- # @param [ Object ] type The invalid type.
350
- #
351
- # @since 2.0.0
352
- def initialize(type)
353
- @type = type
413
+ # @param [ Symbol ] representation The representation to target
414
+ #
415
+ # @return [ String ] the UUID as a string
416
+ def from_uuid_old_to_uuid(representation)
417
+ if representation.nil?
418
+ raise ArgumentError, 'Representation must be specified for BSON::Binary objects of type :uuid_old'
354
419
  end
355
420
 
356
- # Get the custom error message for the exception.
357
- #
358
- # @example Get the message.
359
- # error.message
360
- #
361
- # @return [ String ] The error message.
362
- #
363
- # @since 2.0.0
364
- def message
365
- "#{type.inspect} is not a valid binary type. " +
366
- "Please use one of #{SUBTYPES.keys.map(&:inspect).join(", ")}."
421
+ hex = data.chars.map { |n| '%02x' % n.ord }.join
422
+ handler = :"from_uuid_old_to_#{representation}_uuid"
423
+
424
+ raise ArgumentError, "Invalid representation: #{representation}" unless respond_to?(handler, true)
425
+
426
+ send(handler, hex)
427
+ .sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
428
+ end
429
+
430
+ # Tries to convert a UUID-old object to a standard representation, which is
431
+ # not supported.
432
+ #
433
+ # @param [ String ] hex The hexadecimal string to convert
434
+ #
435
+ # @raise [ ArgumentError ] because standard representation is not supported
436
+ def from_uuid_old_to_standard_uuid(_hex)
437
+ raise ArgumentError, 'BSON::Binary objects of type :uuid_old cannot be stringified to :standard representation'
438
+ end
439
+
440
+ # Converts a UUID-old object to a csharp-legacy representation.
441
+ #
442
+ # @param [ String ] hex The hexadecimal string to convert
443
+ #
444
+ # @return [ String ] the csharp-legacy-formatted UUID
445
+ def from_uuid_old_to_csharp_legacy_uuid(hex)
446
+ hex.sub(/\A(..)(..)(..)(..)(..)(..)(..)(..)(.{16})\z/, '\4\3\2\1\6\5\8\7\9')
447
+ end
448
+
449
+ # Converts a UUID-old object to a java-legacy representation.
450
+ #
451
+ # @param [ String ] hex The hexadecimal string to convert
452
+ #
453
+ # @return [ String ] the java-legacy-formatted UUID
454
+ def from_uuid_old_to_java_legacy_uuid(hex)
455
+ hex.sub(/\A(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)\z/) do
456
+ (::Regexp.last_match[1..8].reverse + ::Regexp.last_match[9..16].reverse).join
367
457
  end
368
458
  end
369
459
 
370
- private
460
+ # Converts a UUID-old object to a python-legacy representation.
461
+ #
462
+ # @param [ String ] hex The hexadecimal string to convert
463
+ #
464
+ # @return [ String ] the python-legacy-formatted UUID
465
+ def from_uuid_old_to_python_legacy_uuid(hex)
466
+ hex
467
+ end
371
468
 
372
469
  # Validate the provided type is a valid type.
373
470
  #
@@ -376,13 +473,58 @@ module BSON
376
473
  # @example Validate the type.
377
474
  # binary.validate_type!(:user)
378
475
  #
379
- # @param [ Object ] type The provided type.
476
+ # @param [ Symbol | String | Integer ] type The provided type.
477
+ #
478
+ # @return [ Symbol ] the symbolic type corresponding to the argument.
380
479
  #
381
- # @raise [ InvalidType ] The the type is invalid.
480
+ # @raise [ BSON::Error::InvalidBinaryType ] The the type is invalid.
382
481
  #
383
482
  # @since 2.0.0
384
483
  def validate_type!(type)
385
- raise InvalidType.new(type) unless SUBTYPES.has_key?(type)
484
+ case type
485
+ when Integer then validate_integer_type!(type)
486
+ when String
487
+ if type.length > 1
488
+ validate_symbol_type!(type.to_sym)
489
+ else
490
+ validate_integer_type!(type.bytes.first)
491
+ end
492
+ when Symbol then validate_symbol_type!(type)
493
+ else raise BSON::Error::InvalidBinaryType, type
494
+ end
495
+ end
496
+
497
+ # Test that the given integer type is valid.
498
+ #
499
+ # @param [ Integer ] type the provided type
500
+ #
501
+ # @return [ Symbol ] the symbolic type corresponding to the argument.
502
+ #
503
+ # @raise [ BSON::Error::InvalidBinaryType] if the type is invalid.
504
+ def validate_integer_type!(type)
505
+ @raw_type = type.chr.force_encoding('BINARY').freeze
506
+
507
+ if type < USER_SUBTYPE
508
+ raise BSON::Error::InvalidBinaryType, type unless TYPES.key?(@raw_type)
509
+
510
+ return TYPES[@raw_type]
511
+ end
512
+
513
+ :user
514
+ end
515
+
516
+ # Test that the given symbol type is valid.
517
+ #
518
+ # @param [ Symbol ] type the provided type
519
+ #
520
+ # @return [ Symbol ] the symbolic type corresponding to the argument.
521
+ #
522
+ # @raise [ BSON::Error::InvalidBinaryType] if the type is invalid.
523
+ def validate_symbol_type!(type)
524
+ raise BSON::Error::InvalidBinaryType, type unless SUBTYPES.key?(type)
525
+
526
+ @raw_type = SUBTYPES[type]
527
+ type
386
528
  end
387
529
 
388
530
  # Register this type when the module is loaded.
data/lib/bson/boolean.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # rubocop:todo all
2
3
  # Copyright (C) 2009-2020 MongoDB Inc.
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
data/lib/bson/code.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # rubocop:todo all
2
3
  # Copyright (C) 2009-2020 MongoDB Inc.
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,27 +49,24 @@ module BSON
48
49
  javascript == other.javascript
49
50
  end
50
51
 
51
- # Get the code as JSON hash data.
52
+ # Return a representation of the object for use in
53
+ # application-level JSON serialization. Since BSON::Code
54
+ # is used exclusively in BSON-related contexts, this
55
+ # method returns the canonical Extended JSON representation.
52
56
  #
53
- # @example Get the code as a JSON hash.
54
- # code.as_json
55
- #
56
- # @return [ Hash ] The code as a JSON hash.
57
- #
58
- # @since 2.0.0
59
- # @deprecated Use as_extended_json instead.
60
- def as_json(*args)
57
+ # @return [ Hash ] The extended json representation.
58
+ def as_json(*_args)
61
59
  as_extended_json
62
60
  end
63
61
 
64
62
  # Converts this object to a representation directly serializable to
65
- # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
63
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
66
64
  #
67
65
  # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
68
66
  # (default is canonical extended JSON)
69
67
  #
70
68
  # @return [ Hash ] The extended json representation.
71
- def as_extended_json(**options)
69
+ def as_extended_json(**_options)
72
70
  { "$code" => javascript }
73
71
  end
74
72
 
@@ -94,7 +92,7 @@ module BSON
94
92
  # @see http://bsonspec.org/#/specification
95
93
  #
96
94
  # @since 2.0.0
97
- def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
95
+ def to_bson(buffer = ByteBuffer.new)
98
96
  buffer.put_string(javascript) # @todo: was formerly to_bson_string
99
97
  end
100
98