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/hash.rb CHANGED
@@ -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,41 +14,32 @@
12
14
  # See the License for the specific language governing permissions and
13
15
  # limitations under the License.
14
16
 
17
+ # The top-level BSON module.
15
18
  module BSON
16
-
17
19
  # Injects behaviour for encoding and decoding hashes to
18
20
  # and from raw bytes as specified by the BSON spec.
19
21
  #
20
22
  # @see http://bsonspec.org/#/specification
21
- #
22
- # @since 2.0.0
23
23
  module Hash
24
-
25
- # An hash (embedded document) is type 0x03 in the BSON spec.
26
- #
27
- # @since 2.0.0
28
- BSON_TYPE = 3.chr.force_encoding(BINARY).freeze
24
+ # A hash, also called an embedded document, is type 0x03 in the BSON spec.
25
+ BSON_TYPE = ::String.new(3.chr, encoding: BINARY).freeze
29
26
 
30
27
  # Get the hash as encoded BSON.
31
28
  #
32
29
  # @example Get the hash as encoded BSON.
33
30
  # { "field" => "value" }.to_bson
34
31
  #
35
- # @return [ String ] The encoded string.
32
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
36
33
  #
37
34
  # @see http://bsonspec.org/#/specification
38
- #
39
- # @since 2.0.0
40
- def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
41
- position = buffer.length
42
- buffer.put_int32(0)
43
- each do |field, value|
44
- buffer.put_byte(value.bson_type)
45
- buffer.put_cstring(field.to_bson_key(validating_keys))
46
- value.to_bson(buffer, validating_keys)
35
+ def to_bson(buffer = ByteBuffer.new)
36
+ # If the native buffer version has an optimized version, we'll call
37
+ # it directly. Otherwise, we'll serialize the hash the hard way.
38
+ if buffer.respond_to?(:put_hash)
39
+ buffer.put_hash(self)
40
+ else
41
+ serialize_to_buffer(buffer)
47
42
  end
48
- buffer.put_byte(NULL_BYTE)
49
- buffer.replace_int32(position, buffer.length - position)
50
43
  end
51
44
 
52
45
  # Converts the hash to a normalized value in a BSON document.
@@ -54,44 +47,166 @@ module BSON
54
47
  # @example Convert the hash to a normalized value.
55
48
  # hash.to_bson_normalized_value
56
49
  #
57
- # @return [ BSON::Document ] The normazlied hash.
58
- #
59
- # @since 3.0.0
50
+ # @return [ BSON::Document ] The normalized hash.
60
51
  def to_bson_normalized_value
61
52
  Document.new(self)
62
53
  end
63
54
 
64
- module ClassMethods
55
+ # Converts this object to a representation directly serializable to
56
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
57
+ #
58
+ # This method recursively invokes +as_extended_json+ with the provided
59
+ # options on each hash value.
60
+ #
61
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
62
+ # (default is canonical extended JSON)
63
+ #
64
+ # @return [ Hash ] This hash converted to extended json representation.
65
+ def as_extended_json(**options)
66
+ transform_values { |value| value.as_extended_json(**options) }
67
+ end
68
+
69
+ private
70
+
71
+ # Serialize this hash instance to the given buffer.
72
+ #
73
+ # @param [ ByteBuf ] buffer The buffer to receive the serialized hash.
74
+ def serialize_to_buffer(buffer)
75
+ position = buffer.length
76
+ buffer.put_int32(0)
77
+ serialize_key_value_pairs(buffer)
78
+ buffer.put_byte(NULL_BYTE)
79
+ buffer.replace_int32(position, buffer.length - position)
80
+ end
81
+
82
+ # Serialize the key/value pairs in this hash instance to the given
83
+ # buffer.
84
+ #
85
+ # @param [ ByteBuf ] buffer The buffer to received the serialized
86
+ # key/value pairs.
87
+ #
88
+ # @raise [ Error::UnserializableClass ] if a value cannot be serialized
89
+ def serialize_key_value_pairs(buffer)
90
+ each do |field, value|
91
+ unless value.respond_to?(:bson_type)
92
+ raise Error::UnserializableClass,
93
+ "Hash value for key '#{field}' does not define its BSON serialized type: #{value}"
94
+ end
65
95
 
96
+ buffer.put_byte(value.bson_type)
97
+ key = field.to_bson_key
98
+ serialize_key(buffer, key)
99
+ value.to_bson(buffer)
100
+ end
101
+ end
102
+
103
+ # Serialize the key/value pairs in this hash instance to the given
104
+ # buffer.
105
+ #
106
+ # @param [ ByteBuf ] buffer The buffer to received the serialized
107
+ # key/value pairs.
108
+ #
109
+ # @raise [ ArgumentError ] if the string cannot be serialized
110
+ # @raise [ EncodingError ] if the string is not a valid encoding
111
+ def serialize_key(buffer, key)
112
+ buffer.put_cstring(key)
113
+ rescue ArgumentError => e
114
+ raise ArgumentError, "Error serializing key #{key}: #{e.class}: #{e}"
115
+ rescue EncodingError => e
116
+ # Note this may convert exception class from a subclass of
117
+ # EncodingError to EncodingError itself
118
+ raise EncodingError, "Error serializing key #{key}: #{e.class}: #{e}"
119
+ end
120
+
121
+ # The methods to augment the Hash class with (class-level methods).
122
+ module ClassMethods
66
123
  # Deserialize the hash from BSON.
67
124
  #
125
+ # @note If the argument cannot be parsed, an exception will be raised
126
+ # and the argument will be left in an undefined state. The caller
127
+ # must explicitly call `rewind` on the buffer before trying to parse
128
+ # it again.
129
+ #
68
130
  # @param [ ByteBuffer ] buffer The byte buffer.
69
131
  #
70
- # @return [ Array ] The decoded hash.
132
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
133
+ #
134
+ # @return [ Hash ] The decoded hash.
71
135
  #
72
136
  # @see http://bsonspec.org/#/specification
137
+ def from_bson(buffer, **options)
138
+ if buffer.respond_to?(:get_hash)
139
+ buffer.get_hash(**options)
140
+ else
141
+ hash = parse_hash_from_buffer(buffer, **options)
142
+ maybe_dbref(hash)
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ # If the hash looks like a DBRef, try and decode it as such. If
149
+ # is turns out to be invalid--or if it doesn't look like a DBRef
150
+ # to begin with--return the hash itself.
73
151
  #
74
- # @since 2.0.0
75
- def from_bson(buffer)
152
+ # @param [ Hash ] hash the hash to try and decode
153
+ #
154
+ # @return [ DBRef | Hash ] the result of decoding the hash
155
+ def maybe_dbref(hash)
156
+ return DBRef.new(hash) if hash['$ref'] && hash['$id']
157
+
158
+ hash
159
+ rescue Error::InvalidDBRefArgument
160
+ hash
161
+ end
162
+
163
+ # Given a byte buffer, extract and return a hash from it.
164
+ #
165
+ # @param [ ByteBuf ] buffer the buffer to read data from
166
+ # @param [ Hash ] options the keyword arguments
167
+ #
168
+ # @return [ Hash ] the hash parsed from the buffer
169
+ def parse_hash_from_buffer(buffer, **options)
76
170
  hash = Document.allocate
77
- buffer.get_int32 # Throw away the size.
171
+ start_position = buffer.read_position
172
+ expected_byte_size = buffer.get_int32
173
+
174
+ parse_hash_contents(hash, buffer, **options)
175
+
176
+ actual_byte_size = buffer.read_position - start_position
177
+ return hash unless actual_byte_size != expected_byte_size
178
+
179
+ raise Error::BSONDecodeError,
180
+ "Expected hash to take #{expected_byte_size} bytes but it took #{actual_byte_size} bytes"
181
+ end
182
+
183
+ # Given an empty hash and a byte buffer, parse the key/value pairs from
184
+ # the buffer and populate the hash with them.
185
+ #
186
+ # @param [ Hash ] hash the hash to populate
187
+ # @param [ ByteBuf ] buffer the buffer to read data from
188
+ # @param [ Hash ] options the keyword arguments
189
+ def parse_hash_contents(hash, buffer, **options)
78
190
  while (type = buffer.get_byte) != NULL_BYTE
79
191
  field = buffer.get_cstring
80
- hash.store(field, BSON::Registry.get(type, field).from_bson(buffer))
192
+ cls = BSON::Registry.get(type, field)
193
+ value = if options.empty?
194
+ # Compatibility with the older Ruby driver versions which define
195
+ # a DBRef class with from_bson accepting a single argument.
196
+ cls.from_bson(buffer)
197
+ else
198
+ cls.from_bson(buffer, **options)
199
+ end
200
+ hash.store(field, value)
81
201
  end
82
- hash
83
202
  end
84
203
  end
85
204
 
86
205
  # Register this type when the module is loaded.
87
- #
88
- # @since 2.0.0
89
206
  Registry.register(BSON_TYPE, ::Hash)
90
207
  end
91
208
 
92
209
  # Enrich the core Hash class with this module.
93
- #
94
- # @since 2.0.0
95
- ::Hash.send(:include, Hash)
96
- ::Hash.send(:extend, Hash::ClassMethods)
210
+ ::Hash.include Hash
211
+ ::Hash.extend Hash::ClassMethods
97
212
  end
data/lib/bson/int32.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.
@@ -14,18 +16,18 @@
14
16
 
15
17
  module BSON
16
18
 
17
- # Represents a $maxKey type, which compares less than any other value in the
18
- # specification.
19
+ # Represents int32 type.
19
20
  #
20
21
  # @see http://bsonspec.org/#/specification
21
22
  #
22
23
  # @since 2.0.0
23
24
  class Int32
25
+ include JSON
24
26
 
25
27
  # A boolean is type 0x08 in the BSON spec.
26
28
  #
27
29
  # @since 2.0.0
28
- BSON_TYPE = 16.chr.force_encoding(BINARY).freeze
30
+ BSON_TYPE = ::String.new(16.chr, encoding: BINARY).freeze
29
31
 
30
32
  # The number of bytes constant.
31
33
  #
@@ -35,21 +37,117 @@ module BSON
35
37
  # Constant for the int 32 pack directive.
36
38
  #
37
39
  # @since 2.0.0
38
- PACK = "l<".freeze
40
+ PACK = "l<"
39
41
 
40
42
  # Deserialize an Integer from BSON.
41
43
  #
42
44
  # @param [ ByteBuffer ] buffer The byte buffer.
43
45
  #
46
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
47
+ #
44
48
  # @return [ Integer ] The decoded Integer.
45
49
  #
46
50
  # @see http://bsonspec.org/#/specification
47
51
  #
48
52
  # @since 2.0.0
49
- def self.from_bson(buffer)
53
+ def self.from_bson(buffer, **options)
50
54
  buffer.get_int32
51
55
  end
52
56
 
57
+ # Instantiate a BSON Int32.
58
+ #
59
+ # @param [ Integer ] value The 32-bit integer.
60
+ #
61
+ # @see http://bsonspec.org/#/specification
62
+ #
63
+ # @since 4.2.0
64
+ def initialize(value)
65
+ if value.is_a?(self.class)
66
+ @value = value.value
67
+ return
68
+ end
69
+
70
+ unless value.bson_int32?
71
+ raise RangeError.new("#{value} cannot be stored in 32 bits")
72
+ end
73
+ @value = value.freeze
74
+ end
75
+
76
+ # Returns the value of this Int32.
77
+ #
78
+ # @return [ Integer ] The integer value.
79
+ attr_reader :value
80
+
81
+ # Append the integer as encoded BSON to a ByteBuffer.
82
+ #
83
+ # @example Encoded the integer and append to a ByteBuffer.
84
+ # int32.to_bson
85
+ #
86
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded integer.
87
+ #
88
+ # @see http://bsonspec.org/#/specification
89
+ #
90
+ # @since 4.2.0
91
+ def to_bson(buffer = ByteBuffer.new)
92
+ buffer.put_int32(value)
93
+ end
94
+
95
+ # Convert the integer to a BSON string key.
96
+ #
97
+ # @example Convert the integer to a BSON key string.
98
+ # int.to_bson_key
99
+ #
100
+ # @return [ String ] The string key.
101
+ #
102
+ # @since 4.2.0
103
+ def to_bson_key
104
+ value
105
+ end
106
+
107
+ # Check equality of the int32 with another object.
108
+ #
109
+ # @param [ Object ] other The object to check against.
110
+ #
111
+ # @return [ true, false ] If the objects are equal.
112
+ #
113
+ # @since 4.4.0
114
+ def ==(other)
115
+ return false unless other.is_a?(Int32)
116
+ value == other.value
117
+ end
118
+ alias :eql? :==
119
+ alias :=== :==
120
+
121
+ # Return a string representation of the Int32 for use in
122
+ # application-level JSON serialization. This method is intentionally
123
+ # different from #as_extended_json.
124
+ #
125
+ # @example Get the Int32 as a JSON-serializable object.
126
+ # int32.as_json
127
+ #
128
+ # @return [ Integer ] The Int32 as an Integer.
129
+ def as_json(**options)
130
+ value
131
+ end
132
+
133
+ # Converts this object to a representation directly serializable to
134
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
135
+ #
136
+ # This method returns the integer value if relaxed representation is
137
+ # requested, otherwise a $numberInt hash.
138
+ #
139
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
140
+ # (default is canonical extended JSON)
141
+ #
142
+ # @return [ Hash | Integer ] The extended json representation.
143
+ def as_extended_json(**options)
144
+ if options[:mode] == :relaxed || options[:mode] == :legacy
145
+ value
146
+ else
147
+ {'$numberInt' => value.to_s}
148
+ end
149
+ end
150
+
53
151
  # Register this type when the module is loaded.
54
152
  #
55
153
  # @since 2.0.0
data/lib/bson/int64.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.
@@ -14,35 +16,136 @@
14
16
 
15
17
  module BSON
16
18
 
17
- # Represents a $maxKey type, which compares less than any other value in the
18
- # specification.
19
+ # Represents int64 type.
19
20
  #
20
21
  # @see http://bsonspec.org/#/specification
21
22
  #
22
23
  # @since 2.0.0
23
24
  class Int64
25
+ include JSON
24
26
 
25
27
  # A boolean is type 0x08 in the BSON spec.
26
28
  #
27
29
  # @since 2.0.0
28
- BSON_TYPE = 18.chr.force_encoding(BINARY).freeze
30
+ BSON_TYPE = ::String.new(18.chr, encoding: BINARY).freeze
29
31
 
30
32
  # Constant for the int 64 pack directive.
31
33
  #
32
34
  # @since 2.0.0
33
- PACK = "q<".freeze
35
+ PACK = "q<"
34
36
 
35
37
  # Deserialize an Integer from BSON.
36
38
  #
37
39
  # @param [ ByteBuffer ] buffer The byte buffer.
38
40
  #
39
- # @return [ Integer ] The decoded Integer.
41
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
42
+ #
43
+ # @return [ Integer | BSON::Int64 ] The decoded Integer.
40
44
  #
41
45
  # @see http://bsonspec.org/#/specification
42
46
  #
43
47
  # @since 2.0.0
44
- def self.from_bson(buffer)
45
- buffer.get_int64
48
+ def self.from_bson(buffer, **options)
49
+ value = buffer.get_int64
50
+ if options[:mode] == :bson
51
+ new(value)
52
+ else
53
+ value
54
+ end
55
+ end
56
+
57
+ # Instantiate a BSON Int64.
58
+ #
59
+ # @param [ Integer ] value The 64-bit integer.
60
+ #
61
+ # @see http://bsonspec.org/#/specification
62
+ #
63
+ # @since 4.2.0
64
+ def initialize(value)
65
+ if value.is_a?(self.class)
66
+ @value = value.value
67
+ return
68
+ end
69
+
70
+ unless value.bson_int64?
71
+ raise RangeError.new("#{value} cannot be stored in 64 bits")
72
+ end
73
+ @value = value.freeze
74
+ end
75
+
76
+ # Returns the value of this Int64.
77
+ #
78
+ # @return [ Integer ] The integer value.
79
+ attr_reader :value
80
+
81
+ # Append the integer as encoded BSON to a ByteBuffer.
82
+ #
83
+ # @example Encoded the integer and append to a ByteBuffer.
84
+ # int64.to_bson
85
+ #
86
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded integer.
87
+ #
88
+ # @see http://bsonspec.org/#/specification
89
+ #
90
+ # @since 4.2.0
91
+ def to_bson(buffer = ByteBuffer.new)
92
+ buffer.put_int64(value)
93
+ end
94
+
95
+ # Convert the integer to a BSON string key.
96
+ #
97
+ # @example Convert the integer to a BSON key string.
98
+ # int.to_bson_key
99
+ #
100
+ # @return [ String ] The string key.
101
+ #
102
+ # @since 4.2.0
103
+ def to_bson_key
104
+ value
105
+ end
106
+
107
+ # Check equality of the int64 with another object.
108
+ #
109
+ # @param [ Object ] other The object to check against.
110
+ #
111
+ # @return [ true, false ] If the objects are equal.
112
+ #
113
+ # @since 4.4.0
114
+ def ==(other)
115
+ return false unless other.is_a?(Int64)
116
+ value == other.value
117
+ end
118
+ alias :eql? :==
119
+ alias :=== :==
120
+
121
+ # Return a string representation of the Int64 for use in
122
+ # application-level JSON serialization. This method is intentionally
123
+ # different from #as_extended_json.
124
+ #
125
+ # @example Get the Int64 as a JSON-serializable object.
126
+ # int64.as_json
127
+ #
128
+ # @return [ Integer ] The Int64 as an Integer.
129
+ def as_json(**options)
130
+ value
131
+ end
132
+
133
+ # Converts this object to a representation directly serializable to
134
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
135
+ #
136
+ # This method returns the integer value if relaxed representation is
137
+ # requested, otherwise a $numberLong hash.
138
+ #
139
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
140
+ # (default is canonical extended JSON)
141
+ #
142
+ # @return [ Hash | Integer ] The extended json representation.
143
+ def as_extended_json(**options)
144
+ if options[:mode] == :relaxed || options[:mode] == :legacy
145
+ value
146
+ else
147
+ {'$numberLong' => value.to_s}
148
+ end
46
149
  end
47
150
 
48
151
  # Register this type when the module is loaded.
data/lib/bson/integer.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.
@@ -45,13 +47,13 @@ module BSON
45
47
  # The BSON index size.
46
48
  #
47
49
  # @since 2.0.0
48
- BSON_INDEX_SIZE = 1024.freeze
50
+ BSON_INDEX_SIZE = 1024
49
51
 
50
52
  # A hash of index values for array optimization.
51
53
  #
52
54
  # @since 2.0.0
53
55
  BSON_ARRAY_INDEXES = ::Array.new(BSON_INDEX_SIZE) do |i|
54
- (i.to_s.force_encoding(BINARY) << NULL_BYTE).freeze
56
+ (i.to_s.b << NULL_BYTE).freeze
55
57
  end.freeze
56
58
 
57
59
  # Is this integer a valid BSON 32 bit value?
@@ -98,12 +100,12 @@ module BSON
98
100
  # @example Get the integer as encoded BSON.
99
101
  # 1024.to_bson
100
102
  #
101
- # @return [ String ] The encoded string.
103
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
102
104
  #
103
105
  # @see http://bsonspec.org/#/specification
104
106
  #
105
107
  # @since 2.0.0
106
- def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
108
+ def to_bson(buffer = ByteBuffer.new)
107
109
  if bson_int32?
108
110
  buffer.put_int32(self)
109
111
  elsif bson_int64?
@@ -150,13 +152,45 @@ module BSON
150
152
  # @example Convert the integer to a BSON key string.
151
153
  # 1.to_bson_key
152
154
  #
153
- # @param [ true, false ] validating_keys If BSON should validate the key.
154
- #
155
155
  # @return [ String ] The string key.
156
156
  #
157
157
  # @since 2.0.0
158
- def to_bson_key(validating_keys = Config.validating_keys?)
159
- to_s.to_bson_key(validating_keys)
158
+ def to_bson_key
159
+ self
160
+ end
161
+
162
+ # Converts this object to a representation directly serializable to
163
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
164
+ #
165
+ # This method returns the integer itself if relaxed representation is
166
+ # requested, otherwise a $numberInt hash if the value fits in 32 bits
167
+ # and a $numberLong otherwise. Regardless of which representation is
168
+ # requested, a value that does not fit in 64 bits raises RangeError.
169
+ #
170
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
171
+ # (default is canonical extended JSON)
172
+ #
173
+ # @return [ Hash | Integer ] The extended json representation.
174
+ def as_extended_json(**options)
175
+ # The behavior of native integers' serialization to extended json is
176
+ # not specified. Following our bson serialization logic in this file,
177
+ # produce explicit $numberInt or $numberLong, choosing $numberInt if
178
+ # the integer fits in 32 bits. In Ruby integers can be arbitrarily
179
+ # big; integers that do not fit into 64 bits raise an error as we do not
180
+ # want to silently perform an effective type conversion of integer ->
181
+ # decimal.
182
+
183
+ unless bson_int64?
184
+ raise RangeError, "Integer #{self} is too big to be represented as a MongoDB integer"
185
+ end
186
+
187
+ if options[:mode] == :relaxed || options[:mode] == :legacy
188
+ self
189
+ elsif bson_int32?
190
+ {'$numberInt' => to_s}
191
+ else
192
+ {'$numberLong' => to_s}
193
+ end
160
194
  end
161
195
 
162
196
  private
data/lib/bson/json.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.