rosarium 0.1.2 → 0.1.7
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 +5 -5
- data/Gemfile +3 -0
- data/Gemfile.lock +34 -13
- data/README.md +10 -13
- data/lib/rosarium.rb +2 -1
- data/lib/rosarium/deferred.rb +3 -5
- data/lib/rosarium/fixed_thread_executor.rb +9 -11
- data/lib/rosarium/promise.rb +165 -40
- data/spec/deferred_spec.rb +2 -2
- data/spec/promise_methods_spec.rb +11 -4
- data/spec/promise_static_spec.rb +3 -8
- metadata +18 -6
- data/lib/rosarium/simple_promise.rb +0 -164
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3789051cf47569f1729bb161433ecb033274b49833e774eec788e5884c490897
|
4
|
+
data.tar.gz: f48ef4f93bc8cadb68d9069d3dd3f8434b45ce43e6f3057918912d97d72f5432
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e108887ddb2cdee8bc0e5abe53915e249028d5f367a7c617874460a4b7b5ec19ca04c364631e3b6235c0da80c2ae2951501e08c6b280dd2609455bdbe9f7858
|
7
|
+
data.tar.gz: 7622eab1b92fcee040b91394422f8b7f1f2c78be004c62988d5e588bf528ab6718da0bc97d3adb8b24022b22498d80843e31c051b5b83ed2f4fed308ff288370
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,25 +1,46 @@
|
|
1
1
|
GEM
|
2
2
|
specs:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
ast (2.4.1)
|
4
|
+
diff-lcs (1.4.4)
|
5
|
+
parallel (1.20.1)
|
6
|
+
parser (3.0.0.0)
|
7
|
+
ast (~> 2.4.1)
|
8
|
+
rainbow (3.0.0)
|
9
|
+
regexp_parser (1.8.2)
|
10
|
+
rexml (3.2.4)
|
11
|
+
rspec (3.9.0)
|
12
|
+
rspec-core (~> 3.9.0)
|
13
|
+
rspec-expectations (~> 3.9.0)
|
14
|
+
rspec-mocks (~> 3.9.0)
|
15
|
+
rspec-core (3.9.2)
|
16
|
+
rspec-support (~> 3.9.3)
|
17
|
+
rspec-expectations (3.9.2)
|
11
18
|
diff-lcs (>= 1.2.0, < 2.0)
|
12
|
-
rspec-support (~> 3.
|
13
|
-
rspec-mocks (3.
|
19
|
+
rspec-support (~> 3.9.0)
|
20
|
+
rspec-mocks (3.9.1)
|
14
21
|
diff-lcs (>= 1.2.0, < 2.0)
|
15
|
-
rspec-support (~> 3.
|
16
|
-
rspec-support (3.
|
22
|
+
rspec-support (~> 3.9.0)
|
23
|
+
rspec-support (3.9.3)
|
24
|
+
rubocop (1.2.0)
|
25
|
+
parallel (~> 1.10)
|
26
|
+
parser (>= 2.7.1.5)
|
27
|
+
rainbow (>= 2.2.2, < 4.0)
|
28
|
+
regexp_parser (>= 1.8)
|
29
|
+
rexml
|
30
|
+
rubocop-ast (>= 1.0.1)
|
31
|
+
ruby-progressbar (~> 1.7)
|
32
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
33
|
+
rubocop-ast (1.4.0)
|
34
|
+
parser (>= 2.7.1.5)
|
35
|
+
ruby-progressbar (1.11.0)
|
36
|
+
unicode-display_width (1.7.0)
|
17
37
|
|
18
38
|
PLATFORMS
|
19
39
|
ruby
|
20
40
|
|
21
41
|
DEPENDENCIES
|
22
42
|
rspec (~> 3.4)
|
43
|
+
rubocop (~> 1.2)
|
23
44
|
|
24
45
|
BUNDLED WITH
|
25
|
-
1.
|
46
|
+
1.17.3
|
data/README.md
CHANGED
@@ -66,9 +66,15 @@ then later, use the "deferred" to fulfill or reject the promise:
|
|
66
66
|
## Methods of promises:
|
67
67
|
|
68
68
|
```
|
69
|
-
# One of: :pending, :
|
69
|
+
# One of: :pending, :fulfilled, :rejected.
|
70
70
|
promise.state
|
71
71
|
|
72
|
+
# Equivalent to "state == :fulfilled"
|
73
|
+
promise.fulfilled?
|
74
|
+
|
75
|
+
# Equivalent to "state == :rejected"
|
76
|
+
promise.rejected?
|
77
|
+
|
72
78
|
# Wait for the promise to be settled, then return its value (if fulfilled -
|
73
79
|
# note the value may be nil), or nil (if rejected).
|
74
80
|
promise.value
|
@@ -77,22 +83,13 @@ then later, use the "deferred" to fulfill or reject the promise:
|
|
77
83
|
# or nil (if fulfilled).
|
78
84
|
promise.reason
|
79
85
|
|
80
|
-
# A hash describing the state of the promise. Always includes :state key;
|
81
|
-
# may include :value or :reason.
|
82
|
-
promise.inspect
|
83
|
-
|
84
|
-
# true iff state == :fulfilled
|
85
|
-
promise.fulfilled?
|
86
|
-
|
87
|
-
# true iff state == :rejected
|
88
|
-
promise.rejected?
|
89
|
-
|
90
86
|
# Wait for the promise to be settled, then return its value (if fulfilled),
|
91
87
|
# or raise with the rejection reason (if rejected).
|
92
88
|
promise.value!
|
93
89
|
|
94
|
-
#
|
95
|
-
|
90
|
+
# A hash describing the state of the promise. Always includes :state key;
|
91
|
+
# may include :value or :reason.
|
92
|
+
promise.inspect
|
96
93
|
```
|
97
94
|
|
98
95
|
Chaining promises together:
|
data/lib/rosarium.rb
CHANGED
data/lib/rosarium/deferred.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module Rosarium
|
3
4
|
class Deferred
|
4
5
|
|
5
6
|
def initialize(promise, resolver, rejecter)
|
@@ -8,9 +9,7 @@ module Rosarium
|
|
8
9
|
@rejecter = rejecter
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
-
@promise
|
13
|
-
end
|
12
|
+
attr_reader :promise
|
14
13
|
|
15
14
|
def resolve(value)
|
16
15
|
@resolver.call(value)
|
@@ -21,5 +20,4 @@ module Rosarium
|
|
21
20
|
end
|
22
21
|
|
23
22
|
end
|
24
|
-
|
25
23
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module Rosarium
|
3
4
|
class FixedThreadExecutor
|
4
5
|
|
5
6
|
def initialize(max = 1)
|
@@ -14,7 +15,7 @@ module Rosarium
|
|
14
15
|
@mutex.synchronize do
|
15
16
|
@waiting << block
|
16
17
|
if @executing < @max
|
17
|
-
@executing
|
18
|
+
@executing += 1
|
18
19
|
t = Thread.new { execute_and_count_down }
|
19
20
|
@threads.push t
|
20
21
|
end
|
@@ -36,26 +37,23 @@ module Rosarium
|
|
36
37
|
private
|
37
38
|
|
38
39
|
def execute_and_count_down
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
@
|
43
|
-
@executing = @executing - 1
|
44
|
-
end
|
40
|
+
execute
|
41
|
+
ensure
|
42
|
+
@mutex.synchronize do
|
43
|
+
@executing -= 1
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
48
47
|
def execute
|
49
|
-
|
48
|
+
loop do
|
50
49
|
block = @mutex.synchronize { @waiting.shift }
|
51
50
|
block or break
|
52
51
|
begin
|
53
52
|
block.call
|
54
|
-
rescue Exception
|
53
|
+
rescue Exception
|
55
54
|
end
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
59
58
|
end
|
60
|
-
|
61
59
|
end
|
data/lib/rosarium/promise.rb
CHANGED
@@ -1,15 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rosarium
|
4
|
+
class Promise
|
2
5
|
|
3
|
-
|
6
|
+
private_class_method :new
|
4
7
|
|
5
|
-
|
6
|
-
|
8
|
+
def self.defer
|
9
|
+
promise = new
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
+
resolver = lambda do |value|
|
12
|
+
promise.send(:try_settle, value, nil)
|
13
|
+
nil # do not leak
|
14
|
+
end
|
15
|
+
|
16
|
+
rejecter = lambda do |reason|
|
17
|
+
raise "reason must be an Exception" unless reason.is_a?(Exception)
|
18
|
+
promise.send(:try_settle, nil, reason)
|
19
|
+
nil # do not leak
|
11
20
|
end
|
12
21
|
|
22
|
+
Deferred.new(promise, resolver, rejecter)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.resolve(value)
|
26
|
+
return value if value.is_a? Promise
|
27
|
+
|
13
28
|
deferred = defer
|
14
29
|
deferred.resolve(value)
|
15
30
|
deferred.promise
|
@@ -22,15 +37,7 @@ module Rosarium
|
|
22
37
|
end
|
23
38
|
|
24
39
|
def self.execute(&block)
|
25
|
-
|
26
|
-
EXECUTOR.submit do
|
27
|
-
begin
|
28
|
-
deferred.resolve block.call
|
29
|
-
rescue Exception => e
|
30
|
-
deferred.reject e
|
31
|
-
end
|
32
|
-
end
|
33
|
-
deferred.promise
|
40
|
+
@resolved.then(&block)
|
34
41
|
end
|
35
42
|
|
36
43
|
def self.all_settled(promises)
|
@@ -42,7 +49,7 @@ module Rosarium
|
|
42
49
|
waiting_for = promises.count
|
43
50
|
mutex = Mutex.new
|
44
51
|
|
45
|
-
check =
|
52
|
+
check = proc do
|
46
53
|
# Includes both fulfilled and rejected, so always hits zero eventually
|
47
54
|
if mutex.synchronize { (waiting_for -= 1) == 0 }
|
48
55
|
deferred.resolve promises
|
@@ -50,7 +57,7 @@ module Rosarium
|
|
50
57
|
end
|
51
58
|
|
52
59
|
promises.each do |promise|
|
53
|
-
promise.
|
60
|
+
promise.send(:when_settled, &check)
|
54
61
|
end
|
55
62
|
|
56
63
|
deferred.promise
|
@@ -65,38 +72,89 @@ module Rosarium
|
|
65
72
|
waiting_for = promises.count
|
66
73
|
mutex = Mutex.new
|
67
74
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
75
|
+
check = lambda do |promise|
|
76
|
+
if promise.fulfilled?
|
77
|
+
# Hits zero iff all promises were fulfilled
|
78
|
+
if mutex.synchronize { (waiting_for -= 1) == 0 }
|
79
|
+
deferred.resolve(promises.map(&:value))
|
80
|
+
end
|
81
|
+
else
|
82
|
+
deferred.reject(promise.reason)
|
73
83
|
end
|
74
84
|
end
|
75
85
|
|
76
86
|
promises.each do |promise|
|
77
|
-
promise.
|
87
|
+
promise.send(:when_settled) { check.call(promise) }
|
78
88
|
end
|
79
89
|
|
80
90
|
deferred.promise
|
81
91
|
end
|
82
92
|
|
93
|
+
def initialize
|
94
|
+
@state = :pending
|
95
|
+
@settling = false
|
96
|
+
@mutex = Mutex.new
|
97
|
+
@condition = ConditionVariable.new
|
98
|
+
@when_settled = []
|
99
|
+
end
|
100
|
+
|
101
|
+
def state
|
102
|
+
synchronize { @state }
|
103
|
+
end
|
104
|
+
|
105
|
+
def value
|
106
|
+
wait_until_settled
|
107
|
+
synchronize { @value }
|
108
|
+
end
|
109
|
+
|
110
|
+
def reason
|
111
|
+
wait_until_settled
|
112
|
+
synchronize { @reason }
|
113
|
+
end
|
114
|
+
|
115
|
+
def value!
|
116
|
+
wait_until_settled
|
117
|
+
synchronize do
|
118
|
+
raise @reason if @state == :rejected
|
119
|
+
@value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def inspect
|
124
|
+
synchronize do
|
125
|
+
r = { state: @state }
|
126
|
+
r[:value] = @value if @state == :fulfilled
|
127
|
+
r[:reason] = @reason if @state == :rejected
|
128
|
+
r
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def fulfilled?
|
133
|
+
state == :fulfilled
|
134
|
+
end
|
135
|
+
|
136
|
+
def rejected?
|
137
|
+
state == :rejected
|
138
|
+
end
|
139
|
+
|
83
140
|
def then(on_rejected = nil, &on_fulfilled)
|
84
141
|
deferred = self.class.defer
|
85
142
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
143
|
+
when_settled do
|
144
|
+
EXECUTOR.submit do
|
145
|
+
begin
|
146
|
+
deferred.resolve(
|
147
|
+
if fulfilled?
|
148
|
+
# User-supplied code
|
149
|
+
on_fulfilled ? on_fulfilled.call(value) : value
|
150
|
+
else
|
151
|
+
# User-supplied code
|
152
|
+
on_rejected ? on_rejected.call(reason) : raise(reason)
|
153
|
+
end
|
154
|
+
)
|
155
|
+
rescue Exception => e
|
156
|
+
deferred.reject e
|
157
|
+
end
|
100
158
|
end
|
101
159
|
end
|
102
160
|
|
@@ -107,9 +165,76 @@ module Rosarium
|
|
107
165
|
self.then(block)
|
108
166
|
end
|
109
167
|
|
110
|
-
|
111
|
-
|
168
|
+
alias catch rescue
|
169
|
+
alias on_error rescue
|
112
170
|
|
113
|
-
|
171
|
+
private
|
172
|
+
|
173
|
+
def wait_until_settled
|
174
|
+
synchronize do
|
175
|
+
loop do
|
176
|
+
return if @state != :pending
|
177
|
+
@condition.wait @mutex
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def synchronize(&block)
|
183
|
+
@mutex.synchronize(&block)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Can be called more than once
|
187
|
+
def try_settle(value, reason)
|
188
|
+
settle_with = nil
|
189
|
+
|
190
|
+
synchronize do
|
191
|
+
return if @state != :pending || @settling
|
192
|
+
|
193
|
+
if value.is_a? Promise
|
194
|
+
@settling = true
|
195
|
+
elsif reason.nil?
|
196
|
+
settle_with = [value, nil]
|
197
|
+
else
|
198
|
+
settle_with = [nil, reason]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
if settle_with
|
203
|
+
settle(*settle_with)
|
204
|
+
else
|
205
|
+
value.when_settled do
|
206
|
+
if value.fulfilled?
|
207
|
+
settle(value.value, nil)
|
208
|
+
else
|
209
|
+
settle(nil, value.reason)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Only called once
|
216
|
+
def settle(value, reason)
|
217
|
+
synchronize do
|
218
|
+
@state = (reason ? :rejected : :fulfilled)
|
219
|
+
@value = value
|
220
|
+
@reason = reason
|
221
|
+
@condition.broadcast
|
222
|
+
@when_settled.slice!(0, @when_settled.length)
|
223
|
+
end.each(&:call)
|
224
|
+
end
|
114
225
|
|
226
|
+
protected
|
227
|
+
|
228
|
+
def when_settled(&block)
|
229
|
+
immediate = synchronize do
|
230
|
+
@when_settled << block if @state == :pending
|
231
|
+
@state != :pending
|
232
|
+
end
|
233
|
+
|
234
|
+
block.call if immediate
|
235
|
+
end
|
236
|
+
|
237
|
+
@resolved = resolve(nil)
|
238
|
+
|
239
|
+
end
|
115
240
|
end
|
data/spec/deferred_spec.rb
CHANGED
@@ -63,7 +63,7 @@ describe "deferred promises" do
|
|
63
63
|
d1.resolve(d2.promise)
|
64
64
|
check_pending d1.promise
|
65
65
|
d2.resolve 7
|
66
|
-
d1.promise.
|
66
|
+
d1.promise.value
|
67
67
|
check_fulfilled d1.promise, 7
|
68
68
|
end
|
69
69
|
|
@@ -74,7 +74,7 @@ describe "deferred promises" do
|
|
74
74
|
check_pending d1.promise
|
75
75
|
e = an_error
|
76
76
|
d2.reject e
|
77
|
-
d1.promise.
|
77
|
+
d1.promise.value
|
78
78
|
check_rejected d1.promise, e
|
79
79
|
end
|
80
80
|
|
@@ -33,7 +33,7 @@ describe Rosarium::Promise do
|
|
33
33
|
chained = deferred.promise.then { then_called = true }
|
34
34
|
check_pending chained
|
35
35
|
deferred.reject e
|
36
|
-
chained.
|
36
|
+
chained.value
|
37
37
|
check_rejected chained, e
|
38
38
|
expect(then_called).to be_falsy
|
39
39
|
end
|
@@ -45,7 +45,7 @@ describe Rosarium::Promise do
|
|
45
45
|
got_args = nil
|
46
46
|
chained = deferred.promise.then(Proc.new {|*args| got_args = args; raise e2 }) { raise "should never be called" }
|
47
47
|
deferred.reject e
|
48
|
-
chained.
|
48
|
+
chained.value
|
49
49
|
check_rejected chained, e2
|
50
50
|
expect(got_args).to eq([e])
|
51
51
|
end
|
@@ -54,7 +54,7 @@ describe Rosarium::Promise do
|
|
54
54
|
deferred = Rosarium::Promise.defer
|
55
55
|
chained = deferred.promise.then(Proc.new {7}) { raise "should never be called" }
|
56
56
|
deferred.reject an_error
|
57
|
-
chained.
|
57
|
+
chained.value
|
58
58
|
check_fulfilled chained, 7
|
59
59
|
end
|
60
60
|
|
@@ -63,9 +63,16 @@ describe Rosarium::Promise do
|
|
63
63
|
deferred = Rosarium::Promise.defer
|
64
64
|
chained = deferred.promise.send(method) { 7 }
|
65
65
|
deferred.reject an_error
|
66
|
-
chained.
|
66
|
+
chained.value
|
67
67
|
check_fulfilled chained, 7
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
it "uses the executor even if immediately callable" do
|
72
|
+
deferred = Rosarium::Promise.resolve(nil).then { sleep 0.1; 7 }
|
73
|
+
check_pending deferred
|
74
|
+
sleep 0.2
|
75
|
+
check_fulfilled deferred, 7
|
76
|
+
end
|
77
|
+
|
71
78
|
end
|
data/spec/promise_static_spec.rb
CHANGED
@@ -14,16 +14,11 @@ describe "instantly-resolved promises" do
|
|
14
14
|
it "creates a fulfilled promise" do
|
15
15
|
t = Rosarium::Promise.resolve 7
|
16
16
|
check_fulfilled t, 7
|
17
|
-
expect(t).not_to respond_to(:fulfill)
|
18
|
-
expect(t).not_to respond_to(:reject)
|
19
17
|
end
|
20
18
|
|
21
19
|
it "creates a rejected promise" do
|
22
|
-
e = an_error
|
23
20
|
t = Rosarium::Promise.reject an_error
|
24
21
|
check_rejected t, an_error
|
25
|
-
expect(t).not_to respond_to(:fulfill)
|
26
|
-
expect(t).not_to respond_to(:reject)
|
27
22
|
end
|
28
23
|
|
29
24
|
it "creates an immediately-executable promise" do
|
@@ -61,7 +56,7 @@ describe "instantly-resolved promises" do
|
|
61
56
|
|
62
57
|
e = an_error
|
63
58
|
d2.reject e
|
64
|
-
promise.
|
59
|
+
promise.value
|
65
60
|
check_fulfilled promise, [ d1.promise, d2.promise ]
|
66
61
|
end
|
67
62
|
|
@@ -82,7 +77,7 @@ describe "instantly-resolved promises" do
|
|
82
77
|
|
83
78
|
e = an_error
|
84
79
|
d3.reject e
|
85
|
-
promise.
|
80
|
+
promise.value
|
86
81
|
check_rejected promise, e
|
87
82
|
end
|
88
83
|
|
@@ -96,7 +91,7 @@ describe "instantly-resolved promises" do
|
|
96
91
|
check_pending promise
|
97
92
|
|
98
93
|
d2.resolve 8
|
99
|
-
promise.
|
94
|
+
promise.value
|
100
95
|
check_fulfilled promise, [7,8]
|
101
96
|
end
|
102
97
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rosarium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rachel Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
27
41
|
description: |2
|
28
42
|
Rosarium implements something that's a bit like Promises,
|
29
43
|
inspired by the stability and ease of use of Q
|
@@ -41,7 +55,6 @@ files:
|
|
41
55
|
- lib/rosarium/deferred.rb
|
42
56
|
- lib/rosarium/fixed_thread_executor.rb
|
43
57
|
- lib/rosarium/promise.rb
|
44
|
-
- lib/rosarium/simple_promise.rb
|
45
58
|
- spec/deferred_spec.rb
|
46
59
|
- spec/fixed_thread_executor_spec.rb
|
47
60
|
- spec/promise_methods_spec.rb
|
@@ -59,15 +72,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
72
|
requirements:
|
60
73
|
- - ">="
|
61
74
|
- !ruby/object:Gem::Version
|
62
|
-
version: '
|
75
|
+
version: '2.4'
|
63
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
77
|
requirements:
|
65
78
|
- - ">="
|
66
79
|
- !ruby/object:Gem::Version
|
67
80
|
version: '0'
|
68
81
|
requirements: []
|
69
|
-
|
70
|
-
rubygems_version: 2.5.1
|
82
|
+
rubygems_version: 3.0.3
|
71
83
|
signing_key:
|
72
84
|
specification_version: 4
|
73
85
|
summary: Promises, or something like them
|
@@ -1,164 +0,0 @@
|
|
1
|
-
module Rosarium
|
2
|
-
|
3
|
-
class SimplePromise
|
4
|
-
|
5
|
-
def self.defer
|
6
|
-
promise = new
|
7
|
-
resolver = promise.method :resolve
|
8
|
-
rejecter = promise.method :reject
|
9
|
-
|
10
|
-
class <<promise
|
11
|
-
undef :resolve
|
12
|
-
undef :reject
|
13
|
-
end
|
14
|
-
|
15
|
-
Deferred.new(promise, resolver, rejecter)
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize
|
19
|
-
@state = :pending
|
20
|
-
@resolving = false
|
21
|
-
@mutex = Mutex.new
|
22
|
-
@condition = ConditionVariable.new
|
23
|
-
@on_resolution = []
|
24
|
-
end
|
25
|
-
|
26
|
-
def state
|
27
|
-
synchronize { @state }
|
28
|
-
end
|
29
|
-
|
30
|
-
def value
|
31
|
-
wait
|
32
|
-
synchronize { @value }
|
33
|
-
end
|
34
|
-
|
35
|
-
def reason
|
36
|
-
wait
|
37
|
-
synchronize { @reason }
|
38
|
-
end
|
39
|
-
|
40
|
-
def inspect
|
41
|
-
synchronize do
|
42
|
-
r = { state: @state }
|
43
|
-
r[:value] = @value if @state == :fulfilled
|
44
|
-
r[:reason] = @reason if @state == :rejected
|
45
|
-
r
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def fulfilled?
|
50
|
-
state == :fulfilled
|
51
|
-
end
|
52
|
-
|
53
|
-
def rejected?
|
54
|
-
state == :rejected
|
55
|
-
end
|
56
|
-
|
57
|
-
def value!
|
58
|
-
wait
|
59
|
-
synchronize do
|
60
|
-
if @state == :rejected
|
61
|
-
raise @reason
|
62
|
-
else
|
63
|
-
@value
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def wait
|
69
|
-
on_resolution do
|
70
|
-
@mutex.synchronize { @condition.broadcast }
|
71
|
-
end
|
72
|
-
|
73
|
-
@mutex.synchronize do
|
74
|
-
loop do
|
75
|
-
return if @state == :fulfilled or @state == :rejected
|
76
|
-
@condition.wait @mutex
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def synchronize
|
84
|
-
@mutex.synchronize { yield }
|
85
|
-
end
|
86
|
-
|
87
|
-
public
|
88
|
-
|
89
|
-
def resolve(value)
|
90
|
-
_resolve(value, nil)
|
91
|
-
end
|
92
|
-
|
93
|
-
def reject(reason)
|
94
|
-
raise "reason must be an Exception" unless reason.kind_of? Exception
|
95
|
-
_resolve(nil, reason)
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def _resolve(value, reason)
|
101
|
-
callbacks = []
|
102
|
-
add_on_resolution = false
|
103
|
-
|
104
|
-
synchronize do
|
105
|
-
if @state == :pending and not @resolving
|
106
|
-
if value.kind_of? SimplePromise
|
107
|
-
@resolving = true
|
108
|
-
add_on_resolution = true
|
109
|
-
elsif reason.nil?
|
110
|
-
@value = value
|
111
|
-
@state = :fulfilled
|
112
|
-
callbacks.concat @on_resolution
|
113
|
-
@on_resolution.clear
|
114
|
-
else
|
115
|
-
@reason = reason
|
116
|
-
@state = :rejected
|
117
|
-
callbacks.concat @on_resolution
|
118
|
-
@on_resolution.clear
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
if add_on_resolution
|
124
|
-
value.on_resolution { copy_resolution_from value }
|
125
|
-
end
|
126
|
-
|
127
|
-
callbacks.each {|c| EXECUTOR.submit { c.call } }
|
128
|
-
end
|
129
|
-
|
130
|
-
def copy_resolution_from(other)
|
131
|
-
callbacks = []
|
132
|
-
|
133
|
-
synchronize do
|
134
|
-
@value = other.value
|
135
|
-
@reason = other.reason
|
136
|
-
@state = other.state
|
137
|
-
@resolving = false
|
138
|
-
callbacks.concat @on_resolution
|
139
|
-
@on_resolution.clear
|
140
|
-
end
|
141
|
-
|
142
|
-
callbacks.each {|c| EXECUTOR.submit { c.call } }
|
143
|
-
end
|
144
|
-
|
145
|
-
protected
|
146
|
-
|
147
|
-
def on_resolution(&block)
|
148
|
-
immediate = synchronize do
|
149
|
-
if @state == :fulfilled or @state == :rejected
|
150
|
-
true
|
151
|
-
else
|
152
|
-
@on_resolution << block
|
153
|
-
false
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
block.call if immediate
|
158
|
-
|
159
|
-
nil
|
160
|
-
end
|
161
|
-
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|