patir 0.2
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/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
|