worker_bee 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 067c96f441e33257c6f7c793659ad797cd74649b858508a8422f843c4c0a75e0
4
+ data.tar.gz: 308d20e8531c852715f84a5de31ddc5c0d7e34488f44b990b4dc25708e7b4f99
5
+ SHA512:
6
+ metadata.gz: c011fa40377d36f291df13302bd356b1de078d4cbc5dae5ea06ae7b6cf1bc9e0045129a815d22f9297518e9b72e7b48833c63efa2a760168ead752b43c4b8900
7
+ data.tar.gz: 0f99f63c706a6ea5998df00ba6713e0e96c0ebc501534a56b28fa49d6952a00d6de8223d330cae74f6c4df9a256a4a6a2cba41e1542fa5f52b21c1a3ca9e382f
checksums.yaml.gz.sig ADDED
Binary file
data/.autotest ADDED
@@ -0,0 +1,26 @@
1
+ # -*- ruby -*-
2
+
3
+ require "autotest/restart"
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = "minitest/autorun"
7
+ at.add_exception "tmp"
8
+
9
+ # at.extra_files << "../some/external/dependency.rb"
10
+ #
11
+ # at.libs << ":../some/external"
12
+ #
13
+ # at.add_exception "vendor"
14
+ #
15
+ # at.add_mapping(/dependency.rb/) do |f, _|
16
+ # at.files_matching(/test_.*rb$/)
17
+ # end
18
+ #
19
+ # %w(TestA TestB).each do |klass|
20
+ # at.extra_class_map[klass] = "test/test_misc.rb"
21
+ # end
22
+ end
23
+
24
+ # Autotest.add_hook :run_command do |at|
25
+ # system "rake build"
26
+ # end
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2011-03-10
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/worker_bee.rb
7
+ test/test_worker_bee.rb
data/README.txt ADDED
@@ -0,0 +1,74 @@
1
+ = worker_bee
2
+
3
+ home :: http://seattlerb.org/
4
+
5
+ == DESCRIPTION:
6
+
7
+ WorkerBee encapsulates a simple pipeline of workers.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Simple API to wrap up the usual Thread/Queue patterns.
12
+ * Expressive shortcuts to make the pipeline really easy to read.
13
+
14
+ == SYNOPSIS:
15
+
16
+ bee = WorkerBee.new
17
+ bee.input enum_of_work_to_do
18
+ bee.work(20) { |work| ... stuff with input ... }
19
+ bee.work(5) { |work| ... stuff with results of previous ... }
20
+ bee.results # the final set of results
21
+
22
+ Example of shortcuts:
23
+
24
+ bee = WorkerBee.new
25
+ bee.input(*urls)
26
+ bee.work { |url| url_to_api url } # -> api_url
27
+ bee.work(20) { |url| url_to_ary url } # -> [ issues_or_pulls ]
28
+ bee.work { |ary| issues_to_url ary } # -> url_to_check
29
+ bee.compact # -> url
30
+ bee.results.sort
31
+
32
+ versus:
33
+
34
+ WorkerBee.new(self) # self = receiver of send for `then`s below
35
+ .input(*urls)
36
+ .then(:url_to_api)
37
+ .then(:url_to_ary, n:20)
38
+ .then(:issues_to_url)
39
+ .compact
40
+ .results
41
+ .sort
42
+
43
+ == REQUIREMENTS:
44
+
45
+ * ruby... awesome, no?
46
+
47
+ == INSTALL:
48
+
49
+ * sudo gem install worker_bee
50
+
51
+ == LICENSE:
52
+
53
+ (The MIT License)
54
+
55
+ Copyright (c) Ryan Davis, seattle.rb
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining
58
+ a copy of this software and associated documentation files (the
59
+ 'Software'), to deal in the Software without restriction, including
60
+ without limitation the rights to use, copy, modify, merge, publish,
61
+ distribute, sublicense, and/or sell copies of the Software, and to
62
+ permit persons to whom the Software is furnished to do so, subject to
63
+ the following conditions:
64
+
65
+ The above copyright notice and this permission notice shall be
66
+ included in all copies or substantial portions of the Software.
67
+
68
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
69
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
70
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
71
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
72
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
73
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
74
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # -*- ruby -*-
2
+
3
+ require "rubygems"
4
+ require "hoe"
5
+
6
+ Hoe.plugin :isolate
7
+ Hoe.plugin :seattlerb
8
+
9
+ Hoe.spec "worker_bee" do
10
+ developer "Ryan Davis", "ryand-ruby@zenspider.com"
11
+
12
+ license "MIT"
13
+
14
+ require_ruby_version "> 3.1"
15
+ end
16
+
17
+ # vim: syntax=ruby
data/lib/worker_bee.rb ADDED
@@ -0,0 +1,478 @@
1
+ require 'thread'
2
+
3
+ ##
4
+ # Defines a WorkerBee instance which provides a simple means of
5
+ # defining pipelines of parallel tasks working from one queue of work
6
+ # to a queue of results (which can then be a queue of work for another
7
+ # pipeline of workers).
8
+ #
9
+ # WorkerBee.new(self) # the self is important for context in `then` calls
10
+ # .input(*urls)
11
+ # .then(:url_to_api)
12
+ # .then(:api_to_ary, n:20)
13
+ # .flatten
14
+ # .then(:issues_to_url)
15
+ # .compact
16
+ # .results
17
+ # .sort
18
+ #
19
+ # General Helper methods:
20
+ #
21
+ # * input *data
22
+ # * trap_interrupt!
23
+ # * periodic time = 5, &update
24
+ # * show_progress([cols])
25
+ # * catch_up!
26
+ # * finish
27
+ # * results
28
+ #
29
+ # Data Transformers / Filters:
30
+ #
31
+ # * work n:1, &block
32
+ # * compact
33
+ # * filter n:1, &work
34
+ # * flatten
35
+ # * grep arg, n:1
36
+ # * grep_v_clear arg, n:1
37
+ # * map arg, n:1
38
+ # * match arg, n:1
39
+ # * non_empty n:1
40
+ # * then msg_name, n:1
41
+
42
+ class WorkerBee
43
+ VERSION = "1.0.0" # :nodoc:
44
+ SENTINAL = Object.new # :nodoc:
45
+
46
+ ##
47
+ # A slightly better Queue.
48
+
49
+ class BQ < Queue
50
+ ##
51
+ # Push multiple objects into the queue
52
+
53
+ def concat objs
54
+ objs.each do |obj|
55
+ self << obj
56
+ end
57
+ end
58
+
59
+ ##
60
+ # Close and return all data as an array.
61
+
62
+ def drain
63
+ close
64
+
65
+ result = []
66
+ result.push self.shift until self.empty?
67
+ result
68
+ end
69
+ end
70
+
71
+ ##
72
+ # The queue of queues of tasks to perform.
73
+
74
+ attr_accessor :tasks
75
+
76
+ ##
77
+ # Pipelines of workers. Each worker is wired up to task queues directly.
78
+
79
+ attr_accessor :workers
80
+
81
+ ##
82
+ # The context for the current run. This is only important for +then+.
83
+
84
+ attr_accessor :context
85
+
86
+ ##
87
+ # Threads that run parallel to everything else. Can be used to
88
+ # show progress or do periodic cleanup. Killed by #finish.
89
+
90
+ attr_accessor :updaters
91
+
92
+ ##
93
+ # Creates a new WorkerBee with one queue and no pipelines of workers.
94
+
95
+ def initialize context = nil
96
+ self.tasks = [BQ.new]
97
+ self.workers = []
98
+ self.context = context || self
99
+ self.updaters = []
100
+ end
101
+
102
+ ##
103
+ # The front of the pipeline
104
+
105
+ def front
106
+ tasks.first
107
+ end
108
+
109
+ ##
110
+ # The current back of the pipeline
111
+
112
+ def back
113
+ tasks.last
114
+ end
115
+
116
+ ##
117
+ # Add another queue to back of the pipeline.
118
+
119
+ def add_to_pipeline
120
+ tasks << BQ.new
121
+ back
122
+ end
123
+
124
+ ##
125
+ # Add +data+ to the front of the pipeline of tasks.
126
+
127
+ def input *data
128
+ front.concat data
129
+ self
130
+ end
131
+
132
+ ##
133
+ # Meant to be used in an INT trap. Clears the remaining input and
134
+ # then finishes all work.
135
+
136
+ def interrupted!
137
+ non_empty = tasks.find { |pool| ! pool.empty? }
138
+ non_empty.clear if non_empty
139
+
140
+ trap "INT", "EXIT"
141
+
142
+ finish
143
+ end
144
+
145
+ ##
146
+ # Set up an interrupt handler that cleanly finishes.
147
+
148
+ def trap_interrupt!
149
+ trap("INT") { $stderr.puts "finishing..."; interrupted! }
150
+ self
151
+ end
152
+
153
+ ##
154
+ # Schedules a side task to run every +time+ seconds parallel to
155
+ # everything else. Shut down by #finish.
156
+
157
+ def periodic time = 5, &update
158
+ self.updaters << Thread.new do
159
+ loop do
160
+ update.call self
161
+ sleep time
162
+ end
163
+ end
164
+ self
165
+ end
166
+
167
+ ##
168
+ # Print out the +columns+, and then periodically print counts to
169
+ # show progress.
170
+
171
+ def show_progress(columns = nil)
172
+ puts columns.join "\t" if columns
173
+ periodic do
174
+ print_progress
175
+ end
176
+ end
177
+
178
+ ##
179
+ # Print 1 line of pipeline counts.
180
+
181
+ def print_progress
182
+ print "\r"
183
+ print counts.join "\t"
184
+ end
185
+
186
+ ##
187
+ # Returns the current counts of all tasks in the pipeline.
188
+
189
+ def counts
190
+ tasks.zip(workers).map { |t, w| t.size + (w || []).count(&:working?) }
191
+ end
192
+
193
+ ##
194
+ # A generic worker bee. Does work in the input queue and puts it
195
+ # into the output queue until it gets the +SENTINAL+ value.
196
+
197
+ class Worker < Thread
198
+ ##
199
+ # True if working on a task
200
+ attr_accessor :working
201
+ alias working? working
202
+
203
+ ##
204
+ # The input queue of work
205
+
206
+ attr_accessor :input
207
+
208
+ ##
209
+ # The output queue of work
210
+
211
+ attr_accessor :output
212
+
213
+ ##
214
+ # The actual work to do (a proc).
215
+
216
+ attr_accessor :work
217
+
218
+ ##
219
+ # Initialize and start a worker.
220
+
221
+ def initialize input, output, &work
222
+ self.input = input
223
+ self.output = output
224
+ self.work = work
225
+ self.working = false
226
+
227
+ self.abort_on_exception = true
228
+
229
+ super() do
230
+ loop do
231
+ task = input.shift
232
+
233
+ break if task == SENTINAL
234
+
235
+ self.working = true
236
+ call task
237
+ self.working = false
238
+ end
239
+ end
240
+ end
241
+
242
+ ##
243
+ # Do +work+ on +task+ and put the result into the +output+ queue.
244
+
245
+ def call task
246
+ output << work[task]
247
+ end
248
+ end
249
+
250
+ ##
251
+ # Add a pipeline of work with +n+ parallel workers of a certain
252
+ # +type+ (defaulting to Worker) performing +block+ as the task for
253
+ # this pipeline.
254
+ #
255
+ # bee.work(n:3) { |task| do_the_work task }
256
+
257
+ def work n:1, type:Worker, &block
258
+ input = back
259
+ output = add_to_pipeline
260
+
261
+ workers << n.times.map { type.new input, output, &block }
262
+
263
+ self
264
+ end
265
+
266
+ alias :toil :work
267
+ alias :slog :work
268
+
269
+ ##
270
+ # A worker that filters non-truthy results of work from the next pipeline.
271
+
272
+ class CompactWorker < Worker
273
+ ##
274
+ # Do +work+ on +task+ and put the truthy results into the +output+
275
+ # queue.
276
+
277
+ def call task
278
+ out = work[task]
279
+ output << out if out
280
+ end
281
+ end
282
+
283
+ ##
284
+ # Add a pipeline of work that removes all non-truthy tasks.
285
+ #
286
+ # bee
287
+ # .input(*urls)
288
+ # .then(:url_or_nil)
289
+ # .compact
290
+
291
+ def compact
292
+ work type:CompactWorker, &:itself
293
+ end
294
+
295
+ ##
296
+ # A worker that flattens the results of work into the next pipeline.
297
+
298
+ class FlattenWorker < Worker
299
+ ##
300
+ # Do +work+ on +task+ and put the (multiple) results into the
301
+ # +output+ queue.
302
+
303
+ def call task
304
+ output.concat work[task]
305
+ end
306
+ end
307
+
308
+ ##
309
+ # Add a pipeline of work that flattens out all tasks. Lets you have
310
+ # one task create arrays of subtasks.
311
+ #
312
+ # bee
313
+ # .input(*urls)
314
+ # .then(:fetch_jobs, n:20)
315
+ # .flatten
316
+
317
+ def flatten
318
+ work type:FlattenWorker, &:itself
319
+ end
320
+
321
+ ##
322
+ # A worker that passes task through only if the work is truthy.
323
+
324
+ class Filter < Worker
325
+ def call task
326
+ output << task if work[task]
327
+ end
328
+ end
329
+
330
+ ##
331
+ # Add a pipeline of work with +n+ parallel workers that filter
332
+ # tasks out if +work+ is doesn't evaluate to truthy. Eg:
333
+ #
334
+ # bee
335
+ # .input(*Dir["**/*"])
336
+ # .filter { |path| File.file? path }
337
+ # .filter(n:4) { |path| `file -b #{path}` =~ /Ruby script/ }
338
+
339
+ def filter n:1, &work
340
+ work n:n, type:Filter, &work
341
+ end
342
+
343
+ ##
344
+ # Add a pipeline of work with +n+ parallel workers that filter tasks
345
+ # that match +arg+ pattern (regexp or anything that responds to
346
+ # +===+).
347
+ #
348
+ # bee
349
+ # .input(*Dir["**/*"])
350
+ # .grep(/\.rb$|Rakefile/)
351
+
352
+ def grep arg, n:1
353
+ work(n:n) { |data| data.grep arg }
354
+ end
355
+
356
+ ##
357
+ # Add a pipeline of work with +n+ parallel workers that maps tasks
358
+ # by calling +arg+ on them and return the results for the next
359
+ # input.
360
+ #
361
+ # bee.map(:to_s)
362
+
363
+ def map arg, n:1
364
+ work(n:n) { |data| data.send arg }
365
+ end
366
+
367
+ ##
368
+ # Add a pipeline of work with +n+ parallel workers that calls clear
369
+ # on all data that does not match pattern +arg+. Usually followed by
370
+ # +non_empty+.
371
+ #
372
+ # bee
373
+ # .grep_v_clear(/ruby/)
374
+ # .non_empty
375
+
376
+ def grep_v_clear arg, n:1
377
+ work(n:n) { |data| data.grep_v(arg).each(&:clear); data }
378
+ end
379
+
380
+ ##
381
+ # Add a pipeline of work with +n+ parallel workers that removes all
382
+ # empty data.
383
+ #
384
+ # bee
385
+ # .grep_v_clear(/ruby/)
386
+ # .non_empty
387
+
388
+ def non_empty n:1
389
+ work(n:n) { |data| data.reject(&:empty?) }
390
+ end
391
+
392
+ ##
393
+ # Add a pipeline of work with +n+ parallel workers that finds data
394
+ # matching +arg+ by calling +===+.
395
+ #
396
+ # bee.match(matcher)
397
+
398
+ def match arg, n:1
399
+ work(n:n) { |data| arg === data }
400
+ end
401
+
402
+ ##
403
+ # Convenience function:
404
+ #
405
+ # bee.then :msg_name, n:3
406
+ #
407
+ # is a shortcut equivalent to:
408
+ #
409
+ # bee.work(n:3) { |task| msg_name task }
410
+ #
411
+ # Also helps prototype systems as the method doesn't need to exist
412
+ # yet. Prints that it doesn't exist and passes the data through
413
+ # as-is.
414
+
415
+ def then msg_name, n:1
416
+ m = context.method(msg_name) rescue nil
417
+ if m then
418
+ work(n:n) { |obj| context.send msg_name, obj }
419
+ else
420
+ warn "warning: prototyping #{msg_name}"
421
+ work(n:n) { |obj| obj }
422
+ end
423
+ end
424
+
425
+ ##
426
+ # Forces a choke point at this point in the pipeline, causing all
427
+ # data prior to this point to finish before moving on. Returns a new
428
+ # instance of the pipeline.
429
+ #
430
+ # bee
431
+ # .input(*urls)
432
+ # .then(:fetch_tasks, n:10)
433
+ # .catch_up!
434
+ # .then(:do_heavy_tasks, n:3)
435
+
436
+ def catch_up!
437
+ updaters = self.updaters
438
+ self.updaters = nil
439
+
440
+ input = self.results
441
+ bee = self.class.new(self.context)
442
+ bee.input(*input)
443
+ bee.updaters = updaters
444
+ bee
445
+ end
446
+
447
+ ##
448
+ # Finish all work on all tasks, from front to back.
449
+
450
+ def finish
451
+ # TODO: zip workers and tasks?
452
+
453
+ workers.each do |pool|
454
+ input = pool.first.input
455
+
456
+ pool.size.times do
457
+ input << SENTINAL
458
+ end
459
+
460
+ pool.each do |thread|
461
+ thread.join
462
+ end
463
+ end
464
+
465
+ updaters.each(&:kill)
466
+
467
+ self
468
+ end
469
+
470
+ ##
471
+ # Finish and return the contents of all work.
472
+
473
+ def results
474
+ finish
475
+
476
+ back.drain - [SENTINAL]
477
+ end
478
+ end
@@ -0,0 +1,31 @@
1
+ require "minitest/autorun"
2
+ require "worker_bee"
3
+
4
+ class TestWorkerBee < Minitest::Test
5
+ def test_sanity_manual
6
+ bee = WorkerBee.new
7
+
8
+ bee.input(*(1..25).to_a)
9
+
10
+ bee.work(n:20) { |n| n ** 2 }
11
+ bee.finish
12
+ bee.work(n:5) { |n| Math.sqrt n }
13
+ bee.finish
14
+
15
+ expected = (1..25).to_a
16
+ assert_equal expected, bee.results.map(&:to_i).sort
17
+ end
18
+
19
+ def test_sanity_automatic
20
+ bee = WorkerBee.new
21
+
22
+ bee.input(*(1..25).to_a)
23
+
24
+ bee.work(n:20) { |n| n ** 2 }
25
+ # bee.finish # commented out on purpose
26
+ bee.work(n:5) { |n| Math.sqrt n }
27
+
28
+ expected = (1..25).to_a
29
+ assert_equal expected, bee.results.map(&:to_i).sort
30
+ end
31
+ end
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: worker_bee
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Davis
8
+ bindir: bin
9
+ cert_chain:
10
+ - |
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIDPjCCAiagAwIBAgIBCTANBgkqhkiG9w0BAQsFADBFMRMwEQYDVQQDDApyeWFu
13
+ ZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB
14
+ GRYDY29tMB4XDTI1MDEwNjIzMjcwMVoXDTI2MDEwNjIzMjcwMVowRTETMBEGA1UE
15
+ AwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS
16
+ JomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda
17
+ b9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx
18
+ taCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT
19
+ oOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh
20
+ GiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt
21
+ qhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV
22
+ gBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw
23
+ HQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBCwUAA4IB
24
+ AQAC0WQJcPOWPFwkojhzweilRVjTJ19UiLhiBTw3C1wJO3LVdBkWDmnnhAmKuX4D
25
+ r7vjQvESlABGIPdutI1Yl7mrHQzTkfLfXvNN6MT0nLChPyIYauT6SZZxubwJrUfA
26
+ 7R0c2CJTIboZ0XaGpLsXqHEF1c29H7TV1QvVuqKAN2mCjh4N82QVn+ZKtys28AwT
27
+ 6GfQX2fqLoi4KSc7xIzHKaNzqxeOICmJofk9w5VZ2rRN6yes8jvFYwz9HR41wdj8
28
+ bwfinv7Yp5fA6AysuZLhCykyfDuZVRrUp0Vb68YCKsLjJly/Theak+euNTxvHsB+
29
+ al9oSgPPHICMEX65qvLywitx
30
+ -----END CERTIFICATE-----
31
+ date: 1980-01-02 00:00:00.000000000 Z
32
+ dependencies:
33
+ - !ruby/object:Gem::Dependency
34
+ name: rdoc
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '4.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '7'
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '4.0'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '7'
53
+ - !ruby/object:Gem::Dependency
54
+ name: hoe
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '4.3'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '4.3'
67
+ description: WorkerBee encapsulates a simple pipeline of workers.
68
+ email:
69
+ - ryand-ruby@zenspider.com
70
+ executables: []
71
+ extensions: []
72
+ extra_rdoc_files:
73
+ - History.txt
74
+ - Manifest.txt
75
+ - README.txt
76
+ files:
77
+ - ".autotest"
78
+ - History.txt
79
+ - Manifest.txt
80
+ - README.txt
81
+ - Rakefile
82
+ - lib/worker_bee.rb
83
+ - test/test_worker_bee.rb
84
+ homepage: http://seattlerb.org/
85
+ licenses:
86
+ - MIT
87
+ metadata:
88
+ homepage_uri: http://seattlerb.org/
89
+ rdoc_options:
90
+ - "--main"
91
+ - README.txt
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">"
97
+ - !ruby/object:Gem::Version
98
+ version: '3.1'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.7.2
106
+ specification_version: 4
107
+ summary: WorkerBee encapsulates a simple pipeline of workers.
108
+ test_files: []
metadata.gz.sig ADDED
Binary file