core-async 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2c40406be47b9385d6252ddfc534383b817249c44aeda7e3410ea12d15afde
4
- data.tar.gz: 48ee77b1bec473726d7deb3f35481c487e946c8fd00668c039d7556164bd70fc
3
+ metadata.gz: 5449a66653642c9fa6f96c2a968bda754e31aefdee4c207d1e3a02e18e7e7dcb
4
+ data.tar.gz: bc9e2ae806089f3a64cb3a3fa15d97505c5be54c0ca455577ddf3f6b1806f73b
5
5
  SHA512:
6
- metadata.gz: 21f8e4bf33f14497c2080450f6ba4db0abde814ffbb86a3b0ed64f187edfd4c042830fcccd4c9044b5ff67d31bdf25904b2430bf715c02ea0334c7152cbe597d
7
- data.tar.gz: b41c4e064774fc736814255aa5f92dfd160967c0370c7a5b76784418793a2550e0403beb6524e37b488ea76e9db25c7f1810b8919f699b9182a3268feaa44431
6
+ metadata.gz: 7fe6d3f541fd066a958453573f027a70321f0176e3db0b1e3afdb414841bcb8d7367414ccc1aa6ae06b97437ef2fc299fee761ab88661470d69bd7cffd2d9c88
7
+ data.tar.gz: a4786ac589bbb949c78ca6832e27b49abe79ed36b2b401529b0a56443404165e665d6f28aecdeeb9d1543e2f7e7c8ffba4ad05219fe5775e74d89fde750d2f9e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,44 @@
1
+ ## [v0.5.0](https://github.com/metabahn/corerb/releases/tag/2021-03-26)
2
+
3
+ *released on 2021-03-26*
4
+
5
+ * `chg` [#25](https://github.com/metabahn/corerb/pull/25) Privatize async behavior ([bryanp](https://github.com/bryanp))
6
+ * `add` [#24](https://github.com/metabahn/corerb/pull/24) Add Is::Async#cancel ([bryanp](https://github.com/bryanp))
7
+ * `fix` [#23](https://github.com/metabahn/corerb/pull/23) Improve control flow during async enumeration / collection building ([bryanp](https://github.com/bryanp))
8
+ * `fix` [#22](https://github.com/metabahn/corerb/pull/22) Expose errors that occur when building a collection ([bryanp](https://github.com/bryanp))
9
+
10
+ ## [v0.4.0](https://github.com/metabahn/corerb/releases/tag/2021-03-17.1)
11
+
12
+ *released on 2021-03-17*
13
+
14
+ * `chg` [#21](https://github.com/metabahn/corerb/pull/21) Cancel async futures without waiting ([bryanp](https://github.com/bryanp))
15
+
16
+ ## [v0.3.0](https://github.com/metabahn/corerb/releases/tag/2021-03-17)
17
+
18
+ *released on 2021-03-17*
19
+
20
+ * `add` [#20](https://github.com/metabahn/corerb/pull/20) Allow futures to be canceled ([bryanp](https://github.com/bryanp))
21
+
22
+ ## [v0.2.1](https://github.com/metabahn/corerb/releases/tag/2021-03-05)
23
+
24
+ *released on 2021-03-05*
25
+
26
+ * `fix` [#18](https://github.com/metabahn/corerb/pull/18) Surface errors that occur in tasks running within an async reactor ([bryanp](https://github.com/bryanp))
27
+
28
+ ## [v0.2.0](https://github.com/metabahn/corerb/releases/tag/2021-03-05)
29
+
30
+ *released on 2021-03-05*
31
+
32
+ * `add` [#17](https://github.com/metabahn/corerb/pull/17) Add Core::Async::Collection for building async collections ([bryanp](https://github.com/bryanp))
33
+ * `add` [#16](https://github.com/metabahn/corerb/pull/16) Add Core::Async::Enumerator for async enumeration ([bryanp](https://github.com/bryanp))
34
+ * `add` [#15](https://github.com/metabahn/corerb/pull/15) Add Is::Async#resolve for resolving potential futures to final values ([bryanp](https://github.com/bryanp))
35
+ * `add` [#14](https://github.com/metabahn/corerb/pull/14) Add Is::Async#aware for guaranteeing an async context without nesting ([bryanp](https://github.com/bryanp))
36
+ * `fix` [#13](https://github.com/metabahn/corerb/pull/13) Correctly return the result from top-level await ([bryanp](https://github.com/bryanp))
37
+ * `fix` [#12](https://github.com/metabahn/corerb/pull/12) Resolve some edge-cases and bugs around raising top-level errors ([bryanp](https://github.com/bryanp))
38
+ * `chg` [#11](https://github.com/metabahn/corerb/pull/11) Raise an argument error when attempting to wrap a non-async-aware object ([bryanp](https://github.com/bryanp))
39
+ * `add` [#10](https://github.com/metabahn/corerb/pull/10) Add a pattern for making a call explicitly async ([bryanp](https://github.com/bryanp))
40
+ * `add` [#9](https://github.com/metabahn/corerb/pull/9) Introduce async futures ([bryanp](https://github.com/bryanp))
41
+
1
42
  ## [v0.1.0](https://github.com/metabahn/corerb/releases/tag/2021-02-11)
2
43
 
3
44
  *released on 2021-02-11*
data/lib/core/async.rb CHANGED
@@ -1,7 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "async/bootstrap"
4
+
3
5
  module Core
4
6
  module Async
7
+ require_relative "async/collection"
8
+ require_relative "async/enumerator"
9
+ require_relative "async/future"
5
10
  require_relative "async/reactor"
6
11
  require_relative "async/version"
7
12
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "async"
4
+ Console.logger.off!
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../is/async"
4
+
5
+ module Core
6
+ module Async
7
+ class Collection
8
+ class << self
9
+ include Is::Async
10
+
11
+ # [public] Builds a collection from an enumerable object.
12
+ #
13
+ def build(object)
14
+ aware do
15
+ errored, stopped = false
16
+
17
+ values = object.map { |value|
18
+ break if errored || stopped
19
+
20
+ async do
21
+ if block_given?
22
+ yield value
23
+ else
24
+ value
25
+ end
26
+ rescue LocalJumpError
27
+ stopped = true
28
+ rescue => error
29
+ errored = true
30
+ raise error
31
+ end
32
+ }
33
+
34
+ new(values) if values
35
+ end
36
+ end
37
+ end
38
+
39
+ include Enumerable
40
+ include Is::Async
41
+
42
+ def initialize(values = [])
43
+ unless values.respond_to?(:each)
44
+ raise ArgumentError, "values are not enumerable"
45
+ end
46
+
47
+ @values = values
48
+ end
49
+
50
+ # [public] Adds a value to the collection.
51
+ #
52
+ def <<(value)
53
+ @values << value
54
+ end
55
+ alias_method :add, :<<
56
+
57
+ # [public] Yields each resolved value.
58
+ #
59
+ def each
60
+ return to_enum(:each) unless block_given?
61
+
62
+ @values.each do |value|
63
+ yield resolve(value)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../is/async"
4
+
5
+ module Core
6
+ module Async
7
+ class Enumerator
8
+ include Enumerable
9
+ include Is::Async
10
+
11
+ def initialize(object)
12
+ unless object.respond_to?(:each)
13
+ raise ArgumentError, "object is not enumerable"
14
+ end
15
+
16
+ @object = object
17
+ end
18
+
19
+ # [public] Yields each value within its own async context, waiting on the enumeration to complete.
20
+ #
21
+ def each
22
+ unless block_given?
23
+ return to_enum(:each)
24
+ end
25
+
26
+ await do
27
+ errored, stopped = false
28
+
29
+ @object.each do |value|
30
+ break if errored || stopped
31
+
32
+ async do
33
+ yield value
34
+ rescue LocalJumpError
35
+ stopped = true
36
+ rescue => error
37
+ errored = true
38
+ raise error
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Core
4
+ module Async
5
+ # [public] Represents a future result.
6
+ #
7
+ class Future
8
+ def initialize(task)
9
+ @task = task
10
+ @error = nil
11
+ end
12
+
13
+ # [public] Wait on the future to resolve, returning self.
14
+ #
15
+ def wait
16
+ unless @error
17
+ resolve
18
+ end
19
+
20
+ self
21
+ end
22
+
23
+ # [public] Attempt to cancel the future, returns true if successful.
24
+ #
25
+ def cancel
26
+ if pending?
27
+ @task.stop
28
+ end
29
+
30
+ self
31
+ end
32
+
33
+ # [public] Return the result, blocking until available.
34
+ #
35
+ def result
36
+ @error || @result ||= resolve
37
+ end
38
+
39
+ private def resolve
40
+ wait_all(@task)
41
+
42
+ resolve_value(@task.result)
43
+ rescue => error
44
+ @error = error
45
+
46
+ raise error
47
+ end
48
+
49
+ # [public] Return any error that occurred without re-raising, blocking until available.
50
+ #
51
+ def error
52
+ @error ||= get_error
53
+ end
54
+
55
+ private def get_error
56
+ result
57
+ nil
58
+ rescue => error
59
+ error
60
+ end
61
+
62
+ # [public] Return the status; one of :pending, :failed, or :complete.
63
+ #
64
+ def status
65
+ if defined?(@status)
66
+ @status
67
+ else
68
+ status = find_status
69
+ if terminal_status?(status)
70
+ @status = status
71
+ end
72
+
73
+ status
74
+ end
75
+ end
76
+
77
+ private def find_status
78
+ case @task.status
79
+ when :running
80
+ :pending
81
+ when :stopped
82
+ :canceled
83
+ when :failed
84
+ :failed
85
+ when :complete
86
+ :complete
87
+ end
88
+ end
89
+
90
+ private def terminal_status?(status)
91
+ status == :failed || status == :complete
92
+ end
93
+
94
+ # [public] Return `true` if pending.
95
+ #
96
+ def pending?
97
+ status == :pending
98
+ end
99
+
100
+ # [public] Return `true` if failed.
101
+ #
102
+ def failed?
103
+ status == :failed
104
+ end
105
+
106
+ # [public] Return `true` if canceled.
107
+ #
108
+ def canceled?
109
+ status == :canceled
110
+ end
111
+
112
+ # [public] Return `true` if complete.
113
+ #
114
+ def complete?
115
+ status == :complete
116
+ end
117
+
118
+ # [public] Return `true` if failed or complete.
119
+ #
120
+ def resolved?
121
+ failed? || complete?
122
+ end
123
+
124
+ private def resolve_value(value)
125
+ if value.is_a?(self.class)
126
+ resolve_value(value.result)
127
+ else
128
+ value
129
+ end
130
+ end
131
+
132
+ private def wait_all(task)
133
+ task.children&.each do |child|
134
+ wait_all(child)
135
+ end
136
+
137
+ task.wait
138
+ end
139
+ end
140
+ end
141
+ end
@@ -33,7 +33,9 @@ module Core
33
33
  @runnable = ::Async::Reactor.new
34
34
 
35
35
  @runnable.run {
36
- yield self
36
+ async {
37
+ yield self
38
+ }.result
37
39
  }.wait
38
40
  end
39
41
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Core
4
4
  module Async
5
- VERSION = "0.1.0"
5
+ VERSION = "0.5.0"
6
6
 
7
7
  def self.version
8
8
  VERSION
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Core
4
+ module Async
5
+ # [public] Wraps an object, calling all methods in an async context.
6
+ #
7
+ class Wrapper < BasicObject
8
+ def initialize(target)
9
+ unless target.respond_to?(:async)
10
+ ::Kernel.raise ::ArgumentError, "object is not async-aware"
11
+ end
12
+
13
+ @target = target
14
+ end
15
+
16
+ def method_missing(name, *args, &block)
17
+ @target.async do
18
+ @target.public_send(name, *args, &block)
19
+ end
20
+ end
21
+
22
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords)
23
+
24
+ def respond_to_missing?(name, *)
25
+ true
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/is/async.rb CHANGED
@@ -1,41 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "async"
4
- Console.logger.off!
5
-
3
+ require_relative "../core/async/bootstrap"
4
+ require_relative "../core/async/future"
6
5
  require_relative "../core/async/timeout"
6
+ require_relative "../core/async/wrapper"
7
7
 
8
8
  module Is
9
9
  # [public] Makes Ruby objects async-aware.
10
10
  #
11
11
  module Async
12
- # [public] Call asynchronous behavior in a proper async context.
12
+ # [public] Call behavior asychronously, returning a future.
13
13
  #
14
14
  def async
15
- ::Async::Reactor.run do
16
- yield
15
+ if block_given?
16
+ task = ::Async::Reactor.run { |current|
17
+ begin
18
+ yield
19
+ ensure
20
+ current.yield
21
+ end
22
+ }
23
+
24
+ Core::Async::Future.new(task)
25
+ else
26
+ Core::Async::Wrapper.new(self)
17
27
  end
18
28
  end
19
29
 
20
- # [public] Call asynchronous behavior synchronously in a proper async context.
30
+ # [public] Call behavior synchronously but within an async context, waiting on the result.
21
31
  #
22
- def await
32
+ private def await
23
33
  if (task = ::Async::Task.current?)
24
- reference = task.async {
25
- yield
34
+ reference = task.async { |current|
35
+ begin
36
+ yield
37
+ ensure
38
+ current.yield
39
+ end
26
40
  }
27
41
 
28
42
  wait_all(reference)
29
43
  else
30
- ::Async::Reactor.run {
31
- yield
44
+ ::Async::Reactor.run { |task|
45
+ async {
46
+ yield
47
+ }.result
32
48
  }.wait
33
49
  end
34
50
  end
35
51
 
52
+ # [public] Call behavior within an async context without additional nesting.
53
+ #
54
+ private def aware
55
+ if ::Async::Task.current?
56
+ yield
57
+ else
58
+ await do
59
+ yield
60
+ end
61
+ end
62
+ end
63
+
36
64
  # [public] Sleeps for `seconds` in a proper async context.
37
65
  #
38
- def sleep(seconds)
66
+ private def sleep(seconds)
39
67
  internal_await do |task|
40
68
  task.sleep(seconds)
41
69
  end
@@ -45,7 +73,7 @@ module Is
45
73
  #
46
74
  # Raises `Core::Async::Timeout` if execution exceeds `seconds`.
47
75
  #
48
- def timeout(seconds, &block)
76
+ private def timeout(seconds, &block)
49
77
  internal_await do |task|
50
78
  if seconds && seconds > 0
51
79
  task.with_timeout(seconds, Core::Async::Timeout) do
@@ -59,12 +87,31 @@ module Is
59
87
 
60
88
  # [public] Yields control to allow other fibers to execute.
61
89
  #
62
- def defer
90
+ private def defer
63
91
  if (task = ::Async::Task.current?)
64
92
  task.yield
65
93
  end
66
94
  end
67
95
 
96
+ # [public] Cancels the current async behavior if in progress.
97
+ #
98
+ private def cancel
99
+ if (task = ::Async::Task.current?)
100
+ task.stop
101
+ end
102
+ end
103
+
104
+ # [public] Resolves a potential future to a final result.
105
+ #
106
+ private def resolve(value)
107
+ case value
108
+ when Core::Async::Future
109
+ value.result
110
+ else
111
+ value
112
+ end
113
+ end
114
+
68
115
  private def internal_async
69
116
  ::Async::Reactor.run do |task|
70
117
  yield task
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: core-async
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Powell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-12 00:00:00.000000000 Z
11
+ date: 2021-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -33,9 +33,14 @@ files:
33
33
  - CHANGELOG.md
34
34
  - LICENSE
35
35
  - lib/core/async.rb
36
+ - lib/core/async/bootstrap.rb
37
+ - lib/core/async/collection.rb
38
+ - lib/core/async/enumerator.rb
39
+ - lib/core/async/future.rb
36
40
  - lib/core/async/reactor.rb
37
41
  - lib/core/async/timeout.rb
38
42
  - lib/core/async/version.rb
43
+ - lib/core/async/wrapper.rb
39
44
  - lib/is/async.rb
40
45
  homepage: https://github.com/metabahn/corerb/
41
46
  licenses: