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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.autotest +26 -0
- data/History.txt +6 -0
- data/Manifest.txt +7 -0
- data/README.txt +74 -0
- data/Rakefile +17 -0
- data/lib/worker_bee.rb +478 -0
- data/test/test_worker_bee.rb +31 -0
- data.tar.gz.sig +0 -0
- metadata +108 -0
- metadata.gz.sig +0 -0
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
data/Manifest.txt
ADDED
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
|