bson 4.13.0 → 4.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6a42388fd836eb9cd8caee93899ca84eea84d14a0eb79346d7c96deba08b0d1
4
- data.tar.gz: d0d20e09019369dea160278abee5929ea89f91efcdbd0523f68dd83fa0d2b30f
3
+ metadata.gz: 542c675ad88650d665d5e077de9a3c7bcbd566647fc1f08f1d3441948d646c1c
4
+ data.tar.gz: b9f41c2f216e982dcdcd2c83c17a4dd59f8e062406023eb6bd1932afa8338698
5
5
  SHA512:
6
- metadata.gz: 3210c3ef66059b6b44e4286755df4ed8508e4c367c20b6ff89698d21dd89151c4aee54e4a31cc92c5b09db3b7d33ec8ece2dc17372920885d9808aa13d5eed09
7
- data.tar.gz: '0086253724f6b26a1e1a40ee89d9c434c5005bbfbdcf28b11aa40b1f5742eb4a030d4ca26c331b71cfabfb911e9a8200719c72e4327cad3e6d825a8e3de5d346'
6
+ metadata.gz: 3a084a08fb6cd5601e6b0095b7086348e602aa42e5b52069531f107879d4a67a7a982ce1938d4ef4bedb4b559648a0139b3c85ab011b5f65fa415300dadef079
7
+ data.tar.gz: 3dde4070b315b1a3da605c11fa6571a9fb641dcdc77ed2edb7aec0e3799a2765af68342ac9a4fb839e0c0a84ad4a10f1641948a76857f649c9680a521aec90bd
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2009-2021 MongoDB Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module BSON
17
+
18
+ # Injects behaviour for encoding and decoding BigDecimals
19
+ # to and from raw bytes as specified by the BSON spec.
20
+ #
21
+ # @see http://bsonspec.org/#/specification
22
+ module BigDecimal
23
+
24
+ # BigDecimals are serialized as Decimal128s under the hood. A Decimal128
25
+ # is type 0x13 in the BSON spec.
26
+ BSON_TYPE = ::String.new(19.chr, encoding: BINARY).freeze
27
+
28
+ # Get the BigDecimal as encoded BSON.
29
+ #
30
+ # @example Get the BigDecimal as encoded BSON.
31
+ # BigDecimal("1").to_bson
32
+ #
33
+ # @return [ BSON::ByteBuffer ] The buffer with the encoded object.
34
+ #
35
+ # @see http://bsonspec.org/#/specification
36
+ def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
37
+ BSON::Decimal128.new(to_s).to_bson(buffer, validating_keys)
38
+ end
39
+
40
+ # Get the BSON type for BigDecimal. This is the same BSON type as
41
+ # BSON::Decimal128.
42
+ def bson_type
43
+ BSON_TYPE
44
+ end
45
+
46
+ module ClassMethods
47
+
48
+ # Deserialize the BigDecimal from raw BSON bytes.
49
+ #
50
+ # @example Get the BigDecimal from BSON.
51
+ # BigDecimal.from_bson(bson)
52
+ #
53
+ # @param [ ByteBuffer ] buffer The byte buffer.
54
+ #
55
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
56
+ #
57
+ # @return [ BigDecimal ] The decimal object.
58
+ def from_bson(buffer, **options)
59
+ Decimal128.from_bson(buffer, **options).to_big_decimal
60
+ end
61
+ end
62
+ end
63
+
64
+ # Enrich the core BigDecimal class with this module.
65
+ ::BigDecimal.send(:include, BigDecimal)
66
+ ::BigDecimal.send(:extend, BigDecimal::ClassMethods)
67
+ end
@@ -85,9 +85,13 @@ module BSON
85
85
  private
86
86
 
87
87
  def validate_range!(exponent, significand)
88
- unless valid_significand?(significand) && valid_exponent?(exponent)
88
+ unless valid_exponent?(exponent)
89
89
  raise Decimal128::InvalidRange.new
90
90
  end
91
+
92
+ unless valid_significand?(significand)
93
+ raise Decimal128::UnrepresentablePrecision.new
94
+ end
91
95
  end
92
96
 
93
97
  def valid_significand?(significand)
@@ -302,11 +306,11 @@ module BSON
302
306
 
303
307
  def to_special_bits
304
308
  case @big_decimal.sign
305
- when BigDecimal::SIGN_POSITIVE_INFINITE
309
+ when ::BigDecimal::SIGN_POSITIVE_INFINITE
306
310
  high = INFINITY_MASK
307
- when BigDecimal::SIGN_NEGATIVE_INFINITE
311
+ when ::BigDecimal::SIGN_NEGATIVE_INFINITE
308
312
  high = INFINITY_MASK | SIGN_BIT_MASK
309
- when BigDecimal::SIGN_NaN
313
+ when ::BigDecimal::SIGN_NaN
310
314
  high = NAN_MASK
311
315
  end
312
316
  [ 0, high ]
@@ -315,7 +319,7 @@ module BSON
315
319
  def to_bits
316
320
  sign, significand_str, base, exp = @big_decimal.split
317
321
  exponent = @big_decimal.zero? ? 0 : exp - significand_str.length
318
- is_negative = (sign == BigDecimal::SIGN_NEGATIVE_FINITE || sign == BigDecimal::SIGN_NEGATIVE_ZERO)
322
+ is_negative = (sign == ::BigDecimal::SIGN_NEGATIVE_FINITE || sign == ::BigDecimal::SIGN_NEGATIVE_ZERO)
319
323
  Builder.parts_to_bits(significand_str.to_i,
320
324
  exponent,
321
325
  is_negative)
@@ -304,9 +304,7 @@ module BSON
304
304
  end
305
305
  end
306
306
 
307
- # Raised when the exponent or significand provided is outside the valid range.
308
- #
309
- # @api private
307
+ # Raised when the exponent is outside the valid range.
310
308
  #
311
309
  # @since 4.2.0
312
310
  class InvalidRange < RuntimeError
@@ -314,6 +312,7 @@ module BSON
314
312
  # The custom error message for this error.
315
313
  #
316
314
  # @since 4.2.0
315
+ # @deprecated
317
316
  MESSAGE = 'Value out of range for Decimal128 representation.'
318
317
 
319
318
  # Get the custom error message for the exception.
@@ -329,6 +328,21 @@ module BSON
329
328
  end
330
329
  end
331
330
 
331
+ # Raised when the significand provided is outside the valid range.
332
+ #
333
+ # @note This class derives from InvalidRange for backwards compatibility,
334
+ # however when RUBY-1806 is implemented it should be changed to derive
335
+ # from the base BSON exception class.
336
+ class UnrepresentablePrecision < InvalidRange
337
+
338
+ # Get the custom error message for the exception.
339
+ #
340
+ # @return [ String ] The error message.
341
+ def message
342
+ 'The value contains too much precision for Decimal128 representation'
343
+ end
344
+ end
345
+
332
346
  Registry.register(BSON_TYPE, self)
333
347
  end
334
348
  end
data/lib/bson/float.rb CHANGED
@@ -16,7 +16,7 @@
16
16
  module BSON
17
17
 
18
18
  # Injects behaviour for encoding and decoding floating point values
19
- # to and from # raw bytes as specified by the BSON spec.
19
+ # to and from raw bytes as specified by the BSON spec.
20
20
  #
21
21
  # @see http://bsonspec.org/#/specification
22
22
  #
data/lib/bson/version.rb CHANGED
@@ -14,5 +14,5 @@
14
14
  # limitations under the License.
15
15
 
16
16
  module BSON
17
- VERSION = "4.13.0"
17
+ VERSION = "4.14.0"
18
18
  end
data/lib/bson.rb CHANGED
@@ -74,6 +74,7 @@ require "bson/date"
74
74
  require "bson/date_time"
75
75
  require "bson/db_pointer"
76
76
  require "bson/decimal128"
77
+ require "bson/big_decimal"
77
78
  require "bson/document"
78
79
  require "bson/ext_json"
79
80
  require "bson/false_class"
@@ -102,7 +103,7 @@ require "bson/version"
102
103
  begin
103
104
  if BSON::Environment.jruby?
104
105
  require "bson-ruby.jar"
105
- org.bson.NativeService.new.basicLoad(JRuby.runtime)
106
+ JRuby::Util.load_ext("org.bson.NativeService")
106
107
  else
107
108
  require "bson_native"
108
109
  end
Binary file
@@ -0,0 +1,316 @@
1
+ # Copyright (C) 2016-2021 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "spec_helper"
16
+
17
+ describe BSON::BigDecimal do
18
+
19
+ describe '#from_bson' do
20
+ shared_examples_for 'a BSON::BigDecimal deserializer' do
21
+
22
+ let(:decimal128) do
23
+ BSON::Decimal128.new(argument)
24
+ end
25
+
26
+ let(:deserialized_big_decimal) do
27
+ BigDecimal.from_bson(decimal128.to_bson)
28
+ end
29
+
30
+ let(:deserialized_decimal128) do
31
+ BSON::Decimal128.from_bson(decimal128.to_bson)
32
+ end
33
+
34
+ it 'deserializes Decimal128 encoded bson correctly' do
35
+ if deserialized_decimal128.to_s == "NaN"
36
+ expect(deserialized_big_decimal.nan?).to be true
37
+ else
38
+ expect(deserialized_big_decimal).to eq(deserialized_decimal128.to_big_decimal)
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'when Infinity is passed' do
44
+
45
+ let(:argument) { "Infinity" }
46
+
47
+ it_behaves_like 'a BSON::BigDecimal deserializer'
48
+ end
49
+
50
+ context 'when -Infinity is passed' do
51
+
52
+ let(:argument) { "-Infinity" }
53
+
54
+ it_behaves_like 'a BSON::BigDecimal deserializer'
55
+ end
56
+
57
+ context 'when NaN is passed' do
58
+
59
+ let(:argument) { "NaN" }
60
+
61
+ it_behaves_like 'a BSON::BigDecimal deserializer'
62
+ end
63
+
64
+ context 'when -NaN is passed' do
65
+ let(:argument) { "-NaN" }
66
+
67
+ it_behaves_like 'a BSON::BigDecimal deserializer'
68
+ end
69
+
70
+ context 'when SNaN is passed' do
71
+ let(:argument) { "SNaN" }
72
+
73
+ it_behaves_like 'a BSON::BigDecimal deserializer'
74
+ end
75
+
76
+ context 'when -SNaN is passed' do
77
+ let(:argument) { "SNaN" }
78
+
79
+ it_behaves_like 'a BSON::BigDecimal deserializer'
80
+ end
81
+
82
+ context 'when -0 is passed' do
83
+ let(:argument) { "-0" }
84
+
85
+ it_behaves_like 'a BSON::BigDecimal deserializer'
86
+ end
87
+
88
+ context 'when a positive integer is passed' do
89
+ let(:argument) { "12" }
90
+
91
+ it_behaves_like 'a BSON::BigDecimal deserializer'
92
+ end
93
+
94
+ context 'when a negative integer is passed' do
95
+ let(:argument) { "-12" }
96
+
97
+ it_behaves_like 'a BSON::BigDecimal deserializer'
98
+ end
99
+
100
+ context 'when a positive float is passed' do
101
+ let(:argument) { "0.12345" }
102
+
103
+ it_behaves_like 'a BSON::BigDecimal deserializer'
104
+ end
105
+
106
+ context 'when a negative float is passed' do
107
+ let(:argument) { "-0.12345" }
108
+
109
+ it_behaves_like 'a BSON::BigDecimal deserializer'
110
+ end
111
+
112
+ context 'when a large positive integer is passed' do
113
+ let(:argument) { "1234567890123456789012345678901234" }
114
+
115
+ it_behaves_like 'a BSON::BigDecimal deserializer'
116
+ end
117
+
118
+ context 'when a large negative integer is passed' do
119
+ let(:argument) { "-1234567890123456789012345678901234" }
120
+
121
+ it_behaves_like 'a BSON::BigDecimal deserializer'
122
+ end
123
+ end
124
+
125
+ describe "#to_bson" do
126
+ shared_examples_for 'a BSON::BigDecimal serializer' do
127
+
128
+ let(:decimal128) do
129
+ BSON::Decimal128.new(BigDecimal(argument).to_s)
130
+ end
131
+
132
+ let(:decimal_128_bson) do
133
+ decimal128.to_bson
134
+ end
135
+
136
+ let(:big_decimal_bson) do
137
+ BigDecimal(argument).to_bson
138
+ end
139
+
140
+ it 'serializes BigDecimals correctly' do
141
+ expect(decimal_128_bson.to_s).to eq(big_decimal_bson.to_s)
142
+ end
143
+ end
144
+
145
+ context 'when Infinity is passed' do
146
+
147
+ let(:argument) { "Infinity" }
148
+
149
+ it_behaves_like 'a BSON::BigDecimal serializer'
150
+ end
151
+
152
+ context 'when -Infinity is passed' do
153
+
154
+ let(:argument) { "-Infinity" }
155
+
156
+ it_behaves_like 'a BSON::BigDecimal serializer'
157
+ end
158
+
159
+ context 'when NaN is passed' do
160
+
161
+ let(:argument) { "NaN" }
162
+
163
+ it_behaves_like 'a BSON::BigDecimal serializer'
164
+ end
165
+
166
+ context 'when -0 is passed' do
167
+ let(:argument) { "-0" }
168
+
169
+ it_behaves_like 'a BSON::BigDecimal serializer'
170
+ end
171
+
172
+ context 'when a positive integer is passed' do
173
+ let(:argument) { "12" }
174
+
175
+ it_behaves_like 'a BSON::BigDecimal serializer'
176
+ end
177
+
178
+ context 'when a negative integer is passed' do
179
+ let(:argument) { "-12" }
180
+
181
+ it_behaves_like 'a BSON::BigDecimal serializer'
182
+ end
183
+
184
+ context 'when a positive float is passed' do
185
+ let(:argument) { "0.12345" }
186
+
187
+ it_behaves_like 'a BSON::BigDecimal serializer'
188
+ end
189
+
190
+ context 'when a negative float is passed' do
191
+ let(:argument) { "-0.12345" }
192
+
193
+ it_behaves_like 'a BSON::BigDecimal serializer'
194
+ end
195
+
196
+ context 'when a large positive integer is passed' do
197
+ let(:argument) { "1234567890123456789012345678901234" }
198
+
199
+ it_behaves_like 'a BSON::BigDecimal serializer'
200
+ end
201
+
202
+ context 'when a large negative integer is passed' do
203
+ let(:argument) { "-1234567890123456789012345678901234" }
204
+
205
+ it_behaves_like 'a BSON::BigDecimal serializer'
206
+ end
207
+
208
+ context "when passing an out of range Decimal128" do
209
+ let(:argument) { "1E1000000" }
210
+
211
+ it "raises an error" do
212
+ expect do
213
+ BigDecimal(argument).to_bson
214
+ end.to raise_error(BSON::Decimal128::InvalidRange)
215
+ end
216
+ end
217
+
218
+ context "when passing a number with too much precision for Decimal128" do
219
+ let(:argument) { "1.000000000000000000000000000000000000000000000000001" }
220
+
221
+ it "raises an error" do
222
+ expect do
223
+ BigDecimal(argument).to_bson
224
+ end.to raise_error(BSON::Decimal128::UnrepresentablePrecision)
225
+ end
226
+ end
227
+ end
228
+
229
+ describe "#from_bson/#to_bson" do
230
+ shared_examples_for 'a BSON::BigDecimal round trip' do
231
+
232
+ let(:big_decimal) do
233
+ BigDecimal(argument)
234
+ end
235
+
236
+ let(:big_decimal_bson) do
237
+ big_decimal.to_bson
238
+ end
239
+
240
+ let(:deserialized_big_decimal) do
241
+ BigDecimal.from_bson(big_decimal_bson)
242
+ end
243
+
244
+ it 'serializes BigDecimals correctly' do
245
+ if big_decimal.nan?
246
+ expect(deserialized_big_decimal.nan?).to be true
247
+ else
248
+ expect(deserialized_big_decimal).to eq(big_decimal)
249
+ end
250
+ end
251
+ end
252
+
253
+ context 'when Infinity is passed' do
254
+
255
+ let(:argument) { "Infinity" }
256
+
257
+ it_behaves_like 'a BSON::BigDecimal round trip'
258
+ end
259
+
260
+ context 'when -Infinity is passed' do
261
+
262
+ let(:argument) { "-Infinity" }
263
+
264
+ it_behaves_like 'a BSON::BigDecimal round trip'
265
+ end
266
+
267
+ context 'when NaN is passed' do
268
+
269
+ let(:argument) { "NaN" }
270
+
271
+ it_behaves_like 'a BSON::BigDecimal round trip'
272
+ end
273
+
274
+ context 'when -0 is passed' do
275
+ let(:argument) { "-0" }
276
+
277
+ it_behaves_like 'a BSON::BigDecimal round trip'
278
+ end
279
+
280
+ context 'when a positive integer is passed' do
281
+ let(:argument) { "12" }
282
+
283
+ it_behaves_like 'a BSON::BigDecimal round trip'
284
+ end
285
+
286
+ context 'when a negative integer is passed' do
287
+ let(:argument) { "-12" }
288
+
289
+ it_behaves_like 'a BSON::BigDecimal round trip'
290
+ end
291
+
292
+ context 'when a positive float is passed' do
293
+ let(:argument) { "0.12345" }
294
+
295
+ it_behaves_like 'a BSON::BigDecimal round trip'
296
+ end
297
+
298
+ context 'when a negative float is passed' do
299
+ let(:argument) { "-0.12345" }
300
+
301
+ it_behaves_like 'a BSON::BigDecimal round trip'
302
+ end
303
+
304
+ context 'when a large positive integer is passed' do
305
+ let(:argument) { "1234567890123456789012345678901234" }
306
+
307
+ it_behaves_like 'a BSON::BigDecimal round trip'
308
+ end
309
+
310
+ context 'when a large negative integer is passed' do
311
+ let(:argument) { "-1234567890123456789012345678901234" }
312
+
313
+ it_behaves_like 'a BSON::BigDecimal round trip'
314
+ end
315
+ end
316
+ end
@@ -309,6 +309,22 @@ describe BSON::Decimal128 do
309
309
  it_behaves_like 'an initialized BSON::Decimal128'
310
310
  end
311
311
  end
312
+
313
+ context 'when range is exceeded' do
314
+ it 'raises InvalidRange' do
315
+ lambda do
316
+ described_class.new('1e10000')
317
+ end.should raise_error(BSON::Decimal128::InvalidRange, /Value out of range/)
318
+ end
319
+ end
320
+
321
+ context 'when precision is exceeded' do
322
+ it 'raises UnrepresentablePrecision' do
323
+ lambda do
324
+ described_class.new('1.000000000000000000000000000000000000000000000000001')
325
+ end.should raise_error(BSON::Decimal128::UnrepresentablePrecision, /The value contains too much precision/)
326
+ end
327
+ end
312
328
  end
313
329
 
314
330
  context 'when deserializing' do
@@ -254,6 +254,26 @@ describe Hash do
254
254
  expect(Hash.from_bson(buffer)).to eq('foo' => 42)
255
255
  end
256
256
  end
257
+
258
+ context 'when round-tripping a BigDecimal' do
259
+ let(:to_bson) do
260
+ {"x" => BigDecimal('1')}.to_bson
261
+ end
262
+
263
+ let(:from_bson) do
264
+ Hash.from_bson(to_bson)
265
+ end
266
+
267
+ it 'doesn\'t raise on serialization' do
268
+ expect do
269
+ to_bson
270
+ end.to_not raise_error
271
+ end
272
+
273
+ it 'deserializes as a BSON::Decimal128' do
274
+ expect(from_bson).to eq({"x" => BSON::Decimal128.new('1')})
275
+ end
276
+ end
257
277
  end
258
278
 
259
279
  describe '#to_bson' do
@@ -319,6 +339,18 @@ describe Hash do
319
339
  end.not_to raise_error
320
340
  end
321
341
  end
342
+
343
+ context 'when serializing a hash with a BigDecimal' do
344
+ let(:hash) do
345
+ {'foo' => BigDecimal('1')}
346
+ end
347
+
348
+ it 'works' do
349
+ expect do
350
+ hash.to_bson
351
+ end.not_to raise_error
352
+ end
353
+ end
322
354
  end
323
355
 
324
356
  describe '#from_bson' do
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-copy options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for new files") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--from=BUCKET:PATH", "Bucket name and key (or path) to copy from") do |v|
21
+ options[:from] = v
22
+ end
23
+
24
+ opts.on("-t", "--to=BUCKET:PATH", "Bucket name and key (or path) to write to (may be specified more than once)") do |v|
25
+ options[:to] ||= []
26
+ options[:to] << v
27
+ end
28
+ end.parse!
29
+
30
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
31
+
32
+ bucket, key = options.fetch(:from).split(':', 2)
33
+
34
+ s3 = Aws::S3::Client.new
35
+
36
+ options.fetch(:to).each do |dest|
37
+ STDERR.puts "Copying to #{dest}"
38
+ dbucket, dkey = dest.split(':', 2)
39
+ s3.copy_object(
40
+ bucket: dbucket,
41
+ key: dkey,
42
+ copy_source: "/#{bucket}/#{key}",
43
+ **options[:params] || {},
44
+ )
45
+ end
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-upload options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for S3 upload") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--file=PATH", "Path to the file to upload, - to upload standard input") do |v|
21
+ options[:file] = v
22
+ end
23
+
24
+ opts.on("-w", "--write=BUCKET:PATH", "Bucket name and key (or path) to upload to") do |v|
25
+ options[:write] = v
26
+ end
27
+
28
+ opts.on("-c", "--copy=BUCKET:PATH", "Bucket name and key (or path) to copy to (may be specified more than once)") do |v|
29
+ options[:copy] ||= []
30
+ options[:copy] << v
31
+ end
32
+ end.parse!
33
+
34
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
35
+
36
+ def upload(f, options)
37
+ s3 = Aws::S3::Client.new
38
+ write = options.fetch(:write)
39
+ STDERR.puts "Writing #{write}"
40
+ bucket, key = write.split(':', 2)
41
+ s3.put_object(
42
+ body: f.read,
43
+ bucket: bucket,
44
+ key: key,
45
+ **options[:params] || {},
46
+ )
47
+ if copy = options[:copy]
48
+ copy.each do |dest|
49
+ STDERR.puts "Copying to #{dest}"
50
+ dbucket, dkey = dest.split(':', 2)
51
+ s3.copy_object(
52
+ bucket: dbucket,
53
+ key: dkey,
54
+ copy_source: "/#{bucket}/#{key}",
55
+ **options[:params] || {},
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ if options[:file] == '-'
62
+ upload(STDIN, options)
63
+ elsif options[:file]
64
+ File.open(options[:file]) do |f|
65
+ upload(f, options)
66
+ end
67
+ else
68
+ upload(STDIN, options)
69
+ end