tsalzer-syscmd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+