t2-server 0.9.3 → 1.0.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/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
|