fiber-collector 1.0.0 → 1.0.1
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 +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/fiber/collector/builder/all.rb +11 -19
- data/lib/fiber/collector/builder/any.rb +10 -22
- data/lib/fiber/collector/builder/polling.rb +42 -0
- data/lib/fiber/collector/builder/race.rb +8 -26
- data/lib/fiber/collector/builder/task.rb +4 -1
- data/lib/fiber/collector/builder.rb +10 -6
- data/lib/fiber/collector/version.rb +1 -1
- data/lib/fiber/collector.rb +4 -2
- metadata +9 -12
- data/.rubocop.yml +0 -13
- data/Gemfile +0 -17
- data/Gemfile.lock +0 -54
- data/Rakefile +0 -16
- data/fiber-collector.gemspec +0 -40
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e18213ff1d06e8e74852e8e97d62f08bf02d4c5633654ee74351f74d1320e5e6
|
|
4
|
+
data.tar.gz: 212d2754251189410de0f87d544df0801eb42197ad61773cbdb93964dc65c535
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84fd72f509e19cf60e942dc18853659fd348db3c7066e8b7090736afb84aa9f1c48b4300f5408d866ad683b0f25aceb4ad8c87476f2cb941c6a9d0e03121edcf
|
|
7
|
+
data.tar.gz: ddfdf89f536393f708aa1ad019eb3b82f59a64af7a7081f89dbcaf0be7a23b77e853f534762e2bde4bb39b80c1b3bb24160d360c1f82d6b6a13a849fa5dbb4fd
|
data/CHANGELOG.md
CHANGED
|
@@ -9,3 +9,15 @@
|
|
|
9
9
|
## [1.0.0] - 2023-01-26
|
|
10
10
|
|
|
11
11
|
- Updating dependencies, fixing CHANGELOG link, adding gem badge to README
|
|
12
|
+
|
|
13
|
+
## [1.0.1] - 2026-09-20
|
|
14
|
+
|
|
15
|
+
- Runs on Ruby 4. The pinned rake required ostruct, which left the standard
|
|
16
|
+
library, so `rake` could not start.
|
|
17
|
+
- `all`, `any` and `race` share one polling implementation instead of each
|
|
18
|
+
carrying its own copy with the timeout and no-timeout cases written out twice.
|
|
19
|
+
No change in behaviour.
|
|
20
|
+
- The default `timeout: nil` and an expiring timeout both have tests. Every
|
|
21
|
+
existing test passed a timeout that never expired, so neither path was covered.
|
|
22
|
+
- The published gem no longer carries the Rakefile, Gemfile or rubocop config.
|
|
23
|
+
- Publishing requires multi-factor auth.
|
|
@@ -1,32 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
class Fiber
|
|
3
4
|
module Collector
|
|
5
|
+
# Settles once every task has finished, failing as soon as any one of them
|
|
6
|
+
# does.
|
|
4
7
|
class All
|
|
8
|
+
include Polling
|
|
9
|
+
|
|
5
10
|
def initialize(procs)
|
|
6
11
|
@tasks = procs.map { Task.new(_1) }
|
|
7
12
|
end
|
|
8
13
|
|
|
9
14
|
def wait(timeout)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
else
|
|
17
|
-
elapsed = 0
|
|
18
|
-
until @tasks.all?(&:done?)
|
|
19
|
-
task = @tasks.find { |t| t.error }
|
|
20
|
-
unless task.nil?
|
|
21
|
-
raise task.error
|
|
22
|
-
end
|
|
23
|
-
t = 0.001
|
|
24
|
-
sleep t
|
|
25
|
-
elapsed += t
|
|
26
|
-
raise "timeout" if elapsed > timeout
|
|
27
|
-
end
|
|
15
|
+
schedule_tasks
|
|
16
|
+
poll_until(timeout) do
|
|
17
|
+
failed = @tasks.find(&:error)
|
|
18
|
+
raise failed.error unless failed.nil?
|
|
19
|
+
|
|
20
|
+
finished(@tasks.map(&:result)) if @tasks.all?(&:done?)
|
|
28
21
|
end
|
|
29
|
-
@tasks.map { |t| t.error && raise(t.error) || t.result }
|
|
30
22
|
end
|
|
31
23
|
end
|
|
32
24
|
end
|
|
@@ -1,34 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
class Fiber
|
|
3
4
|
module Collector
|
|
5
|
+
# Settles on the first task to succeed, and fails only if they all fail.
|
|
4
6
|
class Any
|
|
7
|
+
include Polling
|
|
8
|
+
|
|
5
9
|
def initialize(procs)
|
|
6
10
|
@tasks = procs.map { Task.new(_1) }
|
|
7
11
|
end
|
|
8
12
|
|
|
9
13
|
def wait(timeout)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
task = @tasks.find { |t| t.done? && t.error.nil? }
|
|
17
|
-
return task.result unless task.nil?
|
|
18
|
-
raise @tasks.first.error if @tasks.all? { |t| t.done? && t.error }
|
|
19
|
-
sleep 0.001
|
|
20
|
-
end
|
|
21
|
-
else
|
|
22
|
-
elapsed = 0
|
|
23
|
-
loop do
|
|
24
|
-
task = @tasks.find { |t| t.done? && t.error.nil? }
|
|
25
|
-
return task.result unless task.nil?
|
|
26
|
-
raise @tasks.first.error if @tasks.all? { |t| t.done? && t.error }
|
|
27
|
-
t = 0.001
|
|
28
|
-
sleep t
|
|
29
|
-
elapsed += t
|
|
30
|
-
raise "timeout" if elapsed > timeout
|
|
31
|
-
end
|
|
14
|
+
schedule_tasks
|
|
15
|
+
poll_until(timeout) do
|
|
16
|
+
task = @tasks.find { |t| t.done? && t.error.nil? }
|
|
17
|
+
next finished(task.result) unless task.nil?
|
|
18
|
+
|
|
19
|
+
raise @tasks.first.error if @tasks.all? { |t| t.done? && t.error }
|
|
32
20
|
end
|
|
33
21
|
end
|
|
34
22
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Fiber
|
|
4
|
+
module Collector
|
|
5
|
+
# Shared machinery for the three collectors. Each schedules its tasks, then
|
|
6
|
+
# polls until the block it is given returns something other than nil, or
|
|
7
|
+
# until the timeout passes. A nil timeout waits indefinitely.
|
|
8
|
+
module Polling
|
|
9
|
+
INTERVAL = 0.001
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def schedule_tasks
|
|
14
|
+
raise "must run on non-blocking fiber" if Fiber.current.blocking?
|
|
15
|
+
|
|
16
|
+
@tasks.each { |task| Fiber.schedule { task.run } }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def poll_until(timeout)
|
|
20
|
+
elapsed = 0
|
|
21
|
+
loop do
|
|
22
|
+
outcome = yield
|
|
23
|
+
return outcome.first unless outcome.nil?
|
|
24
|
+
|
|
25
|
+
sleep INTERVAL
|
|
26
|
+
elapsed += INTERVAL
|
|
27
|
+
raise "timeout" if timeout && elapsed > timeout
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Wraps a value so that poll_until can tell "finished with nil" from
|
|
32
|
+
# "not finished yet".
|
|
33
|
+
def finished(value) = [value]
|
|
34
|
+
|
|
35
|
+
def result_of(task)
|
|
36
|
+
raise task.error unless task.error.nil?
|
|
37
|
+
|
|
38
|
+
task.result
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -1,38 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
class Fiber
|
|
3
4
|
module Collector
|
|
5
|
+
# Settles as soon as any task finishes, whether it succeeded or failed.
|
|
4
6
|
class Race
|
|
7
|
+
include Polling
|
|
8
|
+
|
|
5
9
|
def initialize(procs)
|
|
6
10
|
@tasks = procs.map { Task.new(_1) }
|
|
7
11
|
end
|
|
8
12
|
|
|
9
13
|
def wait(timeout)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if timeout.nil?
|
|
15
|
-
loop do
|
|
16
|
-
task = @tasks.find { |t| t.done? }
|
|
17
|
-
unless task.nil?
|
|
18
|
-
raise task.error unless task.error.nil?
|
|
19
|
-
return task.result
|
|
20
|
-
end
|
|
21
|
-
sleep 0.001
|
|
22
|
-
end
|
|
23
|
-
else
|
|
24
|
-
elapsed = 0
|
|
25
|
-
loop do
|
|
26
|
-
task = @tasks.find { |t| t.done? }
|
|
27
|
-
unless task.nil?
|
|
28
|
-
raise task.error unless task.error.nil?
|
|
29
|
-
return task.result
|
|
30
|
-
end
|
|
31
|
-
t = 0.001
|
|
32
|
-
sleep t
|
|
33
|
-
elapsed += t
|
|
34
|
-
raise "timeout" if elapsed > timeout
|
|
35
|
-
end
|
|
14
|
+
schedule_tasks
|
|
15
|
+
poll_until(timeout) do
|
|
16
|
+
task = @tasks.find(&:done?)
|
|
17
|
+
finished(result_of(task)) unless task.nil?
|
|
36
18
|
end
|
|
37
19
|
end
|
|
38
20
|
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
3
|
class Fiber
|
|
3
4
|
module Collector
|
|
5
|
+
# One scheduled block, and whatever it produced: a result, or the error it
|
|
6
|
+
# raised. Never raises out of #run, so a collector can inspect every task.
|
|
4
7
|
class Task
|
|
5
8
|
attr_reader :state, :error, :result
|
|
6
9
|
|
|
@@ -23,4 +26,4 @@ class Fiber
|
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
|
-
end
|
|
29
|
+
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
2
|
+
|
|
3
|
+
require_relative "builder/polling"
|
|
4
|
+
require_relative "builder/all"
|
|
5
|
+
require_relative "builder/any"
|
|
6
|
+
require_relative "builder/race"
|
|
7
|
+
require_relative "builder/task"
|
|
6
8
|
|
|
7
9
|
class Fiber
|
|
8
|
-
module Collector
|
|
10
|
+
module Collector
|
|
11
|
+
# Gathers the scheduled blocks, then hands them to whichever collector the
|
|
12
|
+
# terminating call names: #all, #any or #race.
|
|
9
13
|
class Builder
|
|
10
14
|
def initialize
|
|
11
15
|
@blocks = []
|
|
@@ -23,7 +27,7 @@ class Fiber
|
|
|
23
27
|
def all(timeout: nil)
|
|
24
28
|
All.new(@blocks).wait(timeout)
|
|
25
29
|
end
|
|
26
|
-
|
|
30
|
+
|
|
27
31
|
def any(timeout: nil)
|
|
28
32
|
Any.new(@blocks).wait(timeout)
|
|
29
33
|
end
|
data/lib/fiber/collector.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
3
|
+
require_relative "collector/builder"
|
|
4
|
+
require_relative "collector/version"
|
|
5
5
|
|
|
6
6
|
class Fiber
|
|
7
|
+
# Collects the results of blocks run concurrently on non-blocking fibers,
|
|
8
|
+
# in the manner of JavaScript's Promise.all, Promise.any and Promise.race.
|
|
7
9
|
module Collector
|
|
8
10
|
def self.schedule(&block)
|
|
9
11
|
Builder.new.schedule(&block)
|
metadata
CHANGED
|
@@ -1,35 +1,31 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fiber-collector
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erik Madsen
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: Simple constructs to collect results from multiple concurrently scheduled
|
|
14
|
-
tasks inspired by JavaScript's Promise.all(), Promise.any() and Promise.race()
|
|
13
|
+
tasks, inspired by JavaScript's Promise.all(), Promise.any() and Promise.race()
|
|
14
|
+
methods
|
|
15
15
|
email:
|
|
16
16
|
- beatmadsen@gmail.com
|
|
17
17
|
executables: []
|
|
18
18
|
extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
|
20
20
|
files:
|
|
21
|
-
- ".rubocop.yml"
|
|
22
21
|
- CHANGELOG.md
|
|
23
|
-
- Gemfile
|
|
24
|
-
- Gemfile.lock
|
|
25
22
|
- LICENSE.txt
|
|
26
23
|
- README.md
|
|
27
|
-
- Rakefile
|
|
28
|
-
- fiber-collector.gemspec
|
|
29
24
|
- lib/fiber/collector.rb
|
|
30
25
|
- lib/fiber/collector/builder.rb
|
|
31
26
|
- lib/fiber/collector/builder/all.rb
|
|
32
27
|
- lib/fiber/collector/builder/any.rb
|
|
28
|
+
- lib/fiber/collector/builder/polling.rb
|
|
33
29
|
- lib/fiber/collector/builder/race.rb
|
|
34
30
|
- lib/fiber/collector/builder/task.rb
|
|
35
31
|
- lib/fiber/collector/version.rb
|
|
@@ -40,7 +36,9 @@ metadata:
|
|
|
40
36
|
homepage_uri: https://github.com/beatmadsen/fiber-collector
|
|
41
37
|
source_code_uri: https://github.com/beatmadsen/fiber-collector
|
|
42
38
|
changelog_uri: https://github.com/beatmadsen/fiber-collector/blob/main/CHANGELOG.md
|
|
43
|
-
|
|
39
|
+
bug_tracker_uri: https://github.com/beatmadsen/fiber-collector/issues
|
|
40
|
+
documentation_uri: https://github.com/beatmadsen/fiber-collector#readme
|
|
41
|
+
rubygems_mfa_required: 'true'
|
|
44
42
|
rdoc_options: []
|
|
45
43
|
require_paths:
|
|
46
44
|
- lib
|
|
@@ -55,8 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
55
53
|
- !ruby/object:Gem::Version
|
|
56
54
|
version: '0'
|
|
57
55
|
requirements: []
|
|
58
|
-
rubygems_version:
|
|
59
|
-
signing_key:
|
|
56
|
+
rubygems_version: 4.0.9
|
|
60
57
|
specification_version: 4
|
|
61
58
|
summary: Collect results from multiple concurrently scheduled tasks
|
|
62
59
|
test_files: []
|
data/.rubocop.yml
DELETED
data/Gemfile
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
# Specify your gem's dependencies in fiber-collector.gemspec
|
|
6
|
-
gemspec
|
|
7
|
-
|
|
8
|
-
gem "rake", "~> 13.0"
|
|
9
|
-
|
|
10
|
-
gem "minitest", "~> 5.0"
|
|
11
|
-
|
|
12
|
-
gem "rubocop", "~> 1.21"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
group :development do
|
|
16
|
-
gem 'async', '> 2.0'
|
|
17
|
-
end
|
data/Gemfile.lock
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
fiber-collector (0.1.1)
|
|
5
|
-
|
|
6
|
-
GEM
|
|
7
|
-
remote: https://rubygems.org/
|
|
8
|
-
specs:
|
|
9
|
-
ast (2.4.2)
|
|
10
|
-
async (2.3.1)
|
|
11
|
-
console (~> 1.10)
|
|
12
|
-
io-event (~> 1.1)
|
|
13
|
-
timers (~> 4.1)
|
|
14
|
-
console (1.16.2)
|
|
15
|
-
fiber-local
|
|
16
|
-
fiber-local (1.0.0)
|
|
17
|
-
io-event (1.1.6)
|
|
18
|
-
json (2.6.3)
|
|
19
|
-
minitest (5.17.0)
|
|
20
|
-
parallel (1.22.1)
|
|
21
|
-
parser (3.2.0.0)
|
|
22
|
-
ast (~> 2.4.1)
|
|
23
|
-
rainbow (3.1.1)
|
|
24
|
-
rake (13.0.6)
|
|
25
|
-
regexp_parser (2.6.2)
|
|
26
|
-
rexml (3.2.5)
|
|
27
|
-
rubocop (1.44.1)
|
|
28
|
-
json (~> 2.3)
|
|
29
|
-
parallel (~> 1.10)
|
|
30
|
-
parser (>= 3.2.0.0)
|
|
31
|
-
rainbow (>= 2.2.2, < 4.0)
|
|
32
|
-
regexp_parser (>= 1.8, < 3.0)
|
|
33
|
-
rexml (>= 3.2.5, < 4.0)
|
|
34
|
-
rubocop-ast (>= 1.24.1, < 2.0)
|
|
35
|
-
ruby-progressbar (~> 1.7)
|
|
36
|
-
unicode-display_width (>= 2.4.0, < 3.0)
|
|
37
|
-
rubocop-ast (1.24.1)
|
|
38
|
-
parser (>= 3.1.1.0)
|
|
39
|
-
ruby-progressbar (1.11.0)
|
|
40
|
-
timers (4.3.5)
|
|
41
|
-
unicode-display_width (2.4.2)
|
|
42
|
-
|
|
43
|
-
PLATFORMS
|
|
44
|
-
x86_64-darwin-20
|
|
45
|
-
|
|
46
|
-
DEPENDENCIES
|
|
47
|
-
async (> 2.0)
|
|
48
|
-
fiber-collector!
|
|
49
|
-
minitest (~> 5.0)
|
|
50
|
-
rake (~> 13.0)
|
|
51
|
-
rubocop (~> 1.21)
|
|
52
|
-
|
|
53
|
-
BUNDLED WITH
|
|
54
|
-
2.4.1
|
data/Rakefile
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "bundler/gem_tasks"
|
|
4
|
-
require "rake/testtask"
|
|
5
|
-
|
|
6
|
-
Rake::TestTask.new(:test) do |t|
|
|
7
|
-
t.libs << "test"
|
|
8
|
-
t.libs << "lib"
|
|
9
|
-
t.test_files = FileList["test/**/test_*.rb"]
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
require "rubocop/rake_task"
|
|
13
|
-
|
|
14
|
-
RuboCop::RakeTask.new
|
|
15
|
-
|
|
16
|
-
task default: %i[test rubocop]
|
data/fiber-collector.gemspec
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/fiber/collector/version"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name = "fiber-collector"
|
|
8
|
-
spec.version = Fiber::Collector::VERSION
|
|
9
|
-
spec.authors = ["Erik Madsen"]
|
|
10
|
-
spec.email = ["beatmadsen@gmail.com"]
|
|
11
|
-
|
|
12
|
-
spec.summary = "Collect results from multiple concurrently scheduled tasks"
|
|
13
|
-
spec.description = "Simple constructs to collect results from multiple concurrently scheduled tasks inspired by JavaScript's Promise.all(), Promise.any() and Promise.race() methods"
|
|
14
|
-
spec.homepage = 'https://github.com/beatmadsen/fiber-collector'
|
|
15
|
-
spec.license = "MIT"
|
|
16
|
-
spec.required_ruby_version = ">= 3.1"
|
|
17
|
-
|
|
18
|
-
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
|
19
|
-
|
|
20
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
21
|
-
spec.metadata["source_code_uri"] = 'https://github.com/beatmadsen/fiber-collector'
|
|
22
|
-
spec.metadata["changelog_uri"] = 'https://github.com/beatmadsen/fiber-collector/blob/main/CHANGELOG.md'
|
|
23
|
-
|
|
24
|
-
# Specify which files should be added to the gem when it is released.
|
|
25
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
26
|
-
spec.files = Dir.chdir(__dir__) do
|
|
27
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
28
|
-
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
spec.bindir = "exe"
|
|
32
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
33
|
-
spec.require_paths = ["lib"]
|
|
34
|
-
|
|
35
|
-
# Uncomment to register a new dependency of your gem
|
|
36
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
|
37
|
-
|
|
38
|
-
# For more information and examples about making a new gem, check out our
|
|
39
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
|
40
|
-
end
|