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
@@ -0,0 +1,78 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
#Formatting output
|
6
|
+
module Formatter
|
7
|
+
#Style hash
|
8
|
+
class Hash
|
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
|
+
public
|
20
|
+
|
21
|
+
# Create new hash formatter
|
22
|
+
#
|
23
|
+
# @param [Hash] options
|
24
|
+
# Options for formatter
|
25
|
+
#
|
26
|
+
# @option options [Symbol] :logger
|
27
|
+
# Logger to output information. Needs to have the same interface like
|
28
|
+
# the ruby `Logger`-class.
|
29
|
+
def initialize(options={})
|
30
|
+
@options = {
|
31
|
+
logger: Logger.new($stdout),
|
32
|
+
}.deep_merge options
|
33
|
+
|
34
|
+
@logger = @options[:logger]
|
35
|
+
|
36
|
+
super()
|
37
|
+
end
|
38
|
+
|
39
|
+
def status(value)
|
40
|
+
prepare_status(value)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Build the data structure for output
|
46
|
+
#
|
47
|
+
# @param [Array] fields
|
48
|
+
# which fields should be outputted
|
49
|
+
#
|
50
|
+
# @return [Hash]
|
51
|
+
# the formatted output
|
52
|
+
def prepare_output(fields=[])
|
53
|
+
out = {}
|
54
|
+
|
55
|
+
fields = default_fields if fields.blank?
|
56
|
+
|
57
|
+
fields.each do |f|
|
58
|
+
out[f] = available_fields[f] if available_fields.has_key?(f)
|
59
|
+
end
|
60
|
+
|
61
|
+
out
|
62
|
+
end
|
63
|
+
|
64
|
+
public
|
65
|
+
|
66
|
+
# Output the prepared output
|
67
|
+
#
|
68
|
+
# @param [Array,Symbol) fields
|
69
|
+
# the fields which should be outputted
|
70
|
+
#
|
71
|
+
# @return [Hash]
|
72
|
+
# the formatted output
|
73
|
+
def output(*fields)
|
74
|
+
prepare_output(fields.flatten)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
#Formatting output
|
6
|
+
module Formatter
|
7
|
+
#Style as json string
|
8
|
+
class JSON < CommandExec::Formatter::Hash
|
9
|
+
|
10
|
+
# convert the prepared output to json
|
11
|
+
#
|
12
|
+
# @param [Array,Symbol) fields
|
13
|
+
# the fields which should be outputted
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
# the output formatted as json string
|
17
|
+
def output(*fields)
|
18
|
+
::JSON.generate prepare_output(fields.flatten)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
#Formatting output
|
6
|
+
module Formatter
|
7
|
+
#Style as simple string
|
8
|
+
class String < CommandExec::Formatter::Array
|
9
|
+
|
10
|
+
# convert the prepared output to single string
|
11
|
+
#
|
12
|
+
# @param [Array,Symbol) fields
|
13
|
+
# the fields which should be outputted
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
# the output formatted as simple string
|
17
|
+
def output(*fields)
|
18
|
+
prepare_output(fields.flatten).join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
#Formatting output
|
6
|
+
module Formatter
|
7
|
+
#Style as xml string
|
8
|
+
class XML < CommandExec::Formatter::Hash
|
9
|
+
|
10
|
+
# convert the prepared output to a xml string
|
11
|
+
#
|
12
|
+
# @param [Array,Symbol) fields
|
13
|
+
# the fields which should be outputted
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
# the output formatted as a xml string
|
17
|
+
def output(*fields)
|
18
|
+
XmlSimple.xml_out(prepare_output(fields.flatten), 'RootName' => 'command', 'NoAttr' => true)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
#Formatting output
|
6
|
+
module Formatter
|
7
|
+
#Style as yaml string
|
8
|
+
class YAML < CommandExec::Formatter::Hash
|
9
|
+
|
10
|
+
# convert the prepared output to a yaml string
|
11
|
+
#
|
12
|
+
# @param [Array,Symbol) fields
|
13
|
+
# the fields which should be outputted
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
# the output formatted as a xml string
|
17
|
+
def output(*fields)
|
18
|
+
Psych.dump prepare_output(fields.flatten)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,294 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#Main
|
4
|
+
module CommandExec
|
5
|
+
# The class used to save the data about
|
6
|
+
# the executed command
|
7
|
+
class Process
|
8
|
+
|
9
|
+
include FieldHelper
|
10
|
+
|
11
|
+
# @!attribute [rw] executable
|
12
|
+
# Set/Get the executable of the command
|
13
|
+
attr_accessor :executable
|
14
|
+
|
15
|
+
# @!attribute [r] status
|
16
|
+
# Get the status of the command
|
17
|
+
#
|
18
|
+
# @!attribute [r] stdout
|
19
|
+
# Get stdout of the command
|
20
|
+
#
|
21
|
+
# @!attribute [r] stderr
|
22
|
+
# Get stderr of the command
|
23
|
+
#
|
24
|
+
# @!attribute [r] reason_for_failure
|
25
|
+
# Get the reason why `command_exec` thinks a command failed
|
26
|
+
#
|
27
|
+
# @!attribute [r] return_code
|
28
|
+
# Get the exit code of the command
|
29
|
+
#
|
30
|
+
# @!attribute [r] pid
|
31
|
+
# Get the pid of the command
|
32
|
+
#
|
33
|
+
# @!attribute [r] start_time
|
34
|
+
# Return the time when the execution of the command started
|
35
|
+
#
|
36
|
+
# @!attribute [r] end_time
|
37
|
+
# Return the time when the execution of the command finished
|
38
|
+
attr_reader :status, :log_file, :stdout, :stderr, :reason_for_failure, :return_code, :pid, :start_time, :end_time
|
39
|
+
|
40
|
+
# Create a process object
|
41
|
+
#
|
42
|
+
# @param [Hash] options
|
43
|
+
# options for the process
|
44
|
+
#
|
45
|
+
# @option options [Logger] lib_logger
|
46
|
+
# The logger which is used to output information generated by the
|
47
|
+
# library. The logger which is provided needs to be compatible with api
|
48
|
+
# of the Ruby `Logger`-class.
|
49
|
+
#
|
50
|
+
# @option options [Array] stderr
|
51
|
+
# content of stderr of the process
|
52
|
+
#
|
53
|
+
# @option options [Array] stdout
|
54
|
+
# content of stdout of the process
|
55
|
+
#
|
56
|
+
# @option options [Array] log_file
|
57
|
+
# content of the log file of the process
|
58
|
+
#
|
59
|
+
# @option options [Number,String] pid
|
60
|
+
# the pid of the process
|
61
|
+
#
|
62
|
+
# @option options [Number,String] return_code
|
63
|
+
# the exit status of the process
|
64
|
+
#
|
65
|
+
# @option options [Array] reason_for_failure
|
66
|
+
# the reason for failure
|
67
|
+
#
|
68
|
+
# @option options [Symbol] status
|
69
|
+
# execution was successful, failed
|
70
|
+
def initialize(options={})
|
71
|
+
@options = {
|
72
|
+
lib_logger: Logger.new($stderr),
|
73
|
+
stderr: [],
|
74
|
+
stdout: [],
|
75
|
+
log_file: [],
|
76
|
+
pid: nil,
|
77
|
+
return_code: nil,
|
78
|
+
reason_for_failure: [],
|
79
|
+
status: :success,
|
80
|
+
executable: nil,
|
81
|
+
}.merge options
|
82
|
+
|
83
|
+
@logger = @options[:lib_logger]
|
84
|
+
|
85
|
+
@stderr = @options[:stderr]
|
86
|
+
@stdout = @options[:stdout]
|
87
|
+
@status = @options[:status]
|
88
|
+
@log_file = @options[:log_file]
|
89
|
+
@pid = @options[:pid]
|
90
|
+
@reason_for_failure = @options[:reason_for_failure]
|
91
|
+
@return_code = @options[:return_code]
|
92
|
+
@executable = @options[:executable]
|
93
|
+
|
94
|
+
@start_time = nil
|
95
|
+
@end_time = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
# Set the name of the log file
|
99
|
+
#
|
100
|
+
# @param [String] filename
|
101
|
+
# the name of the log file
|
102
|
+
def log_file=(filename=nil)
|
103
|
+
if filename.blank?
|
104
|
+
file = StringIO.new
|
105
|
+
@logger.debug "No file name for log file given. Using empty String"
|
106
|
+
else
|
107
|
+
begin
|
108
|
+
file = File.open(filename)
|
109
|
+
@logger.debug "read logfile \"#{file}\" "
|
110
|
+
rescue Errno::ENOENT
|
111
|
+
file = StringIO.new
|
112
|
+
@logger.warn "Logfile #{filename} not found!"
|
113
|
+
rescue Exception => e
|
114
|
+
file = StringIO.new
|
115
|
+
@logger.warn "An error happen while reading log_file \"#{filename}\": #{e.message}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
@log_file = file.readlines.map(&:chomp)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Set the pid of the process
|
123
|
+
#
|
124
|
+
# @param [Number,String] value
|
125
|
+
def pid=(value)
|
126
|
+
@pid = value.to_s
|
127
|
+
end
|
128
|
+
|
129
|
+
# Set the value of stdout
|
130
|
+
#
|
131
|
+
# @param [Array, String] content
|
132
|
+
# the content of stdout
|
133
|
+
def stdout=(*content)
|
134
|
+
@stdout += content.flatten
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set the value of stderr
|
138
|
+
#
|
139
|
+
# @param [Array, String] content
|
140
|
+
# the content of stderr
|
141
|
+
def stderr=(*content)
|
142
|
+
@stderr += content.flatten
|
143
|
+
end
|
144
|
+
|
145
|
+
# Set the status of command execution
|
146
|
+
#
|
147
|
+
# @param [Array, String] value
|
148
|
+
# the status
|
149
|
+
def status=(value)
|
150
|
+
case value.to_s
|
151
|
+
when 'success'
|
152
|
+
@status = :success
|
153
|
+
when 'failed'
|
154
|
+
@status = :failed
|
155
|
+
else
|
156
|
+
@status = :failed
|
157
|
+
end
|
158
|
+
|
159
|
+
@status
|
160
|
+
end
|
161
|
+
|
162
|
+
# Why the execution failed
|
163
|
+
#
|
164
|
+
# @param [String] content
|
165
|
+
# the reason for failure. When you run it mulitple times, the string is
|
166
|
+
# added at the end.
|
167
|
+
def reason_for_failure=(content)
|
168
|
+
@reason_for_failure << content.to_s
|
169
|
+
end
|
170
|
+
|
171
|
+
# Set the exit status of the command
|
172
|
+
#
|
173
|
+
# @param [Number,String] value
|
174
|
+
# the exit status of the command
|
175
|
+
def return_code=(value)
|
176
|
+
@return_code = value
|
177
|
+
end
|
178
|
+
|
179
|
+
# Set the path to the executable
|
180
|
+
#
|
181
|
+
# @param [Number,String] value
|
182
|
+
# the path to the executable of the command
|
183
|
+
def executable=(value)
|
184
|
+
@executable = value
|
185
|
+
end
|
186
|
+
|
187
|
+
# Set the start time of the command execution
|
188
|
+
#
|
189
|
+
# @param [Number,String] value
|
190
|
+
# the path to the executable of the command
|
191
|
+
def start_time=(value)
|
192
|
+
@start_time = value
|
193
|
+
end
|
194
|
+
|
195
|
+
# Set the end time of the command execution
|
196
|
+
#
|
197
|
+
# @param [Number,String] value
|
198
|
+
# the path to the executable of the command
|
199
|
+
def end_time=(value)
|
200
|
+
@end_time = value
|
201
|
+
end
|
202
|
+
|
203
|
+
def run_time
|
204
|
+
end_time - start_time
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
# Generate formatted output
|
210
|
+
#
|
211
|
+
# @param [Symbol,Array of Symbols] fields
|
212
|
+
# the field which should be part of the output
|
213
|
+
#
|
214
|
+
# @param [Formatter] formatter
|
215
|
+
# the formatter which is used to format the output
|
216
|
+
def output(*fields,formatter)
|
217
|
+
|
218
|
+
fields.flatten.each do |f|
|
219
|
+
formatter.public_send(f, available_fields[f])
|
220
|
+
end
|
221
|
+
|
222
|
+
formatter.output(fields.flatten)
|
223
|
+
end
|
224
|
+
|
225
|
+
public
|
226
|
+
|
227
|
+
# Output process data as array
|
228
|
+
#
|
229
|
+
# @param [Array of Symbols] fields
|
230
|
+
# the fields which should be outputed
|
231
|
+
#
|
232
|
+
# @param [Formatter] formatter (Formatter::Array.new)
|
233
|
+
# the formatter which is used the format the output
|
234
|
+
def to_a(fields=default_fields, formatter=Formatter::Array.new)
|
235
|
+
output(fields, formatter)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Output process data as hash
|
239
|
+
#
|
240
|
+
# @param [Array of Symbols] fields
|
241
|
+
# the fields which should be outputed
|
242
|
+
#
|
243
|
+
# @param [Formatter] formatter (Formatter::Hash.new)
|
244
|
+
# the formatter which is used the format the output
|
245
|
+
def to_h(fields=default_fields, formatter=Formatter::Hash.new)
|
246
|
+
output(fields, formatter)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Output process data as string
|
250
|
+
#
|
251
|
+
# @param [Array of Symbols] fields
|
252
|
+
# the fields which should be outputed
|
253
|
+
#
|
254
|
+
# @param [Formatter] formatter (Formatter::String.new)
|
255
|
+
# the formatter which is used the format the output
|
256
|
+
def to_s(fields=default_fields, formatter=Formatter::String.new)
|
257
|
+
output(fields, formatter)
|
258
|
+
end
|
259
|
+
|
260
|
+
# Output process data as xml
|
261
|
+
#
|
262
|
+
# @param [Array of Symbols] fields
|
263
|
+
# the fields which should be outputed
|
264
|
+
#
|
265
|
+
# @param [Formatter] formatter (Formatter::XML.new)
|
266
|
+
# the formatter which is used the format the output
|
267
|
+
def to_xml(fields=default_fields, formatter=Formatter::XML.new)
|
268
|
+
output(fields, formatter)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Output process data as json
|
272
|
+
#
|
273
|
+
# @param [Array of Symbols] fields
|
274
|
+
# the fields which should be outputed
|
275
|
+
#
|
276
|
+
# @param [Formatter] formatter (Formatter::JSON.new)
|
277
|
+
# the formatter which is used the format the output
|
278
|
+
def to_json(fields=default_fields, formatter=Formatter::JSON.new)
|
279
|
+
output(fields, formatter)
|
280
|
+
end
|
281
|
+
|
282
|
+
# Output process data as yaml
|
283
|
+
#
|
284
|
+
# @param [Array of Symbols] fields
|
285
|
+
# the fields which should be outputed
|
286
|
+
#
|
287
|
+
# @param [Formatter] formatter (Formatter::YAML.new)
|
288
|
+
# the formatter which is used the format the output
|
289
|
+
def to_yaml(fields=default_fields, formatter=Formatter::YAML.new)
|
290
|
+
output(fields, formatter)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|