simplews 1.1.6 → 1.3.1
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/History.txt +23 -0
- data/Manifest.txt +3 -3
- data/lib/simplews/jobs.rb +405 -0
- data/lib/simplews/version.rb +2 -2
- data/lib/simplews.rb +5 -2
- data/test/simplews/test_jobs.rb +93 -0
- data/test/test_helper.rb +2 -0
- metadata +9 -8
- data/lib/simplews/asynchronous.rb +0 -502
- data/test/simplews/test_asynchronous.rb +0 -88
- /data/{README.rdoc → README.txt} +0 -0
data/History.txt
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
== 1.3.1 2008-12-20
|
2
|
+
|
3
|
+
* 1 minor enhancement:
|
4
|
+
* Use Marshal instead of YAML, it seems faster...
|
5
|
+
|
6
|
+
* 1 mayor enhancement:
|
7
|
+
* Removed old versions of job based servers
|
8
|
+
|
9
|
+
== 1.3.0 2008-12-20
|
10
|
+
|
11
|
+
* 1 minor enhancement:
|
12
|
+
* New version for asynchronous job. In this case it uses fork to spawn new
|
13
|
+
processes.
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
== 1.1.7 2008-11-27
|
18
|
+
|
19
|
+
* 2 minor enhancement:
|
20
|
+
* Inline methods are prepended '_inline_' to avoid polluting namespace
|
21
|
+
* Asynchronous jobs have a hash where they can save information, it is
|
22
|
+
accessible through the 'info' WS method in YAML format
|
23
|
+
|
1
24
|
== 1.1.6 2008-11-24
|
2
25
|
|
3
26
|
* 1 minor enhancement:
|
data/Manifest.txt
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
History.txt
|
2
2
|
Manifest.txt
|
3
3
|
PostInstall.txt
|
4
|
-
README.
|
4
|
+
README.txt
|
5
5
|
Rakefile
|
6
6
|
config/hoe.rb
|
7
7
|
config/requirements.rb
|
8
8
|
lib/simplews.rb
|
9
|
-
lib/simplews/
|
9
|
+
lib/simplews/jobs.rb
|
10
10
|
lib/simplews/version.rb
|
11
11
|
script/console
|
12
12
|
script/destroy
|
@@ -16,7 +16,7 @@ setup.rb
|
|
16
16
|
tasks/deployment.rake
|
17
17
|
tasks/environment.rake
|
18
18
|
tasks/website.rake
|
19
|
-
test/simplews/
|
19
|
+
test/simplews/test_jobs.rb
|
20
20
|
test/test_helper.rb
|
21
21
|
test/test_simplews.rb
|
22
22
|
website/index.html
|
@@ -0,0 +1,405 @@
|
|
1
|
+
require File.join(File.dirname(File.dirname(__FILE__)) + '/simplews')
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'drb'
|
5
|
+
require 'singleton'
|
6
|
+
require 'rand'
|
7
|
+
|
8
|
+
|
9
|
+
class SimpleWS::Jobs < SimpleWS
|
10
|
+
class JobNotFound < Exception; end
|
11
|
+
class ResultNotFound < Exception; end
|
12
|
+
class Aborted < Exception; end
|
13
|
+
|
14
|
+
|
15
|
+
#{{{ Scheduler
|
16
|
+
|
17
|
+
module Scheduler
|
18
|
+
include Process
|
19
|
+
|
20
|
+
@@task_results = {}
|
21
|
+
|
22
|
+
@@names = []
|
23
|
+
@@pids = {}
|
24
|
+
|
25
|
+
@@queue = []
|
26
|
+
@@max_jobs = 3
|
27
|
+
|
28
|
+
def self.random_name(s="", num=20)
|
29
|
+
num.times{
|
30
|
+
r = rand
|
31
|
+
if r < 0.3
|
32
|
+
s << (rand * 10).to_i.to_s
|
33
|
+
elsif r < 0.6
|
34
|
+
s << (rand * (?z - ?a) + ?a).to_i.chr
|
35
|
+
else
|
36
|
+
s << (rand * (?Z - ?A) + ?A).to_i.chr
|
37
|
+
end
|
38
|
+
}
|
39
|
+
s.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.make_name(name = "")
|
43
|
+
name = Scheduler::random_name("job-") unless name =~ /\w/
|
44
|
+
|
45
|
+
taken = @@names.select{|n| n =~ /^#{ name }(-\d+)?$/}
|
46
|
+
if taken.any?
|
47
|
+
if taken.length == 1
|
48
|
+
return name + '-1'
|
49
|
+
else
|
50
|
+
last = taken.collect{|s|
|
51
|
+
if s.match(/-(\d+)$/)
|
52
|
+
$1.to_i
|
53
|
+
else 0
|
54
|
+
end
|
55
|
+
}.sort.last
|
56
|
+
return name + '-' + (last + 1).to_s
|
57
|
+
end
|
58
|
+
else
|
59
|
+
return name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.configure(name, value)
|
64
|
+
Job::configure(name, value)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.helper(name, block)
|
68
|
+
Job.send :define_method, name, block
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.task(name, results, block)
|
72
|
+
@@task_results[name] = results
|
73
|
+
Job.send :define_method, name, block
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.dequeue
|
77
|
+
if @@pids.length < @@max_jobs && @@queue.any?
|
78
|
+
job_info = @@queue.pop
|
79
|
+
|
80
|
+
pid = Job.new.run(job_info[:task], job_info[:name], @@task_results[job_info[:task]], *job_info[:args])
|
81
|
+
@@pids[job_info[:name]] = pid
|
82
|
+
pid
|
83
|
+
else
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.run(task, *args)
|
89
|
+
suggested_name = *args.pop
|
90
|
+
name = make_name(suggested_name)
|
91
|
+
@@names << name
|
92
|
+
|
93
|
+
@@queue.push( {:name => name, :task => task, :args => args})
|
94
|
+
state = {
|
95
|
+
:name => name,
|
96
|
+
:status => :queued,
|
97
|
+
:messages => [],
|
98
|
+
:info => {},
|
99
|
+
}
|
100
|
+
Job.save(name,state)
|
101
|
+
|
102
|
+
name
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.clean_job(pid)
|
106
|
+
name = @@pids.select{|name, p| p == pid}.first[0]
|
107
|
+
puts "Process #{ name } with pid #{ pid } finished with exitstatus #{$?.exitstatus}"
|
108
|
+
state = Job.job_info(name)
|
109
|
+
if ![:error, :done, :aborted].include?(state[:status])
|
110
|
+
state[:status] = :error
|
111
|
+
state[:messages] << "Job finished for unknown reasons"
|
112
|
+
Job.save(name, state)
|
113
|
+
end
|
114
|
+
@@pids.delete(name)
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.job_monitor
|
118
|
+
Thread.new{
|
119
|
+
while true
|
120
|
+
pid = dequeue
|
121
|
+
if pid
|
122
|
+
Thread.new(pid){|pid|
|
123
|
+
begin
|
124
|
+
pid = Process.wait(-1,Process::WUNTRACED) if @@pids.any?
|
125
|
+
clean_job(pid) if @@pids.values.include? pid
|
126
|
+
rescue Exception
|
127
|
+
puts $!.message
|
128
|
+
end
|
129
|
+
}
|
130
|
+
end
|
131
|
+
sleep 2
|
132
|
+
end
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.abort(name)
|
137
|
+
Process.kill "INT", @@pids[name]
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.abort_jobs
|
141
|
+
@@pids.values{|pid|
|
142
|
+
Process.kill "INT", pid
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.job_info(name)
|
147
|
+
Job.job_info(name)
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.workdir=(workdir)
|
151
|
+
Job.workdir = workdir
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.job_results(name)
|
155
|
+
Job.results(name)
|
156
|
+
end
|
157
|
+
|
158
|
+
#{{{ Job
|
159
|
+
|
160
|
+
class Job
|
161
|
+
def self.workdir=(workdir)
|
162
|
+
@@workdir = workdir
|
163
|
+
@@savedir = File.join(@@workdir, '.save')
|
164
|
+
FileUtils.mkdir_p @@workdir unless File.exist? @@workdir
|
165
|
+
FileUtils.mkdir_p @@savedir unless File.exist? @@savedir
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.path(file, name)
|
169
|
+
if file =~ /^\//
|
170
|
+
file.gsub(/\{JOB\}/)
|
171
|
+
else
|
172
|
+
File.join(@@workdir, file.gsub(/\{JOB\}/,name))
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.save(name, state)
|
177
|
+
fout = File.open(File.join(@@savedir,name + '.marshal'),'w')
|
178
|
+
fout.write Marshal::dump(state)
|
179
|
+
fout.close
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.job_info(name)
|
183
|
+
info = nil
|
184
|
+
begin
|
185
|
+
sleep 1
|
186
|
+
info = Marshal::load(File.open(File.join(@@savedir,name + '.marshal')))
|
187
|
+
raise Exception unless info.is_a? Hash
|
188
|
+
rescue Exception
|
189
|
+
raise JobNotFound, "Job with name #{ name } was not found"
|
190
|
+
end
|
191
|
+
|
192
|
+
info
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.results(name)
|
196
|
+
job_info(name)[:results].collect{|file|
|
197
|
+
code = Scheduler.random_name("res-")
|
198
|
+
[code, file]
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
@@config = {}
|
203
|
+
def self.configure(name, value)
|
204
|
+
@@config[name] = value
|
205
|
+
end
|
206
|
+
|
207
|
+
def workdir
|
208
|
+
@@workdir
|
209
|
+
end
|
210
|
+
|
211
|
+
def config
|
212
|
+
@@config
|
213
|
+
end
|
214
|
+
|
215
|
+
def path(file)
|
216
|
+
Job.path(file, @name)
|
217
|
+
end
|
218
|
+
|
219
|
+
def save
|
220
|
+
Job.save(@name, @state)
|
221
|
+
end
|
222
|
+
|
223
|
+
def write(file, contents)
|
224
|
+
path = Job.path(file, @name)
|
225
|
+
fout = File.open(path,'w')
|
226
|
+
fout.write contents
|
227
|
+
fout.close
|
228
|
+
end
|
229
|
+
|
230
|
+
def step(status, message = nil)
|
231
|
+
@state[:status] = status
|
232
|
+
@state[:messages] << message if message && message != ""
|
233
|
+
save
|
234
|
+
end
|
235
|
+
|
236
|
+
def error(message = nil)
|
237
|
+
step(:error, message)
|
238
|
+
save
|
239
|
+
end
|
240
|
+
|
241
|
+
def info(info)
|
242
|
+
@state[:info].merge!(info)
|
243
|
+
save
|
244
|
+
end
|
245
|
+
|
246
|
+
def results(results)
|
247
|
+
@state[:results] = results.collect{|file| path(file)}
|
248
|
+
save
|
249
|
+
end
|
250
|
+
|
251
|
+
def abort
|
252
|
+
raise SimpleWS::Jobs::Aborted
|
253
|
+
save
|
254
|
+
end
|
255
|
+
|
256
|
+
def job_name
|
257
|
+
@name
|
258
|
+
end
|
259
|
+
|
260
|
+
def run(task, name, results, *args)
|
261
|
+
@name = name
|
262
|
+
@state = {
|
263
|
+
:name => @name,
|
264
|
+
:status => :prepared,
|
265
|
+
:messages => [],
|
266
|
+
:info => {},
|
267
|
+
:results => results.collect{|file| path(file)},
|
268
|
+
}
|
269
|
+
save
|
270
|
+
@pid = Process.fork do
|
271
|
+
begin
|
272
|
+
trap(:INT) { raise SimpleWS::Jobs::Aborted }
|
273
|
+
self.send task, *args
|
274
|
+
step :done
|
275
|
+
exit(0)
|
276
|
+
rescue SimpleWS::Jobs::Aborted
|
277
|
+
step(:aborted)
|
278
|
+
exit(-1)
|
279
|
+
rescue Exception
|
280
|
+
if !$!.kind_of? SystemExit
|
281
|
+
error($!.message)
|
282
|
+
puts "Error in job #{ @name }"
|
283
|
+
puts $!.message
|
284
|
+
puts $!.backtrace
|
285
|
+
exit(-1)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
@pid
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
def self.helper(name, &block)
|
297
|
+
Scheduler.helper name, block
|
298
|
+
end
|
299
|
+
|
300
|
+
def helper(name,&block)
|
301
|
+
Scheduler.helper name, block
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.configure(name, value)
|
305
|
+
Scheduler.configure(name, value)
|
306
|
+
end
|
307
|
+
|
308
|
+
def configure(name, value)
|
309
|
+
self.class.configure(name, value)
|
310
|
+
end
|
311
|
+
|
312
|
+
def task(name, params=[], types={}, results = [], &block)
|
313
|
+
Scheduler.task name, results, block
|
314
|
+
serve name.to_s, params + ['suggested_name'], types.merge(:suggested_name => 'string', :return => :string) do |*args|
|
315
|
+
Scheduler.run name, *args
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
@@tasks = {}
|
320
|
+
def self.task(name, params=[], types={}, results =[], &block)
|
321
|
+
Scheduler.task name, results, block
|
322
|
+
@@tasks[name] = {:params => params, :types => types};
|
323
|
+
end
|
324
|
+
|
325
|
+
def abort_jobs
|
326
|
+
Scheduler.abort_jobs
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
def workdir
|
331
|
+
@workdir
|
332
|
+
end
|
333
|
+
|
334
|
+
def initialize(name, description, host, port, workdir = nil, *args)
|
335
|
+
super(name, description, host, port, *args)
|
336
|
+
|
337
|
+
@workdir = workdir || "/tmp/#{ name }"
|
338
|
+
Scheduler.workdir = @workdir
|
339
|
+
@results = {}
|
340
|
+
@@tasks.each{|task, values|
|
341
|
+
serve task.to_s, values[:params] + ['suggested_name'], values[:types].merge(:suggested_name => 'string', :return => :string) do |*args|
|
342
|
+
Scheduler.run task, *args
|
343
|
+
end
|
344
|
+
}
|
345
|
+
|
346
|
+
serve :status, ['job'], :job => :string, :return => :string do |job|
|
347
|
+
Scheduler.job_info(job)[:status].to_s
|
348
|
+
end
|
349
|
+
|
350
|
+
serve :messages, ['job'], :job => :string, :return => :array do |job|
|
351
|
+
Scheduler.job_info(job)[:messages]
|
352
|
+
end
|
353
|
+
|
354
|
+
serve :info, ['job'], :job => :string, :return => :string do |job|
|
355
|
+
Scheduler.job_info(job)[:info].to_yaml
|
356
|
+
end
|
357
|
+
|
358
|
+
serve :abort, %w(job), :job => :string do |job|
|
359
|
+
Scheduler.abort(job)
|
360
|
+
end
|
361
|
+
|
362
|
+
serve :done, %w(job), :job => :string, :return => :boolean do |job|
|
363
|
+
[:done, :error, :aborted].include? Scheduler.job_info(job)[:status]
|
364
|
+
end
|
365
|
+
|
366
|
+
serve :error, %w(job), :job => :string, :return => :boolean do |job|
|
367
|
+
Scheduler.job_info(job)[:status] == :error
|
368
|
+
end
|
369
|
+
|
370
|
+
serve :aborted, %w(job), :job => :string, :return => :boolean do |job|
|
371
|
+
Scheduler.job_info(job)[:status] == :aborted
|
372
|
+
end
|
373
|
+
|
374
|
+
serve :results, %w(job), :return => :array do |job|
|
375
|
+
results = Scheduler.job_results(job)
|
376
|
+
@results.merge! Hash[*results.flatten]
|
377
|
+
results.collect{|p| p[0]}
|
378
|
+
end
|
379
|
+
|
380
|
+
serve :result, %w(result), :return => :binary do |result|
|
381
|
+
path = @results[result]
|
382
|
+
raise ResultNotFound unless File.exist? path
|
383
|
+
File.open(path).read
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
387
|
+
|
388
|
+
alias_method :old_start, :start
|
389
|
+
def start(*args)
|
390
|
+
Scheduler.job_monitor
|
391
|
+
old_start(*args)
|
392
|
+
end
|
393
|
+
|
394
|
+
alias_method :old_shutdown, :shutdown
|
395
|
+
def shutdown(*args)
|
396
|
+
Scheduler.abort_jobs
|
397
|
+
old_shutdown(*args)
|
398
|
+
end
|
399
|
+
|
400
|
+
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
|
data/lib/simplews/version.rb
CHANGED
data/lib/simplews.rb
CHANGED
@@ -109,11 +109,14 @@ class SimpleWS < SOAP::RPC::StandaloneServer
|
|
109
109
|
def serve(name, args=[], types={}, &block)
|
110
110
|
|
111
111
|
if block
|
112
|
-
|
112
|
+
inline_name = "_inline_" + name.to_s
|
113
|
+
add_to_ruby(inline_name, &block)
|
114
|
+
add_method_as(self,inline_name, name.to_s,*args)
|
115
|
+
else
|
116
|
+
add_method(self,name.to_s,*args)
|
113
117
|
end
|
114
118
|
|
115
119
|
add_to_wsdl(name, args, types)
|
116
|
-
add_method(self,name.to_s,*args)
|
117
120
|
|
118
121
|
nil
|
119
122
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(File.dirname(__FILE__)) + '/test_helper.rb'
|
2
|
+
|
3
|
+
|
4
|
+
class TestJobs < Test::Unit::TestCase
|
5
|
+
class TestJWS < SimpleWS::Jobs
|
6
|
+
|
7
|
+
task :process,[],{},['test.txt'] do
|
8
|
+
begin
|
9
|
+
info(:steps => 3)
|
10
|
+
write('test.txt', job_name)
|
11
|
+
step(:init)
|
12
|
+
sleep 2
|
13
|
+
step(:step1, "Step1")
|
14
|
+
sleep 2
|
15
|
+
step(:step2, "Step2")
|
16
|
+
sleep 2
|
17
|
+
step(:step3, "Step3")
|
18
|
+
rescue
|
19
|
+
error($!.message)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def setup
|
26
|
+
@server = TestJWS.new("TestJWS", "Asynchronous Job Server", 'localhost', '1984', "tmp-TestJWS")
|
27
|
+
|
28
|
+
Thread.new do
|
29
|
+
@server.start
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def teardown
|
35
|
+
@server.shutdown
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_client
|
39
|
+
|
40
|
+
require 'soap/wsdlDriver'
|
41
|
+
|
42
|
+
driver = SimpleWS.get_driver('http://localhost:1984', 'TestJWS')
|
43
|
+
|
44
|
+
|
45
|
+
name = driver.process("test")
|
46
|
+
puts "Job name #{ name }"
|
47
|
+
|
48
|
+
while !driver.done(name)
|
49
|
+
puts "status: " + driver.status(name)
|
50
|
+
sleep 1
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_equal(3, YAML.load(driver.info(name))[:steps])
|
54
|
+
assert(!driver.error(name))
|
55
|
+
assert(driver.messages(name).include? "Step3")
|
56
|
+
assert(driver.results(name).length == 1)
|
57
|
+
result = driver.results(name).first
|
58
|
+
assert_match(name, driver.result(result))
|
59
|
+
|
60
|
+
name = driver.process("test-abort")
|
61
|
+
sleep 2
|
62
|
+
puts "status: " + driver.status(name)
|
63
|
+
driver.abort(name)
|
64
|
+
sleep 2
|
65
|
+
puts driver.status(name)
|
66
|
+
puts driver.messages(name)
|
67
|
+
assert(driver.aborted(name))
|
68
|
+
|
69
|
+
FileUtils.cp "tmp-TestJWS/.save/test-abort.yaml", "tmp-TestJWS/.save/copy.yaml"
|
70
|
+
assert(driver.aborted("copy"))
|
71
|
+
|
72
|
+
# Test queue
|
73
|
+
threads = []
|
74
|
+
10.times {
|
75
|
+
threads << Thread.new{
|
76
|
+
test_name = driver.process("test-queue")
|
77
|
+
puts "Starting #{ test_name }"
|
78
|
+
while !driver.done(test_name)
|
79
|
+
puts "#{ test_name }: " + driver.status(test_name)
|
80
|
+
sleep 2
|
81
|
+
end
|
82
|
+
}
|
83
|
+
sleep 2
|
84
|
+
}
|
85
|
+
|
86
|
+
puts "Threads " + threads.length.to_s
|
87
|
+
threads.each{|t| t.join}
|
88
|
+
sleep 10
|
89
|
+
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplews
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-01-14 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 1.
|
23
|
+
version: 1.8.0
|
24
24
|
version:
|
25
25
|
description: Simplifies the development of Web Services, producing WSDL automatically for example. Wraps soap4r.
|
26
26
|
email:
|
@@ -33,17 +33,18 @@ extra_rdoc_files:
|
|
33
33
|
- History.txt
|
34
34
|
- Manifest.txt
|
35
35
|
- PostInstall.txt
|
36
|
+
- README.txt
|
36
37
|
- website/index.txt
|
37
38
|
files:
|
38
39
|
- History.txt
|
39
40
|
- Manifest.txt
|
40
41
|
- PostInstall.txt
|
41
|
-
- README.
|
42
|
+
- README.txt
|
42
43
|
- Rakefile
|
43
44
|
- config/hoe.rb
|
44
45
|
- config/requirements.rb
|
45
46
|
- lib/simplews.rb
|
46
|
-
- lib/simplews/
|
47
|
+
- lib/simplews/jobs.rb
|
47
48
|
- lib/simplews/version.rb
|
48
49
|
- script/console
|
49
50
|
- script/destroy
|
@@ -53,7 +54,7 @@ files:
|
|
53
54
|
- tasks/deployment.rake
|
54
55
|
- tasks/environment.rake
|
55
56
|
- tasks/website.rake
|
56
|
-
- test/simplews/
|
57
|
+
- test/simplews/test_jobs.rb
|
57
58
|
- test/test_helper.rb
|
58
59
|
- test/test_simplews.rb
|
59
60
|
- website/index.html
|
@@ -91,11 +92,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
92
|
requirements: []
|
92
93
|
|
93
94
|
rubyforge_project: simplews
|
94
|
-
rubygems_version: 1.
|
95
|
+
rubygems_version: 1.3.1
|
95
96
|
signing_key:
|
96
97
|
specification_version: 2
|
97
98
|
summary: Simplifies the development of Web Services, producing WSDL automatically for example. Wraps soap4r.
|
98
99
|
test_files:
|
99
100
|
- test/test_simplews.rb
|
100
101
|
- test/test_helper.rb
|
101
|
-
- test/simplews/
|
102
|
+
- test/simplews/test_jobs.rb
|
@@ -1,502 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(File.dirname(__FILE__)) + '/simplews')
|
2
|
-
|
3
|
-
require 'yaml'
|
4
|
-
|
5
|
-
# This class extends SimpleWS and allows provides with a framework to
|
6
|
-
# run asynchronous jobs. Job are initiated and can be latter be accessed
|
7
|
-
# using a unique job id. The status can be queried, and when finished,
|
8
|
-
# the results can be gathered. Jobs are also able to save their states
|
9
|
-
# between executions of the server.
|
10
|
-
#
|
11
|
-
# The class provides a instance function start_job, that receives a
|
12
|
-
# block of code and creates a new thread to run this block of code. It
|
13
|
-
# also provides some helper functions to set the state of the job, as
|
14
|
-
# well as messages or to abort of report errors.
|
15
|
-
#
|
16
|
-
# Each job can produce a number of results saved into files that can
|
17
|
-
# latter be accessed by the client. At any time the code inside the
|
18
|
-
# block can access any of the following helper functions: step, read,
|
19
|
-
# write, abort_process, job_name and save. As in the following example.
|
20
|
-
# (+process+ will be a method served by the server):
|
21
|
-
#
|
22
|
-
# def process(name)
|
23
|
-
# start_job(name, %w(test.txt)) do
|
24
|
-
# begin
|
25
|
-
# write('test.txt', job_name)
|
26
|
-
# step(:init)
|
27
|
-
# sleep 2
|
28
|
-
# step(:step1, "Step1")
|
29
|
-
# sleep 2
|
30
|
-
# step(:step2, "Step2")
|
31
|
-
# sleep 2
|
32
|
-
# step(:step2, "Step3")
|
33
|
-
# rescue
|
34
|
-
# error($!.message)
|
35
|
-
# end
|
36
|
-
# end
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# Note that any file specified in the write or read method is relative
|
40
|
-
# to the workdir directory. The optional array passed as second argument
|
41
|
-
# to start_job lists the results the job will be generating and that
|
42
|
-
# will be accessible latter to the clients, one could have also done
|
43
|
-
# this:
|
44
|
-
#
|
45
|
-
# def process(name)
|
46
|
-
# actual_name = start_job(name) do
|
47
|
-
# begin
|
48
|
-
# write(job_name + ".txt", "Hi")
|
49
|
-
# step(:init)
|
50
|
-
# sleep 2
|
51
|
-
# step(:step1, "Step1")
|
52
|
-
# sleep 2
|
53
|
-
# step(:step2, "Step2")
|
54
|
-
# sleep 2
|
55
|
-
# step(:step2, "Step3")
|
56
|
-
# rescue
|
57
|
-
# error($!.message)
|
58
|
-
# end
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
# set_results(actual_name, [ actual_name + ".txt" ])
|
62
|
-
#
|
63
|
-
# actual_name
|
64
|
-
# end
|
65
|
-
#
|
66
|
-
# Note in this example that the name provided to start_job is only a
|
67
|
-
# suggestion, it a job is already available with that name a number will
|
68
|
-
# be prepended to it. The actual name used is return by the start_job
|
69
|
-
# method and is the one that must be used in the set_results method.
|
70
|
-
# Also note that the +process+ method in the example has to return the
|
71
|
-
# actual name of the job so it will be accessible to the client.
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# The clients have a number of methods available to query and modify the
|
75
|
-
# state of the job, as well as to gather results: status, messages,
|
76
|
-
# done, error, aborted, abort, results and result.
|
77
|
-
#
|
78
|
-
#
|
79
|
-
# While reading the documentation please note that there are three types
|
80
|
-
# of methods here, methods used inside the web server methods, methods
|
81
|
-
# used inside code blocks executed by Job Threads, and methods that are
|
82
|
-
# exported by the web server. Its important to call each method in the
|
83
|
-
# right context.
|
84
|
-
#
|
85
|
-
class SimpleWS::Asynchronous < SimpleWS
|
86
|
-
|
87
|
-
|
88
|
-
class JobNotFound < Exception; end
|
89
|
-
class JobAborted < Exception; end
|
90
|
-
|
91
|
-
|
92
|
-
class Job < Thread # :nodoc:
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
def self.random_name(s="", num=20)
|
97
|
-
num.times{
|
98
|
-
r = rand
|
99
|
-
if r < 0.3
|
100
|
-
s << (rand * 10).to_i.to_s
|
101
|
-
elsif r < 0.6
|
102
|
-
s << (rand * (?z - ?a) + ?a).to_i.chr
|
103
|
-
else
|
104
|
-
s << (rand * (?Z - ?A) + ?A).to_i.chr
|
105
|
-
end
|
106
|
-
}
|
107
|
-
s.to_s
|
108
|
-
end
|
109
|
-
|
110
|
-
def self.make_name(name)
|
111
|
-
|
112
|
-
name = Job::random_name("job-") if name == ""
|
113
|
-
|
114
|
-
taken = @@jobs.keys.select{|n| n =~ /^#{ name }(-\d+)?$/}
|
115
|
-
if taken.any?
|
116
|
-
last = taken.sort.last
|
117
|
-
if last == name
|
118
|
-
return name + '-1'
|
119
|
-
else
|
120
|
-
num = last.match(/-(\d+)$/)[1]
|
121
|
-
return name + '-' + (num.to_i + 1).to_s
|
122
|
-
end
|
123
|
-
else
|
124
|
-
return name
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
public
|
129
|
-
|
130
|
-
# Returns the job specified in the +name+. If it is not currently
|
131
|
-
# found but it has a saved state, then it is loaded.
|
132
|
-
def self.job(name)
|
133
|
-
if @@jobs[name].nil?
|
134
|
-
if File.exists?(File.join(@@workdir, ".save/#{name}.yaml"))
|
135
|
-
job = Job.new(name, [])
|
136
|
-
job.load
|
137
|
-
@@jobs[name] = job
|
138
|
-
else
|
139
|
-
raise SimpleWS::Asynchronous::JobNotFound, "Job with name '#{ name }' was not found"
|
140
|
-
end
|
141
|
-
end
|
142
|
-
@@jobs[name]
|
143
|
-
end
|
144
|
-
|
145
|
-
def self.start(name, results, &block)
|
146
|
-
job = Job.new(name, results, &block)
|
147
|
-
@@jobs[job.name] = job
|
148
|
-
job.name
|
149
|
-
end
|
150
|
-
|
151
|
-
def self.read(name, path)
|
152
|
-
Job::job(name).read(path)
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
@@workdir = nil
|
157
|
-
@@jobs = {}
|
158
|
-
|
159
|
-
def self.workdir
|
160
|
-
@@workdir
|
161
|
-
end
|
162
|
-
|
163
|
-
def self.workdir=(workdir)
|
164
|
-
@@workdir = workdir
|
165
|
-
begin
|
166
|
-
FileUtils.mkdir_p(@@workdir) unless File.exist?( @@workdir)
|
167
|
-
FileUtils.mkdir(File.join(@@workdir,'.save')) unless File.exist?( File.join(@@workdir,'.save'))
|
168
|
-
rescue
|
169
|
-
puts "Error creating work directory:"
|
170
|
-
raise $!
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
attr_reader :messages, :status, :name
|
175
|
-
def initialize(name, results, &block)
|
176
|
-
@status = ''
|
177
|
-
@messages = []
|
178
|
-
@name = Job::make_name(name)
|
179
|
-
@results = results
|
180
|
-
@results_hash = {}
|
181
|
-
|
182
|
-
if block_given?
|
183
|
-
super do
|
184
|
-
begin
|
185
|
-
block.call
|
186
|
-
step(:done)
|
187
|
-
|
188
|
-
rescue SimpleWS::Asynchronous::JobAborted
|
189
|
-
step(:aborted, $!.message)
|
190
|
-
rescue Exception
|
191
|
-
error($!.message)
|
192
|
-
raise $!
|
193
|
-
ensure
|
194
|
-
save
|
195
|
-
Thread.exit
|
196
|
-
end
|
197
|
-
end
|
198
|
-
else
|
199
|
-
super do end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
def save
|
204
|
-
info = {
|
205
|
-
:status => @status,
|
206
|
-
:messages => @messages,
|
207
|
-
:name => @name,
|
208
|
-
:results => @results,
|
209
|
-
:results_hash => @results_hash,
|
210
|
-
}
|
211
|
-
|
212
|
-
fout = File.open(File.join(@@workdir, ".save/#{name}.yaml"),'w')
|
213
|
-
fout.write info.to_yaml
|
214
|
-
fout.close
|
215
|
-
end
|
216
|
-
|
217
|
-
def load
|
218
|
-
info = YAML::load(File.open(File.join(@@workdir, ".save/#{@name}.yaml")))
|
219
|
-
@status = info[:status]
|
220
|
-
@messages = info[:messages]
|
221
|
-
@name = info[:name]
|
222
|
-
@results = info[:results]
|
223
|
-
@results_hash = info[:results_hash]
|
224
|
-
end
|
225
|
-
|
226
|
-
#{{{ Instance methods
|
227
|
-
def name
|
228
|
-
@name
|
229
|
-
end
|
230
|
-
|
231
|
-
def step(status, message= nil)
|
232
|
-
@status = status
|
233
|
-
@messages << message if message && message != ""
|
234
|
-
end
|
235
|
-
|
236
|
-
def read(path)
|
237
|
-
File.open(File.join(@@workdir, path)).read
|
238
|
-
end
|
239
|
-
|
240
|
-
def write(path, content)
|
241
|
-
f = File.open(File.join(@@workdir, path),'w')
|
242
|
-
f.write(content)
|
243
|
-
f.close
|
244
|
-
end
|
245
|
-
|
246
|
-
#{{{ Results Related
|
247
|
-
def results=(list)
|
248
|
-
@results = list
|
249
|
-
end
|
250
|
-
|
251
|
-
def results
|
252
|
-
@results.collect{|res|
|
253
|
-
res_name = Job.random_name("result-")
|
254
|
-
@results_hash[res_name] = res
|
255
|
-
res_name
|
256
|
-
}
|
257
|
-
end
|
258
|
-
|
259
|
-
def result(res_name)
|
260
|
-
raise ArgumentError, "Result #{ res_name } not found" if @results_hash[res_name].nil?
|
261
|
-
File.open(File.join(@@workdir, @results_hash[res_name])).read
|
262
|
-
end
|
263
|
-
|
264
|
-
#{{{ Status related
|
265
|
-
def status
|
266
|
-
@status.to_s
|
267
|
-
end
|
268
|
-
|
269
|
-
def done?
|
270
|
-
@status.to_sym == :done || @status.to_sym == :error || @status.to_sym == :aborted
|
271
|
-
end
|
272
|
-
|
273
|
-
def error?
|
274
|
-
@status.to_sym == :error
|
275
|
-
end
|
276
|
-
|
277
|
-
def error(message)
|
278
|
-
step(:error, message)
|
279
|
-
end
|
280
|
-
|
281
|
-
def abort
|
282
|
-
raise SimpleWS::Asynchronous::JobAborted, "Aborted"
|
283
|
-
nil
|
284
|
-
end
|
285
|
-
|
286
|
-
def aborted?
|
287
|
-
@status.to_sym == :aborted
|
288
|
-
end
|
289
|
-
|
290
|
-
|
291
|
-
end
|
292
|
-
|
293
|
-
############################################################################################3
|
294
|
-
#{{{ Server
|
295
|
-
|
296
|
-
# SERVER METHOD:
|
297
|
-
#
|
298
|
-
# Sets the workdir for the jobs
|
299
|
-
def set_workdir(workdir)
|
300
|
-
Job::workdir = workdir
|
301
|
-
end
|
302
|
-
|
303
|
-
# SERVER METHOD:
|
304
|
-
#
|
305
|
-
# Returns the workdir that any jobs created will be using
|
306
|
-
def workdir
|
307
|
-
Job::workdir
|
308
|
-
end
|
309
|
-
|
310
|
-
# SERVER METHOD:
|
311
|
-
#
|
312
|
-
# Starts a new job. The parameter +name+ is just a suggestion, if it
|
313
|
-
# is not available a number will be prepended to it. Results are a
|
314
|
-
# list of relative paths to files where results are expected to be
|
315
|
-
# stored. All paths are relative to the jobs workdir. Finally, block
|
316
|
-
# is the block of code to be executed by the Job Thread.
|
317
|
-
#
|
318
|
-
# Returns the actual name used as unique identifier for the job
|
319
|
-
def start_job(name, results = [], &block)
|
320
|
-
Job.start(name, results, &block)
|
321
|
-
end
|
322
|
-
|
323
|
-
# SERVER METHOD:
|
324
|
-
#
|
325
|
-
# Sets the list of paths to results for a particular job. This
|
326
|
-
# overwrites the ones specified by start_job.
|
327
|
-
def set_results(name, results)
|
328
|
-
Job.job(name).results = results
|
329
|
-
end
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
#{{{ Thread helper methods
|
334
|
-
|
335
|
-
|
336
|
-
# CODE HELPER FUNCTION:
|
337
|
-
#
|
338
|
-
# Saves the job to it can survive server restarts. This is
|
339
|
-
# automatically called when the code is finished.
|
340
|
-
def save
|
341
|
-
Thread.current.save
|
342
|
-
end
|
343
|
-
|
344
|
-
# CODE HELPER FUNCTION:
|
345
|
-
#
|
346
|
-
# Writes +content+ into the file specified by +path+. The +path+`is
|
347
|
-
# taken relative to the job's workdir.
|
348
|
-
def write(path, content)
|
349
|
-
Thread.current.write(path, content)
|
350
|
-
end
|
351
|
-
|
352
|
-
# CODE HELPER FUNCTION:
|
353
|
-
#
|
354
|
-
# Reads the content of the file specified by +path+ relative to the
|
355
|
-
# job's workdir.
|
356
|
-
def read(path)
|
357
|
-
Thread.current.read(path)
|
358
|
-
end
|
359
|
-
|
360
|
-
|
361
|
-
# CODE HELPER FUNCTION:
|
362
|
-
#
|
363
|
-
# Sets the status of the job and adds a message to the message queue
|
364
|
-
# if the message is specified, and is different from an empty string.
|
365
|
-
# This method is called automatically in the following situations.
|
366
|
-
#
|
367
|
-
# * The block has finished executing:
|
368
|
-
# step(:done)
|
369
|
-
# * The job recieves an untreated SimpleWS::Asynchronous::JobAborted exception
|
370
|
-
# step(:aborted, $!.message)
|
371
|
-
# * The job recieves any other exception
|
372
|
-
# step(:error, $!.message)
|
373
|
-
#
|
374
|
-
def step(status, message=nil)
|
375
|
-
Thread.current.step(status, message)
|
376
|
-
end
|
377
|
-
|
378
|
-
|
379
|
-
# CODE HELPER FUNCTION:
|
380
|
-
#
|
381
|
-
# Returns the actual name of the job running the code.
|
382
|
-
def job_name
|
383
|
-
Thread.current.name
|
384
|
-
end
|
385
|
-
|
386
|
-
# CODE HELPER FUNCTION:
|
387
|
-
#
|
388
|
-
# Aborts the job raising a SimpleWS::Asynchronous::JobAborted
|
389
|
-
# exception.
|
390
|
-
def abort_process
|
391
|
-
Thread.current.abort
|
392
|
-
end
|
393
|
-
|
394
|
-
|
395
|
-
#{{{ WS Methods
|
396
|
-
|
397
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
398
|
-
#
|
399
|
-
# +name+ is the unique job identifier.
|
400
|
-
#
|
401
|
-
# Returns job status of the job.
|
402
|
-
def status(name)
|
403
|
-
Job.job(name).status
|
404
|
-
end
|
405
|
-
|
406
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
407
|
-
#
|
408
|
-
# +name+ is the unique job identifier.
|
409
|
-
#
|
410
|
-
# Raises a SimpleWS::Asynchronous::JobAborted exception in the job
|
411
|
-
def abort(name)
|
412
|
-
Job.job(name).abort
|
413
|
-
end
|
414
|
-
|
415
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
416
|
-
#
|
417
|
-
# +name+ is the unique job identifier.
|
418
|
-
#
|
419
|
-
# Checks if the job is still running or has finished, either normally
|
420
|
-
# or after been aborted or suffering an error.
|
421
|
-
def done(name)
|
422
|
-
Job.job(name).done?
|
423
|
-
end
|
424
|
-
|
425
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
426
|
-
#
|
427
|
-
# +name+ is the unique job identifier.
|
428
|
-
#
|
429
|
-
# Checks if the job has been aborted.
|
430
|
-
def aborted(name)
|
431
|
-
Job.job(name).aborted?
|
432
|
-
end
|
433
|
-
|
434
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
435
|
-
#
|
436
|
-
# +name+ is the unique job identifier.
|
437
|
-
#
|
438
|
-
# Checks if the job has suffered an error
|
439
|
-
def error(name)
|
440
|
-
Job.job(name).error?
|
441
|
-
end
|
442
|
-
|
443
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
444
|
-
#
|
445
|
-
# +name+ is the unique job identifier.
|
446
|
-
#
|
447
|
-
# Returns the list of messages of the job.
|
448
|
-
def messages(name)
|
449
|
-
Job.job(name).messages
|
450
|
-
end
|
451
|
-
|
452
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
453
|
-
#
|
454
|
-
# +name+ is the unique job identifier.
|
455
|
-
#
|
456
|
-
# Returns a list of unique results identifiers, one for each of the
|
457
|
-
# results of the job.
|
458
|
-
def results(name)
|
459
|
-
Job.job(name).results
|
460
|
-
end
|
461
|
-
|
462
|
-
# CLIENT METHOD SERVED BY DEFAULT:
|
463
|
-
#
|
464
|
-
# +name+ is the unique job identifier.
|
465
|
-
# +result+ is the unique result identifier as returned by the results
|
466
|
-
# method.
|
467
|
-
#
|
468
|
-
# Retrieves a result of a given job.
|
469
|
-
def result(name, result)
|
470
|
-
Job.job(name).result(result)
|
471
|
-
end
|
472
|
-
|
473
|
-
|
474
|
-
# Creates an instance. All the parameters are as in SimpleWS, except
|
475
|
-
# for the additional workdir parameter. It the workdir is not provided
|
476
|
-
# a directory in the /tmp directory with the name of the server will
|
477
|
-
# be used.
|
478
|
-
#
|
479
|
-
# It serves by default the methods: abort, messages, status, done,
|
480
|
-
# error, abort, aborted, results and result, as well as the +wsdl+
|
481
|
-
# method from SimpleWS.
|
482
|
-
def initialize(name="WS", description="", host="localhost", port="1984", workdir=nil, *args)
|
483
|
-
super(name, description, host, port, *args)
|
484
|
-
@results = {}
|
485
|
-
|
486
|
-
workdir ||= "/tmp/#{ name }"
|
487
|
-
set_workdir(workdir)
|
488
|
-
|
489
|
-
serve :abort, %w(name), :name => :string
|
490
|
-
|
491
|
-
serve :messages, %w(name), :name => :string, :return => :string
|
492
|
-
|
493
|
-
serve :status, %w(name), :name => :string, :return => :string
|
494
|
-
serve :done, %w(name), :name => :string, :return => :boolean
|
495
|
-
serve :error, %w(name), :name => :string, :return => :boolean
|
496
|
-
serve :aborted, %w(name), :name => :string, :return => :boolean
|
497
|
-
|
498
|
-
serve :results, %w(name), :return => :array
|
499
|
-
serve :result, %w(name result), :return => :binary
|
500
|
-
end
|
501
|
-
|
502
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
require File.dirname(File.dirname(__FILE__)) + '/test_helper.rb'
|
2
|
-
|
3
|
-
class TestSimpleWS < Test::Unit::TestCase
|
4
|
-
|
5
|
-
class TestAWS < SimpleWS::Asynchronous
|
6
|
-
|
7
|
-
def text
|
8
|
-
"Hi"
|
9
|
-
end
|
10
|
-
|
11
|
-
def process(name)
|
12
|
-
actual_name = start_job(name, %w(test.txt)) do
|
13
|
-
begin
|
14
|
-
write('test.txt', job_name)
|
15
|
-
step(:init)
|
16
|
-
sleep 2
|
17
|
-
step(:step1, "Step1")
|
18
|
-
sleep 2
|
19
|
-
step(:step2, "Step2")
|
20
|
-
sleep 2
|
21
|
-
step(:step2, "Step3")
|
22
|
-
rescue
|
23
|
-
error($!.message)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
actual_name
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(*args)
|
31
|
-
super(*args)
|
32
|
-
serve :process, %w(name), :name => :string, :return => :string
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
def setup
|
38
|
-
server = TestAWS.new("TestAWS", "Asynchronous Job Server", 'localhost', '1984')
|
39
|
-
server.set_workdir("tmp-TestAWS")
|
40
|
-
|
41
|
-
Thread.new do
|
42
|
-
server.start
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_client
|
48
|
-
|
49
|
-
require 'soap/wsdlDriver'
|
50
|
-
|
51
|
-
driver = SimpleWS.get_driver('http://localhost:1984', 'TestAWS')
|
52
|
-
|
53
|
-
name = driver.process("")
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
puts "Job name #{ name }"
|
58
|
-
|
59
|
-
while !driver.done(name)
|
60
|
-
puts "status: " + driver.status(name)
|
61
|
-
sleep 1
|
62
|
-
end
|
63
|
-
|
64
|
-
assert(!driver.error(name))
|
65
|
-
assert(driver.messages(name).include? "Step3")
|
66
|
-
assert(driver.results(name).length == 1)
|
67
|
-
|
68
|
-
result = driver.results(name).first
|
69
|
-
assert_match(name, driver.result(name, result))
|
70
|
-
|
71
|
-
|
72
|
-
name = driver.process("test-AWS")
|
73
|
-
sleep 2
|
74
|
-
puts "status: " + driver.status(name)
|
75
|
-
driver.abort(name)
|
76
|
-
sleep 2
|
77
|
-
puts driver.status(name)
|
78
|
-
assert(driver.aborted(name))
|
79
|
-
|
80
|
-
FileUtils.cp "tmp-TestAWS/.save/test-AWS.yaml", "tmp-TestAWS/.save/copy-AWS.yaml"
|
81
|
-
assert(driver.aborted("copy-AWS"))
|
82
|
-
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
/data/{README.rdoc → README.txt}
RENAMED
File without changes
|