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
@@ -0,0 +1,272 @@
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
+ require 'bigdecimal'
18
+ require 'bson/decimal128/builder'
19
+
20
+ module BSON
21
+
22
+ class Decimal128
23
+ include JSON
24
+ include Comparable
25
+
26
+ # A Decimal128 is type 0x13 in the BSON spec.
27
+ #
28
+ # @since 4.2.0
29
+ BSON_TYPE = ::String.new(19.chr, encoding: BINARY).freeze
30
+
31
+ # Exponent offset.
32
+ #
33
+ # @since 4.2.0
34
+ EXPONENT_OFFSET = 6176
35
+
36
+ # Minimum exponent.
37
+ #
38
+ # @since 4.2.0
39
+ MIN_EXPONENT = -6176
40
+
41
+ # Maximum exponent.
42
+ #
43
+ # @since 4.2.0
44
+ MAX_EXPONENT = 6111
45
+
46
+ # Maximum digits of precision.
47
+ #
48
+ # @since 4.2.0
49
+ MAX_DIGITS_OF_PRECISION = 34
50
+
51
+ # Key for this type when converted to extended json.
52
+ #
53
+ # @since 4.2.0
54
+ EXTENDED_JSON_KEY = "$numberDecimal"
55
+
56
+ # The native type to which this object can be converted.
57
+ #
58
+ # @since 4.2.0
59
+ NATIVE_TYPE = BigDecimal
60
+
61
+ # Return a string representation of the Decimal128 use in standard
62
+ # application-level JSON serialization. Returns nil for non-real
63
+ # numbers such as NaN and Infinity to be compatible with ActiveSupport.
64
+ # This method is intentionally different from #as_extended_json.
65
+ #
66
+ # @example Get the Decimal128 as a JSON-serializable object.
67
+ # decimal.as_json
68
+ #
69
+ # @return [ String | nil ] The decimal128 as a String or nil for non-representable numbers.
70
+ def as_json(*args)
71
+ value = to_s
72
+ value unless %w[NaN Infinity -Infinity].include?(value)
73
+ end
74
+
75
+ # Converts this object to a representation directly serializable to
76
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
77
+ #
78
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
79
+ # (default is canonical extended JSON)
80
+ #
81
+ # @return [ Hash ] The extended json representation.
82
+ def as_extended_json(**_options)
83
+ { EXTENDED_JSON_KEY => to_s }
84
+ end
85
+
86
+ # Check equality of the decimal128 object with another object.
87
+ #
88
+ # @example Check if the decimal128 object is equal to the other.
89
+ # decimal == other
90
+ #
91
+ # @param [ Object ] other The object to check against.
92
+ #
93
+ # @return [ true, false ] If the objects are equal.
94
+ #
95
+ # @since 4.2.0
96
+ def ==(other)
97
+ return false unless other.is_a?(Decimal128)
98
+ @high == other.instance_variable_get(:@high) &&
99
+ @low == other.instance_variable_get(:@low)
100
+ end
101
+ alias :eql? :==
102
+
103
+ def <=>(other)
104
+ to_d <=> case other
105
+ when Decimal128
106
+ other.to_d
107
+ else
108
+ other
109
+ end
110
+ end
111
+
112
+ # Create a new Decimal128 from a string or a BigDecimal instance.
113
+ #
114
+ # @example Create a Decimal128 from a BigDecimal.
115
+ # Decimal128.new(big_decimal)
116
+ #
117
+ # @param [ String, BigDecimal ] object The BigDecimal or String to use for
118
+ # instantiating a Decimal128.
119
+ #
120
+ # @raise [ BSON::Error::InvalidDecimal128Argument ] When argument is not a String or BigDecimal.
121
+ #
122
+ # @since 4.2.0
123
+ def initialize(object)
124
+ if object.is_a?(String)
125
+ set_bits(*Builder::FromString.new(object).bits)
126
+ elsif object.is_a?(BigDecimal)
127
+ set_bits(*Builder::FromBigDecimal.new(object).bits)
128
+ else
129
+ raise Error::InvalidDecimal128Argument.new
130
+ end
131
+ end
132
+
133
+ # Get the BSON type for Decimal128.
134
+ def bson_type
135
+ BSON_TYPE
136
+ end
137
+
138
+ # Get the decimal128 as its raw BSON data.
139
+ #
140
+ # @example Get the raw bson bytes in a buffer.
141
+ # decimal.to_bson
142
+ #
143
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
144
+ #
145
+ # @see http://bsonspec.org/#/specification
146
+ #
147
+ # @since 4.2.0
148
+ def to_bson(buffer = ByteBuffer.new)
149
+ buffer.put_decimal128(@low, @high)
150
+ end
151
+
152
+ # Get the hash value for the decimal128.
153
+ #
154
+ # @example Get the hash value.
155
+ # decimal.hash
156
+ #
157
+ # @return [ Integer ] The hash value.
158
+ #
159
+ # @since 4.2.0
160
+ def hash
161
+ num = @high << 64
162
+ num |= @low
163
+ num.hash
164
+ end
165
+
166
+ # Get a nice string for use with object inspection.
167
+ #
168
+ # @example Inspect the decimal128 object.
169
+ # decimal128.inspect
170
+ #
171
+ # @return [ String ] The decimal as a string.
172
+ #
173
+ # @since 4.2.0
174
+ def inspect
175
+ "BSON::Decimal128('#{to_s}')"
176
+ end
177
+
178
+ # Get the string representation of the decimal128.
179
+ #
180
+ # @example Get the decimal128 as a string.
181
+ # decimal128.to_s
182
+ #
183
+ # @return [ String ] The decimal128 as a string.
184
+ #
185
+ # @since 4.2.0
186
+ def to_s
187
+ @string ||= Builder::ToString.new(self).string
188
+ end
189
+ alias :to_str :to_s
190
+
191
+ # Get a Ruby BigDecimal object corresponding to this Decimal128.
192
+ # Note that, when converting to a Ruby BigDecimal, non-zero significant digits
193
+ # are preserved but trailing zeroes may be lost.
194
+ # See the following example:
195
+ #
196
+ # @example
197
+ # decimal128 = BSON::Decimal128.new("0.200")
198
+ # => BSON::Decimal128('0.200')
199
+ # big_decimal = decimal128.to_d
200
+ # => #<BigDecimal:7fc619c95388,'0.2E0',9(18)>
201
+ # big_decimal.to_s
202
+ # => "0.2E0"
203
+ #
204
+ # Note that the the BSON::Decimal128 object can represent -NaN, sNaN,
205
+ # and -sNaN while Ruby's BigDecimal cannot.
206
+ #
207
+ # @return [ BigDecimal ] The decimal as a BigDecimal.
208
+ def to_d
209
+ @big_decimal ||= BigDecimal(to_s)
210
+ end
211
+ alias :to_big_decimal :to_d
212
+
213
+ private
214
+
215
+ def set_bits(low, high)
216
+ @low = low
217
+ @high = high
218
+ end
219
+
220
+ class << self
221
+
222
+ # Deserialize the decimal128 from raw BSON bytes.
223
+ #
224
+ # @example Get the decimal128 from BSON.
225
+ # Decimal128.from_bson(bson)
226
+ #
227
+ # @param [ ByteBuffer ] buffer The byte buffer.
228
+ #
229
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
230
+ #
231
+ # @return [ BSON::Decimal128 ] The decimal object.
232
+ #
233
+ # @since 4.2.0
234
+ def from_bson(buffer, **options)
235
+ from_bits(*buffer.get_decimal128_bytes.unpack('Q<*'))
236
+ end
237
+
238
+ # Instantiate a Decimal128 from a string.
239
+ #
240
+ # @example Create a Decimal128 from a string.
241
+ # BSON::Decimal128.from_string("1.05E+3")
242
+ #
243
+ # @param [ String ] string The string to parse.
244
+ #
245
+ # @raise [ BSON::Error:InvalidDecimal128String ] If the provided string is invalid.
246
+ #
247
+ # @return [ BSON::Decimal128 ] The new decimal128.
248
+ #
249
+ # @since 4.2.0
250
+ def from_string(string)
251
+ from_bits(*Builder::FromString.new(string).bits)
252
+ end
253
+
254
+ # Instantiate a Decimal128 from high and low bits.
255
+ #
256
+ # @example Create a Decimal128 from high and low bits.
257
+ # BSON::Decimal128.from_bits(high, low)
258
+ #
259
+ # @param [ Integer ] high The high order bits.
260
+ # @param [ Integer ] low The low order bits.
261
+ #
262
+ # @return [ BSON::Decimal128 ] The new decimal128.
263
+ #
264
+ # @since 4.2.0
265
+ def from_bits(low, high)
266
+ decimal = allocate
267
+ decimal.send(:set_bits, low, high)
268
+ decimal
269
+ end
270
+ end
271
+ end
272
+ end
data/lib/bson/document.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.
@@ -35,8 +37,43 @@ module BSON
35
37
  class Document < ::Hash
36
38
 
37
39
  # Get a value from the document for the provided key. Can use string or
38
- # symbol access, but the fastest will be to always provide a key that is of
39
- # the same type as the stored keys.
40
+ # symbol access, with string access being the faster of the two.
41
+ #
42
+ # @overload fetch(key)
43
+ # Returns a value from the hash for the given key. If the key does
44
+ # not exist, raises KeyError exception.
45
+ #
46
+ # @overload fetch(key, default)
47
+ # Returns a value from the hash for the given key. If the key does not
48
+ # exist, returns *default*.
49
+ #
50
+ # @overload fetch(key, &block)
51
+ # Returns a value from the hash for the given key. If the key does not
52
+ # exist, returns the value of the block called with the key.
53
+ #
54
+ # @example Get an element for the key.
55
+ # document.fetch("field")
56
+ #
57
+ # @example Get an element for the key by symbol with a default.
58
+ # document.fetch(:field, 'foo')
59
+ #
60
+ # @example Get an element for the key by symbol with a block default.
61
+ # document.fetch(:field) { |key| key.upcase }
62
+ #
63
+ # @param [ String, Symbol ] key The key to look up.
64
+ # @param [ Object ] default Returned value if key does not exist.
65
+ # @yield [key] Block returning default value for the given key.
66
+ #
67
+ # @return [ Object ] The found value. Raises KeyError if none found.
68
+ #
69
+ # @since 4.4.0
70
+ def fetch(key, *args, &block)
71
+ key = convert_key(key)
72
+ super(key, *args, &block)
73
+ end
74
+
75
+ # Get a value from the document for the provided key. Can use string or
76
+ # symbol access, with string access being the faster of the two.
40
77
  #
41
78
  # @example Get an element for the key.
42
79
  # document["field"]
@@ -44,7 +81,7 @@ module BSON
44
81
  # @example Get an element for the key by symbol.
45
82
  # document[:field]
46
83
  #
47
- # @param [ String, Symbol ] key The key to lookup.
84
+ # @param [ String, Symbol ] key The key to look up.
48
85
  #
49
86
  # @return [ Object ] The found value, or nil if none found.
50
87
  #
@@ -53,7 +90,49 @@ module BSON
53
90
  super(convert_key(key))
54
91
  end
55
92
 
56
- # Set a value on the document. Will normalize symbol keys into strings.
93
+ # Stores a key-value pair in the current document.
94
+ #
95
+ # Since BSON documents provide deep indifferent access (both strings and
96
+ # symbols are accepted as keys, recursively), the value may be converted
97
+ # to facilitate indifferent access. This conversion is performed for
98
+ # built-in Array and Hash classes, and other classes can override
99
+ # +to_bson_normalized_value+ method to provide custom conversion logic.
100
+ # For example:
101
+ #
102
+ # doc = BSON::Document.new
103
+ # doc[:a] = {b: {c: 'd'}}
104
+ # doc['a']['b']['c']
105
+ # # => "d"
106
+ #
107
+ # Note that due to this conversion, the object that is stored in the
108
+ # receiver Document may be different from the object supplied as the
109
+ # right hand side of the assignment. In Ruby, the result of assignment
110
+ # is the right hand side, not the return value of []= method.
111
+ # Because of this, modifying the result of assignment generally does not
112
+ # work as intended:
113
+ #
114
+ # doc = BSON::Document.new
115
+ # foo = (doc[:a] = {b: {c: 'd'}})
116
+ # # foo is original Hash with symbol keys
117
+ # foo['test'] = 'test'
118
+ # # doc is not modified
119
+ # doc
120
+ # # => {"a"=>{"b"=>{"c"=>"d"}}}
121
+ #
122
+ # This behavior can be encountered when defaulting document contents with
123
+ # []= in a method, such as:
124
+ #
125
+ # def foo
126
+ # # @doc is a BSON::Document
127
+ # @doc[:foo] ||= calculation
128
+ # end
129
+ #
130
+ # The above method should be written as follows to allow chaining:
131
+ #
132
+ # def foo
133
+ # # @doc is a BSON::Document
134
+ # @doc[:foo] ||= calculation and @doc[:foo]
135
+ # end
57
136
  #
58
137
  # @example Set a value on the document.
59
138
  # document[:test] = "value"
@@ -144,7 +223,7 @@ module BSON
144
223
  #
145
224
  # @param [ BSON::Document, Hash ] other The document/hash to merge with.
146
225
  #
147
- # @eturn [ BSON::Document ] The result of the merge.
226
+ # @return [ BSON::Document ] The result of the merge.
148
227
  #
149
228
  # @since 3.0.0
150
229
  def merge(other, &block)
@@ -159,7 +238,7 @@ module BSON
159
238
  #
160
239
  # @param [ BSON::Document, Hash ] other The document/hash to merge with.
161
240
  #
162
- # @eturn [ BSON::Document ] The result of the merge.
241
+ # @return [ BSON::Document ] The result of the merge.
163
242
  #
164
243
  # @since 3.0.0
165
244
  def merge!(other)
@@ -172,6 +251,97 @@ module BSON
172
251
 
173
252
  alias :update :merge!
174
253
 
254
+ if instance_methods.include?(:dig)
255
+ # Retrieves the value object corresponding to the each key objects repeatedly.
256
+ # Will normalize symbol keys into strings.
257
+ #
258
+ # @example Get value from nested sub-documents, handling missing levels.
259
+ # document # => { :key1 => { "key2" => "value"}}
260
+ # document.dig(:key1, :key2) # => "value"
261
+ # document.dig("key1", "key2") # => "value"
262
+ # document.dig("foo", "key2") # => nil
263
+ #
264
+ # @param [ Array<String, Symbol> ] *keys Keys, which constitute a "path" to the nested value.
265
+ #
266
+ # @return [ Object, NilClass ] The requested value or nil.
267
+ #
268
+ # @since 3.0.0
269
+ def dig(*keys)
270
+ super(*keys.map{|key| convert_key(key)})
271
+ end
272
+ end
273
+
274
+ # Slices a document to include only the given keys.
275
+ # Will normalize symbol keys into strings.
276
+ # (this method is backported from ActiveSupport::Hash)
277
+ #
278
+ # @example Get a document/hash with only the `name` and `age` fields present
279
+ # document # => { _id: <ObjectId>, :name => "John", :age => 30, :location => "Earth" }
280
+ # document.slice(:name, 'age') # => { "name": "John", "age" => 30 }
281
+ # document.slice('name') # => { "name" => "John" }
282
+ # document.slice(:foo) # => {}
283
+ #
284
+ # @param [ Array<String, Symbol> ] *keys Keys, that will be kept in the resulting document
285
+ #
286
+ # @return [ BSON::Document ] The document with only the selected keys
287
+ #
288
+ # @since 4.3.1
289
+ def slice(*keys)
290
+ keys.each_with_object(self.class.new) do |key, hash|
291
+ if key?(key)
292
+ hash[key] = self[key]
293
+ end
294
+ end
295
+ end
296
+
297
+ # Returns a new document consisting of the current document minus the
298
+ # specified keys.
299
+ #
300
+ # The keys to be removed can be specified as either strings or symbols.
301
+ #
302
+ # @example Get a document/hash with only the `name` and `age` fields removed
303
+ # document # => { _id: <ObjectId>, :name => 'John', :age => 30, :location => 'Earth' }
304
+ # document.except(:name, 'age') # => { _id: <ObjectId>, location: 'Earth' }
305
+ #
306
+ # @param [ Array<String, Symbol> ] *keys Keys, that will be removed in the resulting document
307
+ #
308
+ # @return [ BSON::Document ] The document with the specified keys removed.
309
+ #
310
+ # @note This method is always defined, even if Hash already contains a
311
+ # definition of #except, because ActiveSupport unconditionally defines
312
+ # its version of #except which doesn't work for BSON::Document which
313
+ # causes problems if ActiveSupport is loaded after bson-ruby is.
314
+ def except(*keys)
315
+ copy = dup
316
+ keys.each {|key| copy.delete(key)}
317
+ copy
318
+ end
319
+
320
+ def symbolize_keys!
321
+ raise ArgumentError, 'symbolize_keys! is not supported on BSON::Document instances. Please convert the document to hash first (using #to_h), then call #symbolize_keys! on the Hash instance'
322
+ end
323
+
324
+ def deep_symbolize_keys!
325
+ warn <<~WARN
326
+ [DEPRECATION] `deep_symbolize_keys!` is not supported on BSON::Document instances.
327
+ Please convert the document to a Hash first (using `#to_h`), then call `#deep_symbolize_keys!` on the Hash.
328
+ This will raise an error starting with the v6.0.0 release.
329
+ WARN
330
+ end
331
+
332
+ # Override the Hash implementation of to_bson_normalized_value.
333
+ #
334
+ # BSON::Document is already of the correct type and already provides
335
+ # indifferent access to keys, hence no further conversions are necessary.
336
+ #
337
+ # Attempting to perform Hash's conversion on Document instances converts
338
+ # DBRefs to Documents which is wrong.
339
+ #
340
+ # @return [ BSON::Document ] The normalized hash.
341
+ def to_bson_normalized_value
342
+ self
343
+ end
344
+
175
345
  private
176
346
 
177
347
  def convert_key(key)
@@ -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.
@@ -30,7 +32,20 @@ module BSON
30
32
  #
31
33
  # @since 2.0.0
32
34
  def jruby?
33
- defined?(JRUBY_VERSION)
35
+ @jruby ||= defined?(JRUBY_VERSION)
36
+ end
37
+
38
+ # Determine if we are using Ruby version 1.9.
39
+ #
40
+ # @example Are we running with Ruby version 1.9?
41
+ # Environment.ruby_1_9?
42
+ #
43
+ # @return [ true, false ] If the Ruby version is 1.9.
44
+ #
45
+ # @since 4.2.0
46
+ # @deprecated
47
+ def ruby_1_9?
48
+ @ruby_1_9 ||= RUBY_VERSION < '2.0.0'
34
49
  end
35
50
  end
36
51
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+
4
+ module BSON
5
+ class Error
6
+
7
+ # Exception raised when BSON decoding fails.
8
+ class BSONDecodeError < Error
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+
4
+ module BSON
5
+ class Error
6
+
7
+ # Exception raised when Extended JSON parsing fails.
8
+ class ExtJSONParseError < Error
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+
4
+ module BSON
5
+ class Error
6
+
7
+ # Raised when validating keys and a key is illegal in MongoDB
8
+ class IllegalKey < Error
9
+
10
+ # Instantiate the exception.
11
+ #
12
+ # @example Instantiate the exception.
13
+ # BSON::Error::IllegalKey.new(string)
14
+ #
15
+ # @param [ String ] string The illegal string.
16
+ #
17
+ # @api private
18
+ def initialize(string)
19
+ super("'#{string}' is an illegal key in MongoDB. Keys may not start with '$' or contain a '.'.")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+
4
+ module BSON
5
+ class Error
6
+
7
+ # Raised when providing an invalid type to the Binary.
8
+ class InvalidBinaryType < Error
9
+
10
+ # @return [ Object ] The invalid type.
11
+ attr_reader :type
12
+
13
+ # Instantiate the new error.
14
+ #
15
+ # @example Instantiate the error.
16
+ # InvalidBinaryType.new(:error)
17
+ #
18
+ # @param [ Object ] type The invalid type.
19
+ #
20
+ # @api private
21
+ def initialize(type)
22
+ @type = type
23
+ end
24
+
25
+ # Get the custom error message for the exception.
26
+ #
27
+ # @example Get the message.
28
+ # error.message
29
+ #
30
+ # @return [ String ] The error message.
31
+ def message
32
+ "#{type.inspect} is not a valid binary type. " +
33
+ "Please use one of #{BSON::Binary::SUBTYPES.keys.map(&:inspect).join(", ")}."
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+
4
+ module BSON
5
+ class Error
6
+
7
+ # Raised when trying to create a BSON::DBRef from an object that is an invalid DBRef.
8
+ class InvalidDBRefArgument < Error
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+
4
+ module BSON
5
+ class Error
6
+
7
+ # Raised when trying to create a Decimal128 from an object that is neither a String nor a BigDecimal.
8
+ class InvalidDecimal128Argument < Error
9
+
10
+ # The custom error message for this error.
11
+ MESSAGE = 'A Decimal128 can only be created from a String or BigDecimal.'
12
+
13
+ # Get the custom error message for the exception.
14
+ #
15
+ # @example Get the message.
16
+ # error.message
17
+ #
18
+ # @return [ String ] The error message.
19
+ def message
20
+ MESSAGE
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:todo all
3
+
4
+ module BSON
5
+ class Error
6
+
7
+ # Raised when the exponent is outside the valid range.
8
+ class InvalidDecimal128Range < Error
9
+
10
+ # The custom error message for this error.
11
+ #
12
+ # @deprecated
13
+ MESSAGE = 'Value out of range for Decimal128 representation.'
14
+
15
+ # Get the custom error message for the exception.
16
+ #
17
+ # @example Get the message.
18
+ # error.message
19
+ #
20
+ # @return [ String ] The error message.
21
+ def message
22
+ MESSAGE
23
+ end
24
+ end
25
+ end
26
+ end
27
+