bson 4.15.0 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +94 -10
  3. data/Rakefile +68 -39
  4. data/ext/bson/bson-native.h +12 -4
  5. data/ext/bson/extconf.rb +8 -3
  6. data/ext/bson/init.c +11 -11
  7. data/ext/bson/read.c +39 -9
  8. data/ext/bson/util.c +171 -16
  9. data/ext/bson/write.c +34 -39
  10. data/lib/bson/active_support.rb +1 -0
  11. data/lib/bson/array.rb +58 -32
  12. data/lib/bson/big_decimal.rb +16 -6
  13. data/lib/bson/binary.rb +271 -129
  14. data/lib/bson/boolean.rb +1 -0
  15. data/lib/bson/code.rb +10 -12
  16. data/lib/bson/code_with_scope.rb +9 -11
  17. data/lib/bson/config.rb +1 -27
  18. data/lib/bson/date.rb +2 -1
  19. data/lib/bson/date_time.rb +2 -1
  20. data/lib/bson/db_pointer.rb +12 -13
  21. data/lib/bson/dbref.rb +11 -9
  22. data/lib/bson/decimal128/builder.rb +10 -9
  23. data/lib/bson/decimal128.rb +25 -111
  24. data/lib/bson/document.rb +1 -0
  25. data/lib/bson/environment.rb +1 -0
  26. data/lib/bson/error/bson_decode_error.rb +11 -0
  27. data/lib/bson/error/ext_json_parse_error.rb +11 -0
  28. data/lib/bson/error/illegal_key.rb +23 -0
  29. data/lib/bson/error/invalid_binary_type.rb +37 -0
  30. data/lib/bson/error/invalid_dbref_argument.rb +12 -0
  31. data/lib/bson/error/invalid_decimal128_argument.rb +25 -0
  32. data/lib/bson/error/invalid_decimal128_range.rb +27 -0
  33. data/lib/bson/error/invalid_decimal128_string.rb +26 -0
  34. data/lib/bson/error/invalid_key.rb +24 -0
  35. data/lib/bson/error/invalid_object_id.rb +11 -0
  36. data/lib/bson/error/invalid_regexp_pattern.rb +13 -0
  37. data/lib/bson/error/unrepresentable_precision.rb +19 -0
  38. data/lib/bson/error/unserializable_class.rb +13 -0
  39. data/lib/bson/error/unsupported_binary_subtype.rb +12 -0
  40. data/lib/bson/error/unsupported_type.rb +11 -0
  41. data/lib/bson/error.rb +16 -28
  42. data/lib/bson/ext_json.rb +2 -1
  43. data/lib/bson/false_class.rb +2 -1
  44. data/lib/bson/float.rb +3 -2
  45. data/lib/bson/hash.rb +128 -73
  46. data/lib/bson/int32.rb +17 -5
  47. data/lib/bson/int64.rb +17 -5
  48. data/lib/bson/integer.rb +4 -5
  49. data/lib/bson/json.rb +1 -0
  50. data/lib/bson/max_key.rb +8 -10
  51. data/lib/bson/min_key.rb +8 -10
  52. data/lib/bson/nil_class.rb +1 -0
  53. data/lib/bson/object.rb +7 -27
  54. data/lib/bson/object_id.rb +84 -120
  55. data/lib/bson/open_struct.rb +3 -2
  56. data/lib/bson/regexp.rb +36 -65
  57. data/lib/bson/registry.rb +2 -6
  58. data/lib/bson/specialized.rb +2 -1
  59. data/lib/bson/string.rb +4 -27
  60. data/lib/bson/symbol.rb +23 -20
  61. data/lib/bson/time.rb +3 -2
  62. data/lib/bson/time_with_zone.rb +13 -1
  63. data/lib/bson/timestamp.rb +3 -2
  64. data/lib/bson/true_class.rb +2 -1
  65. data/lib/bson/undefined.rb +15 -1
  66. data/lib/bson/version.rb +3 -1
  67. data/lib/bson.rb +3 -2
  68. data/spec/bson/array_spec.rb +19 -60
  69. data/spec/bson/big_decimal_spec.rb +16 -4
  70. data/spec/bson/binary_spec.rb +129 -81
  71. data/spec/bson/binary_uuid_spec.rb +1 -0
  72. data/spec/bson/boolean_spec.rb +1 -0
  73. data/spec/bson/byte_buffer_read_spec.rb +1 -0
  74. data/spec/bson/byte_buffer_spec.rb +1 -0
  75. data/spec/bson/byte_buffer_write_spec.rb +1 -0
  76. data/spec/bson/code_spec.rb +5 -3
  77. data/spec/bson/code_with_scope_spec.rb +5 -3
  78. data/spec/bson/config_spec.rb +1 -35
  79. data/spec/bson/date_spec.rb +1 -0
  80. data/spec/bson/date_time_spec.rb +1 -0
  81. data/spec/bson/dbref_legacy_spec.rb +20 -3
  82. data/spec/bson/dbref_spec.rb +9 -9
  83. data/spec/bson/decimal128_spec.rb +40 -20
  84. data/spec/bson/document_as_spec.rb +1 -0
  85. data/spec/bson/document_spec.rb +1 -1
  86. data/spec/bson/ext_json_parse_spec.rb +1 -0
  87. data/spec/bson/false_class_spec.rb +8 -0
  88. data/spec/bson/float_spec.rb +8 -3
  89. data/spec/bson/hash_as_spec.rb +1 -0
  90. data/spec/bson/hash_spec.rb +87 -75
  91. data/spec/bson/int32_spec.rb +21 -6
  92. data/spec/bson/int64_spec.rb +21 -6
  93. data/spec/bson/integer_spec.rb +45 -13
  94. data/spec/bson/json_spec.rb +1 -0
  95. data/spec/bson/max_key_spec.rb +5 -3
  96. data/spec/bson/min_key_spec.rb +5 -3
  97. data/spec/bson/nil_class_spec.rb +1 -0
  98. data/spec/bson/object_id_spec.rb +57 -4
  99. data/spec/bson/object_spec.rb +2 -1
  100. data/spec/bson/open_struct_spec.rb +14 -71
  101. data/spec/bson/raw_spec.rb +9 -15
  102. data/spec/bson/regexp_spec.rb +4 -3
  103. data/spec/bson/registry_spec.rb +2 -1
  104. data/spec/bson/string_spec.rb +13 -38
  105. data/spec/bson/symbol_raw_spec.rb +25 -0
  106. data/spec/bson/symbol_spec.rb +15 -18
  107. data/spec/bson/time_spec.rb +1 -0
  108. data/spec/bson/time_with_zone_spec.rb +1 -0
  109. data/spec/bson/timestamp_spec.rb +1 -0
  110. data/spec/bson/true_class_spec.rb +8 -0
  111. data/spec/bson/undefined_spec.rb +27 -0
  112. data/spec/bson_spec.rb +1 -0
  113. data/spec/runners/common_driver.rb +6 -5
  114. data/spec/runners/corpus.rb +6 -0
  115. data/spec/runners/corpus_legacy.rb +1 -0
  116. data/spec/spec_helper.rb +1 -0
  117. data/spec/spec_tests/common_driver_spec.rb +9 -4
  118. data/spec/spec_tests/corpus_legacy_spec.rb +1 -0
  119. data/spec/spec_tests/corpus_spec.rb +13 -3
  120. data/spec/spec_tests/data/corpus/binary.json +5 -0
  121. data/spec/spec_tests/data/corpus/code.json +13 -13
  122. data/spec/spec_tests/data/corpus/decimal128-4.json +48 -0
  123. data/spec/spec_tests/data/corpus/decimal128-6.json +12 -0
  124. data/spec/spec_tests/data/corpus/decimal128-7.json +4 -0
  125. data/spec/spec_tests/data/corpus/document.json +20 -0
  126. data/spec/spec_tests/data/corpus/symbol.json +7 -7
  127. data/spec/spec_tests/data/corpus/top.json +18 -3
  128. data/spec/support/shared_examples.rb +28 -5
  129. data/spec/support/spec_config.rb +1 -0
  130. data/spec/support/utils.rb +49 -1
  131. metadata +114 -164
  132. checksums.yaml.gz.sig +0 -0
  133. data/spec/shared/LICENSE +0 -20
  134. data/spec/shared/bin/get-mongodb-download-url +0 -17
  135. data/spec/shared/bin/s3-copy +0 -45
  136. data/spec/shared/bin/s3-upload +0 -69
  137. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  138. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  139. data/spec/shared/lib/mrss/constraints.rb +0 -386
  140. data/spec/shared/lib/mrss/docker_runner.rb +0 -271
  141. data/spec/shared/lib/mrss/event_subscriber.rb +0 -200
  142. data/spec/shared/lib/mrss/lite_constraints.rb +0 -191
  143. data/spec/shared/lib/mrss/server_version_registry.rb +0 -120
  144. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  145. data/spec/shared/lib/mrss/utils.rb +0 -15
  146. data/spec/shared/share/Dockerfile.erb +0 -338
  147. data/spec/shared/share/haproxy-1.conf +0 -16
  148. data/spec/shared/share/haproxy-2.conf +0 -17
  149. data/spec/shared/shlib/distro.sh +0 -74
  150. data/spec/shared/shlib/server.sh +0 -367
  151. data/spec/shared/shlib/set_env.sh +0 -131
  152. data.tar.gz.sig +0 -0
  153. metadata.gz.sig +0 -1
@@ -55,6 +55,11 @@
55
55
  "canonical_bson": "1D000000057800100000000773FFD26444B34C6990E8E7D1DFC035D400",
56
56
  "canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"07\"}}}"
57
57
  },
58
+ {
59
+ "description": "subtype 0x08",
60
+ "canonical_bson": "1D000000057800100000000873FFD26444B34C6990E8E7D1DFC035D400",
61
+ "canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"08\"}}}"
62
+ },
58
63
  {
59
64
  "description": "subtype 0x80",
60
65
  "canonical_bson": "0F0000000578000200000080FFFF00",
@@ -20,48 +20,48 @@
20
20
  },
21
21
  {
22
22
  "description": "two-byte UTF-8 (\u00e9)",
23
- "canonical_bson": "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000",
24
- "canonical_extjson": "{\"a\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}"
23
+ "canonical_bson": "190000000D61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000",
24
+ "canonical_extjson": "{\"a\" : {\"$code\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}}"
25
25
  },
26
26
  {
27
27
  "description": "three-byte UTF-8 (\u2606)",
28
- "canonical_bson": "190000000261000D000000E29886E29886E29886E298860000",
29
- "canonical_extjson": "{\"a\" : \"\\u2606\\u2606\\u2606\\u2606\"}"
28
+ "canonical_bson": "190000000D61000D000000E29886E29886E29886E298860000",
29
+ "canonical_extjson": "{\"a\" : {\"$code\" : \"\\u2606\\u2606\\u2606\\u2606\"}}"
30
30
  },
31
31
  {
32
32
  "description": "Embedded nulls",
33
- "canonical_bson": "190000000261000D0000006162006261620062616261620000",
34
- "canonical_extjson": "{\"a\" : \"ab\\u0000bab\\u0000babab\"}"
33
+ "canonical_bson": "190000000D61000D0000006162006261620062616261620000",
34
+ "canonical_extjson": "{\"a\" : {\"$code\" : \"ab\\u0000bab\\u0000babab\"}}"
35
35
  }
36
36
  ],
37
37
  "decodeErrors": [
38
38
  {
39
39
  "description": "bad code string length: 0 (but no 0x00 either)",
40
- "bson": "0C0000000261000000000000"
40
+ "bson": "0C0000000D61000000000000"
41
41
  },
42
42
  {
43
43
  "description": "bad code string length: -1",
44
- "bson": "0C000000026100FFFFFFFF00"
44
+ "bson": "0C0000000D6100FFFFFFFF00"
45
45
  },
46
46
  {
47
47
  "description": "bad code string length: eats terminator",
48
- "bson": "10000000026100050000006200620000"
48
+ "bson": "100000000D6100050000006200620000"
49
49
  },
50
50
  {
51
51
  "description": "bad code string length: longer than rest of document",
52
- "bson": "120000000200FFFFFF00666F6F6261720000"
52
+ "bson": "120000000D00FFFFFF00666F6F6261720000"
53
53
  },
54
54
  {
55
55
  "description": "code string is not null-terminated",
56
- "bson": "1000000002610004000000616263FF00"
56
+ "bson": "100000000D610004000000616263FF00"
57
57
  },
58
58
  {
59
59
  "description": "empty code string, but extra null",
60
- "bson": "0E00000002610001000000000000"
60
+ "bson": "0E0000000D610001000000000000"
61
61
  },
62
62
  {
63
63
  "description": "invalid UTF-8",
64
- "bson": "0E00000002610002000000E90000"
64
+ "bson": "0E0000000D610002000000E90000"
65
65
  }
66
66
  ]
67
67
  }
@@ -112,6 +112,54 @@
112
112
  {
113
113
  "description": "[basx563] Near-specials (Conversion_syntax)",
114
114
  "string": "NaNs"
115
+ },
116
+ {
117
+ "description": "[dqbas939] overflow results at different rounding modes (Overflow & Inexact & Rounded)",
118
+ "string": "-7e10000"
119
+ },
120
+ {
121
+ "description": "[dqbsr534] negatives (Rounded & Inexact)",
122
+ "string": "-1.11111111111111111111111111111234650"
123
+ },
124
+ {
125
+ "description": "[dqbsr535] negatives (Rounded & Inexact)",
126
+ "string": "-1.11111111111111111111111111111234551"
127
+ },
128
+ {
129
+ "description": "[dqbsr533] negatives (Rounded & Inexact)",
130
+ "string": "-1.11111111111111111111111111111234550"
131
+ },
132
+ {
133
+ "description": "[dqbsr532] negatives (Rounded & Inexact)",
134
+ "string": "-1.11111111111111111111111111111234549"
135
+ },
136
+ {
137
+ "description": "[dqbsr432] check rounding modes heeded (Rounded & Inexact)",
138
+ "string": "1.11111111111111111111111111111234549"
139
+ },
140
+ {
141
+ "description": "[dqbsr433] check rounding modes heeded (Rounded & Inexact)",
142
+ "string": "1.11111111111111111111111111111234550"
143
+ },
144
+ {
145
+ "description": "[dqbsr435] check rounding modes heeded (Rounded & Inexact)",
146
+ "string": "1.11111111111111111111111111111234551"
147
+ },
148
+ {
149
+ "description": "[dqbsr434] check rounding modes heeded (Rounded & Inexact)",
150
+ "string": "1.11111111111111111111111111111234650"
151
+ },
152
+ {
153
+ "description": "[dqbas938] overflow results at different rounding modes (Overflow & Inexact & Rounded)",
154
+ "string": "7e10000"
155
+ },
156
+ {
157
+ "description": "Inexact rounding#1",
158
+ "string": "100000000000000000000000000000000000000000000000000000000001"
159
+ },
160
+ {
161
+ "description": "Inexact rounding#2",
162
+ "string": "1E-6177"
115
163
  }
116
164
  ]
117
165
  }
@@ -75,6 +75,18 @@
75
75
  "description": "Empty string",
76
76
  "string": ""
77
77
  },
78
+ {
79
+ "description": "leading white space positive number",
80
+ "string": " 1"
81
+ },
82
+ {
83
+ "description": "leading white space negative number",
84
+ "string": " -1"
85
+ },
86
+ {
87
+ "description": "trailing white space",
88
+ "string": "1 "
89
+ },
78
90
  {
79
91
  "description": "Invalid",
80
92
  "string": "E"
@@ -267,6 +267,10 @@
267
267
  "description": "[basx570] Near-specials (Conversion_syntax)",
268
268
  "string": "9Inf"
269
269
  },
270
+ {
271
+ "description": "[basx512] The 'baddies' tests from DiagBigDecimal, plus some new ones (Conversion_syntax)",
272
+ "string": "12 "
273
+ },
270
274
  {
271
275
  "description": "[basx517] The 'baddies' tests from DiagBigDecimal, plus some new ones (Conversion_syntax)",
272
276
  "string": "12-"
@@ -17,6 +17,26 @@
17
17
  "description": "Single-character key subdoc",
18
18
  "canonical_bson": "160000000378000E0000000261000200000062000000",
19
19
  "canonical_extjson": "{\"x\" : {\"a\" : \"b\"}}"
20
+ },
21
+ {
22
+ "description": "Dollar-prefixed key in sub-document",
23
+ "canonical_bson": "170000000378000F000000022461000200000062000000",
24
+ "canonical_extjson": "{\"x\" : {\"$a\" : \"b\"}}"
25
+ },
26
+ {
27
+ "description": "Dollar as key in sub-document",
28
+ "canonical_bson": "160000000378000E0000000224000200000061000000",
29
+ "canonical_extjson": "{\"x\" : {\"$\" : \"a\"}}"
30
+ },
31
+ {
32
+ "description": "Dotted key in sub-document",
33
+ "canonical_bson": "180000000378001000000002612E62000200000063000000",
34
+ "canonical_extjson": "{\"x\" : {\"a.b\" : \"c\"}}"
35
+ },
36
+ {
37
+ "description": "Dot as key in sub-document",
38
+ "canonical_bson": "160000000378000E000000022E000200000061000000",
39
+ "canonical_extjson": "{\"x\" : {\".\" : \"a\"}}"
20
40
  }
21
41
  ],
22
42
  "decodeErrors": [
@@ -50,31 +50,31 @@
50
50
  "decodeErrors": [
51
51
  {
52
52
  "description": "bad symbol length: 0 (but no 0x00 either)",
53
- "bson": "0C0000000261000000000000"
53
+ "bson": "0C0000000E61000000000000"
54
54
  },
55
55
  {
56
56
  "description": "bad symbol length: -1",
57
- "bson": "0C000000026100FFFFFFFF00"
57
+ "bson": "0C0000000E6100FFFFFFFF00"
58
58
  },
59
59
  {
60
60
  "description": "bad symbol length: eats terminator",
61
- "bson": "10000000026100050000006200620000"
61
+ "bson": "100000000E6100050000006200620000"
62
62
  },
63
63
  {
64
64
  "description": "bad symbol length: longer than rest of document",
65
- "bson": "120000000200FFFFFF00666F6F6261720000"
65
+ "bson": "120000000E00FFFFFF00666F6F6261720000"
66
66
  },
67
67
  {
68
68
  "description": "symbol is not null-terminated",
69
- "bson": "1000000002610004000000616263FF00"
69
+ "bson": "100000000E610004000000616263FF00"
70
70
  },
71
71
  {
72
72
  "description": "empty symbol, but extra null",
73
- "bson": "0E00000002610001000000000000"
73
+ "bson": "0E0000000E610001000000000000"
74
74
  },
75
75
  {
76
76
  "description": "invalid UTF-8",
77
- "bson": "0E00000002610002000000E90000"
77
+ "bson": "0E0000000E610002000000E90000"
78
78
  }
79
79
  ]
80
80
  }
@@ -3,9 +3,24 @@
3
3
  "bson_type": "0x00",
4
4
  "valid": [
5
5
  {
6
- "description": "Document with keys that start with $",
6
+ "description": "Dollar-prefixed key in top-level document",
7
7
  "canonical_bson": "0F00000010246B6579002A00000000",
8
8
  "canonical_extjson": "{\"$key\": {\"$numberInt\": \"42\"}}"
9
+ },
10
+ {
11
+ "description": "Dollar as key in top-level document",
12
+ "canonical_bson": "0E00000002240002000000610000",
13
+ "canonical_extjson": "{\"$\": \"a\"}"
14
+ },
15
+ {
16
+ "description": "Dotted key in top-level document",
17
+ "canonical_bson": "1000000002612E620002000000630000",
18
+ "canonical_extjson": "{\"a.b\": \"c\"}"
19
+ },
20
+ {
21
+ "description": "Dot as key in top-level document",
22
+ "canonical_bson": "0E000000022E0002000000610000",
23
+ "canonical_extjson": "{\".\": \"a\"}"
9
24
  }
10
25
  ],
11
26
  "decodeErrors": [
@@ -77,11 +92,11 @@
77
92
  },
78
93
  {
79
94
  "description": "Bad $regularExpression (pattern is number, not string)",
80
- "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": 42, \"$options\" : \"\"}}}"
95
+ "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": 42, \"options\" : \"\"}}}"
81
96
  },
82
97
  {
83
98
  "description": "Bad $regularExpression (options are number, not string)",
84
- "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": \"a\", \"$options\" : 0}}}"
99
+ "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": \"a\", \"options\" : 0}}}"
85
100
  },
86
101
  {
87
102
  "description" : "Bad $regularExpression (missing pattern field)",
@@ -1,3 +1,4 @@
1
+ # rubocop:todo all
1
2
  # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -63,6 +64,28 @@ shared_examples_for "a JSON serializable object" do
63
64
  end
64
65
  end
65
66
 
67
+ shared_examples_for "an Extended JSON serializable object" do
68
+
69
+ it "serializes the Extended JSON from #as_extended_json" do
70
+ expect(object.to_extended_json).to eq(object.as_extended_json.to_json)
71
+ end
72
+ end
73
+
74
+ shared_examples_for '#as_extended_json returns self' do
75
+
76
+ it 'returns self' do
77
+ expect(object.as_extended_json).to eq(object)
78
+ end
79
+ end
80
+
81
+ shared_examples_for '#as_json calls #as_extended_json' do
82
+
83
+ it 'calls #as_extended_json' do
84
+ expect(object).to receive(:as_extended_json).with(no_args)
85
+ object.as_json
86
+ end
87
+ end
88
+
66
89
  shared_examples_for "immutable when frozen" do |block|
67
90
 
68
91
  context "when the document is frozen" do
@@ -122,7 +145,7 @@ shared_examples_for "a validated BSON key" do
122
145
  end
123
146
  end
124
147
 
125
- context "when the string is invalid" do
148
+ context "when the string has dots/dollars" do
126
149
 
127
150
  context "when the string starts with $" do
128
151
 
@@ -130,10 +153,10 @@ shared_examples_for "a validated BSON key" do
130
153
  "$testing"
131
154
  end
132
155
 
133
- it "raises an exception" do
156
+ it "does not raise an exception" do
134
157
  expect {
135
158
  validated
136
- }.to raise_error(BSON::String::IllegalKey)
159
+ }.to_not raise_error
137
160
  end
138
161
  end
139
162
 
@@ -143,10 +166,10 @@ shared_examples_for "a validated BSON key" do
143
166
  "testing.testing"
144
167
  end
145
168
 
146
- it "raises an exception" do
169
+ it "does not raise an exception" do
147
170
  expect {
148
171
  validated
149
- }.to raise_error(BSON::String::IllegalKey)
172
+ }.to_not raise_error
150
173
  end
151
174
  end
152
175
  end
@@ -1,3 +1,4 @@
1
+ # rubocop:todo all
1
2
  require 'singleton'
2
3
 
3
4
  class SpecConfig
@@ -1,10 +1,58 @@
1
+ # rubocop:todo all
1
2
  module Utils
3
+ extend self
4
+
2
5
  # JRuby chokes when strings like "\xfe\x00\xff", which are not valid UTF-8,
3
6
  # appear in the source. Use this method to build such strings.
4
7
  # char_array is an array of byte values to use for the string.
5
- module_function def make_byte_string(char_array, encoding = 'BINARY')
8
+ def make_byte_string(char_array, encoding = 'BINARY')
6
9
  char_array.map do |char|
7
10
  char.chr.force_encoding('BINARY')
8
11
  end.join.force_encoding(encoding)
9
12
  end
13
+
14
+ # Forks the current process and executes the given block in the child.
15
+ # The value returned by the block is then returned in the parent process
16
+ # by this method.
17
+ #
18
+ # @return [ Object ] the value returned by the block
19
+ def perform_in_child(&block)
20
+ reader, writer = IO.pipe
21
+
22
+ if fork
23
+ parent_worker(reader, writer)
24
+ else
25
+ child_worker(reader, writer, &block)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # A utility method for #perform_in_child, to handle tasks for the parent
32
+ # side of the fork.
33
+ #
34
+ # @param [ IO ] reader The reader IO for the pipe
35
+ # @param [ IO ] writer The writer IO for the pipe
36
+ #
37
+ # @return [ Object ] the value returned by the child process
38
+ def parent_worker(reader, writer)
39
+ writer.close
40
+ blob = reader.read
41
+ reader.close
42
+ Process.wait
43
+ Marshal.load(blob)
44
+ end
45
+
46
+ # A utility method for #perform_in_child, to handle tasks for the child
47
+ # side of the fork.
48
+ #
49
+ # @param [ IO ] reader The reader IO for the pipe
50
+ # @param [ IO ] writer The writer IO for the pipe
51
+ def child_worker(reader, writer, &block)
52
+ reader.close
53
+ result = block.call
54
+ writer.write Marshal.dump(result)
55
+ writer.close
56
+ exit! 0
57
+ end
10
58
  end