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 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" @exitcode=nil>
27
+ cmd.inspect #=> #<Scmd::Command:0x83220514 @cmd_str="echo hi" @exitstatus=nil>
28
28
 
29
- cmd.pid #=> nil
30
- cmd.exitcode #=> nil
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 #=> 12345
46
- cmd.exitcode #=> 0
47
- cmd.stdout #=> 'hi'
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 'open4'
6
+ require 'posix-spawn'
15
7
 
16
8
  module Scmd
17
- class Command
18
9
 
19
- class Failure < RuntimeError; end
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, :exitcode, :stdout, :stderr
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 success?; @exitcode == 0; end
30
- def to_s; @cmd_str.to_s; end
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
- "#<#{self.class}:0x#{self.object_id.to_s(16)} @cmd_str=#{self.cmd_str.inspect} @exitcode=#{@exitcode.inspect}>"
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 Failure
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
- status = Open4::popen4(@cmd_str) do |pid, stdin, stdout, stderr|
43
- if !input.nil?
44
- [*input].each{|line| stdin.puts line.to_s}
45
- stdin.close
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
- @exitcode = status.to_i
61
+ @pid = pid.to_i
62
+ @stdout += stdout.read.strip
63
+ @stderr += stderr.read.strip
52
64
  rescue Errno::ENOENT => err
53
- @exitcode = -1
54
- @stderr = err.message
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
@@ -1,3 +1,3 @@
1
1
  module Scmd
2
- VERSION = "1.1.0"
2
+ VERSION = "2.0.0"
3
3
  end
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{Wrapper to `open4` for running system commands.}
8
- gem.summary = %q{Wrapper to `open4` for running system commands.}
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("open4")
20
+ gem.add_dependency("posix-spawn")
21
21
  end
@@ -11,7 +11,7 @@ module Scmd
11
11
  end
12
12
  subject { @success_cmd }
13
13
 
14
- should have_readers :cmd_str, :pid, :exitcode, :stdout, :stderr
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.exitcode
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.exitcode
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.exitcode
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!` and a non-zero exitcode" do
48
- assert_raises Scmd::Command::Failure do
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: 19
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
- - 1
8
- - 1
7
+ - 2
9
8
  - 0
10
- version: 1.1.0
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-10-16 00:00:00 Z
19
+ date: 2012-11-08 00:00:00 Z
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: assert
22
- version_requirements: &id001 !ruby/object:Gem::Requirement
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
- requirement: *id001
33
- prerelease: false
34
+ version_requirements: *id001
34
35
  - !ruby/object:Gem::Dependency
35
- name: open4
36
- version_requirements: &id002 !ruby/object:Gem::Requirement
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
- requirement: *id002
47
- prerelease: false
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.24
102
+ rubygems_version: 1.8.15
101
103
  signing_key:
102
104
  specification_version: 3
103
- summary: Wrapper to `open4` for running system commands.
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: