bson 4.1.1 → 5.2.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 (226) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +106 -7
  3. data/Rakefile +86 -43
  4. data/ext/bson/{native-endian.h → bson-endian.h} +14 -107
  5. data/ext/bson/bson-native.h +135 -0
  6. data/ext/bson/bytebuf.c +133 -0
  7. data/ext/bson/endian.c +117 -0
  8. data/ext/bson/extconf.rb +8 -3
  9. data/ext/bson/init.c +364 -0
  10. data/ext/bson/libbson-utf8.c +230 -0
  11. data/ext/bson/read.c +470 -0
  12. data/ext/bson/util.c +250 -0
  13. data/ext/bson/write.c +675 -0
  14. data/lib/bson/active_support.rb +19 -0
  15. data/lib/bson/array.rb +97 -30
  16. data/lib/bson/big_decimal.rb +77 -0
  17. data/lib/bson/binary.rb +510 -70
  18. data/lib/bson/boolean.rb +15 -4
  19. data/lib/bson/code.rb +25 -12
  20. data/lib/bson/code_with_scope.rb +41 -15
  21. data/lib/bson/config.rb +3 -28
  22. data/lib/bson/date.rb +16 -4
  23. data/lib/bson/date_time.rb +6 -4
  24. data/lib/bson/db_pointer.rb +110 -0
  25. data/lib/bson/dbref.rb +154 -0
  26. data/lib/bson/decimal128/builder.rb +456 -0
  27. data/lib/bson/decimal128.rb +272 -0
  28. data/lib/bson/document.rb +177 -7
  29. data/lib/bson/environment.rb +17 -2
  30. data/lib/bson/error/bson_decode_error.rb +11 -0
  31. data/lib/bson/error/ext_json_parse_error.rb +11 -0
  32. data/lib/bson/error/illegal_key.rb +23 -0
  33. data/lib/bson/error/invalid_binary_type.rb +37 -0
  34. data/lib/bson/error/invalid_dbref_argument.rb +12 -0
  35. data/lib/bson/error/invalid_decimal128_argument.rb +25 -0
  36. data/lib/bson/error/invalid_decimal128_range.rb +27 -0
  37. data/lib/bson/error/invalid_decimal128_string.rb +26 -0
  38. data/lib/bson/error/invalid_key.rb +24 -0
  39. data/lib/bson/error/invalid_object_id.rb +11 -0
  40. data/lib/bson/error/invalid_regexp_pattern.rb +13 -0
  41. data/lib/bson/error/unrepresentable_precision.rb +19 -0
  42. data/lib/bson/error/unserializable_class.rb +13 -0
  43. data/lib/bson/error/unsupported_binary_subtype.rb +12 -0
  44. data/lib/bson/error/unsupported_type.rb +11 -0
  45. data/lib/bson/error.rb +22 -0
  46. data/lib/bson/ext_json.rb +389 -0
  47. data/lib/bson/false_class.rb +6 -4
  48. data/lib/bson/float.rb +43 -7
  49. data/lib/bson/hash.rb +152 -37
  50. data/lib/bson/int32.rb +104 -6
  51. data/lib/bson/int64.rb +111 -8
  52. data/lib/bson/integer.rb +43 -9
  53. data/lib/bson/json.rb +3 -1
  54. data/lib/bson/max_key.rb +21 -10
  55. data/lib/bson/min_key.rb +21 -10
  56. data/lib/bson/nil_class.rb +7 -3
  57. data/lib/bson/object.rb +25 -17
  58. data/lib/bson/object_id.rb +98 -113
  59. data/lib/bson/open_struct.rb +59 -0
  60. data/lib/bson/regexp.rb +129 -56
  61. data/lib/bson/registry.rb +7 -10
  62. data/lib/bson/specialized.rb +8 -4
  63. data/lib/bson/string.rb +12 -32
  64. data/lib/bson/symbol.rb +107 -11
  65. data/lib/bson/time.rb +68 -7
  66. data/lib/bson/time_with_zone.rb +67 -0
  67. data/lib/bson/timestamp.rb +50 -10
  68. data/lib/bson/true_class.rb +6 -4
  69. data/lib/bson/undefined.rb +28 -2
  70. data/lib/bson/vector.rb +44 -0
  71. data/lib/bson/version.rb +6 -14
  72. data/lib/bson.rb +22 -12
  73. data/spec/README.md +14 -0
  74. data/spec/bson/array_spec.rb +38 -62
  75. data/spec/bson/big_decimal_spec.rb +328 -0
  76. data/spec/bson/binary_spec.rb +199 -53
  77. data/spec/bson/binary_uuid_spec.rb +190 -0
  78. data/spec/bson/boolean_spec.rb +2 -1
  79. data/spec/bson/byte_buffer_read_spec.rb +198 -0
  80. data/spec/bson/byte_buffer_spec.rb +122 -381
  81. data/spec/bson/byte_buffer_write_spec.rb +855 -0
  82. data/spec/bson/code_spec.rb +6 -4
  83. data/spec/bson/code_with_scope_spec.rb +6 -4
  84. data/spec/bson/config_spec.rb +1 -35
  85. data/spec/bson/date_spec.rb +2 -1
  86. data/spec/bson/date_time_spec.rb +55 -1
  87. data/spec/bson/dbref_legacy_spec.rb +186 -0
  88. data/spec/bson/dbref_spec.rb +487 -0
  89. data/spec/bson/decimal128_spec.rb +1840 -0
  90. data/spec/bson/document_as_spec.rb +61 -0
  91. data/spec/bson/document_spec.rb +205 -32
  92. data/spec/bson/ext_json_parse_spec.rb +346 -0
  93. data/spec/bson/false_class_spec.rb +9 -1
  94. data/spec/bson/float_spec.rb +42 -1
  95. data/spec/bson/hash_as_spec.rb +58 -0
  96. data/spec/bson/hash_spec.rb +318 -66
  97. data/spec/bson/int32_spec.rb +248 -1
  98. data/spec/bson/int64_spec.rb +308 -1
  99. data/spec/bson/integer_spec.rb +61 -3
  100. data/spec/bson/json_spec.rb +2 -1
  101. data/spec/bson/max_key_spec.rb +6 -4
  102. data/spec/bson/min_key_spec.rb +6 -4
  103. data/spec/bson/nil_class_spec.rb +2 -1
  104. data/spec/bson/object_id_spec.rb +95 -5
  105. data/spec/bson/object_spec.rb +3 -2
  106. data/spec/bson/open_struct_spec.rb +87 -0
  107. data/spec/bson/raw_spec.rb +594 -0
  108. data/spec/bson/regexp_spec.rb +61 -8
  109. data/spec/bson/registry_spec.rb +3 -2
  110. data/spec/bson/string_spec.rb +26 -33
  111. data/spec/bson/symbol_raw_spec.rb +70 -0
  112. data/spec/bson/symbol_spec.rb +77 -20
  113. data/spec/bson/time_spec.rb +206 -2
  114. data/spec/bson/time_with_zone_spec.rb +69 -0
  115. data/spec/bson/timestamp_spec.rb +58 -2
  116. data/spec/bson/true_class_spec.rb +9 -1
  117. data/spec/bson/undefined_spec.rb +28 -1
  118. data/spec/bson/vector_spec.rb +33 -0
  119. data/spec/bson_spec.rb +2 -1
  120. data/spec/runners/binary_vector.rb +78 -0
  121. data/spec/runners/common_driver.rb +348 -0
  122. data/spec/runners/corpus.rb +191 -0
  123. data/spec/runners/corpus_legacy.rb +248 -0
  124. data/spec/shared/LICENSE +20 -0
  125. data/spec/shared/bin/get-mongodb-download-url +17 -0
  126. data/spec/shared/bin/s3-copy +45 -0
  127. data/spec/shared/bin/s3-upload +69 -0
  128. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  129. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  130. data/spec/shared/lib/mrss/constraints.rb +378 -0
  131. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  132. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  133. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  134. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  135. data/spec/shared/lib/mrss/release/candidate.rb +281 -0
  136. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  137. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  138. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  139. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  140. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  141. data/spec/shared/lib/mrss/utils.rb +37 -0
  142. data/spec/shared/lib/tasks/candidate.rake +64 -0
  143. data/spec/shared/share/Dockerfile.erb +251 -0
  144. data/spec/shared/share/haproxy-1.conf +16 -0
  145. data/spec/shared/share/haproxy-2.conf +17 -0
  146. data/spec/shared/shlib/config.sh +27 -0
  147. data/spec/shared/shlib/distro.sh +84 -0
  148. data/spec/shared/shlib/server.sh +423 -0
  149. data/spec/shared/shlib/set_env.sh +110 -0
  150. data/spec/spec_helper.rb +61 -1
  151. data/spec/spec_tests/binary_vector_spec.rb +82 -0
  152. data/spec/spec_tests/common_driver_spec.rb +84 -0
  153. data/spec/spec_tests/corpus_legacy_spec.rb +72 -0
  154. data/spec/spec_tests/corpus_spec.rb +134 -0
  155. data/spec/spec_tests/data/binary_vector/README.md +61 -0
  156. data/spec/spec_tests/data/binary_vector/float32.json +65 -0
  157. data/spec/spec_tests/data/binary_vector/int8.json +57 -0
  158. data/spec/spec_tests/data/binary_vector/packed_bit.json +83 -0
  159. data/spec/spec_tests/data/corpus/README.md +15 -0
  160. data/spec/spec_tests/data/corpus/array.json +49 -0
  161. data/spec/spec_tests/data/corpus/binary.json +153 -0
  162. data/spec/spec_tests/data/corpus/boolean.json +27 -0
  163. data/spec/spec_tests/data/corpus/code.json +67 -0
  164. data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
  165. data/spec/spec_tests/data/corpus/datetime.json +42 -0
  166. data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
  167. data/spec/spec_tests/data/corpus/dbref.json +51 -0
  168. data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
  169. data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
  170. data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
  171. data/spec/spec_tests/data/corpus/decimal128-4.json +165 -0
  172. data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
  173. data/spec/spec_tests/data/corpus/decimal128-6.json +131 -0
  174. data/spec/spec_tests/data/corpus/decimal128-7.json +327 -0
  175. data/spec/spec_tests/data/corpus/document.json +60 -0
  176. data/spec/spec_tests/data/corpus/double.json +87 -0
  177. data/spec/spec_tests/data/corpus/int32.json +43 -0
  178. data/spec/spec_tests/data/corpus/int64.json +43 -0
  179. data/spec/spec_tests/data/corpus/maxkey.json +12 -0
  180. data/spec/spec_tests/data/corpus/minkey.json +12 -0
  181. data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
  182. data/spec/spec_tests/data/corpus/multi-type.json +11 -0
  183. data/spec/spec_tests/data/corpus/null.json +12 -0
  184. data/spec/spec_tests/data/corpus/oid.json +28 -0
  185. data/spec/spec_tests/data/corpus/regex.json +65 -0
  186. data/spec/spec_tests/data/corpus/string.json +72 -0
  187. data/spec/spec_tests/data/corpus/symbol.json +80 -0
  188. data/spec/spec_tests/data/corpus/timestamp.json +34 -0
  189. data/spec/spec_tests/data/corpus/top.json +262 -0
  190. data/spec/spec_tests/data/corpus/undefined.json +15 -0
  191. data/spec/spec_tests/data/corpus_legacy/array.json +49 -0
  192. data/spec/spec_tests/data/corpus_legacy/binary.json +69 -0
  193. data/spec/spec_tests/data/corpus_legacy/boolean.json +27 -0
  194. data/spec/spec_tests/data/corpus_legacy/code.json +67 -0
  195. data/spec/spec_tests/data/corpus_legacy/code_w_scope.json +78 -0
  196. data/spec/spec_tests/data/corpus_legacy/document.json +36 -0
  197. data/spec/spec_tests/data/corpus_legacy/double.json +69 -0
  198. data/spec/spec_tests/data/corpus_legacy/failures/datetime.json +31 -0
  199. data/spec/spec_tests/data/corpus_legacy/failures/dbpointer.json +42 -0
  200. data/spec/spec_tests/data/corpus_legacy/failures/int64.json +38 -0
  201. data/spec/spec_tests/data/corpus_legacy/failures/symbol.json +62 -0
  202. data/spec/spec_tests/data/corpus_legacy/int32.json +38 -0
  203. data/spec/spec_tests/data/corpus_legacy/maxkey.json +12 -0
  204. data/spec/spec_tests/data/corpus_legacy/minkey.json +12 -0
  205. data/spec/spec_tests/data/corpus_legacy/null.json +12 -0
  206. data/spec/spec_tests/data/corpus_legacy/oid.json +28 -0
  207. data/spec/spec_tests/data/corpus_legacy/regex.json +37 -0
  208. data/spec/spec_tests/data/corpus_legacy/string.json +67 -0
  209. data/spec/spec_tests/data/corpus_legacy/timestamp.json +18 -0
  210. data/spec/spec_tests/data/corpus_legacy/top.json +62 -0
  211. data/spec/spec_tests/data/corpus_legacy/undefined.json +13 -0
  212. data/spec/spec_tests/data/decimal128/decimal128-1.json +363 -0
  213. data/spec/spec_tests/data/decimal128/decimal128-2.json +793 -0
  214. data/spec/spec_tests/data/decimal128/decimal128-3.json +1771 -0
  215. data/spec/spec_tests/data/decimal128/decimal128-4.json +165 -0
  216. data/spec/spec_tests/data/decimal128/decimal128-5.json +402 -0
  217. data/spec/spec_tests/data/decimal128/decimal128-6.json +131 -0
  218. data/spec/spec_tests/data/decimal128/decimal128-7.json +327 -0
  219. data/spec/support/shared_examples.rb +32 -11
  220. data/spec/support/spec_config.rb +17 -0
  221. data/spec/support/utils.rb +58 -0
  222. metadata +284 -45
  223. checksums.yaml.gz.sig +0 -3
  224. data/ext/bson/native.c +0 -722
  225. data.tar.gz.sig +0 -0
  226. metadata.gz.sig +0 -0
data/lib/bson/max_key.rb CHANGED
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
4
  #
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
4
6
  # you may not use this file except in compliance with the License.
@@ -28,12 +30,12 @@ module BSON
28
30
  # A $maxKey is type 0x7F in the BSON spec.
29
31
  #
30
32
  # @since 2.0.0
31
- BSON_TYPE = 127.chr.force_encoding(BINARY).freeze
33
+ BSON_TYPE = ::String.new(127.chr, encoding: BINARY).freeze
32
34
 
33
35
  # Constant for always evaluating greater in a comparison.
34
36
  #
35
37
  # @since 2.0.0
36
- GREATER = 1.freeze
38
+ GREATER = 1
37
39
 
38
40
  # When comparing a max key with any other object, the max key will always
39
41
  # be greater.
@@ -41,7 +43,7 @@ module BSON
41
43
  # @example Compare with another object.
42
44
  # max_key <=> 1000
43
45
  #
44
- # @param [ Object ] The object to compare against.
46
+ # @param [ Object ] other The object to compare against.
45
47
  #
46
48
  # @return [ Integer ] Always 1.
47
49
  #
@@ -50,15 +52,24 @@ module BSON
50
52
  GREATER
51
53
  end
52
54
 
53
- # Get the max key as JSON hash data.
55
+ # Return a representation of the object for use in
56
+ # application-level JSON serialization. Since BSON::MaxKey
57
+ # is used exclusively in BSON-related contexts, this
58
+ # method returns the canonical Extended JSON representation.
54
59
  #
55
- # @example Get the max key as a JSON hash.
56
- # max_key.as_json
60
+ # @return [ Hash ] The extended json representation.
61
+ def as_json(*_args)
62
+ as_extended_json
63
+ end
64
+
65
+ # Converts this object to a representation directly serializable to
66
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
57
67
  #
58
- # @return [ Hash ] The max key as a JSON hash.
68
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
69
+ # (default is canonical extended JSON)
59
70
  #
60
- # @since 2.0.0
61
- def as_json(*args)
71
+ # @return [ Hash ] The extended json representation.
72
+ def as_extended_json(**options)
62
73
  { "$maxKey" => 1 }
63
74
  end
64
75
 
data/lib/bson/min_key.rb CHANGED
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
4
  #
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
4
6
  # you may not use this file except in compliance with the License.
@@ -28,12 +30,12 @@ module BSON
28
30
  # A $minKey is type 0xFF in the BSON spec.
29
31
  #
30
32
  # @since 2.0.0
31
- BSON_TYPE = 255.chr.force_encoding(BINARY).freeze
33
+ BSON_TYPE = ::String.new(255.chr, encoding: BINARY).freeze
32
34
 
33
35
  # Constant for always evaluating lesser in a comparison.
34
36
  #
35
37
  # @since 2.0.0
36
- LESSER = -1.freeze
38
+ LESSER = -1
37
39
 
38
40
  # When comparing a min key with any other object, the min key will always
39
41
  # be lesser.
@@ -41,7 +43,7 @@ module BSON
41
43
  # @example Compare with another object.
42
44
  # min_key <=> 1000
43
45
  #
44
- # @param [ Object ] The object to compare against.
46
+ # @param [ Object ] other The object to compare against.
45
47
  #
46
48
  # @return [ Integer ] Always -1.
47
49
  #
@@ -50,15 +52,24 @@ module BSON
50
52
  LESSER
51
53
  end
52
54
 
53
- # Get the min key as JSON hash data.
55
+ # Return a representation of the object for use in
56
+ # application-level JSON serialization. Since BSON::MinKey
57
+ # is used exclusively in BSON-related contexts, this
58
+ # method returns the canonical Extended JSON representation.
54
59
  #
55
- # @example Get the min key as a JSON hash.
56
- # min_key.as_json
60
+ # @return [ Hash ] The extended json representation.
61
+ def as_json(*_args)
62
+ as_extended_json
63
+ end
64
+
65
+ # Converts this object to a representation directly serializable to
66
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
57
67
  #
58
- # @return [ Hash ] The min key as a JSON hash.
68
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
69
+ # (default is canonical extended JSON)
59
70
  #
60
- # @since 2.0.0
61
- def as_json(*args)
71
+ # @return [ Hash ] The extended json representation.
72
+ def as_extended_json(**options)
62
73
  { "$minKey" => 1 }
63
74
  end
64
75
 
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
4
  #
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
4
6
  # you may not use this file except in compliance with the License.
@@ -26,7 +28,7 @@ module BSON
26
28
  # A nil is type 0x0A in the BSON spec.
27
29
  #
28
30
  # @since 2.0.0
29
- BSON_TYPE = 10.chr.force_encoding(BINARY).freeze
31
+ BSON_TYPE = ::String.new(10.chr, encoding: BINARY).freeze
30
32
 
31
33
  module ClassMethods
32
34
 
@@ -34,12 +36,14 @@ module BSON
34
36
  #
35
37
  # @param [ ByteBuffer ] buffer The byte buffer.
36
38
  #
39
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
40
+ #
37
41
  # @return [ nil ] The decoded nil value.
38
42
  #
39
43
  # @see http://bsonspec.org/#/specification
40
44
  #
41
45
  # @since 2.0.0
42
- def from_bson(buffer)
46
+ def from_bson(buffer, **options)
43
47
  nil
44
48
  end
45
49
  end
data/lib/bson/object.rb CHANGED
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
4
  #
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
4
6
  # you may not use this file except in compliance with the License.
@@ -26,13 +28,13 @@ module BSON
26
28
  # @example Convert the object to a BSON key.
27
29
  # object.to_bson_key
28
30
  #
29
- # @raise [ InvalidKey ] Always raises an exception.
31
+ # @raise [ BSON::Error::InvalidKey ] Always raises an exception.
30
32
  #
31
33
  # @see http://bsonspec.org/#/specification
32
34
  #
33
35
  # @since 2.2.4
34
- def to_bson_key(validating_keys = Config.validating_keys?)
35
- raise InvalidKey.new(self)
36
+ def to_bson_key
37
+ raise Error::InvalidKey.new(self)
36
38
  end
37
39
 
38
40
  # Converts the object to a normalized key in a BSON document.
@@ -58,23 +60,29 @@ module BSON
58
60
  def to_bson_normalized_value
59
61
  self
60
62
  end
61
- end
62
63
 
63
- # Raised when trying to serialize an object into a key.
64
- #
65
- # @since 2.2.4
66
- class InvalidKey < RuntimeError
67
-
68
- # Instantiate the exception.
64
+ # Serializes this object to Extended JSON
65
+ # (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
69
66
  #
70
- # @example Instantiate the exception.
71
- # BSON::Object::InvalidKey.new(object)
67
+ # Subclasses should override +as_extended_json+ rather than this method.
72
68
  #
73
- # @param [ Object ] object The object that was meant for the key.
69
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
70
+ # (default is canonical extended JSON)
74
71
  #
75
- # @since 2.2.4
76
- def initialize(object)
77
- super("#{object.class} instances are not allowed as keys in a BSON document.")
72
+ # @return [ String ] The extended json serialization.
73
+ def to_extended_json(**options)
74
+ as_extended_json(**options).to_json
75
+ end
76
+
77
+ # Converts this object to a representation directly serializable to
78
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
79
+ #
80
+ # Subclasses should override this method to provide custom serialization
81
+ # to Extended JSON.
82
+ #
83
+ # @return [ Object ] The extended json representation.
84
+ def as_extended_json(**_options)
85
+ self
78
86
  end
79
87
  end
80
88
 
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
4
  #
3
5
  # Licensed under the Apache License, Version 2.0 (the "License");
4
6
  # you may not use this file except in compliance with the License.
@@ -12,12 +14,7 @@
12
14
  # See the License for the specific language governing permissions and
13
15
  # limitations under the License.
14
16
 
15
- require "digest/md5"
16
- require "socket"
17
- require "thread"
18
-
19
17
  module BSON
20
-
21
18
  # Represents object_id data.
22
19
  #
23
20
  # @see http://bsonspec.org/#/specification
@@ -30,7 +27,7 @@ module BSON
30
27
  # A object_id is type 0x07 in the BSON spec.
31
28
  #
32
29
  # @since 2.0.0
33
- BSON_TYPE = 7.chr.force_encoding(BINARY).freeze
30
+ BSON_TYPE = ::String.new(7.chr, encoding: BINARY).freeze
34
31
 
35
32
  # Check equality of the object id with another object.
36
33
  #
@@ -44,9 +41,10 @@ module BSON
44
41
  # @since 2.0.0
45
42
  def ==(other)
46
43
  return false unless other.is_a?(ObjectId)
44
+
47
45
  generate_data == other.send(:generate_data)
48
46
  end
49
- alias :eql? :==
47
+ alias eql? ==
50
48
 
51
49
  # Check case equality on the object id.
52
50
  #
@@ -59,20 +57,29 @@ module BSON
59
57
  #
60
58
  # @since 2.0.0
61
59
  def ===(other)
62
- return to_str === other.to_str if other.respond_to?(:to_str)
60
+ return to_str == other.to_str if other.respond_to?(:to_str)
61
+
63
62
  super
64
63
  end
65
64
 
66
- # Return the object id as a JSON hash representation.
65
+ # Return a string representation of the object id for use in
66
+ # application-level JSON serialization. This method is intentionally
67
+ # different from #as_extended_json.
67
68
  #
68
- # @example Get the object id as JSON.
69
+ # @example Get the object id as a JSON-serializable object.
69
70
  # object_id.as_json
70
71
  #
71
- # @return [ Hash ] The object id as a JSON hash.
72
+ # @return [ String ] The object id as a string.
73
+ def as_json(*_)
74
+ to_s
75
+ end
76
+
77
+ # Converts this object to a representation directly serializable to
78
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
72
79
  #
73
- # @since 2.0.0
74
- def as_json(*args)
75
- { "$oid" => to_s }
80
+ # @return [ Hash ] The extended json representation.
81
+ def as_extended_json(**_)
82
+ { '$oid' => to_s }
76
83
  end
77
84
 
78
85
  # Compare this object id with another object for use in sorting.
@@ -100,8 +107,9 @@ module BSON
100
107
  #
101
108
  # @since 2.0.0
102
109
  def generation_time
103
- ::Time.at(generate_data.unpack("N")[0]).utc
110
+ ::Time.at(generate_data.unpack1('N')).utc
104
111
  end
112
+ alias to_time generation_time
105
113
 
106
114
  # Get the hash value for the object id.
107
115
  #
@@ -124,7 +132,7 @@ module BSON
124
132
  #
125
133
  # @since 2.0.0
126
134
  def inspect
127
- "BSON::ObjectId('#{to_s}')"
135
+ "BSON::ObjectId('#{self}')"
128
136
  end
129
137
 
130
138
  # Dump the raw bson when calling Marshal.dump.
@@ -163,12 +171,12 @@ module BSON
163
171
  # where the object was instantiated in a non-standard way. (Like a
164
172
  # Marshal.load)
165
173
  #
166
- # @return [ String ] The raw bytes.
174
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
167
175
  #
168
176
  # @see http://bsonspec.org/#/specification
169
177
  #
170
178
  # @since 2.0.0
171
- def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
179
+ def to_bson(buffer = ByteBuffer.new)
172
180
  buffer.put_bytes(generate_data)
173
181
  end
174
182
 
@@ -183,18 +191,60 @@ module BSON
183
191
  def to_s
184
192
  generate_data.to_hex_string.force_encoding(UTF8)
185
193
  end
186
- alias :to_str :to_s
194
+ alias to_str to_s
187
195
 
188
- # Raised when trying to create an object id with invalid data.
196
+ # Extract the process-specific part of the object id. This is used only
197
+ # internally, for testing, and should not be used elsewhere.
189
198
  #
190
- # @since 2.0.0
191
- class Invalid < RuntimeError; end
199
+ # @return [ String ] The process portion of the id.
200
+ #
201
+ # @api private
202
+ def _process_part
203
+ to_s[8, 10]
204
+ end
205
+
206
+ # Extract the counter-specific part of the object id. This is used only
207
+ # internally, for testing, and should not be used elsewhere.
208
+ #
209
+ # @return [ String ] The counter portion of the id.
210
+ #
211
+ # @api private
212
+ def _counter_part
213
+ to_s[18, 6]
214
+ end
215
+
216
+ # Extended by native code (see init.c, util.c, GeneratorExtension.java)
217
+ #
218
+ # @api private
219
+ #
220
+ # rubocop:disable Lint/EmptyClass
221
+ class Generator
222
+ end
223
+ # rubocop:enable Lint/EmptyClass
224
+
225
+ # We keep one global generator for object ids.
226
+ @@generator = Generator.new
227
+
228
+ # Accessor for querying the generator directly; used in testing.
229
+ #
230
+ # @api private
231
+ def self._generator
232
+ @@generator
233
+ end
192
234
 
193
235
  private
194
236
 
237
+ def initialize_copy(other)
238
+ generate_data
239
+ other.instance_variable_set(:@raw_data, @raw_data)
240
+ end
241
+
195
242
  def generate_data
196
243
  repair if defined?(@data)
244
+
245
+ # rubocop:disable Naming/MemoizedInstanceVariableName
197
246
  @raw_data ||= @@generator.next_object_id
247
+ # rubocop:enable Naming/MemoizedInstanceVariableName
198
248
  end
199
249
 
200
250
  def repair
@@ -203,18 +253,18 @@ module BSON
203
253
  end
204
254
 
205
255
  class << self
206
-
207
256
  # Deserialize the object id from raw BSON bytes.
208
257
  #
209
258
  # @example Get the object id from BSON.
210
259
  # ObjectId.from_bson(bson)
211
260
  #
212
261
  # @param [ ByteBuffer ] buffer The byte buffer.
262
+ # @param [ Hash ] _ An optional hash of keyword arguments (unused).
213
263
  #
214
264
  # @return [ BSON::ObjectId ] The object id.
215
265
  #
216
266
  # @since 2.0.0
217
- def from_bson(buffer)
267
+ def from_bson(buffer, **_)
218
268
  from_data(buffer.get_bytes(12))
219
269
  end
220
270
 
@@ -241,16 +291,15 @@ module BSON
241
291
  #
242
292
  # @param [ String ] string The string to create the id from.
243
293
  #
244
- # @raise [ BSON::ObjectId::Invalid ] If the provided string is invalid.
294
+ # @raise [ BSON::Error::InvalidObjectId ] If the provided string is invalid.
245
295
  #
246
296
  # @return [ BSON::ObjectId ] The new object id.
247
297
  #
248
298
  # @since 2.0.0
249
299
  def from_string(string)
250
- unless legal?(string)
251
- raise Invalid.new("'#{string}' is an invalid ObjectId.")
252
- end
253
- from_data([ string ].pack("H*"))
300
+ raise Error::InvalidObjectId, "'#{string}' is an invalid ObjectId." unless legal?(string)
301
+
302
+ from_data([ string ].pack('H*'))
254
303
  end
255
304
 
256
305
  # Create a new object id from a time.
@@ -271,7 +320,7 @@ module BSON
271
320
  #
272
321
  # @since 2.0.0
273
322
  def from_time(time, options = {})
274
- from_data(options[:unique] ? @@generator.next_object_id(time.to_i) : [ time.to_i ].pack("Nx8"))
323
+ from_data(options[:unique] ? @@generator.next_object_id(time.to_i) : [ time.to_i ].pack('Nx8'))
275
324
  end
276
325
 
277
326
  # Determine if the provided string is a legal object id.
@@ -279,13 +328,13 @@ module BSON
279
328
  # @example Is the string a legal object id?
280
329
  # BSON::ObjectId.legal?(string)
281
330
  #
282
- # @param [ String ] The string to check.
331
+ # @param [ String ] string The string to check.
283
332
  #
284
333
  # @return [ true, false ] If the string is legal.
285
334
  #
286
335
  # @since 2.0.0
287
336
  def legal?(string)
288
- string.to_s =~ /\A[0-9a-f]{24}\z/i ? true : false
337
+ (string.to_s =~ /\A[0-9a-f]{24}\z/i) ? true : false
289
338
  end
290
339
 
291
340
  # Executes the provided block only if the size of the provided object is
@@ -296,100 +345,36 @@ module BSON
296
345
  #
297
346
  # @param [ String, Array ] object The object to repair.
298
347
  #
299
- # @raise [ Invalid ] If the array is not 12 elements.
348
+ # @raise [ BSON::Error::InvalidObjectId ] If the array is not 12 elements.
300
349
  #
301
350
  # @return [ String ] The result of the block.
302
351
  #
303
352
  # @since 2.0.0
304
353
  def repair(object)
305
- if object.size == 12
306
- block_given? ? yield(object) : object
307
- else
308
- raise Invalid.new("#{object.inspect} is not a valid object id.")
309
- end
310
- end
311
- end
354
+ raise Error::InvalidObjectId, "#{object.inspect} is not a valid object id." if object.size != 12
312
355
 
313
- # Inner class that encapsulates the behaviour of actually generating each
314
- # part of the ObjectId.
315
- #
316
- # @api private
317
- #
318
- # @since 2.0.0
319
- class Generator
320
-
321
- # @!attribute machine_id
322
- # @return [ String ] The unique machine id.
323
- # @since 2.0.0
324
- attr_reader :machine_id
325
-
326
- # Instantiate the new object id generator. Will set the machine id once
327
- # on the initial instantiation.
328
- #
329
- # @example Instantiate the generator.
330
- # BSON::ObjectId::Generator.new
331
- #
332
- # @since 2.0.0
333
- def initialize
334
- @counter = rand(0x1000000)
335
- @machine_id = Digest::MD5.digest(Socket.gethostname).unpack("N")[0]
336
- @mutex = Mutex.new
356
+ block_given? ? yield(object) : object
337
357
  end
338
358
 
339
- # Return object id data based on the current time, incrementing the
340
- # object id counter. Will use the provided time if not nil.
341
- #
342
- # @example Get the next object id data.
343
- # generator.next_object_id
344
- #
345
- # @param [ Time ] time The optional time to generate with.
346
- #
347
- # @return [ String ] The raw object id bytes.
348
- #
349
- # @since 2.0.0
350
- def next_object_id(time = nil)
351
- @mutex.lock
352
- begin
353
- count = @counter = (@counter + 1) % 0xFFFFFF
354
- ensure
355
- @mutex.unlock rescue nil
356
- end
357
- generate(time || ::Time.new.to_i, count)
358
- end
359
+ # The largest numeric value that can be converted to an integer by MRI's
360
+ # NUM2UINT. Further, the spec dictates that the time component of an
361
+ # ObjectID must be no more than 4 bytes long, so the spec itself is
362
+ # constrained in this regard.
363
+ MAX_INTEGER = 2**32
359
364
 
360
- # Generate object id data for a given time using the provided counter.
361
- #
362
- # @example Generate the object id bytes.
363
- # generator.generate(time)
365
+ # Returns an integer timestamp (seconds since the Epoch). Primarily used
366
+ # by the generator to produce object ids.
364
367
  #
365
- # @param [ Integer ] time The time since epoch in seconds.
366
- # @param [ Integer ] counter The optional counter.
368
+ # @note This value is guaranteed to be no more than 4 bytes in length. A
369
+ # time value far enough in the future to require a larger integer than
370
+ # 4 bytes will be truncated to 4 bytes.
367
371
  #
368
- # @return [ String ] The raw object id bytes.
369
- #
370
- # @since 2.0.0
371
- def generate(time, counter = 0)
372
- [ time, machine_id, process_id, counter << 8 ].pack("N NX lXX NX")
373
- end
374
-
375
- private
376
-
377
- if Environment.jruby?
378
- def process_id
379
- "#{Process.pid}#{Thread.current.object_id}".hash % 0xFFFF
380
- end
381
- else
382
- def process_id
383
- Process.pid % 0xFFFF
384
- end
372
+ # @return [ Integer ] the number of seconds since the Epoch.
373
+ def timestamp
374
+ ::Time.now.to_i % MAX_INTEGER
385
375
  end
386
376
  end
387
377
 
388
- # We keep one global generator for object ids.
389
- #
390
- # @since 2.0.0
391
- @@generator = Generator.new
392
-
393
378
  # Register this type when the module is loaded.
394
379
  #
395
380
  # @since 2.0.0
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+ # Copyright (C) 2016-2020 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module BSON
18
+
19
+ # Injects behaviour for encoding OpenStruct objects using hashes
20
+ # to raw bytes as specified by the BSON spec.
21
+ #
22
+ # @see http://bsonspec.org/#/specification
23
+ #
24
+ # @since 4.2.0
25
+ module OpenStruct
26
+
27
+ # Get the OpenStruct as encoded BSON.
28
+ #
29
+ # @example Get the OpenStruct object as encoded BSON.
30
+ # OpenStruct.new({ "field" => "value" }).to_bson
31
+ #
32
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
33
+ #
34
+ # @see http://bsonspec.org/#/specification
35
+ #
36
+ # @since 4.2.0
37
+ def to_bson(buffer = ByteBuffer.new)
38
+ if Environment.ruby_1_9?
39
+ marshal_dump.dup
40
+ else
41
+ to_h
42
+ end.to_bson(buffer)
43
+ end
44
+
45
+ # The BSON type for OpenStruct objects is the Hash type of 0x03.
46
+ #
47
+ # @example Get the bson type.
48
+ # struct.bson_type
49
+ #
50
+ # @return [ String ] The character 0x03.
51
+ #
52
+ # @since 4.2.0
53
+ def bson_type
54
+ ::Hash::BSON_TYPE
55
+ end
56
+ end
57
+
58
+ ::OpenStruct.send(:include, OpenStruct) if defined?(::OpenStruct)
59
+ end