command_exec 0.1.3 → 0.2.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/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
|