bits 0.1.1-mswin32
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/lib/bits.rb +410 -0
- data/test/test_bits.rb +132 -0
- metadata +41 -0
data/lib/bits.rb
ADDED
@@ -0,0 +1,410 @@
|
|
1
|
+
#
|
2
|
+
# An interface for the "Background Intelligent Transfer Service" (BITS) in MS Windows
|
3
|
+
#
|
4
|
+
# A more decent way to interface with BITS would be the COM interface it provides. I did not get it to work, so I had to revert to pasing the bitsadmin command.
|
5
|
+
#
|
6
|
+
# Prereqs (formally stated in ../bits.gemspec):
|
7
|
+
# - "Background Intelligent Transfer Service" (BITS) f�r Windows (KB842773)
|
8
|
+
# - bitsadmin tool, available at http://www.microsoft.com/downloads/details.aspx?amp;displaylang=en&familyid=49AE8576-9BB9-4126-9761-BA8011FABF38&displaylang=en
|
9
|
+
#
|
10
|
+
# Author:: Nicolas E. Rabenau (mailto:nerab@gmx.at)
|
11
|
+
# Copyright:: Copyright (c) 2005 Nicolas E. Rabenau
|
12
|
+
# License:: Distributed under the same terms as Ruby
|
13
|
+
#
|
14
|
+
module BITS
|
15
|
+
#
|
16
|
+
# the basic bitsadmin string that is used in all methods
|
17
|
+
#
|
18
|
+
BITSADMIN = "bitsadmin /rawreturn /nowrap"
|
19
|
+
|
20
|
+
#
|
21
|
+
# The BITS Manager bundles all static BITS operations that are independent from an individual job
|
22
|
+
#
|
23
|
+
class Manager
|
24
|
+
|
25
|
+
#
|
26
|
+
# returns a hash of all BITS jobs where the job's name is the key and the Job itself is the value. If there is more
|
27
|
+
# than one job with the same name the value is an array of all jobs with that name.
|
28
|
+
#
|
29
|
+
def Manager.jobs
|
30
|
+
jobs = Hash.new
|
31
|
+
|
32
|
+
for jobString in `#{BITS::BITSADMIN} /list`.split(/\n/)
|
33
|
+
job = Job.new(jobString)
|
34
|
+
|
35
|
+
# a job's name is not guaranteed to be unique
|
36
|
+
if jobs[job.name].nil?
|
37
|
+
# value has not been assigned before
|
38
|
+
jobs[job.name] = job
|
39
|
+
else
|
40
|
+
if jobs[job.name].is_a? Array
|
41
|
+
# value already contains an array
|
42
|
+
jobs[job.name] << job
|
43
|
+
elsif jobs[job.name].is_a? Job
|
44
|
+
# value contains a Job
|
45
|
+
existingValue = jobs[job.name]
|
46
|
+
jobs[job.name] = Array.new
|
47
|
+
jobs[job.name] << existingValue
|
48
|
+
jobs[job.name] << job
|
49
|
+
else
|
50
|
+
# should never happen. If it does, there is something terribly wrong, so we bail out fast
|
51
|
+
raise "Unexpected class of value (#{jobs[job.name].class}) for jobs['#{job.name}']"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
jobs
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# returns a hash of all BITS jobs where the job's id is the key and the Job itself is the value
|
61
|
+
#
|
62
|
+
def Manager.jobs_by_id
|
63
|
+
jobs = Hash.new
|
64
|
+
|
65
|
+
for jobString in `#{BITS::BITSADMIN} /list`.split(/\n/)
|
66
|
+
job = Job.new(jobString)
|
67
|
+
jobs[job.id] = job
|
68
|
+
end
|
69
|
+
|
70
|
+
jobs
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# create a new job with name
|
75
|
+
#
|
76
|
+
# It is arguable whether this method should be Job.create or Manager.createJob. I decided to use the latter.
|
77
|
+
#
|
78
|
+
def Manager.createJob(name)
|
79
|
+
if "" == name
|
80
|
+
raise "Job name missing"
|
81
|
+
end
|
82
|
+
|
83
|
+
Job.new(`#{BITS::BITSADMIN} /create "#{name}"`)
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# deletes all jobs
|
88
|
+
#
|
89
|
+
def Manager.cancelAllJobs
|
90
|
+
`#{BITS::BITSADMIN} /reset`
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# returns the version number of the underlying BITS
|
95
|
+
#
|
96
|
+
# For troubleshooting, we could use "bitsadmin /util /version /verbose"
|
97
|
+
#
|
98
|
+
def Manager.version
|
99
|
+
parsed = `bitsadmin`.scan /\d\.\d/
|
100
|
+
parsed[0]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Models the description of a downloadable file, consisting of an URL and the local name where the result is going to be stored
|
106
|
+
#
|
107
|
+
class FileDescriptor
|
108
|
+
attr_accessor :remote_url, :local_name, :bytes_transferred, :bytes_total
|
109
|
+
|
110
|
+
#
|
111
|
+
# builds a new FileDescription with the URL and the optional local name
|
112
|
+
#
|
113
|
+
def initialize(url, local_name = nil)
|
114
|
+
@remote_url = url
|
115
|
+
@local_name = local_name
|
116
|
+
@bytes_transferred = 0
|
117
|
+
@bytes_total = 'UNKNOWN'
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_s
|
121
|
+
"#{@remote_url} -> #{@local_name}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Models a BITS job
|
127
|
+
#
|
128
|
+
# A job can be modified through set methods, changes will be effective immediately.
|
129
|
+
#
|
130
|
+
class Job
|
131
|
+
#
|
132
|
+
# used to refer to the job in BITS (primary key)
|
133
|
+
#
|
134
|
+
attr_reader :id
|
135
|
+
|
136
|
+
#
|
137
|
+
# builds a new Job from the supplied id. The job must already exist in BITS
|
138
|
+
#
|
139
|
+
# Ideally this method would have only module visibility in order to prevent instantiations of a job outside Manager.createJob
|
140
|
+
#
|
141
|
+
def initialize(job_id)
|
142
|
+
if %r!([A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12})! =~ job_id
|
143
|
+
@id = $1
|
144
|
+
else
|
145
|
+
raise JobParseException, "Unable to parse '#{job_id}' into a job description. Maybe the format has changed?"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def files
|
150
|
+
output = `#{BITS::BITSADMIN} /listfiles {#{@id}}`
|
151
|
+
|
152
|
+
# bitsadmin typically produces something like this:
|
153
|
+
# 0 / UNKNOWN WORKING http://remote -> c:\temp\local.file
|
154
|
+
|
155
|
+
files = Array.new
|
156
|
+
|
157
|
+
for line in output.split(/\n/)
|
158
|
+
if %r!(\d+) / ((\d+)|UNKNOWN) WORKING (.*) -> (.*)! =~ line
|
159
|
+
# print "found 1: >#{$1}< 2: >#{$2}< 3: >#{$3}< 4: >#{$4}< 5: >#{$5}<"
|
160
|
+
p = FileDescriptor.new($4, $5)
|
161
|
+
p.bytes_transferred = $1
|
162
|
+
p.bytes_total = $2
|
163
|
+
files << p
|
164
|
+
else
|
165
|
+
raise FileDescriptorParseException, "Unable to parse '#{output}' into a file descriptor. Maybe the format has changed?"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
files
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# adds a file to be downloaded and the local name for the file to the job
|
174
|
+
#
|
175
|
+
def addFile(url, local_name)
|
176
|
+
`bitsadmin /rawreturn /addfile {#{@id}} #{url} #{local_name}`
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# suspends the job
|
181
|
+
#
|
182
|
+
def suspend
|
183
|
+
`#{BITS::BITSADMIN} /suspend {#{@id}}`
|
184
|
+
end
|
185
|
+
|
186
|
+
#
|
187
|
+
# resumes the job
|
188
|
+
#
|
189
|
+
def resume
|
190
|
+
`#{BITS::BITSADMIN} /resume {#{@id}}`
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# cancels the job
|
195
|
+
#
|
196
|
+
def cancel
|
197
|
+
`#{BITS::BITSADMIN} /cancel {#{@id}}`
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
# completes the job
|
202
|
+
#
|
203
|
+
def complete
|
204
|
+
`#{BITS::BITSADMIN} /complete {#{@id}}`
|
205
|
+
end
|
206
|
+
|
207
|
+
# returns the job's type
|
208
|
+
def type
|
209
|
+
`#{BITS::BITSADMIN} /gettype {#{@id}}`
|
210
|
+
end
|
211
|
+
|
212
|
+
# returns the job's ACL propagation flags
|
213
|
+
def aclflags
|
214
|
+
`#{BITS::BITSADMIN} /getaclflags {#{@id}}`
|
215
|
+
end
|
216
|
+
|
217
|
+
# returns the size of the job
|
218
|
+
def bytestotal
|
219
|
+
`#{BITS::BITSADMIN} /getbytestotal {#{@id}}`
|
220
|
+
end
|
221
|
+
|
222
|
+
# returns the number of bytes transferred
|
223
|
+
def bytestransferred
|
224
|
+
`#{BITS::BITSADMIN} /getbytestransferred {#{@id}}`
|
225
|
+
end
|
226
|
+
|
227
|
+
# returns the number of files in the job
|
228
|
+
def filestotal
|
229
|
+
`#{BITS::BITSADMIN} /getfilestotal {#{@id}}`
|
230
|
+
end
|
231
|
+
|
232
|
+
# returns the number of files transferred
|
233
|
+
def filestransferred
|
234
|
+
`#{BITS::BITSADMIN} /getfilestransferred {#{@id}}`
|
235
|
+
end
|
236
|
+
|
237
|
+
# returns the job's creation time
|
238
|
+
def creationtime
|
239
|
+
`#{BITS::BITSADMIN} /getcreationtime {#{@id}}`
|
240
|
+
end
|
241
|
+
|
242
|
+
# returns the job's modification time
|
243
|
+
def modificationtime
|
244
|
+
`#{BITS::BITSADMIN} /getmodificationtime {#{@id}}`
|
245
|
+
end
|
246
|
+
|
247
|
+
# returns the job's completion time
|
248
|
+
def completiontime
|
249
|
+
`#{BITS::BITSADMIN} /getcompletiontime {#{@id}}`
|
250
|
+
end
|
251
|
+
|
252
|
+
# returns the job's state
|
253
|
+
def state
|
254
|
+
`#{BITS::BITSADMIN} /getstate {#{@id}}`
|
255
|
+
end
|
256
|
+
|
257
|
+
# returns detailed error information
|
258
|
+
def error
|
259
|
+
`#{BITS::BITSADMIN} /geterror {#{@id}}`
|
260
|
+
end
|
261
|
+
|
262
|
+
# returns the job's owner
|
263
|
+
def owner
|
264
|
+
`#{BITS::BITSADMIN} /getowner {#{@id}}`
|
265
|
+
end
|
266
|
+
|
267
|
+
# returns the job's display name
|
268
|
+
def name
|
269
|
+
`#{BITS::BITSADMIN} /getdisplayname {#{@id}}`
|
270
|
+
end
|
271
|
+
|
272
|
+
# returns the job's description
|
273
|
+
def description
|
274
|
+
`#{BITS::BITSADMIN} /getdescription {#{@id}}`
|
275
|
+
end
|
276
|
+
|
277
|
+
# returns the job's priority
|
278
|
+
def priority
|
279
|
+
`#{BITS::BITSADMIN} /getpriority {#{@id}}`
|
280
|
+
end
|
281
|
+
|
282
|
+
# returns the notify flags
|
283
|
+
def notifyflags
|
284
|
+
`#{BITS::BITSADMIN} /getnotifyflags {#{@id}}`
|
285
|
+
end
|
286
|
+
|
287
|
+
# Determines if notify interface is registered
|
288
|
+
def notifyinterface
|
289
|
+
`#{BITS::BITSADMIN} /getnotifyinterface {#{@id}}`
|
290
|
+
end
|
291
|
+
|
292
|
+
# returns the retry delay in seconds
|
293
|
+
def minretrydelay
|
294
|
+
`#{BITS::BITSADMIN} /getminretrydelay {#{@id}}`
|
295
|
+
end
|
296
|
+
|
297
|
+
# returns the no progress timeout in seconds
|
298
|
+
def noprogresstimeout
|
299
|
+
`#{BITS::BITSADMIN} /getnoprogresstimeout {#{@id}}`
|
300
|
+
end
|
301
|
+
|
302
|
+
# returns an error count for the job
|
303
|
+
def errorcount
|
304
|
+
`#{BITS::BITSADMIN} /geterrorcount {#{@id}}`
|
305
|
+
end
|
306
|
+
|
307
|
+
# returns the proxy usage setting
|
308
|
+
def proxyusage
|
309
|
+
`#{BITS::BITSADMIN} /getproxyusage {#{@id}}`
|
310
|
+
end
|
311
|
+
|
312
|
+
# returns the proxy list
|
313
|
+
def proxylist
|
314
|
+
`#{BITS::BITSADMIN} /getproxylist {#{@id}}`
|
315
|
+
end
|
316
|
+
|
317
|
+
# returns the proxy bypass list
|
318
|
+
def proxybypasslist
|
319
|
+
`#{BITS::BITSADMIN} /getproxybypasslist {#{@id}}`
|
320
|
+
end
|
321
|
+
|
322
|
+
# returns the job's notification command line
|
323
|
+
def notifycmdline
|
324
|
+
`#{BITS::BITSADMIN} /getnotifycmdline {#{@id}}`
|
325
|
+
end
|
326
|
+
|
327
|
+
# returns a String representation of the job
|
328
|
+
def to_s
|
329
|
+
`#{BITS::BITSADMIN} /info {#{@id}}`
|
330
|
+
end
|
331
|
+
|
332
|
+
# attribute writers
|
333
|
+
|
334
|
+
# sets the ACL propagation flags for the job
|
335
|
+
def aclflags=(acl_flags)
|
336
|
+
`#{BITS::BITSADMIN} /setaclflags {#{@id}} #{acl_flags}`
|
337
|
+
end
|
338
|
+
|
339
|
+
# sets the job display name
|
340
|
+
def name=(display_name)
|
341
|
+
`#{BITS::BITSADMIN} /setdisplayname {#{@id}} \"#{display_name}\"`
|
342
|
+
end
|
343
|
+
|
344
|
+
# sets the job description
|
345
|
+
def description=(description)
|
346
|
+
`#{BITS::BITSADMIN} /setdescription {#{@id}} \"#{description}\"`
|
347
|
+
end
|
348
|
+
|
349
|
+
# sets the job priority
|
350
|
+
def priority=(priority)
|
351
|
+
`#{BITS::BITSADMIN} /setpriority {#{@id}} #{priority}`
|
352
|
+
end
|
353
|
+
|
354
|
+
# sets the notify flags
|
355
|
+
def notifyflags=(notify_flags)
|
356
|
+
`#{BITS::BITSADMIN} /setnotifyflags {#{@id}} #{notify_flags}`
|
357
|
+
end
|
358
|
+
|
359
|
+
# sets the retry delay in seconds
|
360
|
+
def minretrydelay=(retry_delay)
|
361
|
+
`#{BITS::BITSADMIN} /setminretrydelay {#{@id}} #{retry_delay}`
|
362
|
+
end
|
363
|
+
|
364
|
+
# sets the no progress timeout in seconds
|
365
|
+
def noprogresstimeout=(timeout)
|
366
|
+
`#{BITS::BITSADMIN} /setnoprogresstimeout {#{@id}} #{timeout}`
|
367
|
+
end
|
368
|
+
|
369
|
+
# sets the proxy usage
|
370
|
+
def proxysettings=(usage)
|
371
|
+
`#{BITS::BITSADMIN} /setproxysettings {#{@id}} #{usage}`
|
372
|
+
end
|
373
|
+
|
374
|
+
# sets a program to execute for notification, and optionally parameters. The program name and parameters can be NULL.
|
375
|
+
# IMPORTANT: if parameters are non-NULL, then the program name should be the first parameter.
|
376
|
+
#
|
377
|
+
# For some reason I didn't get this assignment operation to work with more than a single parameter, therefore I made
|
378
|
+
# it a "setXxx" operation. Not elegant, but it works.
|
379
|
+
#
|
380
|
+
def setnotifycmdline(program_name, program_parameters = "")
|
381
|
+
`#{BITS::BITSADMIN} /setnotifycmdline {#{@id}} #{program_name} \"#{program_parameters}\"`
|
382
|
+
end
|
383
|
+
|
384
|
+
# adds credentials to a job. <target> may be either SERVER or PROXY, <scheme> may be BASIC, DIGEST, NTLM, NEGOTIATE, or PASSPORT.
|
385
|
+
def credentials=(target, scheme, username, password)
|
386
|
+
`#{BITS::BITSADMIN} /setcredentials {#{@id}} #{target} #{scheme} #{username} #{password}`
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
#
|
391
|
+
# Generic parse exception, base class for concrete parse exceptions
|
392
|
+
#
|
393
|
+
class ParseException < Exception
|
394
|
+
|
395
|
+
end
|
396
|
+
|
397
|
+
#
|
398
|
+
# Exception that occurs in case parsing a Job specification fails
|
399
|
+
#
|
400
|
+
class JobParseException < ParseException
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
#
|
405
|
+
# Exception that occurs in case parsing a FileDescriptor specification fails
|
406
|
+
#
|
407
|
+
class FileDescriptorParseException < ParseException
|
408
|
+
|
409
|
+
end
|
410
|
+
end
|
data/test/test_bits.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require "bits"
|
3
|
+
include BITS
|
4
|
+
|
5
|
+
#
|
6
|
+
# Unit tests for bits.rb
|
7
|
+
#
|
8
|
+
# Author:: Nicolas E. Rabenau (mailto:nerab@gmx.at)
|
9
|
+
# Copyright:: Copyright (c) 2005 Nicolas E. Rabenau
|
10
|
+
# License:: Distributed under the same terms as Ruby
|
11
|
+
#
|
12
|
+
class BITS_Test < Test::Unit::TestCase
|
13
|
+
|
14
|
+
@@RND_CHARS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
15
|
+
|
16
|
+
#
|
17
|
+
# create job with random name
|
18
|
+
#
|
19
|
+
def setup
|
20
|
+
@jobName = self.class.name + "_" + BITS_Test.randomString(8)
|
21
|
+
@job = Manager.createJob(@jobName)
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# remove test job
|
26
|
+
#
|
27
|
+
def teardown
|
28
|
+
@job.cancel
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# test setting and reading the job's name
|
33
|
+
#
|
34
|
+
def test_name
|
35
|
+
assert_equal(@jobName, @job.name)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# test setting and reading the job's description
|
40
|
+
#
|
41
|
+
def test_description
|
42
|
+
description = "A test job for bits.rb"
|
43
|
+
@job.description = description
|
44
|
+
assert_equal(description, @job.description)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# tests the Manager's job listing by name
|
49
|
+
#
|
50
|
+
def test_joblist_by_name
|
51
|
+
assert(0 < Manager.jobs.size)
|
52
|
+
assert(Manager.jobs.has_key?(@jobName))
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# tests that the Manager's job listing by name returns all Jobs with duplicate names in an array
|
57
|
+
#
|
58
|
+
def test_joblist_by_name_with_duplicate_jobnames
|
59
|
+
assert(Manager.jobs.has_key?(@jobName))
|
60
|
+
j2 = Manager.createJob(@jobName)
|
61
|
+
assert_equal(Array, Manager.jobs[@jobName].class)
|
62
|
+
assert_equal(2, Manager.jobs[@jobName].size)
|
63
|
+
j2.cancel
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# tests the Manager's job listing by id
|
68
|
+
#
|
69
|
+
def test_joblist_by_id
|
70
|
+
assert(0 < Manager.jobs_by_id.size)
|
71
|
+
assert(Manager.jobs_by_id.has_key?(@job.id))
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# test setting and reading the job's notify commandline
|
76
|
+
#
|
77
|
+
def test_notifycmdline
|
78
|
+
notifycmdline = "net"
|
79
|
+
notifycmdlineparams = "net send * BITS job #{@job.name} (#{@job.id}) successfully completed."
|
80
|
+
@job.setnotifycmdline(notifycmdline, notifycmdlineparams)
|
81
|
+
@job.description = "Testing notifycmdline"
|
82
|
+
assert_equal("the notification command line is '#{notifycmdline}' '#{notifycmdline}'", @job.notifycmdline)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_status_created
|
86
|
+
assert_equal("SUSPENDED", @job.state)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# test resuming a job without an URL attached, must fail
|
91
|
+
#
|
92
|
+
def test_resume_empty
|
93
|
+
@job.resume
|
94
|
+
assert_equal("SUSPENDED", @job.state)
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# test resuming a job with an URL attached, must not be in suspended or cancelled state
|
99
|
+
#
|
100
|
+
def test_resume_nonempty
|
101
|
+
@job.addFile("http://www.google.com", "c:\\temp\\google.com.html")
|
102
|
+
@job.resume
|
103
|
+
assert_not_equal("SUSPENDED", @job.state)
|
104
|
+
assert_not_equal("CANCELLED", @job.state)
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# test attaching files to the job
|
109
|
+
#
|
110
|
+
def test_files
|
111
|
+
@job.addFile("http://www.google.com", "c:\\temp\\google.com.html")
|
112
|
+
@job.addFile("http://www.heise.de", "c:\\temp\\heise.de.html")
|
113
|
+
assert_equal(@job.files[0].remote_url, "http://www.google.com")
|
114
|
+
assert_equal(@job.files[0].local_name, "c:\\temp\\google.com.html")
|
115
|
+
assert_equal(@job.files[1].remote_url, "http://www.heise.de")
|
116
|
+
assert_equal(@job.files[1].local_name, "c:\\temp\\heise.de.html")
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
#
|
122
|
+
# returns a String of defined length, made of random characters and numbers
|
123
|
+
#
|
124
|
+
def BITS_Test.randomString(length)
|
125
|
+
retVal = Array.new
|
126
|
+
for i in 0..length
|
127
|
+
retVal[i] = @@RND_CHARS[rand(@@RND_CHARS.length)]
|
128
|
+
end
|
129
|
+
|
130
|
+
retVal.to_s
|
131
|
+
end
|
132
|
+
end
|
metadata
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.10
|
3
|
+
specification_version: 1
|
4
|
+
name: bits
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2005-10-12
|
8
|
+
summary: A ruby interface for the Background Intelligent Transfer Service (BITS)
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: nerab@gmx.at
|
12
|
+
homepage: http://bits.rubyforge.org
|
13
|
+
rubyforge_project: bits
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: mswin32
|
27
|
+
authors:
|
28
|
+
- Nicolas E. Rabenau
|
29
|
+
files:
|
30
|
+
- lib/bits.rb
|
31
|
+
test_files:
|
32
|
+
- test/test_bits.rb
|
33
|
+
rdoc_options: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
requirements:
|
38
|
+
- Background Intelligent Transfer Service (BITS) f�r Windows (KB842773)
|
39
|
+
- "bitsadmin tool, available at
|
40
|
+
http://www.microsoft.com/downloads/details.aspx?amp;displaylang=en&familyid=49AE8576-9BB9-4126-9761-BA8011FABF38&displaylang=en"
|
41
|
+
dependencies: []
|