awesome_spawn 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/awesome_spawn.rb +19 -16
- data/lib/awesome_spawn/null_logger.rb +11 -0
- data/lib/awesome_spawn/version.rb +1 -1
- data/spec/awesome_spawn_spec.rb +52 -6
- data/spec/command_line_builder_spec.rb +17 -0
- data/spec/command_result_error_spec.rb +17 -0
- data/spec/command_result_spec.rb +28 -13
- data/spec/no_such_file_error_spec.rb +27 -0
- data/spec/spec_helper.rb +3 -3
- metadata +18 -23
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 495db35a5162aeca5013e1c52777938873956d4d
|
4
|
+
data.tar.gz: aaacd54a4c413f3f69bba0a52b52386041dc1e23
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4b491f9bdcf6a10961ed3b38e22a66f2906e639b6f0eee2340151bda4c1bba929c46765245d1b6ea38b206a328567bd4d834ce36f5d4de7aa6ec9845c428319e
|
7
|
+
data.tar.gz: c291f6d3861399c95343a9f787db4ac2ccf36415c7dda00934e4d8df44e8bd79fa45639b4d7e63eb88b3c1f5074042534b43aef8ad3051c2c0abff289ef01e0d
|
data/lib/awesome_spawn.rb
CHANGED
@@ -3,12 +3,19 @@ require "awesome_spawn/command_line_builder"
|
|
3
3
|
require "awesome_spawn/command_result"
|
4
4
|
require "awesome_spawn/command_result_error"
|
5
5
|
require "awesome_spawn/no_such_file_error"
|
6
|
+
require "awesome_spawn/null_logger"
|
6
7
|
|
7
8
|
require "open3"
|
8
9
|
|
9
10
|
module AwesomeSpawn
|
10
11
|
extend self
|
11
12
|
|
13
|
+
attr_writer :logger
|
14
|
+
|
15
|
+
def logger
|
16
|
+
@logger ||= NullLogger.new
|
17
|
+
end
|
18
|
+
|
12
19
|
# Execute `command` synchronously via Kernel.spawn and gather the output
|
13
20
|
# stream, error stream, and exit status in a {CommandResult}.
|
14
21
|
#
|
@@ -45,11 +52,10 @@ module AwesomeSpawn
|
|
45
52
|
#
|
46
53
|
# @param [String] command The command to run
|
47
54
|
# @param [Hash] options The options for running the command. Also accepts any
|
48
|
-
# option that can be passed to Kernel.spawn, except `:out` and `:err`.
|
55
|
+
# option that can be passed to Kernel.spawn, except `:in`, `:out` and `:err`.
|
49
56
|
# @option options [Hash,Array] :params The command line parameters. See
|
50
57
|
# {#build_command_line} for how to specify params.
|
51
|
-
# @option options [String] :in_data Data to be passed on stdin.
|
52
|
-
# is specified you cannot specify `:in`.
|
58
|
+
# @option options [String] :in_data Data to be passed on stdin.
|
53
59
|
#
|
54
60
|
# @raise [NoSuchFileError] if the `command` is not found
|
55
61
|
# @return [CommandResult] the output stream, error stream, and exit status
|
@@ -57,20 +63,17 @@ module AwesomeSpawn
|
|
57
63
|
def run(command, options = {})
|
58
64
|
raise ArgumentError, "options cannot contain :out" if options.include?(:out)
|
59
65
|
raise ArgumentError, "options cannot contain :err" if options.include?(:err)
|
60
|
-
raise ArgumentError, "options cannot contain :in
|
66
|
+
raise ArgumentError, "options cannot contain :in" if options.include?(:in)
|
61
67
|
options = options.dup
|
62
68
|
params = options.delete(:params)
|
63
|
-
in_data = options.delete(:in_data)
|
69
|
+
if (in_data = options.delete(:in_data))
|
70
|
+
options[:stdin_data] = in_data
|
71
|
+
end
|
64
72
|
|
65
73
|
output, error, status = "", "", nil
|
66
74
|
command_line = build_command_line(command, params)
|
67
75
|
|
68
|
-
|
69
|
-
output, error, status = launch(command_line, in_data, options)
|
70
|
-
ensure
|
71
|
-
output ||= ""
|
72
|
-
error ||= ""
|
73
|
-
end
|
76
|
+
output, error, status = launch(command_line, options)
|
74
77
|
rescue Errno::ENOENT => err
|
75
78
|
raise NoSuchFileError.new(err.message) if NoSuchFileError.detected?(err.message)
|
76
79
|
raise
|
@@ -92,8 +95,10 @@ module AwesomeSpawn
|
|
92
95
|
def run!(command, options = {})
|
93
96
|
command_result = run(command, options)
|
94
97
|
|
95
|
-
if command_result.
|
98
|
+
if command_result.failure?
|
96
99
|
message = "#{command} exit code: #{command_result.exit_status}"
|
100
|
+
logger.error("AwesomeSpawn: #{message}")
|
101
|
+
logger.error("AwesomeSpawn: #{command_result.error}")
|
97
102
|
raise CommandResultError.new(message, command_result)
|
98
103
|
end
|
99
104
|
|
@@ -107,10 +112,8 @@ module AwesomeSpawn
|
|
107
112
|
|
108
113
|
private
|
109
114
|
|
110
|
-
def launch(command,
|
111
|
-
spawn_options = spawn_options.merge(:stdin_data => in_data) if in_data
|
115
|
+
def launch(command, spawn_options = {})
|
112
116
|
output, error, status = Open3.capture3(command, spawn_options)
|
113
|
-
status
|
114
|
-
return output, error, status
|
117
|
+
return output || "", error || "", status && status.exitstatus
|
115
118
|
end
|
116
119
|
end
|
data/spec/awesome_spawn_spec.rb
CHANGED
@@ -6,24 +6,38 @@ describe AwesomeSpawn do
|
|
6
6
|
|
7
7
|
shared_examples_for "run" do
|
8
8
|
context "options" do
|
9
|
+
it "params won't be modified" do
|
10
|
+
params = {:params => {:user => "bob"}}
|
11
|
+
orig_params = params.dup
|
12
|
+
allow(subject).to receive(:launch).with("true --user bob", {}).and_return(["", "", 0])
|
13
|
+
subject.send(run_method, "true", params)
|
14
|
+
expect(orig_params).to eq(params)
|
15
|
+
end
|
16
|
+
|
9
17
|
it ":params won't be modified" do
|
10
18
|
params = {:user => "bob"}
|
11
19
|
orig_params = params.dup
|
12
|
-
subject.
|
20
|
+
allow(subject).to receive(:launch).with("true --user bob", {}).and_return(["", "", 0])
|
13
21
|
subject.send(run_method, "true", :params => params)
|
14
22
|
expect(orig_params).to eq(params)
|
15
23
|
end
|
16
24
|
|
17
|
-
it ":
|
18
|
-
expect
|
25
|
+
it ":in is not supported" do
|
26
|
+
expect do
|
27
|
+
subject.send(run_method, "true", :in => "/dev/null")
|
28
|
+
end.to raise_error(ArgumentError, "options cannot contain :in")
|
19
29
|
end
|
20
30
|
|
21
31
|
it ":out is not supported" do
|
22
|
-
expect
|
32
|
+
expect do
|
33
|
+
subject.send(run_method, "true", :out => "/dev/null")
|
34
|
+
end.to raise_error(ArgumentError, "options cannot contain :out")
|
23
35
|
end
|
24
36
|
|
25
37
|
it ":err is not supported" do
|
26
|
-
expect
|
38
|
+
expect do
|
39
|
+
subject.send(run_method, "true", :err => "/dev/null")
|
40
|
+
end.to raise_error(ArgumentError, "options cannot contain :err")
|
27
41
|
end
|
28
42
|
end
|
29
43
|
|
@@ -53,7 +67,9 @@ describe AwesomeSpawn do
|
|
53
67
|
end
|
54
68
|
|
55
69
|
it "command bad" do
|
56
|
-
expect
|
70
|
+
expect do
|
71
|
+
subject.send(run_method, "XXXXX --user=bob")
|
72
|
+
end.to raise_error(AwesomeSpawn::NoSuchFileError, "No such file or directory - XXXXX")
|
57
73
|
end
|
58
74
|
|
59
75
|
context "with option" do
|
@@ -70,6 +86,18 @@ describe AwesomeSpawn do
|
|
70
86
|
end
|
71
87
|
end
|
72
88
|
|
89
|
+
context "#exit_status" do
|
90
|
+
it "command ok exit ok" do
|
91
|
+
expect(subject.send(run_method, "echo", :params => %w(x)).command_line).to eq("echo x")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "command ok exit bad" do
|
95
|
+
if run_method == "run"
|
96
|
+
expect(subject.send(run_method, "echo x && false").command_line).to eq("echo x && false")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
73
101
|
context "#exit_status" do
|
74
102
|
it "command ok exit ok" do
|
75
103
|
expect(subject.send(run_method, "true").exit_status).to eq(0)
|
@@ -88,9 +116,17 @@ describe AwesomeSpawn do
|
|
88
116
|
it "command ok exit bad" do
|
89
117
|
expect(subject.send(run_method, "echo 'bad' && false").output).to eq("bad\n") if run_method == "run"
|
90
118
|
end
|
119
|
+
|
120
|
+
it "has output even though output redirected to stderr" do
|
121
|
+
expect(subject.send(run_method, "echo \"Hello World\" >&2").output).to eq("")
|
122
|
+
end
|
91
123
|
end
|
92
124
|
|
93
125
|
context "#error" do
|
126
|
+
it "has error even though no error" do
|
127
|
+
expect(subject.send(run_method, "echo", :params => ["Hello World"]).error).to eq("")
|
128
|
+
end
|
129
|
+
|
94
130
|
it "command ok exit ok" do
|
95
131
|
expect(subject.send(run_method, "echo \"Hello World\" >&2").error).to eq("Hello World\n")
|
96
132
|
end
|
@@ -102,6 +138,16 @@ describe AwesomeSpawn do
|
|
102
138
|
end
|
103
139
|
end
|
104
140
|
|
141
|
+
context ".build_command_line" do
|
142
|
+
it "should handle single parameter" do
|
143
|
+
expect(subject.build_command_line("cmd")).to eq("cmd")
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should handle multi parameter" do
|
147
|
+
expect(subject.build_command_line("cmd", :status => true)).to eq("cmd --status true")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
105
151
|
context ".run" do
|
106
152
|
include_examples "run" do
|
107
153
|
let(:run_method) {"run"}
|
@@ -16,6 +16,14 @@ describe AwesomeSpawn::CommandLineBuilder do
|
|
16
16
|
expect(subject.build("true", nil)).to eq "true"
|
17
17
|
end
|
18
18
|
|
19
|
+
it "with empty" do
|
20
|
+
expect(subject.build("true", "")).to eq "true"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "with empty" do
|
24
|
+
expect(subject.build("true", [])).to eq "true"
|
25
|
+
end
|
26
|
+
|
19
27
|
it "with Pathname command" do
|
20
28
|
actual = subject.build(Pathname.new("/usr/bin/ruby"))
|
21
29
|
expect(actual).to eq "/usr/bin/ruby"
|
@@ -197,6 +205,10 @@ describe AwesomeSpawn::CommandLineBuilder do
|
|
197
205
|
assert_params([["--abc", ["def", "ghi"]]], "--abc def ghi")
|
198
206
|
end
|
199
207
|
|
208
|
+
it "with value as Array and extra nils" do
|
209
|
+
assert_params([["--abc", [nil, "def", nil, "ghi", nil]]], "--abc def ghi")
|
210
|
+
end
|
211
|
+
|
200
212
|
it "with value as flattened Array" do
|
201
213
|
assert_params([["--abc", "def", "ghi"]], "--abc def ghi")
|
202
214
|
end
|
@@ -250,6 +262,11 @@ describe AwesomeSpawn::CommandLineBuilder do
|
|
250
262
|
assert_params(params, expected)
|
251
263
|
end
|
252
264
|
|
265
|
+
it "as mixed Array" do
|
266
|
+
params = ["log", "feature", "-E", :oneline, :grep, "abc"]
|
267
|
+
assert_params(params, expected)
|
268
|
+
end
|
269
|
+
|
253
270
|
it "as mixed Array with nested Hashes" do
|
254
271
|
params = ["log", "feature", "-E", :oneline, {:grep => "abc"}]
|
255
272
|
assert_params(params, expected)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AwesomeSpawn::CommandResultError do
|
4
|
+
context "basic false command" do
|
5
|
+
before { enable_spawning }
|
6
|
+
subject do
|
7
|
+
begin
|
8
|
+
AwesomeSpawn.run!("false")
|
9
|
+
rescue => e
|
10
|
+
return e
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it { expect(subject.message).to eq("false exit code: 1") }
|
15
|
+
it { expect(subject.result).to be_a_failure }
|
16
|
+
end
|
17
|
+
end
|
data/spec/command_result_spec.rb
CHANGED
@@ -1,23 +1,38 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe AwesomeSpawn::CommandResult do
|
4
|
-
context "
|
5
|
-
|
6
|
-
str = described_class.new("aaa", "bbb", "ccc", 0).inspect
|
4
|
+
context "succeeding object" do
|
5
|
+
subject { described_class.new("aaa", "bbb", "ccc", 0) }
|
7
6
|
|
8
|
-
|
9
|
-
expect(
|
10
|
-
expect(
|
7
|
+
it "should set attributes" do
|
8
|
+
expect(subject.command_line).to eq("aaa")
|
9
|
+
expect(subject.output).to eq("bbb")
|
10
|
+
expect(subject.error).to eq("ccc")
|
11
|
+
expect(subject.exit_status).to eq(0)
|
12
|
+
expect(subject.inspect).to match(/^#<AwesomeSpawn::CommandResult:[0-9a-fx]+ @exit_status=0>$/)
|
11
13
|
end
|
12
14
|
|
13
|
-
it "
|
14
|
-
expect(
|
15
|
-
expect(
|
15
|
+
it "should not display sensitive information" do
|
16
|
+
expect(subject.inspect).to_not include("aaa")
|
17
|
+
expect(subject.inspect).to_not include("bbb")
|
18
|
+
expect(subject.inspect).to_not include("ccc")
|
16
19
|
end
|
17
20
|
|
18
|
-
it
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
it { expect(subject).to be_a_success }
|
22
|
+
it { expect(subject).not_to be_a_failure }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "failing object" do
|
26
|
+
subject { described_class.new("aaa", "bbb", "ccc", 1) }
|
27
|
+
|
28
|
+
it { expect(subject).not_to be_a_success }
|
29
|
+
it { expect(subject).to be_a_failure }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "another failing object" do
|
33
|
+
subject { described_class.new("aaa", "bbb", "ccc", 100) }
|
34
|
+
|
35
|
+
it { expect(subject).not_to be_a_success }
|
36
|
+
it { expect(subject).to be_a_failure }
|
22
37
|
end
|
23
38
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AwesomeSpawn::NoSuchFileError do
|
4
|
+
before do
|
5
|
+
enable_spawning
|
6
|
+
end
|
7
|
+
|
8
|
+
context "single word command" do
|
9
|
+
subject { caught_exception_for("falsey") }
|
10
|
+
it { expect(subject.message).to eq("No such file or directory - falsey") }
|
11
|
+
it { expect(subject.to_s).to eq("No such file or directory - falsey") }
|
12
|
+
it { expect(subject).to be_a(described_class) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "multi word command" do
|
16
|
+
subject { caught_exception_for(" flat --arg 1 --arg 2") }
|
17
|
+
it { expect(subject.message).to eq("No such file or directory - flat") }
|
18
|
+
it { expect(subject.to_s).to eq("No such file or directory - flat") }
|
19
|
+
it { expect(subject).to be_a(described_class) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def caught_exception_for(command)
|
23
|
+
AwesomeSpawn.run!(command)
|
24
|
+
rescue => e
|
25
|
+
return e
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,6 @@
|
|
5
5
|
#
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
7
|
RSpec.configure do |config|
|
8
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
8
|
config.run_all_when_everything_filtered = true
|
10
9
|
config.filter_run :focus
|
11
10
|
|
@@ -19,11 +18,12 @@ RSpec.configure do |config|
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def disable_spawning
|
22
|
-
Open3.
|
21
|
+
allow(Open3).to receive(:capture3)
|
22
|
+
.and_raise("Spawning is not permitted in specs. Please change your spec to use expectations/stubs.")
|
23
23
|
end
|
24
24
|
|
25
25
|
def enable_spawning
|
26
|
-
Open3.
|
26
|
+
allow(Open3).to receive(:capture3).and_call_original
|
27
27
|
end
|
28
28
|
|
29
29
|
begin
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: awesome_spawn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jason Frey
|
@@ -12,12 +11,11 @@ authors:
|
|
12
11
|
autorequire:
|
13
12
|
bindir: bin
|
14
13
|
cert_chain: []
|
15
|
-
date:
|
14
|
+
date: 2015-01-28 00:00:00.000000000 Z
|
16
15
|
dependencies:
|
17
16
|
- !ruby/object:Gem::Dependency
|
18
17
|
name: bundler
|
19
18
|
requirement: !ruby/object:Gem::Requirement
|
20
|
-
none: false
|
21
19
|
requirements:
|
22
20
|
- - ~>
|
23
21
|
- !ruby/object:Gem::Version
|
@@ -25,7 +23,6 @@ dependencies:
|
|
25
23
|
type: :development
|
26
24
|
prerelease: false
|
27
25
|
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
26
|
requirements:
|
30
27
|
- - ~>
|
31
28
|
- !ruby/object:Gem::Version
|
@@ -33,49 +30,43 @@ dependencies:
|
|
33
30
|
- !ruby/object:Gem::Dependency
|
34
31
|
name: rake
|
35
32
|
requirement: !ruby/object:Gem::Requirement
|
36
|
-
none: false
|
37
33
|
requirements:
|
38
|
-
- -
|
34
|
+
- - '>='
|
39
35
|
- !ruby/object:Gem::Version
|
40
36
|
version: '0'
|
41
37
|
type: :development
|
42
38
|
prerelease: false
|
43
39
|
version_requirements: !ruby/object:Gem::Requirement
|
44
|
-
none: false
|
45
40
|
requirements:
|
46
|
-
- -
|
41
|
+
- - '>='
|
47
42
|
- !ruby/object:Gem::Version
|
48
43
|
version: '0'
|
49
44
|
- !ruby/object:Gem::Dependency
|
50
45
|
name: rspec
|
51
46
|
requirement: !ruby/object:Gem::Requirement
|
52
|
-
none: false
|
53
47
|
requirements:
|
54
|
-
- -
|
48
|
+
- - '>='
|
55
49
|
- !ruby/object:Gem::Version
|
56
50
|
version: '0'
|
57
51
|
type: :development
|
58
52
|
prerelease: false
|
59
53
|
version_requirements: !ruby/object:Gem::Requirement
|
60
|
-
none: false
|
61
54
|
requirements:
|
62
|
-
- -
|
55
|
+
- - '>='
|
63
56
|
- !ruby/object:Gem::Version
|
64
57
|
version: '0'
|
65
58
|
- !ruby/object:Gem::Dependency
|
66
59
|
name: coveralls
|
67
60
|
requirement: !ruby/object:Gem::Requirement
|
68
|
-
none: false
|
69
61
|
requirements:
|
70
|
-
- -
|
62
|
+
- - '>='
|
71
63
|
- !ruby/object:Gem::Version
|
72
64
|
version: '0'
|
73
65
|
type: :development
|
74
66
|
prerelease: false
|
75
67
|
version_requirements: !ruby/object:Gem::Requirement
|
76
|
-
none: false
|
77
68
|
requirements:
|
78
|
-
- -
|
69
|
+
- - '>='
|
79
70
|
- !ruby/object:Gem::Version
|
80
71
|
version: '0'
|
81
72
|
description: AwesomeSpawn is a module that provides some useful features over Ruby's
|
@@ -94,43 +85,47 @@ files:
|
|
94
85
|
- lib/awesome_spawn/command_result.rb
|
95
86
|
- lib/awesome_spawn/command_result_error.rb
|
96
87
|
- lib/awesome_spawn/no_such_file_error.rb
|
88
|
+
- lib/awesome_spawn/null_logger.rb
|
97
89
|
- lib/awesome_spawn/version.rb
|
98
90
|
- .yardopts
|
99
91
|
- README.md
|
100
92
|
- LICENSE.txt
|
101
93
|
- spec/awesome_spawn_spec.rb
|
102
94
|
- spec/command_line_builder_spec.rb
|
95
|
+
- spec/command_result_error_spec.rb
|
103
96
|
- spec/command_result_spec.rb
|
97
|
+
- spec/no_such_file_error_spec.rb
|
104
98
|
- spec/spec_helper.rb
|
105
99
|
- .rspec
|
106
100
|
homepage: https://github.com/ManageIQ/awesome_spawn
|
107
101
|
licenses:
|
108
102
|
- MIT
|
103
|
+
metadata: {}
|
109
104
|
post_install_message:
|
110
105
|
rdoc_options: []
|
111
106
|
require_paths:
|
112
107
|
- lib
|
113
108
|
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
-
none: false
|
115
109
|
requirements:
|
116
|
-
- -
|
110
|
+
- - '>='
|
117
111
|
- !ruby/object:Gem::Version
|
118
112
|
version: '0'
|
119
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
-
none: false
|
121
114
|
requirements:
|
122
|
-
- -
|
115
|
+
- - '>='
|
123
116
|
- !ruby/object:Gem::Version
|
124
117
|
version: '0'
|
125
118
|
requirements: []
|
126
119
|
rubyforge_project:
|
127
|
-
rubygems_version:
|
120
|
+
rubygems_version: 2.0.14
|
128
121
|
signing_key:
|
129
|
-
specification_version:
|
122
|
+
specification_version: 4
|
130
123
|
summary: AwesomeSpawn is a module that provides some useful features over Ruby's Kernel.spawn.
|
131
124
|
test_files:
|
132
125
|
- spec/awesome_spawn_spec.rb
|
133
126
|
- spec/command_line_builder_spec.rb
|
127
|
+
- spec/command_result_error_spec.rb
|
134
128
|
- spec/command_result_spec.rb
|
129
|
+
- spec/no_such_file_error_spec.rb
|
135
130
|
- spec/spec_helper.rb
|
136
131
|
- .rspec
|