simplews 1.1.5 → 1.1.6

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 CHANGED
@@ -1,3 +1,9 @@
1
+ == 1.1.6 2008-11-24
2
+
3
+ * 1 minor enhancement:
4
+ * Added documentation
5
+
6
+
1
7
  == 1.1.5 2008-10-28
2
8
 
3
9
  * 1 major enhancement:
@@ -1,13 +1,98 @@
1
1
  require File.join(File.dirname(File.dirname(__FILE__)) + '/simplews')
2
2
 
3
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
+ #
4
85
  class SimpleWS::Asynchronous < SimpleWS
5
86
 
87
+
6
88
  class JobNotFound < Exception; end
7
89
  class JobAborted < Exception; end
8
90
 
9
91
 
10
- class Job < Thread
92
+ class Job < Thread # :nodoc:
93
+
94
+ private
95
+
11
96
  def self.random_name(s="", num=20)
12
97
  num.times{
13
98
  r = rand
@@ -40,6 +125,10 @@ class SimpleWS::Asynchronous < SimpleWS
40
125
  end
41
126
  end
42
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.
43
132
  def self.job(name)
44
133
  if @@jobs[name].nil?
45
134
  if File.exists?(File.join(@@workdir, ".save/#{name}.yaml"))
@@ -47,7 +136,7 @@ class SimpleWS::Asynchronous < SimpleWS
47
136
  job.load
48
137
  @@jobs[name] = job
49
138
  else
50
- raise JobNotFound, "Job with name '#{ name }' was not found"
139
+ raise SimpleWS::Asynchronous::JobNotFound, "Job with name '#{ name }' was not found"
51
140
  end
52
141
  end
53
142
  @@jobs[name]
@@ -74,7 +163,7 @@ class SimpleWS::Asynchronous < SimpleWS
74
163
  def self.workdir=(workdir)
75
164
  @@workdir = workdir
76
165
  begin
77
- FileUtils.mkdir(@@workdir) unless File.exist?( @@workdir)
166
+ FileUtils.mkdir_p(@@workdir) unless File.exist?( @@workdir)
78
167
  FileUtils.mkdir(File.join(@@workdir,'.save')) unless File.exist?( File.join(@@workdir,'.save'))
79
168
  rescue
80
169
  puts "Error creating work directory:"
@@ -139,9 +228,9 @@ class SimpleWS::Asynchronous < SimpleWS
139
228
  @name
140
229
  end
141
230
 
142
- def step(status, message="")
231
+ def step(status, message= nil)
143
232
  @status = status
144
- @messages << message if message != ""
233
+ @messages << message if message && message != ""
145
234
  end
146
235
 
147
236
  def read(path)
@@ -204,18 +293,37 @@ class SimpleWS::Asynchronous < SimpleWS
204
293
  ############################################################################################3
205
294
  #{{{ Server
206
295
 
296
+ # SERVER METHOD:
297
+ #
298
+ # Sets the workdir for the jobs
207
299
  def set_workdir(workdir)
208
300
  Job::workdir = workdir
209
301
  end
210
302
 
303
+ # SERVER METHOD:
304
+ #
305
+ # Returns the workdir that any jobs created will be using
211
306
  def workdir
212
307
  Job::workdir
213
308
  end
214
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
215
319
  def start_job(name, results = [], &block)
216
320
  Job.start(name, results, &block)
217
321
  end
218
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.
219
327
  def set_results(name, results)
220
328
  Job.job(name).results = results
221
329
  end
@@ -223,68 +331,154 @@ class SimpleWS::Asynchronous < SimpleWS
223
331
 
224
332
 
225
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.
226
340
  def save
227
341
  Thread.current.save
228
342
  end
229
343
 
230
-
231
- def write(*args)
232
- Thread.current.write(*args)
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)
233
350
  end
234
351
 
235
- def step(*args)
236
- Thread.current.step(*args)
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)
237
358
  end
238
359
 
239
- def read(*args)
240
- Thread.current.read(*args)
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)
241
376
  end
242
377
 
378
+
379
+ # CODE HELPER FUNCTION:
380
+ #
381
+ # Returns the actual name of the job running the code.
243
382
  def job_name
244
383
  Thread.current.name
245
384
  end
246
385
 
386
+ # CODE HELPER FUNCTION:
387
+ #
388
+ # Aborts the job raising a SimpleWS::Asynchronous::JobAborted
389
+ # exception.
247
390
  def abort_process
248
391
  Thread.current.abort
249
392
  end
250
393
 
251
394
 
252
-
253
395
  #{{{ WS Methods
254
396
 
397
+ # CLIENT METHOD SERVED BY DEFAULT:
398
+ #
399
+ # +name+ is the unique job identifier.
400
+ #
401
+ # Returns job status of the job.
255
402
  def status(name)
256
403
  Job.job(name).status
257
404
  end
258
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
259
411
  def abort(name)
260
412
  Job.job(name).abort
261
413
  end
262
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.
263
421
  def done(name)
264
422
  Job.job(name).done?
265
423
  end
266
424
 
425
+ # CLIENT METHOD SERVED BY DEFAULT:
426
+ #
427
+ # +name+ is the unique job identifier.
428
+ #
429
+ # Checks if the job has been aborted.
267
430
  def aborted(name)
268
431
  Job.job(name).aborted?
269
432
  end
270
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
271
439
  def error(name)
272
440
  Job.job(name).error?
273
441
  end
274
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.
275
448
  def messages(name)
276
449
  Job.job(name).messages
277
450
  end
278
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.
279
458
  def results(name)
280
459
  Job.job(name).results
281
460
  end
282
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.
283
469
  def result(name, result)
284
470
  Job.job(name).result(result)
285
471
  end
286
472
 
287
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.
288
482
  def initialize(name="WS", description="", host="localhost", port="1984", workdir=nil, *args)
289
483
  super(name, description, host, port, *args)
290
484
  @results = {}
@@ -2,7 +2,7 @@ module Simplews
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
4
  MINOR = 1
5
- TINY = 5
5
+ TINY = 6
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  self
data/lib/simplews.rb CHANGED
@@ -2,8 +2,55 @@ require 'soap/rpc/standaloneServer'
2
2
  require 'builder'
3
3
 
4
4
 
5
+ # SimpleWS is a class that wraps SOAP::RPC::StandaloneServer to ease the
6
+ # implementation of Web Services, specifically the generation of the
7
+ # +WSDL+ file. It provides a particular syntax that allows to specify
8
+ # the methods that are going to be served along with the types of the
9
+ # parameters and of the response so that the +WSDL+ can be generated
10
+ # accordingly. Actual Servers can be instances of this class where
11
+ # methods are assigned dynamically or of classes that extend SimpleWS.
12
+ # class TestWS < SimpleWS
13
+ # def hi(name)
14
+ # "Hi #{name}, how are you?"
15
+ # end
16
+ #
17
+ # def initialize(*args)
18
+ # super(*args)
19
+ # serve :hi, %w(name), :name => :string, :return => :string
20
+ #
21
+ # serve :bye, %w(name), :name => :string, :return => :string do |name|
22
+ # "Bye bye #{name}. See you soon."
23
+ # end
24
+ #
25
+ # end
26
+ # end
27
+ #
28
+ # # Creating a server and starting it
29
+ #
30
+ # server = TestWS.new("TestWS", "Greeting Services", 'localhost', '1984')
31
+ # server.wsdl("TestWS.wsdl")
32
+ #
33
+ # Thread.new do
34
+ # server.start
35
+ # end
36
+ #
37
+ # # Client code. This could be run in another process.
38
+ #
39
+ # driver = SimpleWS::get_driver('http://localhost:1984', "TestWS")
40
+ # puts driver.hi('Gladis') #=> "Hi Gladis, how are you?"
41
+ # puts driver.bye('Gladis') #=> "Bye bye Gladis. See you soon."
42
+ #
43
+
44
+
45
+
5
46
  class SimpleWS < SOAP::RPC::StandaloneServer
6
47
 
48
+ # This is a helper function for clients. Given the +url+ where the
49
+ # server is listening, as well as the name of the server, it can
50
+ # manually access the +wsdl+ function and retrieve the complete +WSDL+
51
+ # file. This works *only* in web servers of class SimpleWS, not on
52
+ # the general SOAP::RPC::StandaloneServer or any other type of web
53
+ # server.
7
54
  def self.get_wsdl(url, name)
8
55
  require 'soap/rpc/driver'
9
56
  driver = SOAP::RPC::Driver.new(url, "urn:#{ name }")
@@ -11,6 +58,8 @@ class SimpleWS < SOAP::RPC::StandaloneServer
11
58
  driver.wsdl
12
59
  end
13
60
 
61
+ # Builds on the get_wsdl function to provide the client with a
62
+ # reference to the driver. Again, only works with SimpleWS servers.
14
63
  def self.get_driver(url, name)
15
64
  require 'soap/wsdlDriver'
16
65
  require 'fileutils'
@@ -24,6 +73,12 @@ class SimpleWS < SOAP::RPC::StandaloneServer
24
73
  driver
25
74
  end
26
75
 
76
+ # Creates an instance of a Server. The parameter +name+ specifies the
77
+ # +namespace+ used in the +WSDL+, +description+ is the description
78
+ # also included in the +WSDL+. The parameters +host+ and +port+,
79
+ # specify where the server will be listening, they are parameters of
80
+ # the +super+ class SOAP::RPC::StandaloneServer, they are made
81
+ # explicit here because they are included in the +WSDL+ as well.
27
82
  def initialize(name="WS", description="", host="localhost", port="1984", *args)
28
83
  super(description, "urn:#{ name }", host, port, *args)
29
84
  @host = host
@@ -37,11 +92,20 @@ class SimpleWS < SOAP::RPC::StandaloneServer
37
92
  serve :wsdl, %w(), :return => :string
38
93
  end
39
94
 
40
- def name
41
- @name
42
- end
43
-
44
95
 
96
+ # This method tells the server to provide a method named by the +name+
97
+ # parameter, with arguments listed in the +args+ parameter. The
98
+ # +types+ parameter specifies the types of the arguments as will be
99
+ # described in the +WSDL+ file (see the TYPES2WSDL constant). The
100
+ # actual method called will be the one with the same name. Optionally
101
+ # a block can be provided, this block will be used to define a
102
+ # function named as in name.
103
+ #
104
+ # If the method returns something, then the type of the return value
105
+ # must be specified in the +types+ parameter as :return. If not value
106
+ # for :return parameter is specified in the +types+ parameter the
107
+ # method is taken to return no value. Other than that, if a parameter
108
+ # type is omitted it is taken to be :string.
45
109
  def serve(name, args=[], types={}, &block)
46
110
 
47
111
  if block
@@ -54,6 +118,11 @@ class SimpleWS < SOAP::RPC::StandaloneServer
54
118
  nil
55
119
  end
56
120
 
121
+ # If +filename+ is specified it saves the +WSDL+ file in that file. If
122
+ # no +filename+ is specified it returns a string containing the
123
+ # +WSDL+. The no parameter version is served by default, so that
124
+ # clients can use this method to access the complete +WSDL+ file, as
125
+ # used in get_wsdl and get_driver.
57
126
  def wsdl(filename = nil)
58
127
  wsdl = WSDL_STUB.dup
59
128
  wsdl.gsub!(/\$\{MESSAGES\}/m,@messages.join("\n"))
@@ -83,7 +152,7 @@ class SimpleWS < SOAP::RPC::StandaloneServer
83
152
  args.each{|param|
84
153
  type = types[param.to_s] || types[param.to_sym] || :string
85
154
  type = type.to_sym
86
- xml.part :name => param, :type => @@type2wsdl[type]
155
+ xml.part :name => param, :type => TYPES2WSDL[type]
87
156
  }
88
157
  end
89
158
  @messages << message
@@ -91,7 +160,7 @@ class SimpleWS < SOAP::RPC::StandaloneServer
91
160
  type = types[:return] || types["return"]
92
161
  if type
93
162
  type = type.to_sym
94
- xml.part :name => 'return', :type => @@type2wsdl[type]
163
+ xml.part :name => 'return', :type => TYPES2WSDL[type]
95
164
  end
96
165
  end
97
166
  @messages << message
@@ -117,6 +186,7 @@ class SimpleWS < SOAP::RPC::StandaloneServer
117
186
 
118
187
  end
119
188
 
189
+
120
190
  WSDL_STUB =<<EOT
121
191
  <?xml version="1.0"?>
122
192
  <definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
@@ -167,7 +237,7 @@ ${BINDINGS}
167
237
  </definitions>
168
238
  EOT
169
239
 
170
- @@type2wsdl = {
240
+ TYPES2WSDL = {
171
241
  :boolean => 'xsd:boolean',
172
242
  :string => 'xsd:string',
173
243
  :integer => 'xsd:integer',
@@ -9,7 +9,7 @@ class TestSimpleWS < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  def process(name)
12
- name = start_job(name, %w(test.txt)) do
12
+ actual_name = start_job(name, %w(test.txt)) do
13
13
  begin
14
14
  write('test.txt', job_name)
15
15
  step(:init)
@@ -20,12 +20,11 @@ class TestSimpleWS < Test::Unit::TestCase
20
20
  sleep 2
21
21
  step(:step2, "Step3")
22
22
  rescue
23
- puts $!.message
24
- raise $!
23
+ error($!.message)
25
24
  end
26
25
  end
27
26
 
28
- name
27
+ actual_name
29
28
  end
30
29
 
31
30
  def initialize(*args)
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.5
4
+ version: 1.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez