awesome_spawn 1.2.1 → 1.3.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.
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. If this option
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 if :in_data is specified" if options.include?(:in) && options.include?(:in_data)
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
- begin
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.exit_status != 0
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, in_data, spawn_options = {})
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 &&= status.exitstatus
114
- return output, error, status
117
+ return output || "", error || "", status && status.exitstatus
115
118
  end
116
119
  end
@@ -0,0 +1,11 @@
1
+ require 'logger'
2
+
3
+ module AwesomeSpawn
4
+ class NullLogger < Logger
5
+ def initialize(*_args)
6
+ end
7
+
8
+ def add(*_args, &_block)
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module AwesomeSpawn
2
- VERSION = "1.2.1"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -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.stub(:launch => ["", "", 0])
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 ":in_data cannot be passed with :in" do
18
- expect { subject.send(run_method, "true", :in_data => "XXXXX", :in => "/dev/null") } .to raise_error(ArgumentError)
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 { subject.send(run_method, "true", :out => "/dev/null") }.to raise_error(ArgumentError)
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 { subject.send(run_method, "true", :err => "/dev/null") }.to raise_error(ArgumentError)
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 { subject.send(run_method, "XXXXX --user=bob") }.to raise_error(Errno::ENOENT, "No such file or directory - XXXXX")
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
@@ -1,23 +1,38 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AwesomeSpawn::CommandResult do
4
- context "#inspect" do
5
- it "will not display sensitive information" do
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
- expect(str).to_not include("aaa")
9
- expect(str).to_not include("bbb")
10
- expect(str).to_not include("ccc")
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 "will know if a command succeeded" do
14
- expect(described_class.new("c", "o", "e", 0)).to be_a_success
15
- expect(described_class.new("c", "o", "e", 0)).not_to be_a_failure
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 "will know if a command failed" do
19
- expect(described_class.new("c", "o", "e", 1)).to be_a_failure
20
- expect(described_class.new("c", "o", "e", 1)).not_to be_a_success
21
- end
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.stub(:capture3).and_raise("Spawning is not permitted in specs. Please change your spec to use expectations/stubs.")
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.stub(:capture3).and_call_original
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.2.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: 2014-07-17 00:00:00.000000000 Z
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: 1.8.23.2
120
+ rubygems_version: 2.0.14
128
121
  signing_key:
129
- specification_version: 3
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