tsalzer-syscmd 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Till Salzer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,27 @@
1
+ = syscmd
2
+
3
+ A simple wrapper to execute system commands, which provides stdout, stderr and
4
+ exit code.
5
+
6
+ == Usage
7
+
8
+ require 'syscmd'
9
+ cmd = Syscmd.exec!('command') # => Syscmd::Command object
10
+
11
+ cmd.stdout # => String
12
+ cmd.stderr # => String
13
+ cmd.exitcode # => Integer
14
+
15
+ Note that <code>Syscmd.exec!</code> immediately executes the given command.
16
+ You will get the Syscmd::Command object executed and can examine it.
17
+
18
+
19
+ == Heritage/Acknoledgement
20
+
21
+ The <code>Syscmd::popen</code> code is mostly taken from the original Ruby 1.8
22
+ implementation of Open3::open3 (author: Yukihiro Matsumoto: documentation: Konrad Meyer).
23
+
24
+
25
+ == Copyright
26
+
27
+ Copyright (c) 2009 Till Salzer. See LICENSE for details.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 1
@@ -0,0 +1,58 @@
1
+ module Syscmd
2
+ # Taken from the original Ruby 1.8 implementation of Open3::open3 and slightly
3
+ # tweaked to return an exit status.
4
+ # This function is meant as in internal method of the Syscmd module; do not use
5
+ # it on it's own, as the interface may change anytime.
6
+ #
7
+ # cmd:: the command to execute
8
+ # returns:: status, stdout, stderr
9
+ #
10
+ # orignal author:: Yukihiro Matsumoto
11
+ # orignal socumentation:: Konrad Meyer
12
+ def popen(*cmd)
13
+ pw = IO::pipe # pipe[0] for read, pipe[1] for write
14
+ pr = IO::pipe
15
+ pe = IO::pipe
16
+ status = 0 # status of the inner fork
17
+
18
+ pid = fork do
19
+ # child
20
+ gcpid = fork do
21
+ # grandchild
22
+ pw[1].close
23
+ STDIN.reopen(pw[0])
24
+ pw[0].close
25
+
26
+ pr[0].close
27
+ STDOUT.reopen(pr[1])
28
+ pr[1].close
29
+
30
+ pe[0].close
31
+ STDERR.reopen(pe[1])
32
+ pe[1].close
33
+
34
+ exec(*cmd)
35
+ end
36
+ gcpid, gcstatus = Process.wait2(gcpid)
37
+ exit!(gcstatus.exitstatus)
38
+ end
39
+
40
+ pw[0].close
41
+ pr[1].close
42
+ pe[1].close
43
+ pid, status = Process.wait2(pid)
44
+
45
+ #pi = [status.exitstatus, pw[1], pr[0], pe[0]]
46
+ pi = [status, pr[0], pe[0]]
47
+ pw[1].sync = true
48
+ if defined? yield
49
+ begin
50
+ return yield(*pi)
51
+ ensure
52
+ pi.each{|p| p.close unless p.closed?}
53
+ end
54
+ end
55
+ pi
56
+ end
57
+ module_function :popen
58
+ end
data/lib/syscmd.rb ADDED
@@ -0,0 +1,89 @@
1
+ require 'syscmd/popen'
2
+
3
+ # Module.
4
+ module Syscmd
5
+ # Error raise if a command object is executed twice.
6
+ class AlreadyExecutedError < RuntimeError ; end
7
+
8
+ # A command to execute.
9
+ # You usually just use Syscmd::exec! to create and execute the command.
10
+ # A command object can be executed only once.
11
+ class Command
12
+ # +true+ if the command was already executed, otherwise +false+.
13
+ attr_reader :executed
14
+
15
+ # the command to execute.
16
+ attr_reader :cmd
17
+ # array of arguments.
18
+ attr_reader :args
19
+ # the constructed command line.
20
+ attr_reader :cmdline
21
+
22
+ # standard output of the executed command.
23
+ attr_reader :stdout
24
+ # standard error of the executed command.
25
+ attr_reader :stderr
26
+ # exit code of the executed command.
27
+ # Note that the exit code is just a Byte value ranged 0..255 on
28
+ # Unix platforms, so don't expect any negative values or values greated
29
+ # than 255 here.
30
+ attr_reader :exitcode
31
+ # exit status of the executed command.
32
+ attr_reader :process_status
33
+
34
+ # execute a system command.
35
+ # cmd: the command to execute
36
+ # args: command line arguments
37
+ def initialize(cmd, *args)
38
+ @cmd = cmd
39
+ @args = *args
40
+ @process_status = nil
41
+ @executed = false
42
+ end
43
+
44
+ # execute the command.
45
+ # raises AlreadyExecutedError if the command is already executed.
46
+ def exec!
47
+ raise AlreadyExecutedError.new("already executed with status #{process_status}") if self.executed?
48
+ @process_status, pread, perr = Syscmd::popen(self.cmdline)
49
+ @stdout = pread.read
50
+ @stderr = perr.read
51
+ self
52
+ end
53
+
54
+ # the exitcode of the executed command.
55
+ # returns nil
56
+ def exitcode
57
+ @process_status ? @process_status.exitstatus : nil
58
+ end
59
+
60
+ # build the command line for this command.
61
+ # Once the command line is created, it will be cached.
62
+ def cmdline
63
+ if @cmdline.nil?
64
+ cmdline = "#{@cmd}"
65
+ @args.each do |arg|
66
+ cmdline << (arg.to_s.index(" ") ? " \"#{arg}\"" : " #{arg}")
67
+ end if @args
68
+ @cmdline = cmdline
69
+ end
70
+ @cmdline
71
+ end
72
+
73
+ # check if the command was executed.
74
+ def executed
75
+ @process_status.nil? ? false : true
76
+ end
77
+ alias executed? executed
78
+ end
79
+
80
+ # execute a system command.
81
+ # cmd:: the command to execute
82
+ # args:: command line arguments
83
+ # returns:: executed Syscmd::Command object
84
+ def exec!(cmd, *args)
85
+ cmd = Command.new(cmd, args)
86
+ cmd.exec!
87
+ end
88
+ module_function :'exec!'
89
+ end
@@ -0,0 +1,58 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Syscmd::Command, ".exec! for -s 'Hello World'" do
4
+ subject { Syscmd::Command.new(TESTER, '-s', 'Hello World') }
5
+
6
+ it "should have stdout 'Hello World'" do
7
+ subject.exec!.stdout.should == 'Hello World'
8
+ end
9
+
10
+ it "should have stderr \"\"" do
11
+ subject.exec!.stderr.should == ""
12
+ end
13
+
14
+ it "should have exitcode 0" do
15
+ subject.exec!.exitcode.should == 0
16
+ end
17
+ end
18
+
19
+ describe Syscmd::Command, ".exec! for positive exit codes" do
20
+ [1, 2, 254, 255].each do |st|
21
+ it "should return exit code #{st} for -x #{st}" do
22
+ cmd = Syscmd::Command.new(TESTER, '-x', st)
23
+ cmd.cmdline.should == "#{TESTER} -x #{st}"
24
+ cmd.exec!
25
+ cmd.exitcode.should == st
26
+ end
27
+ end
28
+ end
29
+
30
+ describe Syscmd::Command, ".exec! for hugh positive exit codes" do
31
+ [256, 257, 512, 4000].each do |st|
32
+ it "should return exit code #{st % 256} for -x #{st}" do
33
+ cmd = Syscmd::Command.new(TESTER, '-x', st)
34
+ cmd.cmdline.should == "#{TESTER} -x #{st}"
35
+ cmd.exec!
36
+ cmd.exitcode.should == st % 256
37
+ end
38
+ end
39
+ end
40
+
41
+ describe Syscmd::Command, ".exec! for negative exit codes" do
42
+ [-1, -2, -255, -256].each do |st|
43
+ it "should return exit code #{st % 256} for -x #{st}" do
44
+ cmd = Syscmd::Command.new(TESTER, '-x', st)
45
+ cmd.cmdline.should == "#{TESTER} -x #{st}"
46
+ cmd.exec!
47
+ cmd.exitcode.should == st % 256
48
+ end
49
+ end
50
+ end
51
+
52
+ describe Syscmd::Command, ".exec! called twice" do
53
+ subject { Syscmd::Command.new(TESTER) }
54
+ it "should fail with AlreadyExecutedError" do
55
+ subject.exec!
56
+ lambda { subject.exec! }.should raise_error(Syscmd::AlreadyExecutedError)
57
+ end
58
+ end
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Syscmd::Command, ".new" do
4
+ subject { Syscmd::Command.new(TESTER) }
5
+
6
+ it "should have #{TESTER} as cmd attribute" do
7
+ subject.cmd.should == TESTER
8
+ end
9
+
10
+ it "should not be executed after creation" do
11
+ subject.executed?.should == false
12
+ end
13
+ end
14
+
15
+ describe Syscmd::Command, ".new with -s 'Hello World'" do
16
+ subject { Syscmd::Command.new(TESTER, '-s', 'Hello World') }
17
+
18
+ it "should have the cmd attribute #{TESTER}" do
19
+ subject.cmd.should == TESTER
20
+ end
21
+
22
+ it "should have the command line arguments ['-s', 'Hello World']" do
23
+ subject.args.should == ['-s', 'Hello World']
24
+ end
25
+
26
+ it "should have the command line '#{TESTER} -s \"Hello World\"'" do
27
+ subject.cmdline.should == "#{TESTER} -s \"Hello World\""
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'syscmd'
6
+
7
+ TESTER=File.join(File.dirname(__FILE__), 'tester.rb') unless defined?(TESTER)
8
+
9
+ Spec::Runner.configure do |config|
10
+
11
+ end
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'spec_helper'
3
+
4
+ describe Syscmd, ".execute" do
5
+ subject { Syscmd }
6
+
7
+ it "should provide a .execute method" do
8
+ subject.should respond_to(:exec!)
9
+ end
10
+
11
+ it "should return a Syscmd::Command object" do
12
+ subject.exec!(TESTER).should be_a(Syscmd::Command)
13
+ end
14
+
15
+ it "should return an executed Syscmd::Command object" do
16
+ cmd = subject.exec!(TESTER)
17
+ cmd.executed?.should == true
18
+ end
19
+
20
+ end
data/spec/tester.rb ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ # == Synopsis
3
+ #
4
+ # tester: test script for syscmd
5
+ #
6
+ # == Usage
7
+ #
8
+ # tester [OPTION]
9
+ #
10
+ # COMMANDS:
11
+ # list:
12
+ # list all available project. No option required.
13
+ # show:
14
+ # show a project. You must provide a PROJECTNAME and a SCOPE. Valid SCOPEs
15
+ # are: [env]ironment, [ver]sion
16
+ # deploy:
17
+ # deploys a given build to an environment. You must provide PROJECTNAME,
18
+ # BUILD and ENVRIONMENT.
19
+ #
20
+ # OPTIONS:
21
+ # --help, -h
22
+ # --stdout, -s
23
+ # --stderr, -e
24
+ # --exitcode, -x
25
+ # --debug
26
+
27
+ require "getoptlong"
28
+ require "rdoc/usage"
29
+
30
+ opts = GetoptLong.new(
31
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
32
+ [ '--stdout', '-s', GetoptLong::REQUIRED_ARGUMENT ],
33
+ [ '--stderr', '-e', GetoptLong::REQUIRED_ARGUMENT ],
34
+ [ '--exitcode', '-x', GetoptLong::REQUIRED_ARGUMENT ],
35
+ [ '--debug', GetoptLong::OPTIONAL_ARGUMENT ]
36
+ )
37
+
38
+ options = {}
39
+ exitcode = 0
40
+
41
+ opts.each do |opt, arg|
42
+ case opt
43
+ when '--help'
44
+ RDoc::usage
45
+ when '--stdout'
46
+ $stdout.print arg
47
+ when '--stderr'
48
+ $stderr.print arg
49
+ when '--exitcode'
50
+ exitcode = arg.to_i
51
+ when '--debug'
52
+ # not yet implemented
53
+ end
54
+ end
55
+
56
+ exit exitcode
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tsalzer-syscmd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Till Salzer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-13 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: till.salzer@googlemail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - LICENSE
25
+ files:
26
+ - README.rdoc
27
+ - VERSION.yml
28
+ - lib/syscmd
29
+ - lib/syscmd/popen.rb
30
+ - lib/syscmd.rb
31
+ - spec/command_exec_spec.rb
32
+ - spec/command_spec.rb
33
+ - spec/spec_helper.rb
34
+ - spec/syscmd_spec.rb
35
+ - spec/tester.rb
36
+ - LICENSE
37
+ has_rdoc: true
38
+ homepage: http://github.com/tsalzer/syscmd
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --inline-source
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: Wrapper for OS command execution
64
+ test_files: []
65
+