rosarium 0.1.1 → 0.1.6
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 -17
- 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 +179 -35
- data/spec/deferred_spec.rb +2 -2
- data/spec/promise_methods_spec.rb +11 -4
- data/spec/promise_static_spec.rb +3 -8
- metadata +21 -9
- 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: 7953c9f3adbdebbd4299ddef80c064fe76f32fe74e8bd8abafd160d0f9b6d816
|
4
|
+
data.tar.gz: 80b937b9e1e8750f1bbac9a9913a0e129b5dc80048d97b3ea360774b02eb1e2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22821d81eaccb0357841982eb2095c57e58f786d535834600b5903719c180d91af364b9e8778ac1256236d19db95835262fd77b8c313bedafdf60ae0e04a7579
|
7
|
+
data.tar.gz: 34ac659ece6ea48a111db7dab0ea89f8c0adcf90cfcf0802aaecba89eec49b1e8831fb9d55147691c4b686e51c1e68e38164fcd6dd27b4464fd5363ca802bcc9
|
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:
|
@@ -117,7 +114,3 @@ Promise code (every time a ruby block appears in the above examples) is run
|
|
117
114
|
via a fixed-size thread pool, currently set to 10 threads. Execution order is
|
118
115
|
not defined.
|
119
116
|
|
120
|
-
In comparison to the Promises/A+ spec <https://promisesaplus.com/>, these
|
121
|
-
promises have an extra possible state, `:resolving`. You are encouraged to
|
122
|
-
use `#fulfilled?` and `#rejected?` instead anyway.
|
123
|
-
|
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
|
+
DEFAULT_ON_FULFILL = proc { |value| value }
|
7
|
+
DEFAULT_ON_REJECT = proc { |reason| raise reason }
|
4
8
|
|
5
|
-
|
6
|
-
DEFAULT_ON_REJECT = Proc.new {|reason| raise reason}
|
9
|
+
private_class_method :new
|
7
10
|
|
8
|
-
def self.
|
9
|
-
|
10
|
-
|
11
|
+
def self.defer
|
12
|
+
promise = new
|
13
|
+
|
14
|
+
resolver = ->(value) { promise.send(:try_settle, value, nil) }
|
15
|
+
|
16
|
+
rejecter = lambda do |reason|
|
17
|
+
raise "reason must be an Exception" unless reason.is_a?(Exception)
|
18
|
+
|
19
|
+
promise.send(:try_settle, nil, reason)
|
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.then(check
|
60
|
+
promise.then(check, &check)
|
54
61
|
end
|
55
62
|
|
56
63
|
deferred.promise
|
@@ -65,16 +72,16 @@ module Rosarium
|
|
65
72
|
waiting_for = promises.count
|
66
73
|
mutex = Mutex.new
|
67
74
|
|
68
|
-
do_reject =
|
69
|
-
do_fulfill =
|
75
|
+
do_reject = ->(reason) { deferred.reject(reason) }
|
76
|
+
do_fulfill = proc do
|
70
77
|
# Only fulfilled (not rejected), so hits zero iff all promises were fulfilled
|
71
78
|
if mutex.synchronize { (waiting_for -= 1) == 0 }
|
72
|
-
deferred.resolve(promises.map
|
79
|
+
deferred.resolve(promises.map(&:value))
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
76
83
|
promises.each do |promise|
|
77
|
-
promise.then(do_reject
|
84
|
+
promise.then(do_reject, &do_fulfill)
|
78
85
|
end
|
79
86
|
|
80
87
|
deferred.promise
|
@@ -86,17 +93,21 @@ module Rosarium
|
|
86
93
|
on_fulfilled ||= DEFAULT_ON_FULFILL
|
87
94
|
on_rejected ||= DEFAULT_ON_REJECT
|
88
95
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
96
|
+
when_settled do
|
97
|
+
EXECUTOR.submit do
|
98
|
+
begin
|
99
|
+
deferred.resolve(
|
100
|
+
if fulfilled?
|
101
|
+
# User-supplied code
|
102
|
+
on_fulfilled.call value
|
103
|
+
else
|
104
|
+
# User-supplied code
|
105
|
+
on_rejected.call reason
|
106
|
+
end
|
107
|
+
)
|
108
|
+
rescue Exception => e
|
109
|
+
deferred.reject e
|
110
|
+
end
|
100
111
|
end
|
101
112
|
end
|
102
113
|
|
@@ -107,9 +118,142 @@ module Rosarium
|
|
107
118
|
self.then(block)
|
108
119
|
end
|
109
120
|
|
110
|
-
|
111
|
-
|
121
|
+
alias catch rescue
|
122
|
+
alias on_error rescue
|
112
123
|
|
113
|
-
|
124
|
+
public
|
125
|
+
|
126
|
+
def initialize
|
127
|
+
@state = :pending
|
128
|
+
@resolving = false
|
129
|
+
@mutex = Mutex.new
|
130
|
+
@condition = ConditionVariable.new
|
131
|
+
@when_settled = []
|
132
|
+
end
|
133
|
+
|
134
|
+
def state
|
135
|
+
synchronize { @state }
|
136
|
+
end
|
137
|
+
|
138
|
+
def value
|
139
|
+
wait
|
140
|
+
synchronize { @value }
|
141
|
+
end
|
142
|
+
|
143
|
+
def reason
|
144
|
+
wait
|
145
|
+
synchronize { @reason }
|
146
|
+
end
|
147
|
+
|
148
|
+
def inspect
|
149
|
+
synchronize do
|
150
|
+
r = { state: @state }
|
151
|
+
r[:value] = @value if @state == :fulfilled
|
152
|
+
r[:reason] = @reason if @state == :rejected
|
153
|
+
r
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def fulfilled?
|
158
|
+
state == :fulfilled
|
159
|
+
end
|
160
|
+
|
161
|
+
def rejected?
|
162
|
+
state == :rejected
|
163
|
+
end
|
164
|
+
|
165
|
+
def value!
|
166
|
+
wait
|
167
|
+
synchronize do
|
168
|
+
raise @reason if @state == :rejected
|
169
|
+
@value
|
170
|
+
end
|
171
|
+
end
|
114
172
|
|
173
|
+
private
|
174
|
+
|
175
|
+
def wait
|
176
|
+
when_settled do
|
177
|
+
synchronize { @condition.broadcast }
|
178
|
+
end
|
179
|
+
|
180
|
+
synchronize do
|
181
|
+
loop do
|
182
|
+
return if @state == :fulfilled || @state == :rejected
|
183
|
+
@condition.wait @mutex
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def synchronize(&block)
|
189
|
+
@mutex.synchronize(&block)
|
190
|
+
end
|
191
|
+
|
192
|
+
def try_settle(value, reason)
|
193
|
+
callbacks = []
|
194
|
+
add_when_settled = false
|
195
|
+
|
196
|
+
synchronize do
|
197
|
+
if @state == :pending && !@resolving
|
198
|
+
if value.is_a? Promise
|
199
|
+
@resolving = true
|
200
|
+
add_when_settled = true
|
201
|
+
elsif reason.nil?
|
202
|
+
@value = value
|
203
|
+
@state = :fulfilled
|
204
|
+
callbacks.concat @when_settled
|
205
|
+
@when_settled.clear
|
206
|
+
else
|
207
|
+
@reason = reason
|
208
|
+
@state = :rejected
|
209
|
+
callbacks.concat @when_settled
|
210
|
+
@when_settled.clear
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# rubocop:disable Style/IfUnlessModifier
|
216
|
+
if add_when_settled
|
217
|
+
value.when_settled { copy_settlement_from value }
|
218
|
+
end
|
219
|
+
# rubocop:enable Style/IfUnlessModifier
|
220
|
+
|
221
|
+
callbacks.each { |c| EXECUTOR.submit(&c) }
|
222
|
+
end
|
223
|
+
|
224
|
+
def copy_settlement_from(other)
|
225
|
+
callbacks = []
|
226
|
+
|
227
|
+
synchronize do
|
228
|
+
@value = other.value
|
229
|
+
@reason = other.reason
|
230
|
+
@state = other.state
|
231
|
+
@resolving = false
|
232
|
+
callbacks.concat @when_settled
|
233
|
+
@when_settled.clear
|
234
|
+
end
|
235
|
+
|
236
|
+
callbacks.each { |c| EXECUTOR.submit(&c) }
|
237
|
+
end
|
238
|
+
|
239
|
+
protected
|
240
|
+
|
241
|
+
def when_settled(&block)
|
242
|
+
immediate = synchronize do
|
243
|
+
if @state == :fulfilled || @state == :rejected
|
244
|
+
true
|
245
|
+
else
|
246
|
+
@when_settled << block
|
247
|
+
false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
block.call if immediate
|
252
|
+
|
253
|
+
nil
|
254
|
+
end
|
255
|
+
|
256
|
+
@resolved = resolve(nil)
|
257
|
+
|
258
|
+
end
|
115
259
|
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.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rachel Evans
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-13 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
|
@@ -51,7 +64,7 @@ homepage: http://rve.org.uk/gems/rosarium
|
|
51
64
|
licenses:
|
52
65
|
- Apache-2.0
|
53
66
|
metadata: {}
|
54
|
-
post_install_message:
|
67
|
+
post_install_message:
|
55
68
|
rdoc_options: []
|
56
69
|
require_paths:
|
57
70
|
- lib
|
@@ -59,16 +72,15 @@ 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
|
-
|
71
|
-
signing_key:
|
82
|
+
rubygems_version: 3.0.8
|
83
|
+
signing_key:
|
72
84
|
specification_version: 4
|
73
85
|
summary: Promises, or something like them
|
74
86
|
test_files: []
|
@@ -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
|