command_exec 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -2
- data/Gemfile.lock +42 -18
- data/README.md +707 -72
- data/RELEASE_NOTES.md +62 -0
- data/Rakefile +40 -9
- data/TODO.md +8 -2
- data/command_exec.gemspec +3 -2
- data/gemfiles/Gemfile.default +28 -0
- data/gemfiles/Gemfile.travis +16 -0
- data/gemfiles/Gemfile.travis.lock +48 -0
- data/lib/command_exec.rb +22 -2
- data/lib/command_exec/command.rb +307 -157
- data/lib/command_exec/exceptions.rb +16 -6
- data/lib/command_exec/field_helper.rb +263 -0
- data/lib/command_exec/formatter/array.rb +158 -0
- data/lib/command_exec/formatter/hash.rb +78 -0
- data/lib/command_exec/formatter/json.rb +22 -0
- data/lib/command_exec/formatter/string.rb +22 -0
- data/lib/command_exec/formatter/xml.rb +22 -0
- data/lib/command_exec/formatter/yaml.rb +22 -0
- data/lib/command_exec/logger.rb +9 -0
- data/lib/command_exec/process.rb +294 -0
- data/lib/command_exec/spec_helper_module.rb +52 -0
- data/lib/command_exec/version.rb +1 -1
- data/script/console +8 -0
- data/spec/command/command_spec.rb +413 -117
- data/spec/command/test_data/echo_test +3 -0
- data/spec/command/test_data/exit_status_test +2 -0
- data/spec/command/test_data/log_file_test +3 -0
- data/spec/command/test_data/logger_test +2 -0
- data/spec/command/test_data/not_raise_error_test +4 -0
- data/spec/command/test_data/not_throw_error_test +4 -0
- data/spec/command/test_data/output_test +6 -0
- data/spec/command/test_data/raise_error_test +6 -0
- data/spec/command/test_data/runner_open3_test +4 -0
- data/spec/command/test_data/runner_system_test +4 -0
- data/spec/command/test_data/stderr_test +4 -0
- data/spec/command/test_data/stdout_multiple_lines_test +4 -0
- data/spec/command/test_data/stdout_test +4 -0
- data/spec/command/test_data/throw_error_test +6 -0
- data/spec/command/test_data/true_test +2 -0
- data/spec/formatter/array_spec.rb +215 -0
- data/spec/formatter/hash_spec.rb +117 -0
- data/spec/formatter/json_spec.rb +21 -0
- data/spec/formatter/xml_spec.rb +33 -0
- data/spec/formatter/yaml_spec.rb +21 -0
- data/spec/process/process_spec.rb +329 -0
- data/spec/spec_helper.rb +15 -4
- metadata +79 -5
@@ -1,20 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
# Main
|
2
4
|
module CommandExec
|
3
5
|
# Classed concerning pdflatex exceptions
|
4
6
|
module Exceptions
|
5
7
|
# Class used to indicate that a command
|
6
8
|
# could not be found in file system
|
7
|
-
class CommandNotFound < RuntimeError
|
8
|
-
|
9
|
+
class CommandNotFound < RuntimeError; end
|
10
|
+
|
11
|
+
# Class used to indicate that a command
|
12
|
+
# is not flagged as executable
|
13
|
+
#
|
14
|
+
# @example Counter measure for this error
|
15
|
+
# chmod +x <executable>
|
16
|
+
class CommandNotExecutable < RuntimeError; end
|
17
|
+
|
18
|
+
# Class used to indicate that a command
|
19
|
+
# is not a file
|
20
|
+
class CommandIsNotAFile < RuntimeError; end
|
9
21
|
|
10
22
|
# Class used to indicate that a command run
|
11
23
|
# ended with a failure
|
12
|
-
class
|
13
|
-
end
|
24
|
+
class CommandExecutionFailed < RuntimeError; end
|
14
25
|
|
15
26
|
# Class used to indicate that a logfile
|
16
27
|
# could not be found in file system
|
17
|
-
class LogfileNotFound < RuntimeError
|
18
|
-
end
|
28
|
+
class LogfileNotFound < RuntimeError; end
|
19
29
|
end
|
20
30
|
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
# Shared methods for fields
|
6
|
+
module FieldHelper
|
7
|
+
|
8
|
+
# Initialize helper
|
9
|
+
#
|
10
|
+
# will be used from array and hash via super call although there's no
|
11
|
+
# inheritance.
|
12
|
+
# See for more information http://stackoverflow.com/questions/1645398/ruby-include-question
|
13
|
+
def initialize
|
14
|
+
@end_time = []
|
15
|
+
@executable = []
|
16
|
+
@log_file = []
|
17
|
+
@pid = []
|
18
|
+
@reason_for_failure = []
|
19
|
+
@return_code = []
|
20
|
+
@start_time = []
|
21
|
+
@status = []
|
22
|
+
@stderr = []
|
23
|
+
@stdout = []
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Return the available header names
|
29
|
+
#
|
30
|
+
# @return [Hash] the names of the headers
|
31
|
+
#
|
32
|
+
# * :status [String]: 'STATUS'
|
33
|
+
# * :return_code [String]: 'RETURN CODE'
|
34
|
+
# * :log_file [String]: 'LOG FILE'
|
35
|
+
# * :stderr [String]: 'STDERR'
|
36
|
+
# * :stdout [String]: 'STDOUT'
|
37
|
+
# * :pid [String]: 'PID'
|
38
|
+
# * :reason\_for\_failure [String]: 'REASON FOR FAILURE'
|
39
|
+
# * :executable [String]: 'EXECUTABLE'
|
40
|
+
#
|
41
|
+
def header_names
|
42
|
+
{
|
43
|
+
headers: {
|
44
|
+
names: {
|
45
|
+
status: 'STATUS',
|
46
|
+
return_code: 'RETURN CODE',
|
47
|
+
log_file: 'LOG FILE',
|
48
|
+
stderr: 'STDERR',
|
49
|
+
stdout: 'STDOUT',
|
50
|
+
pid: 'PID',
|
51
|
+
reason_for_failure: 'REASON FOR FAILURE',
|
52
|
+
executable: 'EXECUTABLE',
|
53
|
+
start_time: 'START TIME',
|
54
|
+
end_time: 'END TIME',
|
55
|
+
},
|
56
|
+
}
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return the available fields for output
|
61
|
+
#
|
62
|
+
# @return [Hash] the available fields with the corresponding instance
|
63
|
+
# variable
|
64
|
+
def available_fields
|
65
|
+
{
|
66
|
+
:status => @status,
|
67
|
+
:return_code => @return_code,
|
68
|
+
:stderr => @stderr,
|
69
|
+
:stdout => @stdout,
|
70
|
+
:log_file => @log_file,
|
71
|
+
:pid => @pid,
|
72
|
+
:reason_for_failure => @reason_for_failure,
|
73
|
+
:executable => @executable,
|
74
|
+
:start_time => @start_time,
|
75
|
+
:end_time => @end_time,
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return the default fields for output
|
80
|
+
#
|
81
|
+
# @return [Array] the names of the fields which should be outputted by
|
82
|
+
# default
|
83
|
+
def default_fields
|
84
|
+
[:status,
|
85
|
+
:return_code,
|
86
|
+
:stderr,
|
87
|
+
:stdout,
|
88
|
+
:log_file,
|
89
|
+
:pid,
|
90
|
+
:reason_for_failure,
|
91
|
+
:executable,
|
92
|
+
:start_time,
|
93
|
+
:end_time,
|
94
|
+
]
|
95
|
+
end
|
96
|
+
|
97
|
+
# Set the status of the command
|
98
|
+
#
|
99
|
+
# @param [String,Symbol] value
|
100
|
+
# Set the status of the command based on input.
|
101
|
+
#
|
102
|
+
# @param [Hash] options
|
103
|
+
# Options for status
|
104
|
+
#
|
105
|
+
# @option options [True,False] :color
|
106
|
+
# Should the output be colored
|
107
|
+
#
|
108
|
+
# @return [Array]
|
109
|
+
# the formatted status. It returns `OK` (in bold and green) if status is
|
110
|
+
# `:success` and `FAILED` (in bold and red) if status is `:failed`.
|
111
|
+
#
|
112
|
+
def prepare_status(value,options={})
|
113
|
+
|
114
|
+
case value.to_s
|
115
|
+
when 'success'
|
116
|
+
@status[0] = message_success(color: options[:color])
|
117
|
+
when 'failed'
|
118
|
+
@status[0] = message_failure(color: options[:color])
|
119
|
+
else
|
120
|
+
@status[0] = message_failure(color: options[:color])
|
121
|
+
end
|
122
|
+
|
123
|
+
@status
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the success message
|
127
|
+
#
|
128
|
+
# @param [Hash] options
|
129
|
+
# options
|
130
|
+
#
|
131
|
+
# @option options [True,False] :color
|
132
|
+
# should the message return in color
|
133
|
+
#
|
134
|
+
# @return [String] the message
|
135
|
+
def message_success(options={})
|
136
|
+
message = 'OK'
|
137
|
+
|
138
|
+
if options[:color]
|
139
|
+
return message.green.bold
|
140
|
+
else
|
141
|
+
return message
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the failure message
|
146
|
+
#
|
147
|
+
# @param [Hash] options
|
148
|
+
# options
|
149
|
+
#
|
150
|
+
# @option options [True,False] :color
|
151
|
+
# should the message return in color
|
152
|
+
#
|
153
|
+
# @return [String] the message
|
154
|
+
def message_failure(options)
|
155
|
+
message = 'FAILED'
|
156
|
+
|
157
|
+
if options[:color]
|
158
|
+
return message.red.bold
|
159
|
+
else
|
160
|
+
return message
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
public
|
165
|
+
|
166
|
+
# Set the content of the log file
|
167
|
+
#
|
168
|
+
# @param content [Array,String]
|
169
|
+
# The content of log file
|
170
|
+
#
|
171
|
+
# @return [Array] the content of the log file
|
172
|
+
def log_file(*content)
|
173
|
+
@log_file += content.flatten
|
174
|
+
end
|
175
|
+
|
176
|
+
# Set the return code of the command
|
177
|
+
#
|
178
|
+
# @param value [Number,String]
|
179
|
+
# Set the return code(s) of the command.
|
180
|
+
#
|
181
|
+
# @return [Array] the return code
|
182
|
+
def return_code(value)
|
183
|
+
@return_code[0] = value.to_s
|
184
|
+
|
185
|
+
@return_code
|
186
|
+
end
|
187
|
+
|
188
|
+
# Set the content of stdout
|
189
|
+
#
|
190
|
+
# @param content [Array,String]
|
191
|
+
# The content of stdout
|
192
|
+
#
|
193
|
+
# @return [Array]
|
194
|
+
def stdout(*content)
|
195
|
+
@stdout += content.flatten
|
196
|
+
end
|
197
|
+
|
198
|
+
# Set the content of stderr
|
199
|
+
#
|
200
|
+
# @param content [Array,String]
|
201
|
+
# The content of stderr
|
202
|
+
#
|
203
|
+
# @return [Array]
|
204
|
+
def stderr(*content)
|
205
|
+
@stderr += content.flatten
|
206
|
+
end
|
207
|
+
|
208
|
+
# Set the pid of the command
|
209
|
+
#
|
210
|
+
# @param value [Number,String]
|
211
|
+
# Set the pid of the command.
|
212
|
+
#
|
213
|
+
# @return [Array]
|
214
|
+
def pid(value)
|
215
|
+
@pid[0] = value.to_s
|
216
|
+
|
217
|
+
@pid
|
218
|
+
end
|
219
|
+
|
220
|
+
# Set the reason for failure
|
221
|
+
#
|
222
|
+
# @param content [Array, String]
|
223
|
+
# Set the reason for failure.
|
224
|
+
#
|
225
|
+
# @return [Array]
|
226
|
+
def reason_for_failure(*content)
|
227
|
+
@reason_for_failure += content.flatten
|
228
|
+
end
|
229
|
+
|
230
|
+
# Set the path to the executable of the command
|
231
|
+
#
|
232
|
+
# @param [String] value
|
233
|
+
# the path to the executable
|
234
|
+
#
|
235
|
+
# @return [Array]
|
236
|
+
# the executable
|
237
|
+
def executable(value)
|
238
|
+
@executable[0] = value
|
239
|
+
end
|
240
|
+
|
241
|
+
# Set the start time of command execution
|
242
|
+
#
|
243
|
+
# @param [Time] value
|
244
|
+
# the start time of command execution
|
245
|
+
#
|
246
|
+
# @return [Array]
|
247
|
+
# the start time
|
248
|
+
def start_time(value)
|
249
|
+
@start_time[0] = value
|
250
|
+
end
|
251
|
+
|
252
|
+
# Set the end time of command execution
|
253
|
+
#
|
254
|
+
# @param [Time] value
|
255
|
+
# the end time of command execution
|
256
|
+
#
|
257
|
+
# @return [Array]
|
258
|
+
# the end time
|
259
|
+
def end_time(value)
|
260
|
+
@end_time[0] = value
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
#Formatting output
|
6
|
+
module Formatter
|
7
|
+
#Style array
|
8
|
+
class Array
|
9
|
+
|
10
|
+
include FieldHelper
|
11
|
+
|
12
|
+
# @!attribute [r] output
|
13
|
+
# return the formatted output
|
14
|
+
attr_reader :output
|
15
|
+
# @!attribute [w] logger
|
16
|
+
# set the logger after object creation
|
17
|
+
attr_writer :logger
|
18
|
+
|
19
|
+
# Create new array formatter
|
20
|
+
#
|
21
|
+
# @param [Hash] options
|
22
|
+
# Options for formatter
|
23
|
+
#
|
24
|
+
# @option options [Hash] :headers
|
25
|
+
# It is used to configure how the headers will be formatted
|
26
|
+
#
|
27
|
+
# There are several sub-options:
|
28
|
+
#
|
29
|
+
# * :names [Hash]: What should be output as name for the header (filled via deep_merge and FieldHelper-Module)
|
30
|
+
# * :prefix [String]: What is placed before the header ('=' * 5)
|
31
|
+
# * :suffix [String]: What is placed after the header ('=' * 5)
|
32
|
+
# * :halign [Symbol]: How to align the header: :center [default], :left, :right
|
33
|
+
# * :show (Boolean): Should the header be shown (true)
|
34
|
+
#
|
35
|
+
# @option options [Symbol] :logger
|
36
|
+
# Logger to output information. Needs to have the same interface like
|
37
|
+
# the ruby `Logger`-class.
|
38
|
+
#
|
39
|
+
def initialize(options={})
|
40
|
+
@options = {
|
41
|
+
headers: {
|
42
|
+
names: {},
|
43
|
+
prefix: '=' * 5,
|
44
|
+
suffix: '=' * 5,
|
45
|
+
halign: :center,
|
46
|
+
show: true,
|
47
|
+
},
|
48
|
+
logger: Logger.new($stdout),
|
49
|
+
}.deep_merge(header_names.deep_merge(options))
|
50
|
+
|
51
|
+
@headers_options = @options[:headers]
|
52
|
+
@logger = @options[:logger]
|
53
|
+
|
54
|
+
super()
|
55
|
+
end
|
56
|
+
|
57
|
+
def status(value)
|
58
|
+
prepare_status(value, color: true)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Get the maximum length over all headers
|
64
|
+
#
|
65
|
+
# @return [Number] the maxium header length
|
66
|
+
def max_header_length
|
67
|
+
@max_header_length ||= @headers_options[:names].values.inject(0) { |max_length, name| max_length < name.length ? name.length : max_length }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Align header names
|
71
|
+
#
|
72
|
+
# @param [String] name
|
73
|
+
# the name which should be aligned
|
74
|
+
#
|
75
|
+
# @param max_length [Number]
|
76
|
+
# the maximum length which is used to align the name
|
77
|
+
#
|
78
|
+
# @param orientation [Symbol]
|
79
|
+
# how to align the header name
|
80
|
+
#
|
81
|
+
# @return [String] the aligned header name
|
82
|
+
def halign(name, max_length, orientation)
|
83
|
+
|
84
|
+
name = name.to_s
|
85
|
+
|
86
|
+
case orientation
|
87
|
+
when :center
|
88
|
+
name.center(max_length)
|
89
|
+
when :left
|
90
|
+
name.ljust(max_length)
|
91
|
+
when :right
|
92
|
+
name.rjust(max_length)
|
93
|
+
else
|
94
|
+
name.center(max_length)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Format header but only if given header is defined.
|
99
|
+
#
|
100
|
+
# @param [Symbol] header
|
101
|
+
# the name of the header. It has to be defined in opts[:names]
|
102
|
+
#
|
103
|
+
# @param [Hash] options
|
104
|
+
# used to change format options like `prefix`, `suffix` etc. after the
|
105
|
+
# creation of the `Formatter::Array`-object. Those options defined at the
|
106
|
+
# creation of the `Formatter`-object are default and can be overwritten
|
107
|
+
# using this `Hash`.
|
108
|
+
#
|
109
|
+
# @return [String] the formatted header
|
110
|
+
def format_header(header,options={})
|
111
|
+
opts = @headers_options.deep_merge options
|
112
|
+
|
113
|
+
output=""
|
114
|
+
unless opts[:names][header] == ""
|
115
|
+
output += "#{opts[:prefix]} " unless opts[:prefix].blank?
|
116
|
+
output += halign(opts[:names][header], max_header_length, opts[:halign])
|
117
|
+
output += " #{opts[:suffix]}" unless opts[:suffix].blank?
|
118
|
+
end
|
119
|
+
|
120
|
+
output
|
121
|
+
end
|
122
|
+
|
123
|
+
# Build the data structure for output
|
124
|
+
#
|
125
|
+
# @param [Array] fields
|
126
|
+
# which fields should be outputted
|
127
|
+
#
|
128
|
+
# @return [Array]
|
129
|
+
# the formatted output
|
130
|
+
def prepare_output(fields=[])
|
131
|
+
out = []
|
132
|
+
fields = fields.flatten
|
133
|
+
|
134
|
+
fields = default_fields if fields.blank?
|
135
|
+
|
136
|
+
fields.each do |var|
|
137
|
+
out << format_header(var,@headers_options) if @headers_options[:show] = true and available_fields.has_key?(var)
|
138
|
+
out += available_fields[var] if available_fields.has_key?(var)
|
139
|
+
end
|
140
|
+
|
141
|
+
out
|
142
|
+
end
|
143
|
+
|
144
|
+
public
|
145
|
+
|
146
|
+
# Output the prepared output
|
147
|
+
#
|
148
|
+
# @param [Array,Symbol) fields
|
149
|
+
# the fields which should be outputted
|
150
|
+
#
|
151
|
+
# @return [Array]
|
152
|
+
# the formatted output
|
153
|
+
def output(*fields)
|
154
|
+
prepare_output(fields.flatten)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|