patir 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.txt +340 -0
- data/History.txt +8 -0
- data/Manifest.txt +12 -0
- data/README.txt +88 -0
- data/Rakefile +18 -0
- data/lib/patir/base.rb +46 -0
- data/lib/patir/command.rb +486 -0
- data/lib/patir/configuration.rb +84 -0
- data/test/samples/empty.cfg +0 -0
- data/test/test_patir_base.rb +26 -0
- data/test/test_patir_command.rb +261 -0
- data/test/test_patir_configuration.rb +19 -0
- metadata +76 -0
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__),"lib")
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require 'patir/base'
|
6
|
+
|
7
|
+
Hoe.new('patir', "#{Patir::VERSION_MAJOR}.#{Patir::VERSION_MINOR}") do |p|
|
8
|
+
p.author = "Vassilis Rizopoulos"
|
9
|
+
p.email = "riva@braveworld.net"
|
10
|
+
p.summary = 'patir (Project Automation Tools in Ruby) provides libraries for use in automation tools'
|
11
|
+
p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
12
|
+
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
13
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
14
|
+
p.rubyforge_name="patir"
|
15
|
+
p.extra_deps<<['systemu']
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=Ruby
|
data/lib/patir/base.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# Copyright (c) 2007 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
|
3
|
+
#This is the base module of the Patir system. It contains some usefull helper methods used by all child projects.
|
4
|
+
module Patir
|
5
|
+
require 'logger'
|
6
|
+
#Version information
|
7
|
+
VERSION_MAJOR="0"
|
8
|
+
VERSION_MINOR="2"
|
9
|
+
VERSION="#{VERSION_MAJOR}.#{VERSION_MINOR}"
|
10
|
+
#Error thrown usually in initialize methods when missing required parameters
|
11
|
+
#from the initialization hash.
|
12
|
+
class ParameterException<RuntimeError
|
13
|
+
end
|
14
|
+
#creates a drb service URL from an ip and a port number
|
15
|
+
def self.drb_service ip,port
|
16
|
+
return "druby://#{ip}:#{port}"
|
17
|
+
end
|
18
|
+
#Just making Logger usage easier
|
19
|
+
#
|
20
|
+
#This is for use on top level scripts.
|
21
|
+
#
|
22
|
+
#It creates a logger just as we want it.
|
23
|
+
#
|
24
|
+
#mode can be
|
25
|
+
#
|
26
|
+
#:mute to set the level to FATAL
|
27
|
+
#
|
28
|
+
#:silent to set the level to WARN
|
29
|
+
#
|
30
|
+
#:debug to set the level to DEBUG. Debug is set also if $DEBUG is true.
|
31
|
+
#
|
32
|
+
#The default logger level is INFO
|
33
|
+
def self.setup_logger(filename=nil,mode=nil)
|
34
|
+
if filename
|
35
|
+
logger=Logger.new(filename)
|
36
|
+
else
|
37
|
+
logger=Logger.new(STDOUT)
|
38
|
+
end
|
39
|
+
logger.level=Logger::INFO
|
40
|
+
logger.level=Logger::FATAL if mode==:mute
|
41
|
+
logger.level=Logger::WARN if mode==:silent
|
42
|
+
logger.level=Logger::DEBUG if mode==:debug || $DEBUG
|
43
|
+
logger.datetime_format="%Y%m%d %H:%M:%S"
|
44
|
+
return logger
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,486 @@
|
|
1
|
+
# Copyright (c) 2007 Vassilis Rizopoulos. All rights reserved.
|
2
|
+
require 'observer'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'systemu'
|
6
|
+
require 'patir/base'
|
7
|
+
|
8
|
+
module Patir
|
9
|
+
#This module defines the interface for a Command object.
|
10
|
+
#
|
11
|
+
#It more or less serves the purpose of documenting the interface/contract expected
|
12
|
+
#by a class that executes commands and returns their output and exit status.
|
13
|
+
#
|
14
|
+
#There is also that bit of functionality that facilitates grouping multiple commands into command sequences
|
15
|
+
#
|
16
|
+
#The various methods initialize member variables with meaningful values where needed.
|
17
|
+
#
|
18
|
+
#Using the contract means implementing the Command#run method. This method should then set
|
19
|
+
#the output, exec_time and status values according to the implementation.
|
20
|
+
#
|
21
|
+
#Take a look at ShellCommand and RubyCommand for a couple of practical examples.
|
22
|
+
#
|
23
|
+
#It is a good idea to rescue all exceptions. You can then set error to return the exception message.
|
24
|
+
module Command
|
25
|
+
attr_writer :output, :name, :exec_time,:error,:status
|
26
|
+
attr_accessor :number,:strategy
|
27
|
+
#returns the commands alias/name
|
28
|
+
def name
|
29
|
+
#initialize nil values to something meaningful
|
30
|
+
@name||=""
|
31
|
+
return @name
|
32
|
+
end
|
33
|
+
#returns the output of the command
|
34
|
+
def output
|
35
|
+
#initialize nil values to something meaningful
|
36
|
+
@output||=""
|
37
|
+
return @output
|
38
|
+
end
|
39
|
+
#returns the error output for the command
|
40
|
+
def error
|
41
|
+
#initialize nil values to something meaningful
|
42
|
+
@error||=""
|
43
|
+
return @error
|
44
|
+
end
|
45
|
+
#returns the execution time (duration) for the command
|
46
|
+
def exec_time
|
47
|
+
#initialize nil values to something meaningful
|
48
|
+
@exec_time||=0
|
49
|
+
return @exec_time
|
50
|
+
end
|
51
|
+
#returns true if the command has finished succesfully
|
52
|
+
def success?
|
53
|
+
return true if self.status==:success
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
#returns true if the command has been executed
|
57
|
+
def run?
|
58
|
+
#use the accessor, because it initializes nil values
|
59
|
+
return false if self.status==:not_executed
|
60
|
+
return true
|
61
|
+
end
|
62
|
+
#executes the command and returns the status of the command.
|
63
|
+
#
|
64
|
+
#overwrite this method in classes that include Command
|
65
|
+
def run
|
66
|
+
@status=:success
|
67
|
+
return self.status
|
68
|
+
end
|
69
|
+
#clears the status and output of the command.
|
70
|
+
#
|
71
|
+
#Call this if you want to pretend that it was never executed
|
72
|
+
def reset
|
73
|
+
@exec_time=0
|
74
|
+
@output=""
|
75
|
+
@error=""
|
76
|
+
@status=:not_executed
|
77
|
+
end
|
78
|
+
#returns false if the command has not been run
|
79
|
+
def executed?
|
80
|
+
return false if self.status==:not_executed
|
81
|
+
return true
|
82
|
+
end
|
83
|
+
#returns the command status.
|
84
|
+
#
|
85
|
+
#valid stati are
|
86
|
+
#
|
87
|
+
#:not_executed when the command was not run
|
88
|
+
#
|
89
|
+
#:success when the command has finished succesfully
|
90
|
+
#
|
91
|
+
#:error when the command has an error
|
92
|
+
#
|
93
|
+
#:warning when the command finished without errors, but there where warnings
|
94
|
+
def status
|
95
|
+
#initialize nil values to something meaningful
|
96
|
+
@status||=:not_executed
|
97
|
+
return @status
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
#This class wraps the http://codeforpeople.com/lib/ruby/systemu/ as a Command duck.
|
102
|
+
#
|
103
|
+
#It allows for execution of any shell command.
|
104
|
+
#
|
105
|
+
#Accepted keys are
|
106
|
+
#
|
107
|
+
#:cmd should be the shell command to execute (required - ParameterException will be raised).
|
108
|
+
#
|
109
|
+
#:working_directory for specifying the working directory (default is '.') and :name for assigning a name to the command (default is "").
|
110
|
+
#
|
111
|
+
class ShellCommand
|
112
|
+
include Command
|
113
|
+
#The constructor will throw CommandError if :cmd is missing.
|
114
|
+
#
|
115
|
+
#CommandError will also be thrown if :working_directory does not exist.
|
116
|
+
def initialize *args
|
117
|
+
params=args[0] if args
|
118
|
+
raise ParameterException,"No ShellCommand parameters defined" unless params
|
119
|
+
@name=params[:name]
|
120
|
+
@working_directory=params[:working_directory]
|
121
|
+
#initialize the working directory value. This actually means ./
|
122
|
+
@working_directory||="."
|
123
|
+
#we need a command line :)
|
124
|
+
raise ParameterException,"No :command defined" unless params[:cmd]
|
125
|
+
@command=params[:cmd]
|
126
|
+
@status=:not_executed
|
127
|
+
end
|
128
|
+
|
129
|
+
#Executes the shell command and returns the status
|
130
|
+
def run
|
131
|
+
start_time=Time.now
|
132
|
+
super
|
133
|
+
#create the working directory if it does not exist
|
134
|
+
FileUtils::mkdir_p(@working_directory) unless File.exists?(@working_directory)
|
135
|
+
#create the actual command, run it, grab stderr and stdout and set output,error, status and execution time
|
136
|
+
status, @output, @error = systemu(@command,:cwd=>@working_directory)
|
137
|
+
#lets get the status how we want it
|
138
|
+
if status.exited?
|
139
|
+
if status.exitstatus==0
|
140
|
+
@status=:success
|
141
|
+
else
|
142
|
+
@status=:error
|
143
|
+
end
|
144
|
+
else
|
145
|
+
@status=:warning
|
146
|
+
end
|
147
|
+
#set the time it took us
|
148
|
+
@exec_time=Time.now-start_time
|
149
|
+
return @status
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_s
|
153
|
+
return "#{@name}: #{@command} in #{File.expand_path(@working_directory)}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
#CommandSequence describes a set of commands to be executed in sequence.
|
158
|
+
#
|
159
|
+
#Each instance of CommandSequence contains a set of Patir::Command instances, which are the steps to perform.
|
160
|
+
#
|
161
|
+
#The steps are executed in the sequence they are added. A CommandSequence can terminate immediately on step failure or it can continue. It will still be marked as failed as long as a single step fails.
|
162
|
+
#
|
163
|
+
#Access to the CommandSequence status is achieved using the Observer pattern.
|
164
|
+
#
|
165
|
+
#The :sequence_status message contains the status of the sequence, an instance of the class CommandSequenceStatus.
|
166
|
+
#
|
167
|
+
#CommandSequence is designed to be reusable, so it does not correspond to a single sequence run, rather it corresponds to
|
168
|
+
#the currently active run. Calling reset, or run will discard the old state and create a new sequence 'instance' and status.
|
169
|
+
#
|
170
|
+
#No threads are spawned by CommandSequence (that doesn't mean the commands cannot, but it is not advisable).
|
171
|
+
class CommandSequence
|
172
|
+
include Observable
|
173
|
+
attr_reader :name,:state,:steps
|
174
|
+
attr_reader :sequence_runner
|
175
|
+
attr_reader :sequence_id
|
176
|
+
|
177
|
+
def initialize name,sequence_runner=""
|
178
|
+
@name=name
|
179
|
+
@steps||=Array.new
|
180
|
+
@sequence_runner=sequence_runner
|
181
|
+
#intialize the status for the currently active build (not executed)
|
182
|
+
reset
|
183
|
+
end
|
184
|
+
|
185
|
+
#sets the sequence runner attribute updating status
|
186
|
+
def sequence_runner=name
|
187
|
+
@sequence_runner=name
|
188
|
+
@state.sequence_runner=name
|
189
|
+
end
|
190
|
+
|
191
|
+
#sets the sequence id attribute updating status
|
192
|
+
def sequence_id=name
|
193
|
+
@sequence_id=name
|
194
|
+
@state.sequence_id=name
|
195
|
+
end
|
196
|
+
#Executes the CommandSequence.
|
197
|
+
#
|
198
|
+
#Will run all step instances in sequence observing the exit strategies on warning/failures
|
199
|
+
def run
|
200
|
+
#set the start time
|
201
|
+
@state.start_time=Time.now
|
202
|
+
#reset the stop time
|
203
|
+
@state.stop_time=nil
|
204
|
+
#we started running, lets tell the world
|
205
|
+
@state.status=:running
|
206
|
+
notify(:sequence_status=>@state)
|
207
|
+
#we are optimistic
|
208
|
+
running_status=:success
|
209
|
+
#but not that much
|
210
|
+
running_status=:warning if @steps.empty?
|
211
|
+
#execute the steps in sequence
|
212
|
+
@steps.each do |step|
|
213
|
+
#the step is running, tell the world
|
214
|
+
@state.step=step
|
215
|
+
step.status=:running
|
216
|
+
notify(:sequence_status=>@state)
|
217
|
+
#run it, get the result and notify
|
218
|
+
result=step.run
|
219
|
+
@state.step=step
|
220
|
+
step.status=:running
|
221
|
+
notify(:sequence_status=>@state)
|
222
|
+
#evaluate the results' effect on execution status at the end
|
223
|
+
case result
|
224
|
+
when :success
|
225
|
+
#everything is fine, continue
|
226
|
+
when :error
|
227
|
+
#this will be the final status
|
228
|
+
running_status=:error
|
229
|
+
#stop if we fail on error
|
230
|
+
if :fail_on_error==step.strategy
|
231
|
+
@state.status=:error
|
232
|
+
break
|
233
|
+
end
|
234
|
+
when :warning
|
235
|
+
#a previous failure overrides a warning
|
236
|
+
running_status=:warning unless :error==running_status
|
237
|
+
#escalate this to a failure if the strategy says so
|
238
|
+
running_status=:error if :flunk_on_warning==step.strategy
|
239
|
+
#stop if we fail on warning
|
240
|
+
if :fail_on_warning==step.strategy
|
241
|
+
@state.status=:error
|
242
|
+
break
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
#we finished
|
247
|
+
@state.stop_time=Time.now
|
248
|
+
@state.status=running_status
|
249
|
+
notify(:sequence_status=>@state)
|
250
|
+
end
|
251
|
+
#Adds a step to the CommandSequence using the given exit strategy.
|
252
|
+
#
|
253
|
+
#Steps are always added at the end of the build sequence. A step should quack like a Patir::Command.
|
254
|
+
#
|
255
|
+
#Valid exit strategies are
|
256
|
+
# :fail_on_error - CommandSequence terminates on failure of this step
|
257
|
+
# :flunk_on_error - CommandSequence is flagged as failed but continues to the next step
|
258
|
+
# :fail_on_warning - CommandSequence terminates on warnings in this step
|
259
|
+
# :flunk_on_warning - CommandSequence is flagged as failed on warning in this step
|
260
|
+
def add_step step,exit_strategy=:fail_on_error
|
261
|
+
#duplicate the command
|
262
|
+
bstep=step.dup
|
263
|
+
#reset it
|
264
|
+
bstep.reset
|
265
|
+
#set the extended attributes
|
266
|
+
bstep.number=@steps.size
|
267
|
+
bstep.strategy=exit_strategy
|
268
|
+
#add it to the lot
|
269
|
+
@steps<<bstep
|
270
|
+
#add it to status as well
|
271
|
+
@state.step=bstep
|
272
|
+
notify(:sequence_status=>@state)
|
273
|
+
end
|
274
|
+
|
275
|
+
#Resets the status. This will set :not_executed status,
|
276
|
+
#and set the start and end times to nil.
|
277
|
+
def reset
|
278
|
+
#reset all the steps (stati and execution times)
|
279
|
+
@steps.each{|step| step.reset}
|
280
|
+
#reset the status
|
281
|
+
@state=CommandSequenceStatus.new(@name)
|
282
|
+
@steps.each{|step| @state.step=step}
|
283
|
+
@state.start_time=Time.now
|
284
|
+
@state.stop_time=nil
|
285
|
+
@state.sequence_runner=@sequence_runner
|
286
|
+
#tell the world
|
287
|
+
notify(:sequence_status=>@state)
|
288
|
+
end
|
289
|
+
|
290
|
+
#Returns true if the sequence has finished executing
|
291
|
+
def completed?
|
292
|
+
return @state.completed?
|
293
|
+
end
|
294
|
+
|
295
|
+
def to_s
|
296
|
+
"#{sequence_id}:#{:name} on #{@sequence_runner}, #{@steps.size} steps"
|
297
|
+
end
|
298
|
+
private
|
299
|
+
#observer notification
|
300
|
+
def notify *params
|
301
|
+
changed
|
302
|
+
notify_observers(*params)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
#CommandSequenceStatus represents the status of a CommandSequence, including the status of all the steps for this sequence.
|
307
|
+
#
|
308
|
+
#In order to extract the status from steps, classes should quack to the rythm of Command. CommandSequenceStatus does this, so you can nest Stati
|
309
|
+
#
|
310
|
+
#The status of an action sequence is :not_executed, :running, :success, :warning or :error and represents the overall status
|
311
|
+
#
|
312
|
+
#:not_executed is set when all steps are :not_executed
|
313
|
+
#
|
314
|
+
#:running is set while the sequence is running.
|
315
|
+
#
|
316
|
+
#Upon completion or interruption one of :success, :error or :warning will be set.
|
317
|
+
#
|
318
|
+
#:success is set when all steps are succesfull.
|
319
|
+
#
|
320
|
+
#:warning is set when at least one step generates warnings and there are no failures.
|
321
|
+
#
|
322
|
+
#:error is set when after execution at least one step has the :error status
|
323
|
+
class CommandSequenceStatus
|
324
|
+
attr_accessor :start_time,:stop_time,:sequence_runner,:sequence_name,:status,:step_states,:sequence_id
|
325
|
+
#You can pass an array of Commands to initialize CommandSequenceStatus
|
326
|
+
def initialize sequence_name,steps=nil
|
327
|
+
@sequence_name=sequence_name
|
328
|
+
@sequence_runner=""
|
329
|
+
@sequence_id=nil
|
330
|
+
@step_states||=Hash.new
|
331
|
+
#not run yet
|
332
|
+
@status=:not_executed
|
333
|
+
#translate the array of steps as we need it in number=>state form
|
334
|
+
steps.each{|step| self.step=step } if steps
|
335
|
+
@start_time=Time.now
|
336
|
+
end
|
337
|
+
def running?
|
338
|
+
return true if :running==@status
|
339
|
+
return false
|
340
|
+
end
|
341
|
+
#true is returned when all steps were succesfull.
|
342
|
+
def success?
|
343
|
+
return true if :success==@status
|
344
|
+
return false
|
345
|
+
end
|
346
|
+
|
347
|
+
#A sequence is considered completed when:
|
348
|
+
#
|
349
|
+
#a step has errors and the :fail_on_error strategy is used
|
350
|
+
#
|
351
|
+
#a step has warnings and the :fail_on_warning strategy is used
|
352
|
+
#
|
353
|
+
#in all other cases if any steps have :not_executed or :running status
|
354
|
+
def completed?
|
355
|
+
#this saves us iterating once+1 when no execution took place
|
356
|
+
return false if !self.executed?
|
357
|
+
@step_states.each do |state|
|
358
|
+
return true if state[1][:status]==:error && state[1][:strategy]==:fail_on_error
|
359
|
+
return true if state[1][:status]==:warning && state[1][:strategy]==:fail_on_warning
|
360
|
+
end
|
361
|
+
@step_states.each{|state| return false if state[1][:status]==:not_executed || state[1][:status]==:running }
|
362
|
+
return true
|
363
|
+
end
|
364
|
+
#A nil means there is no step with that number
|
365
|
+
def step_state number
|
366
|
+
s=@step_states[number] if @step_states[number]
|
367
|
+
return s
|
368
|
+
end
|
369
|
+
#Adds a step to the state. The step state is inferred from the Command instance __step__
|
370
|
+
def step=step
|
371
|
+
@step_states[step.number]={:name=>step.name,
|
372
|
+
:status=>step.status,
|
373
|
+
:output=>step.output,
|
374
|
+
:duration=>step.exec_time,
|
375
|
+
:error=>step.error,
|
376
|
+
:strategy=>step.strategy}
|
377
|
+
#this way we don't have to compare all the step states we always get the worst last stable state
|
378
|
+
#:not_executed<:success<:warning<:success
|
379
|
+
@previous_status=@status unless @status==:running
|
380
|
+
case step.status
|
381
|
+
when :running
|
382
|
+
@status=:running
|
383
|
+
when :warning
|
384
|
+
@status=:warning unless @status==:error
|
385
|
+
@status=:error if @previous_status==:error
|
386
|
+
when :error
|
387
|
+
@status=:error
|
388
|
+
when :success
|
389
|
+
@status=:success unless @status==:error || @status==:warning
|
390
|
+
@status=:warning if @previous_status==:warning
|
391
|
+
@status=:error if @previous_status==:error
|
392
|
+
when :not_executed
|
393
|
+
@status=@previous_status
|
394
|
+
end
|
395
|
+
end
|
396
|
+
#produces a brief text summary for this status
|
397
|
+
def summary
|
398
|
+
sum=""
|
399
|
+
sum<<"#{@sequence_id}:" if @sequence_id
|
400
|
+
sum<<"#{@sequence_name}. " unless @sequence_name.empty?
|
401
|
+
sum<<"Status - #{@status}"
|
402
|
+
if !@step_states.empty?
|
403
|
+
sum<<".States #{@step_states.size}\nStep status summary:"
|
404
|
+
if :not_executed!=@status
|
405
|
+
@step_states.each do |number,state|
|
406
|
+
sum<<"\n\t#{number}:'#{state[:name]}' - #{state[:status]}"
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
return sum
|
411
|
+
end
|
412
|
+
def to_s
|
413
|
+
"'#{sequence_id}':'#{@sequence_name}' on '#{@sequence_runner}' started at #{@start_time}.#{@step_states.size} steps"
|
414
|
+
end
|
415
|
+
def exec_time
|
416
|
+
return @stop_time-@start_time if @stop_time
|
417
|
+
return 0
|
418
|
+
end
|
419
|
+
def name
|
420
|
+
return @sequence_name
|
421
|
+
end
|
422
|
+
def number
|
423
|
+
return @sequence_id
|
424
|
+
end
|
425
|
+
def output
|
426
|
+
return self.summary
|
427
|
+
end
|
428
|
+
def error
|
429
|
+
return ""
|
430
|
+
end
|
431
|
+
def executed?
|
432
|
+
return true unless @status==:not_executed
|
433
|
+
return false
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
#This class allows you to wrap Ruby blocks and handle them like Command
|
438
|
+
#
|
439
|
+
#Provide a block to RubyCommand#new and you can execute the block using
|
440
|
+
#RubyCommand#run
|
441
|
+
#
|
442
|
+
#The block receives the instance of RubyCommand so you can set the output and error output.
|
443
|
+
#
|
444
|
+
#The return value of the block is assigned as the command status.
|
445
|
+
#
|
446
|
+
#== Examples
|
447
|
+
#An example (using the excellent HighLine lib) of a CLI prompt as a RubyCommand
|
448
|
+
# RubyCommand.new("prompt") do |cmd|
|
449
|
+
# cmd.output=""
|
450
|
+
# cmd.error=""
|
451
|
+
# if HighLine.agree("#{step.text}?")
|
452
|
+
# :success
|
453
|
+
# else
|
454
|
+
# :error
|
455
|
+
# end
|
456
|
+
# end
|
457
|
+
class RubyCommand
|
458
|
+
include Patir::Command
|
459
|
+
def initialize name,working_directory=nil,&block
|
460
|
+
@name=name
|
461
|
+
@working_directory=working_directory
|
462
|
+
if block_given?
|
463
|
+
@cmd=block
|
464
|
+
else
|
465
|
+
raise "You need to provide a block"
|
466
|
+
end
|
467
|
+
end
|
468
|
+
#Runs the associated block
|
469
|
+
def run
|
470
|
+
@run=true
|
471
|
+
prev_dir=Dir.pwd
|
472
|
+
begin
|
473
|
+
Dir.chdir(@working_directory) if @working_directory
|
474
|
+
t1=Time.now
|
475
|
+
@status=@cmd.call(self)
|
476
|
+
@exec_time=Time.now-t1
|
477
|
+
rescue SystemCallError
|
478
|
+
@output="#{$!}"
|
479
|
+
@success=false
|
480
|
+
ensure
|
481
|
+
Dir.chdir(prev_dir)
|
482
|
+
end
|
483
|
+
return @success
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|