t2-server 0.9.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.rdoc +79 -0
- data/LICENCE.rdoc +1 -1
- data/README.rdoc +94 -26
- data/Rakefile +6 -5
- data/bin/t2-delete-runs +25 -23
- data/bin/t2-get-output +11 -13
- data/bin/t2-run-workflow +91 -29
- data/bin/t2-server-info +29 -12
- data/extras/t2-server-stress +184 -0
- data/lib/t2-server-cli.rb +48 -23
- data/lib/t2-server.rb +2 -1
- data/lib/t2-server/admin.rb +7 -4
- data/lib/t2-server/exceptions.rb +23 -4
- data/lib/t2-server/interaction.rb +241 -0
- data/lib/t2-server/net/connection.rb +90 -60
- data/lib/t2-server/net/credentials.rb +25 -9
- data/lib/t2-server/net/parameters.rb +21 -6
- data/lib/t2-server/port.rb +229 -140
- data/lib/t2-server/run-cache.rb +99 -0
- data/lib/t2-server/run.rb +349 -332
- data/lib/t2-server/server.rb +115 -164
- data/lib/t2-server/util.rb +11 -9
- data/lib/t2-server/xml/libxml.rb +3 -2
- data/lib/t2-server/xml/nokogiri.rb +4 -3
- data/lib/t2-server/xml/rexml.rb +3 -2
- data/lib/t2-server/xml/xml.rb +47 -36
- data/lib/{t2server.rb → t2-server/xml/xpath_cache.rb} +29 -7
- data/t2-server.gemspec +16 -5
- data/test/tc_misc.rb +61 -0
- data/test/tc_perms.rb +17 -1
- data/test/tc_run.rb +164 -34
- data/test/tc_secure.rb +11 -2
- data/test/tc_server.rb +23 -2
- data/test/ts_t2server.rb +10 -8
- data/test/workflows/missing_outputs.t2flow +440 -0
- data/version.yml +3 -3
- metadata +42 -4
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2010
|
1
|
+
# Copyright (c) 2010-2012 The University of Manchester, UK.
|
2
2
|
#
|
3
3
|
# All rights reserved.
|
4
4
|
#
|
@@ -30,23 +30,33 @@
|
|
30
30
|
#
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
|
+
# :stopdoc:
|
34
|
+
# This comment is needed to stop the above licence from being included in the
|
35
|
+
# documentation multiple times. Sigh.
|
33
36
|
module T2Server
|
37
|
+
# :startdoc:
|
34
38
|
|
35
39
|
# This class serves as a base class for concrete HTTP credential systems.
|
40
|
+
#
|
41
|
+
# Instances of this class cannot be used to authenticate a connection;
|
42
|
+
# please use HttpBasic instead.
|
36
43
|
class HttpCredentials
|
37
44
|
# The username held by these credentials.
|
38
45
|
attr_reader :username
|
39
46
|
|
47
|
+
# :stopdoc:
|
40
48
|
# Create a set of credentials with the supplied username and password.
|
41
49
|
def initialize(username, password)
|
42
50
|
@username = username
|
43
51
|
@password = password
|
44
52
|
end
|
53
|
+
# :startdoc:
|
45
54
|
|
46
55
|
# :call-seq:
|
47
|
-
# to_s
|
56
|
+
# to_s -> string
|
48
57
|
#
|
49
|
-
# Return
|
58
|
+
# Return a String representation of these credentials. Just the username
|
59
|
+
# is returned; the password is kept hidden.
|
50
60
|
def to_s
|
51
61
|
@username
|
52
62
|
end
|
@@ -55,7 +65,7 @@ module T2Server
|
|
55
65
|
@@to_s = Kernel.instance_method(:to_s)
|
56
66
|
|
57
67
|
# :call-seq:
|
58
|
-
# inspect
|
68
|
+
# inspect -> string
|
59
69
|
#
|
60
70
|
# Override the Kernel#inspect method so that the password is not exposed
|
61
71
|
# when it is called.
|
@@ -64,21 +74,27 @@ module T2Server
|
|
64
74
|
end
|
65
75
|
end
|
66
76
|
|
67
|
-
# A class representing HTTP Basic credentials.
|
77
|
+
# A class representing HTTP Basic credentials. Use this class to
|
78
|
+
# authenticate operations on a Taverna Server that require it.
|
79
|
+
#
|
80
|
+
# See also Util.strip_uri_credentials.
|
68
81
|
class HttpBasic < HttpCredentials
|
69
82
|
|
70
|
-
#
|
83
|
+
# :call-seq:
|
84
|
+
# new(username, password) -> HttpBasic
|
85
|
+
#
|
86
|
+
# Create a set of basic credentials using the supplied username and
|
87
|
+
# password.
|
71
88
|
def initialize(username, password)
|
72
89
|
super(username, password)
|
73
90
|
end
|
74
91
|
|
75
|
-
# :
|
76
|
-
# authenticate(request)
|
77
|
-
#
|
92
|
+
# :stopdoc:
|
78
93
|
# Authenticate the supplied HTTP request with the credentials held within
|
79
94
|
# this class.
|
80
95
|
def authenticate(request)
|
81
96
|
request.basic_auth @username, @password
|
82
97
|
end
|
98
|
+
# :startdoc:
|
83
99
|
end
|
84
100
|
end
|
@@ -39,11 +39,14 @@ module T2Server
|
|
39
39
|
# stored.
|
40
40
|
#
|
41
41
|
# The parameters that can be set are:
|
42
|
-
# * :ca_file
|
43
|
-
# * :ca_path
|
44
|
-
# * :verify_peer
|
45
|
-
# * :client_certificate
|
46
|
-
# * :client_password
|
42
|
+
# * :ca_file - A file with the correct CA chain to verify the remote server.
|
43
|
+
# * :ca_path - A directory containing the CA files for server verification.
|
44
|
+
# * :verify_peer - Use peer verification? (true or false).
|
45
|
+
# * :client_certificate - File with the client's certificate and private key.
|
46
|
+
# * :client_password - The password to unlock the client's private key.
|
47
|
+
# * :ssl_version - The TLS/SSL version to use (:TLSv1, :SSLv23 or :SSLv3).
|
48
|
+
# * :open_timeout - The number of seconds to wait while opening a connection.
|
49
|
+
# * :read_timeout - The number of seconds to wait while reading from a connection.
|
47
50
|
# All others will be ignored. Any parameters not set will return +nil+ when
|
48
51
|
# queried.
|
49
52
|
class ConnectionParameters
|
@@ -53,7 +56,10 @@ module T2Server
|
|
53
56
|
:ca_path,
|
54
57
|
:verify_peer,
|
55
58
|
:client_certificate,
|
56
|
-
:client_password
|
59
|
+
:client_password,
|
60
|
+
:ssl_version,
|
61
|
+
:open_timeout,
|
62
|
+
:read_timeout
|
57
63
|
]
|
58
64
|
# :startdoc:
|
59
65
|
|
@@ -98,6 +104,15 @@ module T2Server
|
|
98
104
|
end
|
99
105
|
end
|
100
106
|
|
107
|
+
# Connection parameters that specify the use of SSL version 3.
|
108
|
+
class SSL3ConnectionParameters < DefaultConnectionParameters
|
109
|
+
# Create connection parameters that specify the use of SSL version 3.
|
110
|
+
def initialize
|
111
|
+
super
|
112
|
+
self[:ssl_version] = :SSLv3
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
101
116
|
# Connection parameters that simplify setting up verification of servers with
|
102
117
|
# "self-signed" or non-standard certificates.
|
103
118
|
class CustomCASSLConnectionParameters < DefaultConnectionParameters
|
data/lib/t2-server/port.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2010-
|
1
|
+
# Copyright (c) 2010-2013 The University of Manchester, UK.
|
2
2
|
#
|
3
3
|
# All rights reserved.
|
4
4
|
#
|
@@ -30,7 +30,11 @@
|
|
30
30
|
#
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
|
+
# :stopdoc:
|
34
|
+
# This comment is needed to stop the above licence from being included in the
|
35
|
+
# documentation multiple times. Sigh.
|
33
36
|
module T2Server
|
37
|
+
# :startdoc:
|
34
38
|
|
35
39
|
# Base class of InputPort and OutputPort
|
36
40
|
class Port
|
@@ -91,7 +95,7 @@ module T2Server
|
|
91
95
|
end
|
92
96
|
|
93
97
|
# :call-seq:
|
94
|
-
# file? ->
|
98
|
+
# file? -> true or false
|
95
99
|
#
|
96
100
|
# Is this port's data being supplied by a file? The file could be local or
|
97
101
|
# remote (already on the server) for this to return true.
|
@@ -100,7 +104,7 @@ module T2Server
|
|
100
104
|
end
|
101
105
|
|
102
106
|
# :call-seq:
|
103
|
-
# remote_file? ->
|
107
|
+
# remote_file? -> true or false
|
104
108
|
#
|
105
109
|
# Is this port's data being supplied by a remote (one that is already on
|
106
110
|
# the server) file?
|
@@ -135,7 +139,7 @@ module T2Server
|
|
135
139
|
end
|
136
140
|
|
137
141
|
# :call-seq:
|
138
|
-
# baclava? ->
|
142
|
+
# baclava? -> true or false
|
139
143
|
#
|
140
144
|
# Has this port been set via a baclava document?
|
141
145
|
def baclava?
|
@@ -143,11 +147,11 @@ module T2Server
|
|
143
147
|
end
|
144
148
|
|
145
149
|
# :call-seq:
|
146
|
-
# set? ->
|
150
|
+
# set? -> true or false
|
147
151
|
#
|
148
152
|
# Has this port been set?
|
149
153
|
def set?
|
150
|
-
!value.nil?
|
154
|
+
!value.nil? || file? || baclava?
|
151
155
|
end
|
152
156
|
end
|
153
157
|
|
@@ -173,13 +177,28 @@ module T2Server
|
|
173
177
|
# :startdoc:
|
174
178
|
|
175
179
|
# :call-seq:
|
176
|
-
# error? ->
|
180
|
+
# error? -> true or false
|
177
181
|
#
|
178
182
|
# Is there an error associated with this output port?
|
179
183
|
def error?
|
180
184
|
@error
|
181
185
|
end
|
182
186
|
|
187
|
+
# :call-seq:
|
188
|
+
# empty? -> true or false
|
189
|
+
#
|
190
|
+
# Is this output port empty?
|
191
|
+
#
|
192
|
+
# Note that if the output port holds a list then it is not considered
|
193
|
+
# empty, even if that list is empty. This is because the port itself is
|
194
|
+
# not empty, there is a list there! A separate test should be performed to
|
195
|
+
# see if that list is empty or not.
|
196
|
+
def empty?
|
197
|
+
# Funnily enough, an empty list does *not* make a port empty!
|
198
|
+
return false if @structure.instance_of? Array
|
199
|
+
@structure.empty?
|
200
|
+
end
|
201
|
+
|
183
202
|
# :call-seq:
|
184
203
|
# [int] -> obj
|
185
204
|
#
|
@@ -195,20 +214,27 @@ module T2Server
|
|
195
214
|
end
|
196
215
|
|
197
216
|
# :call-seq:
|
198
|
-
# value ->
|
199
|
-
# value(range) ->
|
200
|
-
# value
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
|
217
|
+
# value -> binary blob
|
218
|
+
# value(range) -> binary blob
|
219
|
+
# value {|chunk| ...}
|
220
|
+
# value(range) {|chunk| ...}
|
221
|
+
# value -> array
|
222
|
+
#
|
223
|
+
# For singleton outputs download or stream the data (or part of it) held
|
224
|
+
# by the output port. Please see the documentation for PortValue#value for
|
225
|
+
# full details.
|
226
|
+
#
|
227
|
+
# For list outputs all data values are downloaded into memory and returned
|
228
|
+
# in an Array structure that mirrors the structure of the output port. Do
|
229
|
+
# not use this form if the output port has large amounts of data! To get
|
230
|
+
# part of a value from a list use something like:
|
231
|
+
# run.output_port("port_name")[0].value(0..100)
|
232
|
+
def value(range = nil, &block)
|
207
233
|
if depth == 0
|
208
234
|
if range.nil?
|
209
|
-
@structure.value
|
235
|
+
@structure.value(&block)
|
210
236
|
else
|
211
|
-
@structure.value(range)
|
237
|
+
@structure.value(range, &block)
|
212
238
|
end
|
213
239
|
else
|
214
240
|
@values = strip(:value) if @values.nil?
|
@@ -217,8 +243,55 @@ module T2Server
|
|
217
243
|
end
|
218
244
|
|
219
245
|
# :call-seq:
|
220
|
-
#
|
221
|
-
#
|
246
|
+
# stream_value(stream) -> fixnum
|
247
|
+
# stream_value(stream, range) -> fixnum
|
248
|
+
#
|
249
|
+
# Stream a singleton port value directly to another stream and return the
|
250
|
+
# number of bytes written. If a range is supplied then only that range of
|
251
|
+
# data is streamed from the server. The stream passed in may be anything
|
252
|
+
# that provides a +write+ method; instances of IO and File, for example.
|
253
|
+
# No data is cached by this method.
|
254
|
+
#
|
255
|
+
# To stream parts of a list port, use PortValue#stream_value on the list
|
256
|
+
# item directly:
|
257
|
+
# run.output_port("port_name")[0].stream_value(stream)
|
258
|
+
def stream_value(stream, range = nil)
|
259
|
+
return 0 unless depth == 0
|
260
|
+
raise ArgumentError,
|
261
|
+
"Stream passed in must provide a write method" unless
|
262
|
+
stream.respond_to? :write
|
263
|
+
|
264
|
+
if range.nil?
|
265
|
+
@structure.stream_value(stream)
|
266
|
+
else
|
267
|
+
@structure.stream_value(stream, range)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# :call-seq:
|
272
|
+
# write_value_to_file(filename) -> fixnum
|
273
|
+
# write_value_to_file(filename, range) -> fixnum
|
274
|
+
#
|
275
|
+
# Stream a singleton port value to a file and return the number of bytes
|
276
|
+
# written. If a range is supplied then only that range of data is
|
277
|
+
# downloaded from the server.
|
278
|
+
#
|
279
|
+
# To save parts of a list port to a file, use
|
280
|
+
# PortValue#write_value_to_file on the list item directly:
|
281
|
+
# run.output_port("port_name")[0].write_value_to_file
|
282
|
+
def write_value_to_file(filename, range = nil)
|
283
|
+
return 0 unless depth == 0
|
284
|
+
|
285
|
+
if range.nil?
|
286
|
+
@structure.write_value_to_file(filename)
|
287
|
+
else
|
288
|
+
@structure.write_value_to_file(filename, range)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# :call-seq:
|
293
|
+
# reference -> string
|
294
|
+
# reference -> array
|
222
295
|
#
|
223
296
|
# Get URI references to the data values of this output port as strings.
|
224
297
|
#
|
@@ -226,50 +299,35 @@ module T2Server
|
|
226
299
|
# uris is returned. For an individual reference from a list use
|
227
300
|
# 'port[].reference'.
|
228
301
|
def reference
|
229
|
-
@refs
|
230
|
-
@refs
|
302
|
+
@refs ||= strip(:reference)
|
231
303
|
end
|
232
304
|
|
233
305
|
# :call-seq:
|
234
|
-
# type ->
|
235
|
-
# type ->
|
306
|
+
# type -> string
|
307
|
+
# type -> array
|
236
308
|
#
|
237
309
|
# Get the mime type of the data value in this output port.
|
238
310
|
#
|
239
311
|
# For a singleton output a single type is returned. For lists an array of
|
240
312
|
# types is returned. For an individual type from a list use 'port[].type'.
|
241
313
|
def type
|
242
|
-
@types
|
243
|
-
@types
|
314
|
+
@types ||= strip(:type)
|
244
315
|
end
|
245
316
|
|
246
317
|
# :call-seq:
|
247
|
-
# size ->
|
248
|
-
# size ->
|
318
|
+
# size -> fixnum
|
319
|
+
# size -> array
|
249
320
|
#
|
250
321
|
# Get the data size of the data value in this output port.
|
251
322
|
#
|
252
323
|
# For a singleton output a single size is returned. For lists an array of
|
253
324
|
# sizes is returned. For an individual size from a list use 'port[].size'.
|
254
325
|
def size
|
255
|
-
@sizes
|
256
|
-
@sizes
|
257
|
-
end
|
258
|
-
|
259
|
-
# :call-seq:
|
260
|
-
# error -> String
|
261
|
-
#
|
262
|
-
# Get the error message (if there is one) of this output port.
|
263
|
-
#
|
264
|
-
# This method is only for use on outputs of depth 0. For other depths use
|
265
|
-
# 'port[].error'
|
266
|
-
def error
|
267
|
-
return nil unless depth == 0
|
268
|
-
@structure.error
|
326
|
+
@sizes ||= strip(:size)
|
269
327
|
end
|
270
328
|
|
271
329
|
# :call-seq:
|
272
|
-
# total_size ->
|
330
|
+
# total_size -> fixnum
|
273
331
|
#
|
274
332
|
# Return the total data size of all the data in this output port.
|
275
333
|
def total_size
|
@@ -282,9 +340,42 @@ module T2Server
|
|
282
340
|
end
|
283
341
|
end
|
284
342
|
|
343
|
+
# :call-seq:
|
344
|
+
# zip -> binary blob
|
345
|
+
# zip(filename) -> fixnum
|
346
|
+
# zip(stream) -> fixnum
|
347
|
+
# zip {|chunk| ...}
|
348
|
+
#
|
349
|
+
# Get the data in this output port directly from the server in zip format.
|
350
|
+
#
|
351
|
+
# This method does not work with singleton ports. Taverna Server cannot
|
352
|
+
# currently return zip files of singleton ports on their own. If you wish
|
353
|
+
# to get a singleton port in a zip file then you can use Run#zip_output
|
354
|
+
# which will return all outputs in a single file.
|
355
|
+
#
|
356
|
+
# If this method is called on a singleton port it will return +nil+ and
|
357
|
+
# streaming from it will return nothing.
|
358
|
+
#
|
359
|
+
# Calling this method with no parameters will simply return a blob of
|
360
|
+
# zipped data. Providing a filename will stream the data directly to that
|
361
|
+
# file and return the number of bytes written. Passing in an object that
|
362
|
+
# has a +write+ method (for example, an instance of File or IO) will
|
363
|
+
# stream the zip data directly to that object and return the number of
|
364
|
+
# bytes that were streamed. Passing in a block will allow access to the
|
365
|
+
# underlying data stream:
|
366
|
+
# port.zip do |chunk|
|
367
|
+
# print chunk
|
368
|
+
# end
|
369
|
+
#
|
370
|
+
# Raises RunStateError if the run has not finished running.
|
371
|
+
def zip(param = nil, &block)
|
372
|
+
return nil if depth == 0
|
373
|
+
@run.zip_output(param, name, &block)
|
374
|
+
end
|
375
|
+
|
285
376
|
# :stopdoc:
|
286
|
-
def download(uri, range = nil)
|
287
|
-
@run.download_output_data(uri, range)
|
377
|
+
def download(uri, range = nil, &block)
|
378
|
+
@run.download_output_data(uri, range, &block)
|
288
379
|
end
|
289
380
|
# :startdoc:
|
290
381
|
|
@@ -301,14 +392,15 @@ module T2Server
|
|
301
392
|
return data
|
302
393
|
when 'value'
|
303
394
|
return PortValue.new(self, xml_node_attribute(node, 'href'), false,
|
304
|
-
xml_node_attribute(node, '
|
305
|
-
xml_node_attribute(node, '
|
395
|
+
xml_node_attribute(node, 'contentByteLength').to_i,
|
396
|
+
xml_node_attribute(node, 'contentType'))
|
306
397
|
when 'error'
|
307
398
|
@error = true
|
308
|
-
return PortValue.new(self, xml_node_attribute(node, 'href'), true
|
399
|
+
return PortValue.new(self, xml_node_attribute(node, 'href'), true,
|
400
|
+
xml_node_attribute(node, 'errorByteLength').to_i)
|
309
401
|
when 'absent'
|
310
402
|
if current_depth == @depth
|
311
|
-
return PortValue.new(self, "", false, "application/x-empty")
|
403
|
+
return PortValue.new(self, "", false, 0, "application/x-empty")
|
312
404
|
else
|
313
405
|
return []
|
314
406
|
end
|
@@ -345,95 +437,116 @@ module T2Server
|
|
345
437
|
# The size (in bytes) of the port value.
|
346
438
|
attr_reader :size
|
347
439
|
|
440
|
+
# The mime-type we use for an error value.
|
441
|
+
ERROR_TYPE = "application/x-error"
|
442
|
+
|
443
|
+
# The mime-type we use for an empty value. Note that an empty value is not
|
444
|
+
# simply an empty string. It is the complete absence of a value.
|
445
|
+
EMPTY_TYPE = "application/x-empty"
|
446
|
+
|
348
447
|
# :stopdoc:
|
349
|
-
def initialize(port, ref, error, type = ""
|
448
|
+
def initialize(port, ref, error, size, type = "")
|
350
449
|
@port = port
|
351
450
|
@reference = URI.parse(ref)
|
352
|
-
@type = type
|
451
|
+
@type = (error ? ERROR_TYPE : type)
|
353
452
|
@size = size
|
354
|
-
@
|
355
|
-
@vgot = nil
|
356
|
-
@error = nil
|
357
|
-
|
358
|
-
if error
|
359
|
-
@error = @port.download(@reference)
|
360
|
-
@type = "error"
|
361
|
-
end
|
453
|
+
@error = error
|
362
454
|
end
|
363
455
|
# :startdoc:
|
364
456
|
|
365
457
|
# :call-seq:
|
366
|
-
# value ->
|
367
|
-
# value(range) ->
|
368
|
-
#
|
369
|
-
#
|
370
|
-
#
|
371
|
-
#
|
372
|
-
#
|
373
|
-
#
|
374
|
-
#
|
375
|
-
#
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
458
|
+
# value -> binary blob
|
459
|
+
# value(range) -> binary blob
|
460
|
+
# value {|chunk| ...}
|
461
|
+
# value(range) {|chunk| ...}
|
462
|
+
#
|
463
|
+
# Get the value of this port from the server.
|
464
|
+
#
|
465
|
+
# If no parameters are supplied then this method will simply download and
|
466
|
+
# return all the data.
|
467
|
+
#
|
468
|
+
# Passing in a block will allow access to the underlying data stream so
|
469
|
+
# the data is not stored in memory:
|
470
|
+
# run.output_port("port") do |chunk|
|
471
|
+
# print chunk
|
472
|
+
# end
|
473
|
+
#
|
474
|
+
# In both cases supplying a Range will download and return the data in
|
475
|
+
# that range.
|
476
|
+
#
|
477
|
+
# This method does not cache any data.
|
478
|
+
#
|
479
|
+
# If this port is an error then this value will be the error message.
|
480
|
+
def value(range = 0...@size, &block)
|
481
|
+
# The following block is a workaround for Taverna Server versions prior
|
482
|
+
# to 2.4.1 and can be removed when support for those versions is no
|
483
|
+
# longer required.
|
484
|
+
if error? && @size == 0
|
485
|
+
value = @port.download(@reference)
|
486
|
+
@size = value.size
|
487
|
+
range = 0...@size if range.min.nil?
|
488
|
+
return value[range]
|
489
|
+
end
|
490
|
+
|
491
|
+
return "" if @type == EMPTY_TYPE
|
492
|
+
|
493
|
+
# Check that the range provided is sensible
|
382
494
|
range = 0..range.max if range.min < 0
|
383
495
|
range = range.min...@size if range.max >= @size
|
384
496
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
@value = new_data + @value
|
407
|
-
@value[0..range.max]
|
408
|
-
else
|
409
|
-
@vgot = @vgot.min..range.max
|
410
|
-
@value = @value + new_data
|
411
|
-
@value[(range.min - @vgot.min)..@vgot.max]
|
412
|
-
end
|
413
|
-
end
|
414
|
-
when 2
|
415
|
-
# we definitely have some data and it is in the middle of the
|
416
|
-
# range requested. @vgot cannot be nil here.
|
417
|
-
@vgot = range
|
418
|
-
@value = @port.download(@reference, need[0]) + @value +
|
419
|
-
@port.download(@reference, need[1])
|
497
|
+
@port.download(@reference, range, &block)
|
498
|
+
end
|
499
|
+
|
500
|
+
# :call-seq:
|
501
|
+
# stream_value(stream) -> fixnum
|
502
|
+
# stream_value(stream, range) -> fixnum
|
503
|
+
#
|
504
|
+
# Stream this port value directly into another stream. The stream passed
|
505
|
+
# in may be anything that provides a +write+ method; instances of IO and
|
506
|
+
# File, for example. No data is cached by this method.
|
507
|
+
#
|
508
|
+
# The number of bytes written to the stream is returned.
|
509
|
+
def stream_value(stream, range = 0...@size)
|
510
|
+
raise ArgumentError,
|
511
|
+
"Stream passed in must provide a write method" unless
|
512
|
+
stream.respond_to? :write
|
513
|
+
|
514
|
+
bytes = 0
|
515
|
+
|
516
|
+
value(range) do |chunk|
|
517
|
+
bytes += stream.write(chunk)
|
420
518
|
end
|
519
|
+
|
520
|
+
bytes
|
421
521
|
end
|
422
522
|
|
423
523
|
# :call-seq:
|
424
|
-
#
|
524
|
+
# write_value_to_file(filename) -> fixnum
|
525
|
+
# write_value_to_file(filename, range) -> fixnum
|
526
|
+
#
|
527
|
+
# Stream this port value directly to a file. If a range is supplied then
|
528
|
+
# just that range of data is downloaded from the server. No data is cached
|
529
|
+
# by this method.
|
530
|
+
def write_value_to_file(filename, range = 0...@size)
|
531
|
+
File.open(filename, "wb") do |file|
|
532
|
+
stream_value(file, range)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
# :call-seq:
|
537
|
+
# error? -> true or false
|
425
538
|
#
|
426
539
|
# Does this port represent an error?
|
427
540
|
def error?
|
428
|
-
|
541
|
+
@error
|
429
542
|
end
|
430
543
|
|
431
544
|
# :call-seq:
|
432
|
-
#
|
545
|
+
# empty? -> true or false
|
433
546
|
#
|
434
|
-
#
|
435
|
-
def
|
436
|
-
@
|
547
|
+
# Is this port value empty?
|
548
|
+
def empty?
|
549
|
+
@type == EMPTY_TYPE
|
437
550
|
end
|
438
551
|
|
439
552
|
# Used within #inspect, below to help override the built in version.
|
@@ -450,29 +563,5 @@ module T2Server
|
|
450
563
|
}
|
451
564
|
end
|
452
565
|
|
453
|
-
private
|
454
|
-
def fill(got, want)
|
455
|
-
return [want] if got.nil?
|
456
|
-
|
457
|
-
if got.member? want.min
|
458
|
-
if got.member? want.max
|
459
|
-
return []
|
460
|
-
else
|
461
|
-
return [(got.max + 1)..want.max]
|
462
|
-
end
|
463
|
-
else
|
464
|
-
if got.member? want.max
|
465
|
-
return [want.min..(got.min - 1)]
|
466
|
-
else
|
467
|
-
if want.max < got.min
|
468
|
-
return [want.min..(got.min - 1)]
|
469
|
-
elsif want.min > got.max
|
470
|
-
return [(got.max + 1)..want.max]
|
471
|
-
else
|
472
|
-
return [want.min..(got.min - 1), (got.max + 1)..want.max]
|
473
|
-
end
|
474
|
-
end
|
475
|
-
end
|
476
|
-
end
|
477
566
|
end
|
478
567
|
end
|