mongoid 8.0.9 → 8.0.11
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.
- checksums.yaml +4 -4
- data/Rakefile +9 -9
- data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +21 -4
- data/lib/mongoid/validatable/associated.rb +1 -1
- data/lib/mongoid/validatable/macros.rb +15 -0
- data/lib/mongoid/validatable/numericality.rb +19 -0
- data/lib/mongoid/validatable.rb +1 -0
- data/lib/mongoid/version.rb +5 -1
- data/spec/integration/app_spec.rb +10 -0
- data/spec/integration/associations/embeds_one_spec.rb +25 -5
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +28 -37
- data/spec/mongoid/association_spec.rb +0 -60
- data/spec/mongoid/validatable/numericality_spec.rb +16 -0
- data/spec/shared/LICENSE +20 -0
- data/spec/shared/bin/get-mongodb-download-url +17 -0
- data/spec/shared/bin/s3-copy +45 -0
- data/spec/shared/bin/s3-upload +69 -0
- data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
- data/spec/shared/lib/mrss/cluster_config.rb +231 -0
- data/spec/shared/lib/mrss/constraints.rb +378 -0
- data/spec/shared/lib/mrss/docker_runner.rb +298 -0
- data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
- data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
- data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
- data/spec/shared/lib/mrss/release/candidate.rb +281 -0
- data/spec/shared/lib/mrss/release/product_data.rb +144 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
- data/spec/shared/lib/mrss/session_registry.rb +69 -0
- data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
- data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
- data/spec/shared/lib/mrss/utils.rb +37 -0
- data/spec/shared/lib/tasks/candidate.rake +64 -0
- data/spec/shared/share/Dockerfile.erb +251 -0
- data/spec/shared/share/haproxy-1.conf +16 -0
- data/spec/shared/share/haproxy-2.conf +17 -0
- data/spec/shared/shlib/config.sh +27 -0
- data/spec/shared/shlib/distro.sh +84 -0
- data/spec/shared/shlib/server.sh +423 -0
- data/spec/shared/shlib/set_env.sh +110 -0
- data/spec/support/expectations.rb +20 -17
- data/spec/support/models/book.rb +1 -0
- data/spec/support/models/cover.rb +10 -0
- metadata +60 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78040726b40c1709f9218bc28850b802cf5aedfbdd178c2ed90322ffb16ba0ee
|
4
|
+
data.tar.gz: 438d1fee26567d71150518c5df2f764d6671c49ea6cc02c0582a7cd15e80fd7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8420a185b4baf6c1183ff188bc16f5d79fc75f44f1784c37df163836b70939250efbefceaeb53269a9d97f0b7bc9ae997a6586306223bea424db8738a3f2513e
|
7
|
+
data.tar.gz: 1f8dc137d3de2c5bc6ff676f8330471e4a792fe259839e6edec6107e145fd3b81bd4013df64e4b2410a62793ce2d35855069672a4a8784cf2de076f3ac4fda61
|
data/Rakefile
CHANGED
@@ -10,16 +10,16 @@ $: << File.join(ROOT, 'spec/shared/lib')
|
|
10
10
|
require "rake"
|
11
11
|
require "rspec/core/rake_task"
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
if File.exist?('./spec/shared/lib/tasks/candidate.rake')
|
14
|
+
load 'spec/shared/lib/tasks/candidate.rake'
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Build the gem'
|
17
18
|
task :build do
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
WARNING
|
19
|
+
command = %w[ gem build ]
|
20
|
+
command << "--output=#{ENV['GEM_FILE_NAME']}" if ENV['GEM_FILE_NAME']
|
21
|
+
command << (ENV['GEMSPEC'] || 'mongoid.gemspec')
|
22
|
+
system(*command)
|
23
23
|
end
|
24
24
|
|
25
25
|
# `rake version` is used by the deployment system so get the release version
|
@@ -422,11 +422,28 @@ module Mongoid
|
|
422
422
|
#
|
423
423
|
# @return [ Integer ] The size of the enumerable.
|
424
424
|
def size
|
425
|
-
|
426
|
-
|
427
|
-
|
425
|
+
# If _unloaded is present, then it will match the set of documents
|
426
|
+
# that belong to this association, which have already been persisted
|
427
|
+
# to the database. This set of documents must be considered when
|
428
|
+
# computing the size of the association, along with anything that has
|
429
|
+
# since been added.
|
430
|
+
if _unloaded
|
431
|
+
if _added.any?
|
432
|
+
# Note that _added may include records that _unloaded already
|
433
|
+
# matches. This is the case if the association is assigned an array
|
434
|
+
# of items and some of them were already elements of the association.
|
435
|
+
#
|
436
|
+
# we need to thus make sure _unloaded.count excludes any elements
|
437
|
+
# that already exist in _added.
|
438
|
+
|
439
|
+
count = _unloaded.not(:_id.in => _added.values.map(&:id)).count
|
440
|
+
count + _added.values.count
|
441
|
+
else
|
442
|
+
_unloaded.count
|
443
|
+
end
|
444
|
+
|
428
445
|
else
|
429
|
-
count + _added.
|
446
|
+
_loaded.count + _added.count
|
430
447
|
end
|
431
448
|
end
|
432
449
|
|
@@ -73,7 +73,7 @@ module Mongoid
|
|
73
73
|
# use map.all? instead of just all?, because all? will do short-circuit
|
74
74
|
# evaluation and terminate on the first failed validation.
|
75
75
|
list.map do |value|
|
76
|
-
if value && !value.flagged_for_destroy?
|
76
|
+
if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
|
77
77
|
value.validated? ? true : value.valid?
|
78
78
|
else
|
79
79
|
true
|
@@ -84,6 +84,21 @@ module Mongoid
|
|
84
84
|
def validates_presence_of(*args)
|
85
85
|
validates_with(PresenceValidator, _merge_attributes(args))
|
86
86
|
end
|
87
|
+
|
88
|
+
# Validates whether or not a field contains a numeric value.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# class Person
|
92
|
+
# include Mongoid::Document
|
93
|
+
# field :cost
|
94
|
+
#
|
95
|
+
# validates_numericality_of :cost
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# @param [ Object... ] *args The names of the field(s) to validate.
|
99
|
+
def validates_numericality_of(*args)
|
100
|
+
validates_with(NumericalityValidator, _merge_attributes(args))
|
101
|
+
end
|
87
102
|
end
|
88
103
|
end
|
89
104
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Validatable
|
5
|
+
# A specialization of the ActiveModel numericality validator, which adds
|
6
|
+
# logic to recognize and accept BSON::Decimal128 as a number.
|
7
|
+
class NumericalityValidator < ActiveModel::Validations::NumericalityValidator
|
8
|
+
private
|
9
|
+
|
10
|
+
# Ensure that BSON::Decimal128 is treated as a BigDecimal during the
|
11
|
+
# validation step.
|
12
|
+
def prepare_value_for_validation(value, record, attr_name)
|
13
|
+
result = super
|
14
|
+
|
15
|
+
result.is_a?(BSON::Decimal128) ? result.to_big_decimal : result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/mongoid/validatable.rb
CHANGED
@@ -5,6 +5,7 @@ require "mongoid/validatable/localizable"
|
|
5
5
|
require "mongoid/validatable/associated"
|
6
6
|
require "mongoid/validatable/format"
|
7
7
|
require "mongoid/validatable/length"
|
8
|
+
require "mongoid/validatable/numericality"
|
8
9
|
require "mongoid/validatable/queryable"
|
9
10
|
require "mongoid/validatable/presence"
|
10
11
|
require "mongoid/validatable/uniqueness"
|
data/lib/mongoid/version.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Mongoid
|
4
|
-
|
4
|
+
# The current version of Mongoid
|
5
|
+
#
|
6
|
+
# Note that this file is automatically updated via `rake candidate:create`.
|
7
|
+
# Manual changes to this file will be overwritten by that rake task.
|
8
|
+
VERSION = '8.0.11'
|
5
9
|
end
|
@@ -16,6 +16,10 @@ describe 'Mongoid application tests' do
|
|
16
16
|
skip 'Set APP_TESTS=1 in environment to run application tests'
|
17
17
|
end
|
18
18
|
|
19
|
+
if SpecConfig.instance.rails_version < '7.1'
|
20
|
+
skip 'App tests require Rails > 7.0 (see https://stackoverflow.com/questions/79360526)'
|
21
|
+
end
|
22
|
+
|
19
23
|
require 'fileutils'
|
20
24
|
require 'mrss/child_process_helper'
|
21
25
|
require 'open-uri'
|
@@ -87,6 +91,12 @@ describe 'Mongoid application tests' do
|
|
87
91
|
end
|
88
92
|
|
89
93
|
context 'new application - rails' do
|
94
|
+
before(:all) do
|
95
|
+
if SpecConfig.instance.rails_version < '7.1'
|
96
|
+
skip '`rails new` with rails < 7.1 fails because modern concurrent-ruby removed logger dependency'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
90
100
|
it 'creates' do
|
91
101
|
install_rails
|
92
102
|
|
@@ -3,8 +3,7 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe 'embeds_one associations' do
|
6
|
-
|
7
|
-
context 're-associating the same object' do
|
6
|
+
context 'when re-associating the same object' do
|
8
7
|
context 'with dependent: destroy' do
|
9
8
|
let(:canvas) do
|
10
9
|
Canvas.create!(palette: Palette.new)
|
@@ -16,7 +15,7 @@ describe 'embeds_one associations' do
|
|
16
15
|
canvas.palette = canvas.palette
|
17
16
|
canvas.save!
|
18
17
|
canvas.reload
|
19
|
-
canvas.palette.
|
18
|
+
expect(canvas.palette).to eq palette
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
@@ -30,12 +29,33 @@ describe 'embeds_one associations' do
|
|
30
29
|
end
|
31
30
|
|
32
31
|
it 'loads the association correctly' do
|
33
|
-
expect { klass }.
|
34
|
-
expect { klass.new.address }.
|
32
|
+
expect { klass }.not_to raise_error
|
33
|
+
expect { klass.new.address }.not_to raise_error
|
35
34
|
instance = klass.new
|
36
35
|
address = Address.new
|
37
36
|
instance.address = address
|
38
37
|
expect(instance.address).to eq address
|
39
38
|
end
|
40
39
|
end
|
40
|
+
|
41
|
+
context 'when parent is persisted' do
|
42
|
+
let!(:person) do
|
43
|
+
Person.create!
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when assigning the new child' do
|
47
|
+
context 'when assigning an attribute to the child' do
|
48
|
+
before do
|
49
|
+
# person.reload
|
50
|
+
person.name = Name.new
|
51
|
+
person.name.first_name = 'Dmitry'
|
52
|
+
person.save!
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'persists the child' do
|
56
|
+
expect(person.reload.name.first_name).to eq 'Dmitry'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
41
61
|
end
|
@@ -1757,43 +1757,6 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
|
1757
1757
|
end
|
1758
1758
|
end
|
1759
1759
|
|
1760
|
-
describe "#any?" do
|
1761
|
-
|
1762
|
-
let(:person) do
|
1763
|
-
Person.create!
|
1764
|
-
end
|
1765
|
-
|
1766
|
-
context "when nothing exists on the relation" do
|
1767
|
-
|
1768
|
-
context "when no document is added" do
|
1769
|
-
|
1770
|
-
let!(:sandwich) do
|
1771
|
-
Sandwich.create!
|
1772
|
-
end
|
1773
|
-
|
1774
|
-
it "returns false" do
|
1775
|
-
expect(sandwich.meats.any?).to be false
|
1776
|
-
end
|
1777
|
-
end
|
1778
|
-
|
1779
|
-
context "when the document is destroyed" do
|
1780
|
-
|
1781
|
-
before do
|
1782
|
-
Meat.create!
|
1783
|
-
end
|
1784
|
-
|
1785
|
-
let!(:sandwich) do
|
1786
|
-
Sandwich.create!
|
1787
|
-
end
|
1788
|
-
|
1789
|
-
it "returns false" do
|
1790
|
-
sandwich.destroy
|
1791
|
-
expect(sandwich.meats.any?).to be false
|
1792
|
-
end
|
1793
|
-
end
|
1794
|
-
end
|
1795
|
-
end
|
1796
|
-
|
1797
1760
|
context "when documents have been persisted" do
|
1798
1761
|
|
1799
1762
|
let!(:preference) do
|
@@ -3063,6 +3026,34 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
|
3063
3026
|
end
|
3064
3027
|
end
|
3065
3028
|
|
3029
|
+
# MONGOID-5844
|
3030
|
+
#
|
3031
|
+
# Specifically, this tests the case where the association is
|
3032
|
+
# initialized with a single element (so that Proxy#push does not take
|
3033
|
+
# the `concat` route), which causes `reset_unloaded` to be called, which
|
3034
|
+
# sets the `_unloaded` Criteria object to match only the specific element
|
3035
|
+
# that was given.
|
3036
|
+
#
|
3037
|
+
# The issue now is that when the events list is updated to be both events,
|
3038
|
+
# _unloaded matches one of them already, and the other has previously been
|
3039
|
+
# persisted so `new_record?` won't match it. We need to make sure the
|
3040
|
+
# `#size` logic properly accounts for this case.
|
3041
|
+
context 'when documents have been previously persisted' do
|
3042
|
+
let(:person1) { Person.create! }
|
3043
|
+
let(:person2) { Person.create! }
|
3044
|
+
let(:event1) { Event.create!(administrators: [ person1 ]) }
|
3045
|
+
let(:event2) { Event.create!(administrators: [ person2 ]) }
|
3046
|
+
|
3047
|
+
before do
|
3048
|
+
person1.administrated_events = [ event1, event2 ]
|
3049
|
+
end
|
3050
|
+
|
3051
|
+
it 'returns the number of associated documents [MONGOID-5844]' do
|
3052
|
+
expect(person1.administrated_events.to_a.size).to eq(2)
|
3053
|
+
expect(person1.administrated_events.size).to eq(2)
|
3054
|
+
end
|
3055
|
+
end
|
3056
|
+
|
3066
3057
|
context "when documents have not been persisted" do
|
3067
3058
|
|
3068
3059
|
before do
|
@@ -100,66 +100,6 @@ describe Mongoid::Association do
|
|
100
100
|
expect(name).to_not be_an_embedded_many
|
101
101
|
end
|
102
102
|
end
|
103
|
-
|
104
|
-
context "when validation depends on association" do
|
105
|
-
before(:all) do
|
106
|
-
class Author
|
107
|
-
include Mongoid::Document
|
108
|
-
embeds_many :books, cascade_callbacks: true
|
109
|
-
field :condition, type: Boolean
|
110
|
-
end
|
111
|
-
|
112
|
-
class Book
|
113
|
-
include Mongoid::Document
|
114
|
-
embedded_in :author
|
115
|
-
validate :parent_condition_is_not_true
|
116
|
-
|
117
|
-
def parent_condition_is_not_true
|
118
|
-
return unless author&.condition
|
119
|
-
errors.add :base, "Author condition is true."
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
Author.delete_all
|
124
|
-
Book.delete_all
|
125
|
-
end
|
126
|
-
|
127
|
-
let(:author) { Author.new }
|
128
|
-
let(:book) { Book.new }
|
129
|
-
|
130
|
-
context "when author is not persisted" do
|
131
|
-
it "is valid without books" do
|
132
|
-
expect(author.valid?).to be true
|
133
|
-
end
|
134
|
-
|
135
|
-
it "is valid with a book" do
|
136
|
-
author.books << book
|
137
|
-
expect(author.valid?).to be true
|
138
|
-
end
|
139
|
-
|
140
|
-
it "is not valid when condition is true with a book" do
|
141
|
-
author.condition = true
|
142
|
-
author.books << book
|
143
|
-
expect(author.valid?).to be false
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
context "when author is persisted" do
|
148
|
-
before do
|
149
|
-
author.books << book
|
150
|
-
author.save
|
151
|
-
end
|
152
|
-
|
153
|
-
it "remains valid initially" do
|
154
|
-
expect(author.valid?).to be true
|
155
|
-
end
|
156
|
-
|
157
|
-
it "becomes invalid when condition is set to true" do
|
158
|
-
author.update_attributes(condition: true)
|
159
|
-
expect(author.valid?).to be false
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
103
|
end
|
164
104
|
|
165
105
|
describe "#embedded_one?" do
|
@@ -28,5 +28,21 @@ describe ActiveModel::Validations::NumericalityValidator do
|
|
28
28
|
expect(model).to_not be_valid
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
context 'when the value is numeric' do
|
33
|
+
let(:model) { TestModel.new(amount: '15.0') }
|
34
|
+
|
35
|
+
it 'returns true' do
|
36
|
+
expect(model).to be_valid
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when the value is a BSON::Decimal128' do
|
41
|
+
let(:model) { TestModel.new(amount: BSON::Decimal128.new('15.0')) }
|
42
|
+
|
43
|
+
it 'returns true' do
|
44
|
+
expect(model).to be_valid
|
45
|
+
end
|
46
|
+
end
|
31
47
|
end
|
32
48
|
end
|
data/spec/shared/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2020 MongoDB, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
desired_version, arch = ARGV
|
4
|
+
if arch.nil?
|
5
|
+
STDERR.puts "Usage: get-mongodb-download-url desired-version arch"
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
|
9
|
+
$: << File.join(File.dirname(__FILE__), '../lib')
|
10
|
+
require 'mrss/server_version_registry'
|
11
|
+
|
12
|
+
begin
|
13
|
+
puts Mrss::ServerVersionRegistry.new(desired_version, arch).download_url
|
14
|
+
rescue Mrss::ServerVersionRegistry::Error => exc
|
15
|
+
STDERR.puts "Error: #{exc}"
|
16
|
+
exit 2
|
17
|
+
end
|
@@ -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
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
autoload :ChildProcess, 'childprocess'
|
5
|
+
autoload :Tempfile, 'tempfile'
|
6
|
+
|
7
|
+
module Mrss
|
8
|
+
module ChildProcessHelper
|
9
|
+
class SpawnError < StandardError; end
|
10
|
+
|
11
|
+
module_function def call(cmd, env: nil, cwd: nil)
|
12
|
+
process = ChildProcess.new(*cmd)
|
13
|
+
process.io.inherit!
|
14
|
+
if cwd
|
15
|
+
process.cwd = cwd
|
16
|
+
end
|
17
|
+
if env
|
18
|
+
env.each do |k, v|
|
19
|
+
process.environment[k.to_s] = v
|
20
|
+
end
|
21
|
+
end
|
22
|
+
process.start
|
23
|
+
process.wait
|
24
|
+
process
|
25
|
+
end
|
26
|
+
|
27
|
+
module_function def check_call(cmd, env: nil, cwd: nil)
|
28
|
+
process = call(cmd, env: env, cwd: cwd)
|
29
|
+
unless process.exit_code == 0
|
30
|
+
raise SpawnError, "Failed to execute: #{cmd}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module_function def get_output(cmd, env: nil, cwd: nil)
|
35
|
+
process = ChildProcess.new(*cmd)
|
36
|
+
process.io.inherit!
|
37
|
+
if cwd
|
38
|
+
process.cwd = cwd
|
39
|
+
end
|
40
|
+
if env
|
41
|
+
env.each do |k, v|
|
42
|
+
process.environment[k.to_s] = v
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
output = ''
|
47
|
+
r, w = IO.pipe
|
48
|
+
|
49
|
+
begin
|
50
|
+
process.io.stdout = w
|
51
|
+
process.start
|
52
|
+
w.close
|
53
|
+
|
54
|
+
thread = Thread.new do
|
55
|
+
begin
|
56
|
+
loop do
|
57
|
+
output << r.readpartial(16384)
|
58
|
+
end
|
59
|
+
rescue EOFError
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
process.wait
|
64
|
+
thread.join
|
65
|
+
ensure
|
66
|
+
r.close
|
67
|
+
end
|
68
|
+
|
69
|
+
[process, output]
|
70
|
+
end
|
71
|
+
|
72
|
+
module_function def check_output(*args)
|
73
|
+
process, output = get_output(*args)
|
74
|
+
unless process.exit_code == 0
|
75
|
+
raise SpawnError,"Failed to execute: #{args}"
|
76
|
+
end
|
77
|
+
output
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|