core-async 0.1.0 → 0.2.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: 36564cdb777065399358b390e554b89b367902f9d065ed3def243a0816b17049
4
+ data.tar.gz: ec4cb82ad1b7a97d60af94fc4be07aca732f03f9fe5db91367d7628631d79bc7
5
5
  SHA512:
6
- metadata.gz: 21f8e4bf33f14497c2080450f6ba4db0abde814ffbb86a3b0ed64f187edfd4c042830fcccd4c9044b5ff67d31bdf25904b2430bf715c02ea0334c7152cbe597d
7
- data.tar.gz: b41c4e064774fc736814255aa5f92dfd160967c0370c7a5b76784418793a2550e0403beb6524e37b488ea76e9db25c7f1810b8919f699b9182a3268feaa44431
6
+ metadata.gz: 7cf03e8b149e9e0d5185d907025fd833c6d63074be1461970862ae138f3f3744e361422068d8bd4e8696f28fe459d65d7f3e2caa153c2b0f5b374f8d550e21da
7
+ data.tar.gz: 11b9a8ea0a25975bbf9f92ca7b41c1f0db6b2a7ae2ab0a16bbf6d3057d6d6add5875b30b3646e4028747f4eebb8863769fe8403a825c6e5a8a71cd25e677de22
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [v0.2.0](https://github.com/metabahn/corerb/releases/tag/2021-03-05)
2
+
3
+ *released on 2021-03-05*
4
+
5
+ * `add` [#17](https://github.com/metabahn/corerb/pull/17) Add Core::Async::Collection for building async collections ([bryanp](https://github.com/bryanp))
6
+ * `add` [#16](https://github.com/metabahn/corerb/pull/16) Add Core::Async::Enumerator for async enumeration ([bryanp](https://github.com/bryanp))
7
+ * `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))
8
+ * `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))
9
+ * `fix` [#13](https://github.com/metabahn/corerb/pull/13) Correctly return the result from top-level await ([bryanp](https://github.com/bryanp))
10
+ * `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))
11
+ * `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))
12
+ * `add` [#10](https://github.com/metabahn/corerb/pull/10) Add a pattern for making a call explicitly async ([bryanp](https://github.com/bryanp))
13
+ * `add` [#9](https://github.com/metabahn/corerb/pull/9) Introduce async futures ([bryanp](https://github.com/bryanp))
14
+
1
15
  ## [v0.1.0](https://github.com/metabahn/corerb/releases/tag/2021-02-11)
2
16
 
3
17
  *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,59 @@
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
+ values = object.map { |value|
16
+ async do
17
+ if block_given?
18
+ yield value
19
+ else
20
+ value
21
+ end
22
+ end
23
+ }
24
+
25
+ new(values)
26
+ end
27
+ end
28
+ end
29
+
30
+ include Enumerable
31
+ include Is::Async
32
+
33
+ def initialize(values = [])
34
+ unless values.respond_to?(:each)
35
+ raise ArgumentError, "values are not enumerable"
36
+ end
37
+
38
+ @values = values
39
+ end
40
+
41
+ # [public] Adds a value to the collection.
42
+ #
43
+ def <<(value)
44
+ @values << value
45
+ end
46
+ alias_method :add, :<<
47
+
48
+ # [public] Yields each resolved value.
49
+ #
50
+ def each
51
+ return to_enum(:each) unless block_given?
52
+
53
+ @values.each do |value|
54
+ yield resolve(value)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,46 @@
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 = false
28
+ @object.each do |value|
29
+ break if errored
30
+
31
+ async {
32
+ begin
33
+ yield value
34
+ rescue => error
35
+ errored = true
36
+ raise error
37
+ ensure
38
+ defer
39
+ end
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,123 @@
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 task to complete, returning self.
14
+ #
15
+ def wait
16
+ unless @error
17
+ resolve
18
+ end
19
+
20
+ self
21
+ end
22
+
23
+ # [public] Return the result, blocking until available.
24
+ #
25
+ def result
26
+ @error || @result ||= resolve
27
+ end
28
+
29
+ private def resolve
30
+ wait_all(@task)
31
+
32
+ resolve_value(@task.result)
33
+ rescue => error
34
+ @error = error
35
+
36
+ raise error
37
+ end
38
+
39
+ # [public] Return any error that occurred without re-raising, blocking until available.
40
+ #
41
+ def error
42
+ @error ||= get_error
43
+ end
44
+
45
+ private def get_error
46
+ result
47
+ nil
48
+ rescue => error
49
+ error
50
+ end
51
+
52
+ # [public] Return the status; one of :pending, :failed, or :complete.
53
+ #
54
+ def status
55
+ if defined?(@status)
56
+ @status
57
+ else
58
+ status = find_status
59
+ if terminal_status?(status)
60
+ @status = status
61
+ end
62
+
63
+ status
64
+ end
65
+ end
66
+
67
+ private def find_status
68
+ case @task.status
69
+ when :running
70
+ :pending
71
+ when :failed
72
+ :failed
73
+ when :complete
74
+ :complete
75
+ end
76
+ end
77
+
78
+ private def terminal_status?(status)
79
+ status == :failed || status == :complete
80
+ end
81
+
82
+ # [public] Return `true` if pending.
83
+ #
84
+ def pending?
85
+ status == :pending
86
+ end
87
+
88
+ # [public] Return `true` if failed.
89
+ #
90
+ def failed?
91
+ status == :failed
92
+ end
93
+
94
+ # [public] Return `true` if complete.
95
+ #
96
+ def complete?
97
+ status == :complete
98
+ end
99
+
100
+ # [public] Return `true` if failed or complete.
101
+ #
102
+ def resolved?
103
+ failed? || complete?
104
+ end
105
+
106
+ private def resolve_value(value)
107
+ if value.is_a?(self.class)
108
+ resolve_value(value.result)
109
+ else
110
+ value
111
+ end
112
+ end
113
+
114
+ private def wait_all(task)
115
+ task.children&.each do |child|
116
+ wait_all(child)
117
+ end
118
+
119
+ task.wait
120
+ end
121
+ end
122
+ end
123
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Core
4
4
  module Async
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.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,38 +1,66 @@
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
32
  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
+ 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
66
  def sleep(seconds)
@@ -65,6 +93,17 @@ module Is
65
93
  end
66
94
  end
67
95
 
96
+ # [public] Resolves a potential future to a final result.
97
+ #
98
+ def resolve(value)
99
+ case value
100
+ when Core::Async::Future
101
+ value.result
102
+ else
103
+ value
104
+ end
105
+ end
106
+
68
107
  private def internal_async
69
108
  ::Async::Reactor.run do |task|
70
109
  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.2.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-05 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: