scmd 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -9
- data/lib/scmd/command.rb +49 -33
- data/lib/scmd/version.rb +1 -1
- data/scmd.gemspec +5 -5
- data/test/command_tests.rb +12 -6
- metadata +18 -15
data/README.md
CHANGED
@@ -24,12 +24,12 @@ Create a command object:
|
|
24
24
|
cmd = Scmd.new("echo hi")
|
25
25
|
|
26
26
|
cmd.to_s #=> "echo hi"
|
27
|
-
cmd.inspect #=> #<Scmd::Command:0x83220514 @cmd_str="echo hi" @
|
27
|
+
cmd.inspect #=> #<Scmd::Command:0x83220514 @cmd_str="echo hi" @exitstatus=nil>
|
28
28
|
|
29
|
-
cmd.pid
|
30
|
-
cmd.
|
31
|
-
cmd.stdout
|
32
|
-
cmd.stderr
|
29
|
+
cmd.pid #=> nil
|
30
|
+
cmd.exitstatus #=> nil
|
31
|
+
cmd.stdout #=> ''
|
32
|
+
cmd.stderr #=> ''
|
33
33
|
```
|
34
34
|
|
35
35
|
Run it:
|
@@ -42,10 +42,10 @@ Results:
|
|
42
42
|
|
43
43
|
```ruby
|
44
44
|
# written to the cmd instance
|
45
|
-
cmd.pid
|
46
|
-
cmd.
|
47
|
-
cmd.stdout
|
48
|
-
cmd.stderr
|
45
|
+
cmd.pid #=> 12345
|
46
|
+
cmd.exitstatus #=> 0
|
47
|
+
cmd.stdout #=> 'hi'
|
48
|
+
cmd.stderr #=> ''
|
49
49
|
|
50
50
|
# the cmd instance is returned by `run` for chaining as well
|
51
51
|
cmd.run.stdout #=> 'hi'
|
data/lib/scmd/command.rb
CHANGED
@@ -2,66 +2,82 @@
|
|
2
2
|
# with with a string specifying the command to execute. You can then run the
|
3
3
|
# command and inspect its results. It can be used as is, or inherited from to
|
4
4
|
# create a more custom command wrapper.
|
5
|
-
#
|
6
|
-
# Notes:
|
7
|
-
# * Uses `open4`. Open4 is more reliable for actually getting the subprocesses
|
8
|
-
# exit code (compared to `open3`).
|
9
|
-
# * The inspect method is overwritten to only display the name of the class and
|
10
|
-
# the command string. This is to help reduce ridiculous inspect strings due to
|
11
|
-
# result data that is stored in instance variables.
|
12
|
-
# * See the README.md for a walkthrough of the API.
|
13
5
|
|
14
|
-
require '
|
6
|
+
require 'posix-spawn'
|
15
7
|
|
16
8
|
module Scmd
|
17
|
-
class Command
|
18
9
|
|
19
|
-
|
10
|
+
class RunError < ::RuntimeError
|
11
|
+
def initialize(stderr, called_from)
|
12
|
+
super(stderr)
|
13
|
+
set_backtrace(called_from)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Command
|
20
18
|
|
21
19
|
attr_reader :cmd_str
|
22
|
-
attr_reader :pid, :
|
20
|
+
attr_reader :pid, :exitstatus, :stdout, :stderr
|
23
21
|
|
24
22
|
def initialize(cmd_str)
|
25
23
|
@cmd_str = cmd_str
|
26
24
|
reset_results
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
27
|
+
def reset_results
|
28
|
+
@pid = @exitstatus = nil
|
29
|
+
@stdout = @stderr = ''
|
30
|
+
end
|
31
|
+
|
32
|
+
def success?
|
33
|
+
@exitstatus == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
@cmd_str.to_s
|
38
|
+
end
|
39
|
+
|
31
40
|
def inspect
|
32
|
-
|
41
|
+
reference = '0x0%x' % (self.object_id << 1)
|
42
|
+
"#<#{self.class}:#{reference}"\
|
43
|
+
" @cmd_str=#{self.cmd_str.inspect}"\
|
44
|
+
" @exitstatus=#{@exitstatus.inspect}>"
|
33
45
|
end
|
34
46
|
|
35
47
|
def run(input=nil)
|
36
|
-
run!(input) rescue
|
48
|
+
run!(input) rescue RunError
|
37
49
|
self
|
38
50
|
end
|
39
51
|
|
40
52
|
def run!(input=nil)
|
53
|
+
called_from = caller
|
54
|
+
|
41
55
|
begin
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
@pid = pid.to_i
|
48
|
-
@stdout += stdout.read.strip
|
49
|
-
@stderr += stderr.read.strip
|
56
|
+
pid, stdin, stdout, stderr = POSIX::Spawn::popen4(@cmd_str)
|
57
|
+
if !input.nil?
|
58
|
+
[*input].each{|line| stdin.puts line.to_s}
|
59
|
+
stdin.close
|
50
60
|
end
|
51
|
-
@
|
61
|
+
@pid = pid.to_i
|
62
|
+
@stdout += stdout.read.strip
|
63
|
+
@stderr += stderr.read.strip
|
52
64
|
rescue Errno::ENOENT => err
|
53
|
-
@
|
54
|
-
@stderr
|
65
|
+
@exitstatus = -1
|
66
|
+
@stderr = err.message
|
67
|
+
ensure
|
68
|
+
[stdin, stdout, stderr].each{|io| io.close if !io.closed?}
|
69
|
+
::Process::waitpid(pid)
|
70
|
+
|
71
|
+
# `$?` is a thread-safe predefined variable that returns the exit status
|
72
|
+
# of the last child process to terminate:
|
73
|
+
# http://phrogz.net/ProgrammingRuby/language.html#predefinedvariables
|
74
|
+
@exitstatus ||= $?.exitstatus
|
75
|
+
|
76
|
+
raise RunError.new(@stderr, called_from) if !success?
|
55
77
|
end
|
56
78
|
|
57
|
-
raise Failure, @stderr if !success?
|
58
79
|
self
|
59
80
|
end
|
60
81
|
|
61
|
-
def reset_results
|
62
|
-
@pid = @exitcode = nil
|
63
|
-
@stdout = @stderr = ''
|
64
|
-
end
|
65
|
-
|
66
82
|
end
|
67
83
|
end
|
data/lib/scmd/version.rb
CHANGED
data/scmd.gemspec
CHANGED
@@ -4,11 +4,11 @@ require File.expand_path('../lib/scmd/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.name = "scmd"
|
6
6
|
gem.version = Scmd::VERSION
|
7
|
-
gem.description = %q{
|
8
|
-
gem.summary = %q{
|
7
|
+
gem.description = %q{Build and run system commands.}
|
8
|
+
gem.summary = %q{Build and run system commands.}
|
9
9
|
|
10
|
-
gem.authors = ["Kelly Redding"]
|
11
|
-
gem.email = ["kelly@kellyredding.com"]
|
10
|
+
gem.authors = ["Kelly Redding", "Collin Redding"]
|
11
|
+
gem.email = ["kelly@kellyredding.com", "collin.redding@me.com"]
|
12
12
|
gem.homepage = "http://github.com/redding/scmd"
|
13
13
|
|
14
14
|
gem.files = `git ls-files`.split("\n")
|
@@ -17,5 +17,5 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.require_paths = ["lib"]
|
18
18
|
|
19
19
|
gem.add_development_dependency("assert")
|
20
|
-
gem.add_dependency("
|
20
|
+
gem.add_dependency("posix-spawn")
|
21
21
|
end
|
data/test/command_tests.rb
CHANGED
@@ -11,7 +11,7 @@ module Scmd
|
|
11
11
|
end
|
12
12
|
subject { @success_cmd }
|
13
13
|
|
14
|
-
should have_readers :cmd_str, :pid, :
|
14
|
+
should have_readers :cmd_str, :pid, :exitstatus, :stdout, :stderr
|
15
15
|
should have_instance_methods :run, :run!
|
16
16
|
|
17
17
|
should "know and return its cmd string" do
|
@@ -21,7 +21,7 @@ module Scmd
|
|
21
21
|
|
22
22
|
should "default its result values" do
|
23
23
|
assert_nil subject.pid
|
24
|
-
assert_nil subject.
|
24
|
+
assert_nil subject.exitstatus
|
25
25
|
assert_equal '', subject.stdout
|
26
26
|
assert_equal '', subject.stderr
|
27
27
|
end
|
@@ -30,7 +30,7 @@ module Scmd
|
|
30
30
|
@success_cmd.run
|
31
31
|
|
32
32
|
assert_not_nil @success_cmd.pid
|
33
|
-
assert_equal 0, @success_cmd.
|
33
|
+
assert_equal 0, @success_cmd.exitstatus
|
34
34
|
assert @success_cmd.success?
|
35
35
|
assert_equal 'hi', @success_cmd.stdout
|
36
36
|
assert_equal '', @success_cmd.stderr
|
@@ -38,16 +38,22 @@ module Scmd
|
|
38
38
|
@failure_cmd.run
|
39
39
|
|
40
40
|
assert_not_nil @failure_cmd.pid
|
41
|
-
assert_not_equal 0, @failure_cmd.
|
41
|
+
assert_not_equal 0, @failure_cmd.exitstatus
|
42
42
|
assert_not @failure_cmd.success?
|
43
43
|
assert_equal '', @failure_cmd.stdout
|
44
44
|
assert_not_equal '', @failure_cmd.stderr
|
45
45
|
end
|
46
46
|
|
47
|
-
should "raise an exception on `run!`
|
48
|
-
|
47
|
+
should "raise an exception with proper backtrace on `run!`" do
|
48
|
+
err = begin;
|
49
49
|
@failure_cmd.run!
|
50
|
+
rescue Exception => err
|
51
|
+
err
|
50
52
|
end
|
53
|
+
|
54
|
+
assert_kind_of Scmd::RunError, err
|
55
|
+
assert_includes 'No such file or directory', err.message
|
56
|
+
assert_includes 'test/command_tests.rb:', err.backtrace.first
|
51
57
|
end
|
52
58
|
|
53
59
|
should "return itself on `run`, `run!`" do
|
metadata
CHANGED
@@ -1,25 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scmd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
8
|
-
- 1
|
7
|
+
- 2
|
9
8
|
- 0
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 2.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kelly Redding
|
14
|
+
- Collin Redding
|
14
15
|
autorequire:
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2012-
|
19
|
+
date: 2012-11-08 00:00:00 Z
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: assert
|
22
|
-
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
25
|
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
@@ -29,11 +31,11 @@ dependencies:
|
|
29
31
|
- 0
|
30
32
|
version: "0"
|
31
33
|
type: :development
|
32
|
-
|
33
|
-
prerelease: false
|
34
|
+
version_requirements: *id001
|
34
35
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
36
|
-
|
36
|
+
name: posix-spawn
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
39
|
none: false
|
38
40
|
requirements:
|
39
41
|
- - ">="
|
@@ -43,11 +45,11 @@ dependencies:
|
|
43
45
|
- 0
|
44
46
|
version: "0"
|
45
47
|
type: :runtime
|
46
|
-
|
47
|
-
|
48
|
-
description: Wrapper to `open4` for running system commands.
|
48
|
+
version_requirements: *id002
|
49
|
+
description: Build and run system commands.
|
49
50
|
email:
|
50
51
|
- kelly@kellyredding.com
|
52
|
+
- collin.redding@me.com
|
51
53
|
executables: []
|
52
54
|
|
53
55
|
extensions: []
|
@@ -97,12 +99,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
99
|
requirements: []
|
98
100
|
|
99
101
|
rubyforge_project:
|
100
|
-
rubygems_version: 1.8.
|
102
|
+
rubygems_version: 1.8.15
|
101
103
|
signing_key:
|
102
104
|
specification_version: 3
|
103
|
-
summary:
|
105
|
+
summary: Build and run system commands.
|
104
106
|
test_files:
|
105
107
|
- test/command_tests.rb
|
106
108
|
- test/helper.rb
|
107
109
|
- test/irb.rb
|
108
110
|
- test/scmd_tests.rb
|
111
|
+
has_rdoc:
|