t2-server 0.0.4 → 0.1.0

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/README.rdoc CHANGED
@@ -1,7 +1,7 @@
1
1
  = Taverna[http://www.taverna.org.uk/] 2 Server Interaction Gem
2
2
 
3
3
  Authors:: Robert Haines
4
- Gem Version:: 0.0.4
4
+ Gem Version:: 0.1.0
5
5
  API Version:: 2.2a1
6
6
  Contact:: mailto:rhaines@manchester.ac.uk
7
7
  URL:: http://taverna.sourceforge.net/
@@ -29,7 +29,14 @@ http://github.com/myGrid/t2-server-gem
29
29
 
30
30
  == Usage
31
31
 
32
- As well as the library there are also a couple of example scripts which
32
+ There are two entry points for the T2Server API:
33
+ * T2Server::Run - Use this for running single jobs on a server.
34
+ * T2Server::Server - Use this if you are providing a web interface to one or
35
+ more Taverna 2 Server instances.
36
+
37
+ See the rdoc for more information.
38
+
39
+ As well as rdoc there are also a couple of example scripts which
33
40
  demonstrate good use of the T2Server API. These are available in the
34
41
  <tt>bin</tt> directory:
35
42
  * run_workflow
data/bin/delete_all_runs CHANGED
@@ -62,4 +62,9 @@ if ARGV[0] == nil
62
62
  end
63
63
 
64
64
  # connect and delete them all!
65
- T2Server::Server.connect(ARGV[0]).delete_all_runs
65
+ begin
66
+ T2Server::Server.connect(ARGV[0]).delete_all_runs
67
+ rescue T2Server::T2ServerError => e
68
+ puts e
69
+ exit 1
70
+ end
data/bin/run_workflow CHANGED
@@ -47,8 +47,8 @@ def get_outputs(run, pout, dir="")
47
47
  if pout
48
48
  p data
49
49
  else
50
- # remove leading slash
51
- filename = "#{dir[1..-1]}/#{out}".gsub('/', '-')
50
+ # strip path and convert remove /'s for file output
51
+ filename = "#{dir.strip_path}/#{out}".gsub('/', '-')
52
52
  File.open(filename, "w") do |file|
53
53
  file.syswrite(data)
54
54
  end
@@ -106,11 +106,17 @@ else
106
106
  wkf = IO.read(wkf_file)
107
107
  end
108
108
 
109
- # create run and set inputs
110
- run = T2Server::Run.create(uri, wkf)
109
+ # create run
110
+ begin
111
+ run = T2Server::Run.create(uri, wkf)
112
+ rescue T2Server::T2ServerError => e
113
+ puts e
114
+ exit 1
115
+ end
111
116
  puts "Created run with uuid: #{run.uuid}"
112
117
  puts "Created at #{run.create_time}"
113
118
 
119
+ # set inputs
114
120
  inputs.each do |input, value|
115
121
  puts "Set input '#{input}' to #{value}"
116
122
  run.set_input(input, value)
@@ -126,12 +132,15 @@ puts "Finished at #{run.finish_time}"
126
132
  # get outputs
127
133
  stdout = run.stdout
128
134
  stderr = run.stderr
129
- puts "Exitcode: #{run.exitcode}"
130
- if stdout != "" then puts "Stdout:\n #{stdout}" end
131
- if stderr != "" then puts "Stderr:\n #{stderr}" end
135
+ exitcd = run.exitcode
136
+ puts "Exitcode: #{exitcd}"
137
+ if stdout != "" then puts "Stdout:\n#{stdout}" end
138
+ if stderr != "" then puts "Stderr:\n#{stderr}" end
132
139
 
133
- puts "Outputs:"
134
- get_outputs(run, print_output)
140
+ if exitcd == 0
141
+ puts "Outputs:"
142
+ get_outputs(run, print_output)
143
+ end
135
144
 
136
145
  # delete run
137
146
  run.delete
data/bin/server_info CHANGED
@@ -62,14 +62,19 @@ if uri == nil
62
62
  end
63
63
 
64
64
  # connect to server and output information
65
- server = T2Server::Server.connect(uri)
66
- print " Server: #{uri}\n"
67
- print " Run limit: #{server.run_limit}\n"
68
- runs = server.runs
69
- print "No. of runs: #{runs.length}\n"
70
- if runs.length > 0
71
- print " Run list: #{runs[0].uuid} - #{runs[0].expiry}\n"
72
- runs[1..-1].each do |run|
73
- print " #{run.uuid} - #{run.expiry}\n"
65
+ begin
66
+ server = T2Server::Server.connect(uri)
67
+ print " Server: #{uri}\n"
68
+ print " Run limit: #{server.run_limit}\n"
69
+ runs = server.runs
70
+ print "No. of runs: #{runs.length}\n"
71
+ if runs.length > 0
72
+ print " Run list: #{runs[0].uuid} - #{runs[0].expiry}\n"
73
+ runs[1..-1].each do |run|
74
+ print " #{run.uuid} - #{run.expiry}\n"
75
+ end
74
76
  end
77
+ rescue T2Server::T2ServerError => e
78
+ puts e
79
+ exit 1
75
80
  end
data/lib/t2server.rb CHANGED
@@ -31,10 +31,41 @@
31
31
  # Author: Robert Haines
32
32
 
33
33
  require 't2server/xml'
34
+ require 't2server/exceptions'
34
35
  require 't2server/server'
35
36
  require 't2server/run'
36
37
 
38
+ # This is a Ruby library to interface with the Taverna 2 Server REST API.
39
+ #
40
+ # There are two API entry points:
41
+ # * T2Server::Run - Use this for running single jobs on a server.
42
+ # * T2Server::Server - Use this if you are providing a web interface to a
43
+ # Taverna 2 Server instance.
37
44
  module T2Server
45
+ # The version of this library
38
46
  GEM_VERSION = "0.0.4"
47
+ # The version of the Taverna 2 Server API that this library can interface with
39
48
  API_VERSION = "2.2a1"
40
49
  end
50
+
51
+ # Add methods to the String class to operate on file paths.
52
+ class String
53
+ # :call-seq:
54
+ # str.strip_path -> string
55
+ #
56
+ # Returns a new String with one leading and one trailing slash
57
+ # removed from the ends of _str_ (if present).
58
+ def strip_path
59
+ self.gsub(/^\//, "").chomp("/")
60
+ end
61
+
62
+ # :call-seq:
63
+ # str.strip_path! -> str or nil
64
+ #
65
+ # Modifies _str_ in place as described for String#strip_path,
66
+ # returning _str_, or returning +nil+ if no modifications were made.
67
+ def strip_path!
68
+ g = self.gsub!(/^\//, "")
69
+ self.chomp!("/") || g
70
+ end
71
+ end
@@ -0,0 +1,153 @@
1
+ # Copyright (c) 2010, The University of Manchester, UK.
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # * Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # * Neither the names of The University of Manchester nor the names of its
16
+ # contributors may be used to endorse or promote products derived from this
17
+ # software without specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ # POSSIBILITY OF SUCH DAMAGE.
30
+ #
31
+ # Author: Robert Haines
32
+
33
+ require 'net/http'
34
+
35
+ module T2Server
36
+ # An internal module to collect all the exceptions that we
37
+ # can't really do anything about ourselves, such as
38
+ # timeouts and lost connections. This is further wrapped
39
+ # and exposed in the API as T2Server::ConnectionError below.
40
+ module InternalHTTPError #:nodoc:
41
+ end
42
+
43
+ # These are the HTTP errors we want to catch.
44
+ # Add the above exception as an ancestor to them all.
45
+ [
46
+ EOFError,
47
+ SocketError,
48
+ Timeout::Error,
49
+ Errno::EINVAL,
50
+ Errno::ETIMEDOUT,
51
+ Errno::ECONNRESET,
52
+ Errno::ECONNREFUSED,
53
+ Net::HTTPBadResponse,
54
+ Net::HTTPHeaderSyntaxError,
55
+ Net::ProtocolError
56
+ ].each {|err| err.send(:include, InternalHTTPError)}
57
+
58
+ # This is a superclass for all T2Server exceptions. It is provided as a
59
+ # useful catch-all for all the internally raised/thrown exceptions.
60
+ class T2ServerError < RuntimeError
61
+ end
62
+
63
+ # Raised when there is an error with the connection to the server in some
64
+ # way. This could be due to the server not accepting the connection, the
65
+ # connection being dropped unexpectedly or a timeout of some sort.
66
+ class ConnectionError < T2ServerError
67
+ attr_reader :cause
68
+
69
+ # Create a new ConnectionError with the specified cause. The cause to be
70
+ # passed in should be the exception object that caused the connection
71
+ # error.
72
+ def initialize(cause)
73
+ @cause = cause
74
+ super "Connection error (#{@cause.class.name}): #{@cause.message}"
75
+ end
76
+ end
77
+
78
+ # Raised when there is an unexpected response from the server. This does
79
+ # not necessarily indicate a problem with the server.
80
+ class UnexpectedServerResponse < T2ServerError
81
+
82
+ # Create a new UnexpectedServerResponse with the specified unexpected
83
+ # response. The response to be passed in is that which was returned by a
84
+ # call to Net::HTTP#request.
85
+ def initialize(response)
86
+ body = "\n#{response.body}" if response.body
87
+ super "Unexpected server response: #{response.code}\n#{response.error!}#{body}"
88
+ end
89
+ end
90
+
91
+ # Raised when the run that is being operated on cannot be found. If the
92
+ # expectation is that the run exists then it could have been destroyed by
93
+ # a timeout or another user.
94
+ class RunNotFoundError < T2ServerError
95
+ attr_reader :uuid
96
+
97
+ # Create a new RunNotFoundError with the specified UUID.
98
+ def initialize(uuid)
99
+ @uuid = uuid
100
+ super "Could not find run #{@uuid}"
101
+ end
102
+ end
103
+
104
+ # Indicates that the attribute that the user is trying to read/change does
105
+ # not exist. The attribute could be a server or run attribute.
106
+ class AttributeNotFoundError < T2ServerError
107
+ attr_reader :path
108
+
109
+ # Create a new AttributeNotFoundError with the path to the erroneous
110
+ # attribute.
111
+ def initialize(path)
112
+ @path = path
113
+ super "Could not find attribute at #{@path}"
114
+ end
115
+ end
116
+
117
+ # The server is at capacity and cannot accept anymore runs at this time.
118
+ class ServerAtCapacityError < T2ServerError
119
+ attr_reader :limit
120
+
121
+ # Create a new ServerAtCapacityError with the specified limit for
122
+ # information.
123
+ def initialize(limit)
124
+ @limit = limit
125
+ super "The server is already running its configured limit of concurrent workflows (#{@limit})"
126
+ end
127
+ end
128
+
129
+ # Access to the entity (run or attribute) is denied. The credentials
130
+ # supplied are not sufficient or the server does not allow the operation.
131
+ class AccessForbiddenError < T2ServerError
132
+ attr_reader :path
133
+
134
+ # Create a new AccessForbiddenError with the path to the restricted
135
+ # attribute.
136
+ def initialize(path)
137
+ @path = path
138
+ super "Access to #{@path} is forbidden. Either you do not have the required credentials or the server does not allow the requested operation"
139
+ end
140
+ end
141
+
142
+ # Raised if an operation is performed on a run when it is in the wrong
143
+ # state. Trying to start a run if it is the finished state would cause this
144
+ # exception to be raised.
145
+ class RunStateError < T2ServerError
146
+
147
+ # Create a new RunStateError specifying both the current state and that
148
+ # which is needed to run the operation.
149
+ def initialize(current, need)
150
+ super "The run is in the wrong state (#{current}); it should be '#{need}' to perform that action"
151
+ end
152
+ end
153
+ end
data/lib/t2server/run.rb CHANGED
@@ -34,19 +34,29 @@ require 'rexml/document'
34
34
  include REXML
35
35
 
36
36
  module T2Server
37
-
37
+
38
+ # An interface for easily running jobs on a Taverna 2 Server with minimal
39
+ # setup and configuration required.
40
+ #
41
+ # A run can be in one of three states:
42
+ # * Initialized: The run has been accepted by the server. It may not yet be
43
+ # ready to run though as its input port may not have been set.
44
+ # * Running: The run is being run by the server.
45
+ # * Finished: The run has finished running and its outputs are available for
46
+ # download.
38
47
  class Run
39
-
48
+ private_class_method :new
49
+ attr_reader :uuid
50
+
51
+ # :stopdoc:
40
52
  STATE = {
41
53
  :initialized => "Initialized",
42
54
  :running => "Operating",
43
55
  :finished => "Finished",
44
56
  :stopped => "Stopped"
45
57
  }
46
-
47
- private_class_method :new
48
- attr_reader :uuid
49
-
58
+
59
+ # New is private but rdoc does not get it right! Hence :stopdoc: section.
50
60
  def initialize(server, uuid)
51
61
  @server = server
52
62
  @uuid = uuid
@@ -56,7 +66,16 @@ module T2Server
56
66
  @links = get_attributes(@server.get_run_attribute(uuid, ""))
57
67
  #@links.each {|key, val| puts "#{key}: #{val}"}
58
68
  end
69
+ # :startdoc:
59
70
 
71
+ # :call-seq:
72
+ # Run.create(server, workflow) -> run
73
+ #
74
+ # Create a new run in the +Initialized+ state. The run will be created on
75
+ # the server with address supplied by _server_. This can either be a
76
+ # String of the form <tt>http://example.com:8888/blah</tt> or an already
77
+ # created instance of T2Server::Server. The _workflow_ must also be
78
+ # supplied as a string in t2flow or scufl format.
60
79
  def Run.create(server, workflow, uuid="")
61
80
  if server.class == String
62
81
  server = Server.connect(server)
@@ -67,55 +86,130 @@ module T2Server
67
86
  new(server, uuid)
68
87
  end
69
88
  end
70
-
89
+
90
+ # :call-seq:
91
+ # run.delete
92
+ #
93
+ # Delete this run from the server.
71
94
  def delete
72
95
  @server.delete_run uuid
73
96
  end
74
-
97
+
98
+ # :call-seq:
99
+ # run.inputs -> string
100
+ #
101
+ # Return the path to the input ports of this run on the server.
75
102
  def inputs
76
103
  @links[:inputs]
77
104
  end
78
-
105
+
106
+ # :call-seq:
107
+ # run.set_input(input, value) -> bool
108
+ #
109
+ # Set the workflow input port _input_ to _value_.
110
+ #
111
+ # Raises RunStateError if the run is not in the +Initialized+ state.
79
112
  def set_input(input, value)
113
+ state = status
114
+ raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
115
+
80
116
  @server.set_run_input(self, input, value)
81
117
  end
82
-
118
+
119
+ # :call-seq:
120
+ # run.set_input_file(input, filename) -> bool
121
+ #
122
+ # Set the workflow input port _input_ to use the file at _filename_ as its
123
+ # input data.
124
+ #
125
+ # Raises RunStateError if the run is not in the +Initialized+ state.
83
126
  def set_input_file(input, filename)
127
+ state = status
128
+ raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
129
+
84
130
  @server.set_run_input_file(self, input, filename)
85
131
  end
86
-
132
+
133
+ # :call-seq:
134
+ # run.get_output(output) -> string
135
+ #
136
+ # Return the value of the workflow output port _output_.
137
+ #
138
+ # Raises RunStateError if the run is not in the +Finished+ state.
87
139
  def get_output(output, type="text/plain")
88
- return unless finished? ### raise exception?
140
+ state = status
141
+ raise RunStateError.new(state, STATE[:finished]) if state != STATE[:finished]
142
+
143
+ output.strip_path!
89
144
  doc = @server.get_run_attribute(@uuid, "#{@links[:wdir]}/out/#{output}")
90
145
  doc
91
146
  end
92
-
147
+
148
+ # :call-seq:
149
+ # run.expiry -> string
150
+ #
151
+ # Return the expiry time of this run. It is formatted as an ISO-8601
152
+ # timestamp.
93
153
  def expiry
94
154
  @server.get_run_attribute(@uuid, @links[:expiry])
95
155
  end
96
-
156
+
157
+ # :call-seq:
158
+ # run.expiry=(time) -> bool
159
+ #
160
+ # Set the expiry time of this run to _time_. The format of _time_ should
161
+ # be an ISO-8601 timestamp.
97
162
  def expiry=(date)
98
163
  @server.set_run_attribute(@uuid, @links[:expiry], date)
99
164
  end
100
165
 
166
+ # :call-seq:
167
+ # run.workflow -> string
168
+ #
169
+ # Get the workflow that this run represents.
101
170
  def workflow
102
171
  if @workflow == ""
103
172
  @workflow = @server.get_run_attribute(@uuid, @links[:workflow])
104
173
  end
105
174
  @workflow
106
175
  end
107
-
176
+
177
+ # :call-seq:
178
+ # run.status -> string
179
+ #
180
+ # Get the status of this run.
108
181
  def status
109
182
  @server.get_run_attribute(@uuid, @links[:status])
110
183
  end
111
-
184
+
185
+ # :call-seq:
186
+ # run.start
187
+ #
188
+ # Start this run on the server.
189
+ #
190
+ # Raises RunStateError if the run is not in the +Initialized+ state.
112
191
  def start
192
+ state = status
193
+ raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
194
+
113
195
  @server.set_run_attribute(@uuid, @links[:status], STATE[:running])
114
196
  end
115
-
197
+
198
+ # :call-seq:
199
+ # run.wait(params={})
200
+ #
201
+ # Wait (block) for this run to finish. Possible values that can be passed
202
+ # in via _params_ are:
203
+ # * :interval - How often (in seconds) to test for run completion.
204
+ # Default +1+.
205
+ # * :progress - Print a dot (.) each interval to show that something is
206
+ # actually happening. Default +false+.
207
+ #
208
+ # Raises RunStateError if the run is not in the +Running+ state.
116
209
  def wait(params={})
117
- return unless running?
118
-
210
+ state = status
211
+ raise RunStateError.new(state, STATE[:running]) if state != STATE[:running]
212
+
119
213
  interval = params[:interval] || 1
120
214
  progress = params[:progress] || false
121
215
  keepalive = params[:keepalive] || false ### TODO maybe move out of params
@@ -132,42 +226,108 @@ module T2Server
132
226
  # tidy up output if there is any
133
227
  puts if progress
134
228
  end
135
-
229
+
230
+ # :call-seq:
231
+ # run.exitcode -> integer
232
+ #
233
+ # Get the return code of the run. Zero indicates success.
136
234
  def exitcode
137
235
  @server.get_run_attribute(@uuid, @links[:exitcode]).to_i
138
236
  end
139
-
237
+
238
+ # :call-seq:
239
+ # run.stdout -> string
240
+ #
241
+ # Get anything that the run printed to the standard out stream.
140
242
  def stdout
141
243
  @server.get_run_attribute(@uuid, @links[:stdout])
142
244
  end
143
-
245
+
246
+ # :call-seq:
247
+ # run.stderr -> string
248
+ #
249
+ # Get anything that the run printed to the standard error stream.
144
250
  def stderr
145
251
  @server.get_run_attribute(@uuid, @links[:stderr])
146
252
  end
147
-
253
+
254
+ # :call-seq:
255
+ # run.mkdir(dir) -> bool
256
+ #
257
+ # Create a directory in the run's working directory on the server. This
258
+ # could be used to store input data.
148
259
  def mkdir(dir)
149
- @server.make_run_dir(@uuid, @links[:wdir], dir)
260
+ dir.strip_path!
261
+ if dir.include? ?/
262
+ # if a path is given then separate the leaf from the
263
+ # end and add the rest of the path to the wdir link
264
+ leaf = dir.split("/")[-1]
265
+ path = dir[0...-(leaf.length + 1)]
266
+ @server.make_run_dir(@uuid, "#{@links[:wdir]}/#{path}", leaf)
267
+ else
268
+ @server.make_run_dir(@uuid, @links[:wdir], dir)
269
+ end
150
270
  end
151
-
271
+
272
+ # :call-seq:
273
+ # run.upload_file(filename, params={}) -> string
274
+ #
275
+ # Upload a file, with name _filename_, to the server. Possible values that
276
+ # can be passed in via _params_ are:
277
+ # * :dir - The directory to upload to. If this is not left blank the
278
+ # corresponding directory will need to have been created by Run#mkdir.
279
+ # * :rename - Save the file on the server with a different name.
280
+ #
281
+ # The name of the file on the server is returned.
152
282
  def upload_file(filename, params={})
153
283
  location = params[:dir] || ""
154
284
  location = "#{@links[:wdir]}/#{location}"
155
285
  rename = params[:rename] || ""
156
286
  @server.upload_run_file(@uuid, filename, location, rename)
157
287
  end
158
-
288
+
289
+ # :call-seq:
290
+ # run.upload_input_file(input, filename, params={}) -> string
291
+ #
292
+ # Upload a file, with name _filename_, to the server and set it as the
293
+ # input data for input port _input_. Possible values that can be passed
294
+ # in via _params_ are:
295
+ # * :dir - The directory to upload to. If this is not left blank the
296
+ # corresponding directory will need to have been created by Run#mkdir.
297
+ # * :rename - Save the file on the server with a different name.
298
+ #
299
+ # The name of the file on the server is returned.
300
+ #
301
+ # Raises RunStateError if the run is not in the +Initialized+ state.
159
302
  def upload_input_file(input, filename, params={})
303
+ state = status
304
+ raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
305
+
160
306
  file = upload_file(filename, params)
161
307
  set_input_file(input, file)
162
308
  end
163
-
309
+
310
+ # :call-seq:
311
+ # run.upload_baclava_file(filename) -> bool
312
+ #
313
+ # Upload a baclava file to be used for the workflow inputs.
164
314
  def upload_baclava_file(filename)
315
+ state = status
316
+ raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
317
+
165
318
  @baclava = true
166
319
  rename = upload_file(filename)
167
320
  @server.set_run_attribute(@uuid, @links[:baclava], rename)
168
321
  end
169
322
 
323
+ # :call-seq:
324
+ # run.ls(dir="") -> [[dirs], [objects]]
325
+ #
326
+ # List a directory in the run's workspace on the server. If _dir_ is left
327
+ # blank then / is listed. The contents of a directory are returned as a
328
+ # list of two lists, directories and "objects" respectively.
170
329
  def ls(dir="")
330
+ dir.strip_path!
171
331
  dir_list = @server.get_run_attribute(@uuid, "#{@links[:wdir]}/#{dir}")
172
332
  doc = Document.new(dir_list)
173
333
 
@@ -180,26 +340,50 @@ module T2Server
180
340
  [dirs, files]
181
341
  end
182
342
 
343
+ # :call-seq:
344
+ # run.initialized? -> bool
345
+ #
346
+ # Is this run in the +Initialized+ state?
183
347
  def initialized?
184
348
  status == STATE[:initialized]
185
349
  end
186
-
350
+
351
+ # :call-seq:
352
+ # run.running? -> bool
353
+ #
354
+ # Is this run in the +Running+ state?
187
355
  def running?
188
356
  status == STATE[:running]
189
357
  end
190
-
358
+
359
+ # :call-seq:
360
+ # run.finished? -> bool
361
+ #
362
+ # Is this run in the +Finished+ state?
191
363
  def finished?
192
364
  status == STATE[:finished]
193
365
  end
194
-
366
+
367
+ # :call-seq:
368
+ # run.create_time -> string
369
+ #
370
+ # Get the creation time of this run formatted as an ISO-8601 timestamp.
195
371
  def create_time
196
372
  @server.get_run_attribute(@uuid, @links[:createtime])
197
373
  end
198
-
374
+
375
+ # :call-seq:
376
+ # run.start_time -> string
377
+ #
378
+ # Get the start time of this run formatted as an ISO-8601 timestamp.
199
379
  def start_time
200
380
  @server.get_run_attribute(@uuid, @links[:starttime])
201
381
  end
202
382
 
383
+ # :call-seq:
384
+ # run.finish_time -> string
385
+ #
386
+ # Get the finish time of this run formatted as an ISO-8601 timestamp.
203
387
  def finish_time
204
388
  @server.get_run_attribute(@uuid, @links[:finishtime])
205
389
  end