scmd 1.1.0 → 2.0.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.
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: