command_exec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,52 @@
1
+ .$*$
2
+ .*
3
+ *.*~
4
+ *.acn
5
+ *.acr
6
+ *.alg
7
+ *.aux
8
+ *.auxlock
9
+ *.bak
10
+ *.bbl
11
+ *.blg
12
+ .bundle
13
+ coverage/
14
+ doc/yardoc/
15
+ *.dvi
16
+ *.fdb_latexmk
17
+ *.fls
18
+ *.gem
19
+ !.git*
20
+ *.glg
21
+ *.glo
22
+ *.gls
23
+ *.htm
24
+ *.html
25
+ *.idx
26
+ *.ilg
27
+ *.ind
28
+ *.ist
29
+ *.loc
30
+ *.lof
31
+ *.log
32
+ *.log
33
+ *.lol
34
+ *.lot
35
+ *.nlo
36
+ *.nls
37
+ nohup*
38
+ *.old
39
+ *.out
40
+ pkg/
41
+ pkg/*
42
+ .rvmrc
43
+ *.s??
44
+ *.s*p
45
+ *.synctex.gz*
46
+ *.tex.meta
47
+ *.toc
48
+ .yardoc
49
+ tmp/
50
+ *.pdf
51
+ !doc/*/*.pdf
52
+ *.pid
data/Gemfile ADDED
@@ -0,0 +1,24 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ruby-pdflatex.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'cucumber'
8
+ gem 'fakefs'
9
+ gem 'fuubar'
10
+ gem 'github-markup'
11
+ gem 'guard'
12
+ gem 'guard-cucumber'
13
+ gem 'guard-rspec'
14
+ gem 'guard-yard'
15
+ gem 'libnotify'
16
+ gem 'pry'
17
+ gem 'rb-inotify'
18
+ gem 'rb-readline'
19
+ gem 'redcarpet' , '~>1.17.2'
20
+ gem 'rspec'
21
+ gem 'simplecov'
22
+ gem 'terminal_multiplexer'
23
+ gem 'yard'
24
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,101 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ command_exec (0.0.1)
5
+ POpen4
6
+ colored
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ POpen4 (0.1.4)
12
+ Platform (>= 0.4.0)
13
+ open4
14
+ Platform (0.4.0)
15
+ builder (3.0.0)
16
+ coderay (1.0.5)
17
+ colored (1.2)
18
+ cucumber (1.1.9)
19
+ builder (>= 2.1.2)
20
+ diff-lcs (>= 1.1.2)
21
+ gherkin (~> 2.9.0)
22
+ json (>= 1.4.6)
23
+ term-ansicolor (>= 1.0.6)
24
+ diff-lcs (1.1.3)
25
+ fakefs (0.4.0)
26
+ ffi (1.0.11)
27
+ fuubar (1.0.0)
28
+ rspec (~> 2.0)
29
+ rspec-instafail (~> 0.2.0)
30
+ ruby-progressbar (~> 0.0.10)
31
+ gherkin (2.9.1)
32
+ json (>= 1.4.6)
33
+ github-markup (0.7.1)
34
+ guard (1.0.1)
35
+ ffi (>= 0.5.0)
36
+ thor (~> 0.14.6)
37
+ guard-cucumber (0.7.5)
38
+ cucumber (>= 0.10)
39
+ guard (>= 0.8.3)
40
+ guard-rspec (0.6.0)
41
+ guard (>= 0.10.0)
42
+ guard-yard (1.0.2)
43
+ guard (>= 0.2.2)
44
+ yard (>= 0.7.0)
45
+ highline (1.6.11)
46
+ json (1.6.5)
47
+ libnotify (0.7.2)
48
+ method_source (0.7.1)
49
+ multi_json (1.1.0)
50
+ open4 (1.3.0)
51
+ pry (0.9.8.4)
52
+ coderay (~> 1.0.5)
53
+ method_source (~> 0.7.1)
54
+ slop (>= 2.4.4, < 3)
55
+ rb-inotify (0.8.8)
56
+ ffi (>= 0.5.0)
57
+ rb-readline (0.4.2)
58
+ redcarpet (1.17.2)
59
+ rspec (2.9.0)
60
+ rspec-core (~> 2.9.0)
61
+ rspec-expectations (~> 2.9.0)
62
+ rspec-mocks (~> 2.9.0)
63
+ rspec-core (2.9.0)
64
+ rspec-expectations (2.9.0)
65
+ diff-lcs (~> 1.1.3)
66
+ rspec-instafail (0.2.2)
67
+ rspec-mocks (2.9.0)
68
+ ruby-progressbar (0.0.10)
69
+ simplecov (0.6.1)
70
+ multi_json (~> 1.0)
71
+ simplecov-html (~> 0.5.3)
72
+ simplecov-html (0.5.3)
73
+ slop (2.4.4)
74
+ term-ansicolor (1.0.7)
75
+ terminal_multiplexer (1.0.2)
76
+ highline
77
+ thor (0.14.6)
78
+ yard (0.7.5)
79
+
80
+ PLATFORMS
81
+ ruby
82
+
83
+ DEPENDENCIES
84
+ command_exec!
85
+ cucumber
86
+ fakefs
87
+ fuubar
88
+ github-markup
89
+ guard
90
+ guard-cucumber
91
+ guard-rspec
92
+ guard-yard
93
+ libnotify
94
+ pry
95
+ rb-inotify
96
+ rb-readline
97
+ redcarpet (~> 1.17.2)
98
+ rspec
99
+ simplecov
100
+ terminal_multiplexer
101
+ yard
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+ command-exec(1) -- execute shell commands with ease
2
+ ===================================================
3
+
4
+ ## DESCRIPTION
5
+
6
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "command_exec/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "command_exec"
7
+ s.version = CommandExec::VERSION
8
+ s.authors = ["Max Meyer"]
9
+ s.email = ["dev@fedux.org"]
10
+ s.homepage = ""
11
+ s.summary = %q{Helper gem to exectue arbitrary shell commands}
12
+ s.description = %q{This adds bells and whistles to ease shell command execution}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ['lib']
18
+
19
+ # specify any dependencies here; for example:
20
+ s.add_runtime_dependency 'POpen4'
21
+ s.add_runtime_dependency 'colored'
22
+ end
@@ -0,0 +1,238 @@
1
+ # encoding: utf-8
2
+
3
+ require 'popen4'
4
+ require 'colored'
5
+ require 'logger'
6
+
7
+ # Classes concerning command execution
8
+ module CommandExec
9
+ # Run commands
10
+ class Command
11
+
12
+ attr_accessor :logfile, :options , :parameter, :error_keywords
13
+ attr_reader :result, :path, :working_directory
14
+
15
+ # Create a new command to execute
16
+ #
17
+ # @param [Symbol] name name of command
18
+ # @param [optional,Hash] opts options for the command
19
+ # @option opts [String] :options options for binary
20
+ # @option opts [String] :parameter parameter for binary
21
+ # @option opts [String] :error_keywords keyword indicating an error on stdout
22
+ # @option opts [String] :working_directory working directory where the process should run in
23
+ # @option opts [String] :logfile file path to log file of process
24
+ def initialize(name,opts={})
25
+
26
+ @name = name
27
+ @opts = {
28
+ :logger => Logger.new($stderr),
29
+ :options => '',
30
+ :parameter => '',
31
+ :error_keywords => [],
32
+ :working_directory => Dir.pwd,
33
+ :logfile => '',
34
+ :debug => false,
35
+ }.update opts
36
+
37
+ @logger = @opts[:logger]
38
+ @options = @opts[:options]
39
+ @parameter = @opts[:parameter]
40
+ @path = resolve_cmd_name(name)
41
+ @error_keywords = @opts[:error_keywords]
42
+ @logfile = @opts[:logfile]
43
+
44
+ @working_directory = @opts[:working_directory]
45
+ Dir.chdir(working_directory)
46
+
47
+ end
48
+
49
+ private
50
+
51
+ # Find utility path
52
+ #
53
+ # @param [Symbol] name Name of utility
54
+ # @return [Path] Returns the path to the binary of the binary
55
+ def resolve_cmd_name(name)
56
+ path=''
57
+ path = %x[which #{name} 2>/dev/null].chomp
58
+
59
+ if path.empty?
60
+ @logger.fatal("Command not found #{name}")
61
+ raise Exceptions::CommandNotFound
62
+ end
63
+
64
+ return path
65
+ end
66
+
67
+ # Build string to execute command
68
+ #
69
+ # @return [String] Returns to whole command with parameters and options
70
+ def build_cmd_string
71
+ cmd = ''
72
+ cmd += path
73
+ cmd += options.empty? ? "" : " #{options}"
74
+ cmd += parameter.empty? ? "" : " #{parameter}"
75
+
76
+ return cmd
77
+ end
78
+
79
+ # Checks for errors
80
+ #
81
+ # @raise [String]
82
+
83
+ public
84
+
85
+ # Output the textual representation of a command
86
+ #
87
+ # @return [String] command in text form
88
+ def to_txt
89
+ return build_cmd_string
90
+ end
91
+
92
+ # Run the program
93
+ #
94
+ def run
95
+
96
+ _stdout = ''
97
+ _stderr = ''
98
+
99
+ status = POpen4::popen4(build_cmd_string) do |stdout, stderr, stdin, pid|
100
+ _stdout = stdout.read.strip
101
+ _stderr = stderr.read.strip
102
+ end
103
+
104
+
105
+ error_in_stdout_found = error_in_string_found?(error_keywords,_stdout)
106
+ @result = run_successful?( status.success? , error_in_stdout_found )
107
+
108
+ if @result == false
109
+ msg = message(
110
+ @result,
111
+ help_output(
112
+ {
113
+ :error_in_exec => not(status.success?),
114
+ :error_in_stdout => error_in_stdout_found
115
+ }, {
116
+ :stdout => StringIO.new(_stdout),
117
+ :stderr => StringIO.new(_stderr),
118
+ :logfile => read_logfile(logfile),
119
+ }
120
+ )
121
+ )
122
+ else
123
+ msg = message(@result)
124
+ end
125
+
126
+ @logger.info "#{@name.to_s}: #{msg}"
127
+
128
+ return @result
129
+
130
+ end
131
+
132
+ # Read the content of the logfile
133
+ #
134
+ # @param [Path] file path to logfile
135
+ # @param [Integer num_of_lines the number of lines which should be read -- e.g. 30 lines = -30
136
+ def read_logfile(file, num_of_lines=-30)
137
+ content = StringIO.new
138
+
139
+ unless file.empty?
140
+ begin
141
+ content << File.readlines(logfile)[num_of_lines..-1].join("")
142
+ rescue Errno::ENOENT
143
+ @logger.warn "Warning: logfile not found!"
144
+ end
145
+ end
146
+
147
+ return content
148
+ end
149
+
150
+ # Decide if a program run was successful
151
+ #
152
+ # @return [Boolean] Returns the decision
153
+ def run_successful?(success,error_in_stdout)
154
+ if success == false or error_in_stdout == true
155
+ return false
156
+ else
157
+ return true
158
+ end
159
+ end
160
+
161
+ # Decide which output to return to the user
162
+ # to help him with debugging
163
+ #
164
+ # @return [Array] Returns lines of log/stdout/stderr
165
+ def help_output(error_indicators={},output={})
166
+ error_in_exec = error_indicators[:error_in_exec]
167
+ error_in_stdout = error_indicators[:error_in_stdout]
168
+
169
+ logfile = output[:logfile].string
170
+ stdout = output[:stdout].string
171
+ stderr = output[:stderr].string
172
+
173
+ result = []
174
+
175
+ if error_in_exec == true
176
+ result << '================== LOGFILE ================== '
177
+ result << logfile if logfile.empty? == false
178
+ result << '================== STDOUT ================== '
179
+ result << stdout if stdout.empty? == false
180
+ result << '================== STDERR ================== '
181
+ result << stderr if stderr.empty? == false
182
+ elsif error_in_stdout == true
183
+ result << '================== STDOUT ================== '
184
+ result << stdout
185
+ end
186
+
187
+ return result
188
+
189
+ end
190
+
191
+ # Find error in stdout
192
+ #
193
+ # @return [Boolean] Returns true if it finds an error
194
+ def error_in_string_found? (keywords=[], string )
195
+ return false if keywords.empty? or not keywords.is_a? Array
196
+ return false if string.nil? or not string.is_a? String
197
+
198
+ error_found = false
199
+ keywords.each do |word|
200
+ if string.include? word
201
+ error_found = true
202
+ break
203
+ end
204
+ end
205
+
206
+ return error_found
207
+
208
+ end
209
+
210
+ # Generate the message which is return to the user
211
+ #
212
+ # @param [Boolean] run_successful true if a positive message should be returned
213
+ # @param [Array] msg Message which should be returned
214
+ def message(run_successful, *msg)
215
+
216
+ message = []
217
+ if run_successful
218
+ message << 'OK'.green.bold
219
+ else
220
+ message << 'FAILED'.red.bold
221
+ message.concat msg.flatten
222
+ end
223
+
224
+ return message.join("\n")
225
+ end
226
+
227
+ # Constructur to initiate a new command and run it later
228
+ #
229
+ # @see #initialize
230
+ def Command.execute(name,opts={})
231
+ command = new(name,opts)
232
+ command.run
233
+
234
+ return command
235
+ end
236
+
237
+ end
238
+ end
@@ -0,0 +1,20 @@
1
+ # Main
2
+ module CommandExec
3
+ # Classed concerning pdflatex exceptions
4
+ module Exceptions
5
+ # Class used to indicate that a command
6
+ # could not be found in file system
7
+ class CommandNotFound < RuntimeError
8
+ end
9
+
10
+ # Class used to indicate that a command run
11
+ # ended with a failure
12
+ class ExecuteCommandFailed < RuntimeError
13
+ end
14
+
15
+ # Class used to indicate that a logfile
16
+ # could not be found in file system
17
+ class LogfileNotFound < RuntimeError
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module CommandExec
2
+ module SpecHelper
3
+ def capture_stderr(&block)
4
+ previous_stderr, $stderr = $stderr, StringIO.new
5
+ block.call
6
+ return $stderr.string
7
+ ensure
8
+ $stderr = previous_stderr
9
+ end
10
+
11
+ def capture_stdout(&block)
12
+ previous_stdout, $stdout = $stdout, StringIO.new
13
+ block.call
14
+ return $stdout.string
15
+ ensure
16
+ $stdout = previous_stdout
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module CommandExec
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'command_exec/version'
2
+ require 'command_exec/exceptions'
3
+ require 'command_exec/command'
4
+
5
+ module CommandExec
6
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ describe Command do
4
+ let(:logger) {Logger.new('/dev/null')}
5
+ #let(:logger) {Logger.new($stdout)}
6
+ let(:debug) {false}
7
+ #let(:debug) {true}
8
+ let(:command) { Command.new(:echo , :debug=>debug, :logger => logger, :parameter => "hello world" , :error_keywords => %q[abc def], :working_directory => '/tmp' ) }
9
+
10
+
11
+ it "has a path" do
12
+ command.path.should == '/bin/echo'
13
+ end
14
+
15
+ it "has parameter" do
16
+ command.parameter.should == 'hello world'
17
+ end
18
+
19
+ it "has options" do
20
+ command.options.should == ''
21
+ end
22
+
23
+ it "offers the possibility to change the working directory of the process" do
24
+ command.working_directory.should == '/tmp'
25
+ Dir.pwd.should == '/tmp'
26
+ end
27
+
28
+ it "has special keywords indicating errors in stdout" do
29
+ command.error_keywords.should == %q[abc def]
30
+ end
31
+
32
+ it "can be used to construct a command string, which can be executed" do
33
+ command = Command.new(:pdflatex, :debug => debug, :logger => logger, :parameter => "index.tex blub.tex", :options => "-a -b")
34
+ command.send(:build_cmd_string).should == "/usr/bin/pdflatex -a -b index.tex blub.tex"
35
+ end
36
+
37
+ it "runs programms" do
38
+ command.run
39
+ command.result.should == true
40
+ end
41
+
42
+ it "returns the textual rep of a command" do
43
+ command.to_txt.should == '/bin/echo hello world'
44
+ end
45
+
46
+ it "execute existing programs" do
47
+ command = Command.execute(:echo, :debug => debug, :logger => logger ,:parameter => "index.tex blub.tex", :options => "-- -a -b")
48
+ command.result.should == true
49
+ end
50
+
51
+ it "does not execute non-existing programs" do
52
+ command = Command.execute(:grep, :debug => debug, :logger => logger, :parameter => "index.tex blub.tex", :options => "-- -a -b")
53
+ command.result.should == false
54
+ end
55
+
56
+ it "checks if errors have happend during execution" do
57
+ lambda { Command.new(:echo1, :debug => debug, :logger => logger, :parameter => "index.tex blub.tex", :options => "-- -a -b") }.should raise_error CommandNotFound
58
+ end
59
+
60
+ it "decides which output should be returned to the user" do
61
+ logfile = StringIO.new
62
+ logfile << 'Error in ... found'
63
+
64
+ stderr = StringIO.new
65
+ stderr << 'Error found'
66
+
67
+ stdout = StringIO.new
68
+ stdout << 'Error found'
69
+
70
+ #result = command.send(:help_logger)({ :error_in_exec => true , :error_in_stdout => false} , { :logfile => logfile, :stderr => stderr , :stdout => stdout })
71
+ result = command.send(:help_output, { :error_in_exec => true , :error_in_stdout => false} , { :logfile => logfile, :stderr => stderr , :stdout => stdout })
72
+ result.should == ["================== LOGFILE ================== ", "Error in ... found", "================== STDOUT ================== ", "Error found", "================== STDERR ================== ", "Error found"]
73
+
74
+ result = command.send(:help_output, { :error_in_exec => false , :error_in_stdout => true} , { :logfile => logfile, :stderr => stderr , :stdout => stdout })
75
+ result.should == ["================== STDOUT ================== ", "Error found"]
76
+
77
+ result = command.send(:help_output, { :error_in_exec => true , :error_in_stdout => true} , { :logfile => logfile, :stderr => stderr , :stdout => stdout })
78
+ result.should == ["================== LOGFILE ================== ", "Error in ... found", "================== STDOUT ================== ", "Error found", "================== STDERR ================== ", "Error found"]
79
+
80
+
81
+ result = command.send(:help_output, { :error_in_exec => false , :error_in_stdout => false} , { :logfile => logfile, :stderr => stderr , :stdout => stdout })
82
+ result.should == []
83
+
84
+ end
85
+
86
+ it "finds errors in stdout" do
87
+ command.send(:error_in_string_found?, ['error'] , 'long string witherror inside' ).should == true
88
+ command.send(:error_in_string_found?, ['long', 'inside'] , 'long string witherror inside' ).should == true
89
+ command.send(:error_in_string_found?, ['error'] , 'long string with erro inside' ).should == false
90
+ end
91
+
92
+ it "output a message" do
93
+ command.send(:message, false, 'Hello_world').should == "\e[1m\e[31mFAILED\e[0m\e[0m\nHello_world"
94
+ command.send(:message, true, 'Hello_world').should == "\e[1m\e[32mOK\e[0m\e[0m"
95
+ command.send(:message, true ).should == "\e[1m\e[32mOK\e[0m\e[0m"
96
+ end
97
+
98
+
99
+
100
+ end
@@ -0,0 +1,19 @@
1
+ #encoding: utf-8
2
+ $LOAD_PATH << File.expand_path('../lib' , File.dirname(__FILE__))
3
+
4
+ require 'pry'
5
+ require 'stringio'
6
+
7
+ require 'simplecov'
8
+ SimpleCov.start
9
+
10
+ require 'command_exec'
11
+ require 'command_exec/spec_helper_module'
12
+
13
+ include CommandExec
14
+ include CommandExec::Exceptions
15
+
16
+ RSpec.configure do |c|
17
+ c.include CommandExec::SpecHelper
18
+ end
19
+
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: command_exec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Max Meyer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-21 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: POpen4
16
+ requirement: &21841820 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *21841820
25
+ - !ruby/object:Gem::Dependency
26
+ name: colored
27
+ requirement: &21889840 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *21889840
36
+ description: This adds bells and whistles to ease shell command execution
37
+ email:
38
+ - dev@fedux.org
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - Gemfile
45
+ - Gemfile.lock
46
+ - Guardfile
47
+ - README.md
48
+ - Rakefile
49
+ - command_exec.gemspec
50
+ - lib/command_exec.rb
51
+ - lib/command_exec/command.rb
52
+ - lib/command_exec/exceptions.rb
53
+ - lib/command_exec/spec_helper_module.rb
54
+ - lib/command_exec/version.rb
55
+ - spec/command/command_spec.rb
56
+ - spec/spec_helper.rb
57
+ homepage: ''
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.17
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Helper gem to exectue arbitrary shell commands
81
+ test_files:
82
+ - spec/command/command_spec.rb
83
+ - spec/spec_helper.rb
84
+ has_rdoc: