promise.rb 0.7.0.rc1 → 0.7.0.rc2
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 +17 -0
- data/config/flog.yml +1 -1
- data/config/reek.yml +1 -1
- data/lib/promise.rb +42 -26
- data/lib/promise/callback.rb +19 -30
- data/lib/promise/group.rb +1 -1
- data/lib/promise/version.rb +1 -1
- data/spec/unit/promise_spec.rb +84 -23
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 487aaf8c555487ea2f7b3bbdb77a5a63b0be1f38
|
4
|
+
data.tar.gz: 1a1ede2482f03dd9c55c0050b913bc718b077bea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31b9d4895eca8ceab77fcce63730ad620b2d7f294b2bd53eb47c6da412557c714ce58bb99cc1a87995dac172e554e61ea4b0ae94ef6004d63746a3acc2ed8399
|
7
|
+
data.tar.gz: 6bf132f844d4f83b7369ffd79711aa9b772c2d285507908510993099a4c1a54a3aab66eb2de7d2d4271509c62bbea24c71229086deff567adf73f5867d3b4f54
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# promise.rb changelog
|
2
2
|
|
3
|
+
## 0.7.0.rc2 (February 17, 2016)
|
4
|
+
|
5
|
+
### Bug Fixes
|
6
|
+
|
7
|
+
* Avoid re-raising exception that occur in then callbacks (pull #13)
|
8
|
+
* Wait for an instance of a subclass of Promise in Promise.all (pull #15)
|
9
|
+
|
10
|
+
### Features
|
11
|
+
|
12
|
+
* Instantiate exception classes and set missing backtrace in reject (pull #12)
|
13
|
+
* Allow Promise#fulfill to be called with a promise (pull #14)
|
14
|
+
|
15
|
+
### Breaking Changes
|
16
|
+
|
17
|
+
* Make add_callback, dispatch, dispatch! and Promise::Callback private (pull #11)
|
18
|
+
* Remove Promise#backtrace, use #reason.backtrace instead (pull #12)
|
19
|
+
|
3
20
|
## 0.7.0.rc1 (February 9, 2016)
|
4
21
|
|
5
22
|
### Bug Fixes
|
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold: 10.
|
2
|
+
threshold: 10.6
|
data/config/reek.yml
CHANGED
data/lib/promise.rb
CHANGED
@@ -11,10 +11,10 @@ class Promise
|
|
11
11
|
|
12
12
|
include Promise::Progress
|
13
13
|
|
14
|
-
attr_reader :state, :value, :reason
|
14
|
+
attr_reader :state, :value, :reason
|
15
15
|
|
16
16
|
def self.resolve(obj)
|
17
|
-
return obj if obj.
|
17
|
+
return obj if obj.is_a?(self)
|
18
18
|
new.tap { |promise| promise.fulfill(obj) }
|
19
19
|
end
|
20
20
|
|
@@ -43,52 +43,68 @@ class Promise
|
|
43
43
|
on_fulfill ||= block
|
44
44
|
next_promise = self.class.new
|
45
45
|
|
46
|
-
add_callback
|
46
|
+
add_callback(Callback.new(self, on_fulfill, on_reject, next_promise))
|
47
47
|
next_promise
|
48
48
|
end
|
49
49
|
|
50
|
-
def add_callback(&generator)
|
51
|
-
if pending?
|
52
|
-
@callbacks << generator
|
53
|
-
else
|
54
|
-
dispatch!(generator.call)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
50
|
def sync
|
59
51
|
wait if pending?
|
60
52
|
raise reason if rejected?
|
61
53
|
value
|
62
54
|
end
|
63
55
|
|
64
|
-
def fulfill(value = nil
|
65
|
-
|
66
|
-
|
67
|
-
|
56
|
+
def fulfill(value = nil)
|
57
|
+
if Promise === value
|
58
|
+
Callback.assume_state(value, self)
|
59
|
+
else
|
60
|
+
dispatch do
|
61
|
+
@state = :fulfilled
|
62
|
+
@value = value
|
63
|
+
end
|
68
64
|
end
|
65
|
+
nil
|
69
66
|
end
|
70
67
|
|
71
|
-
def reject(reason = nil
|
72
|
-
dispatch
|
68
|
+
def reject(reason = nil)
|
69
|
+
dispatch do
|
73
70
|
@state = :rejected
|
74
|
-
@reason = reason || Error
|
71
|
+
@reason = reason_coercion(reason || Error)
|
75
72
|
end
|
76
73
|
end
|
77
74
|
|
78
|
-
def
|
75
|
+
def defer
|
76
|
+
yield
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def reason_coercion(reason)
|
82
|
+
case reason
|
83
|
+
when Exception
|
84
|
+
reason.set_backtrace(caller) unless reason.backtrace
|
85
|
+
when Class
|
86
|
+
reason = reason_coercion(reason.new) if reason <= Exception
|
87
|
+
end
|
88
|
+
reason
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_callback(callback)
|
92
|
+
if pending?
|
93
|
+
@callbacks << callback
|
94
|
+
else
|
95
|
+
dispatch!(callback)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def dispatch
|
79
100
|
if pending?
|
80
101
|
yield
|
81
|
-
@
|
82
|
-
@callbacks.each { |generator| dispatch!(generator.call) }
|
102
|
+
@callbacks.each { |callback| dispatch!(callback) }
|
83
103
|
nil
|
84
104
|
end
|
85
105
|
end
|
86
106
|
|
87
107
|
def dispatch!(callback)
|
88
|
-
defer { callback.
|
89
|
-
end
|
90
|
-
|
91
|
-
def defer
|
92
|
-
yield
|
108
|
+
defer { callback.call }
|
93
109
|
end
|
94
110
|
end
|
data/lib/promise/callback.rb
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
class Promise
|
4
4
|
class Callback
|
5
|
+
def self.assume_state(source, target)
|
6
|
+
on_fulfill = target.method(:fulfill)
|
7
|
+
on_reject = target.method(:reject)
|
8
|
+
source.then(on_fulfill, on_reject)
|
9
|
+
end
|
10
|
+
|
5
11
|
def initialize(promise, on_fulfill, on_reject, next_promise)
|
6
12
|
@promise = promise
|
7
13
|
@on_fulfill = on_fulfill
|
@@ -9,42 +15,25 @@ class Promise
|
|
9
15
|
@next_promise = next_promise
|
10
16
|
end
|
11
17
|
|
12
|
-
def
|
13
|
-
@promise.fulfilled?
|
14
|
-
|
15
|
-
|
16
|
-
def param
|
17
|
-
@promise.fulfilled? ? @promise.value : @promise.reason
|
18
|
-
end
|
19
|
-
|
20
|
-
def dispatch
|
21
|
-
if block
|
22
|
-
handle_result { execute }
|
18
|
+
def call
|
19
|
+
if @promise.fulfilled?
|
20
|
+
call_block(@on_fulfill, @promise.value)
|
23
21
|
else
|
24
|
-
|
22
|
+
call_block(@on_reject, @promise.reason)
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
|
-
def
|
29
|
-
block
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
def handle_result
|
36
|
-
if Promise === (result = yield)
|
37
|
-
assume_state(result, @next_promise)
|
26
|
+
def call_block(block, param)
|
27
|
+
if block
|
28
|
+
begin
|
29
|
+
@next_promise.fulfill(block.call(param))
|
30
|
+
rescue => ex
|
31
|
+
@next_promise.reject(ex)
|
32
|
+
end
|
38
33
|
else
|
39
|
-
|
34
|
+
self.class.assume_state(@promise, @next_promise)
|
40
35
|
end
|
41
36
|
end
|
42
|
-
|
43
|
-
def assume_state(source, target)
|
44
|
-
on_fulfill = proc { target.fulfill(source.value, source.backtrace) }
|
45
|
-
on_reject = proc { target.reject(source.reason, source.backtrace) }
|
46
|
-
|
47
|
-
source.then(on_fulfill, on_reject)
|
48
|
-
end
|
49
37
|
end
|
38
|
+
private_constant :Callback
|
50
39
|
end
|
data/lib/promise/group.rb
CHANGED
data/lib/promise/version.rb
CHANGED
data/spec/unit/promise_spec.rb
CHANGED
@@ -10,10 +10,10 @@ describe Promise do
|
|
10
10
|
|
11
11
|
let(:backtrace) { caller }
|
12
12
|
let(:reason) do
|
13
|
-
StandardError.new('reason').tap { |
|
13
|
+
StandardError.new('reason').tap { |err| err.set_backtrace(backtrace) }
|
14
14
|
end
|
15
15
|
let(:other_reason) do
|
16
|
-
StandardError.new('other_reason').tap { |
|
16
|
+
StandardError.new('other_reason').tap { |err| err.set_backtrace(caller) }
|
17
17
|
end
|
18
18
|
|
19
19
|
describe '3.1.1 pending' do
|
@@ -200,6 +200,25 @@ describe Promise do
|
|
200
200
|
expect(order).to eq([1, 2, 3])
|
201
201
|
end
|
202
202
|
|
203
|
+
it 'calls all on_fulfill callbacks even if one raises an exception' do
|
204
|
+
order = []
|
205
|
+
on_fulfill = proc do |i, val|
|
206
|
+
order << i
|
207
|
+
expect(val).to eq(value)
|
208
|
+
end
|
209
|
+
|
210
|
+
subject.then(on_fulfill.curry[1])
|
211
|
+
subject.then do |_|
|
212
|
+
order << 2
|
213
|
+
raise 'middle then error'
|
214
|
+
end
|
215
|
+
subject.then(on_fulfill.curry[3])
|
216
|
+
|
217
|
+
subject.fulfill(value)
|
218
|
+
|
219
|
+
expect(order).to eq([1, 2, 3])
|
220
|
+
end
|
221
|
+
|
203
222
|
it 'calls multiple on_reject callbacks in order of definition' do
|
204
223
|
order = []
|
205
224
|
on_reject = proc do |i, reas|
|
@@ -232,7 +251,6 @@ describe Promise do
|
|
232
251
|
|
233
252
|
expect(promise2).to be_fulfilled
|
234
253
|
expect(promise2.value).to eq(other_value)
|
235
|
-
expect(promise2.backtrace).to be(subject.backtrace)
|
236
254
|
end
|
237
255
|
|
238
256
|
it 'fulfills promise2 with value returned by on_reject' do
|
@@ -241,25 +259,22 @@ describe Promise do
|
|
241
259
|
|
242
260
|
expect(promise2).to be_fulfilled
|
243
261
|
expect(promise2.value).to eq(other_value)
|
244
|
-
expect(promise2.backtrace).to be(subject.backtrace)
|
245
262
|
end
|
246
263
|
|
247
264
|
it 'rejects promise2 with error raised by on_fulfill' do
|
248
265
|
promise2 = subject.then(proc { |_| raise error })
|
249
|
-
|
266
|
+
subject.fulfill(value)
|
250
267
|
|
251
268
|
expect(promise2).to be_rejected
|
252
269
|
expect(promise2.reason).to eq(error)
|
253
|
-
expect(promise2.backtrace).to be(subject.backtrace)
|
254
270
|
end
|
255
271
|
|
256
272
|
it 'rejects promise2 with error raised by on_reject' do
|
257
273
|
promise2 = subject.then(nil, proc { |_| raise error })
|
258
|
-
|
274
|
+
subject.reject(reason)
|
259
275
|
|
260
276
|
expect(promise2).to be_rejected
|
261
277
|
expect(promise2.reason).to eq(error)
|
262
|
-
expect(promise2.backtrace).to be(subject.backtrace)
|
263
278
|
end
|
264
279
|
|
265
280
|
describe 'on_fulfill returns promise' do
|
@@ -272,7 +287,6 @@ describe Promise do
|
|
272
287
|
returned_promise.fulfill(other_value)
|
273
288
|
expect(promise2).to be_fulfilled
|
274
289
|
expect(promise2.value).to eq(other_value)
|
275
|
-
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
276
290
|
end
|
277
291
|
|
278
292
|
it 'makes promise2 assume rejected state of returned promise' do
|
@@ -284,7 +298,6 @@ describe Promise do
|
|
284
298
|
returned_promise.reject(other_reason)
|
285
299
|
expect(promise2).to be_rejected
|
286
300
|
expect(promise2.reason).to eq(other_reason)
|
287
|
-
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
288
301
|
end
|
289
302
|
end
|
290
303
|
|
@@ -298,7 +311,6 @@ describe Promise do
|
|
298
311
|
returned_promise.fulfill(other_value)
|
299
312
|
expect(promise2).to be_fulfilled
|
300
313
|
expect(promise2.value).to eq(other_value)
|
301
|
-
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
302
314
|
end
|
303
315
|
|
304
316
|
it 'makes promise2 assume rejected state of returned promise' do
|
@@ -310,7 +322,6 @@ describe Promise do
|
|
310
322
|
returned_promise.reject(other_reason)
|
311
323
|
expect(promise2).to be_rejected
|
312
324
|
expect(promise2.reason).to eq(other_reason)
|
313
|
-
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
314
325
|
end
|
315
326
|
end
|
316
327
|
|
@@ -321,7 +332,6 @@ describe Promise do
|
|
321
332
|
|
322
333
|
expect(promise2).to be_fulfilled
|
323
334
|
expect(promise2.value).to eq(value)
|
324
|
-
expect(promise2.backtrace).to be(subject.backtrace)
|
325
335
|
end
|
326
336
|
end
|
327
337
|
|
@@ -332,7 +342,6 @@ describe Promise do
|
|
332
342
|
|
333
343
|
expect(promise2).to be_rejected
|
334
344
|
expect(promise2.reason).to eq(reason)
|
335
|
-
expect(promise2.backtrace).to be(subject.backtrace)
|
336
345
|
end
|
337
346
|
end
|
338
347
|
end
|
@@ -371,15 +380,24 @@ describe Promise do
|
|
371
380
|
expect(subject.fulfill(nil)).to eq(nil)
|
372
381
|
end
|
373
382
|
|
383
|
+
it 'does not return anything when given a promise' do
|
384
|
+
expect(subject.fulfill(Promise.new)).to eq(nil)
|
385
|
+
end
|
386
|
+
|
374
387
|
it 'does not require a value' do
|
375
388
|
subject.fulfill
|
376
389
|
expect(subject.value).to be(nil)
|
377
390
|
end
|
378
391
|
|
379
|
-
it '
|
380
|
-
|
381
|
-
|
382
|
-
|
392
|
+
it 'assumes the state of a given promise' do
|
393
|
+
promise = Promise.new
|
394
|
+
|
395
|
+
subject.fulfill(promise)
|
396
|
+
expect(subject).to be_pending
|
397
|
+
promise.fulfill(123)
|
398
|
+
|
399
|
+
expect(subject).to be_fulfilled
|
400
|
+
expect(subject.value).to eq(123)
|
383
401
|
end
|
384
402
|
end
|
385
403
|
|
@@ -390,14 +408,34 @@ describe Promise do
|
|
390
408
|
|
391
409
|
it 'does not require a reason' do
|
392
410
|
subject.reject
|
393
|
-
expect(subject.reason).to
|
411
|
+
expect(subject.reason).to be_a(Promise::Error)
|
394
412
|
end
|
395
413
|
|
396
414
|
it 'sets the backtrace' do
|
397
415
|
subject.reject
|
398
|
-
expect(subject.backtrace.join)
|
416
|
+
expect(subject.reason.backtrace.join)
|
399
417
|
.to include(__FILE__ + ':' + (__LINE__ - 2).to_s)
|
400
418
|
end
|
419
|
+
|
420
|
+
it 'leaves backtrace if already set' do
|
421
|
+
subject.reject(reason)
|
422
|
+
expect(subject.reason.backtrace).to eq(backtrace)
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'instantiates exception class' do
|
426
|
+
subject.reject(Exception)
|
427
|
+
expect(subject.reason).to be_a(Exception)
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'instantiates exception subclasses' do
|
431
|
+
subject.reject(RuntimeError)
|
432
|
+
expect(subject.reason).to be_a(RuntimeError)
|
433
|
+
end
|
434
|
+
|
435
|
+
it "doesn't instantiate non-error classes" do
|
436
|
+
subject.reject(Hash)
|
437
|
+
expect(subject.reason).to eq(Hash)
|
438
|
+
end
|
401
439
|
end
|
402
440
|
|
403
441
|
describe '#sync' do
|
@@ -425,12 +463,25 @@ describe Promise do
|
|
425
463
|
expect(promise.value).to eq(123)
|
426
464
|
end
|
427
465
|
|
428
|
-
it '
|
466
|
+
it 'returns a given promise' do
|
429
467
|
promise = Promise.new
|
430
468
|
new_promise = Promise.resolve(promise)
|
431
|
-
expect(new_promise.
|
469
|
+
expect(new_promise.object_id).to eq(promise.object_id)
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'returns a given promise of a subclass of itself' do
|
473
|
+
promise = DelayedPromise.new
|
474
|
+
new_promise = Promise.resolve(promise)
|
475
|
+
expect(new_promise.object_id).to eq(promise.object_id)
|
476
|
+
end
|
477
|
+
|
478
|
+
it 'assumes the state of a given promise of another class' do
|
479
|
+
promise = Promise.new
|
480
|
+
new_promise = DelayedPromise.resolve(promise)
|
481
|
+
expect(new_promise).to be_an_instance_of(DelayedPromise)
|
482
|
+
expect(new_promise).to be_pending
|
432
483
|
promise.fulfill(42)
|
433
|
-
expect(new_promise
|
484
|
+
expect(new_promise).to be_fulfilled
|
434
485
|
expect(new_promise.value).to eq(42)
|
435
486
|
end
|
436
487
|
end
|
@@ -492,6 +543,16 @@ describe Promise do
|
|
492
543
|
p1.fulfill(1.0)
|
493
544
|
expect(result.sync).to eq([1.0, 2])
|
494
545
|
end
|
546
|
+
|
547
|
+
it 'returns an instance of the class it is called on' do
|
548
|
+
p1 = DelayedPromise.new
|
549
|
+
|
550
|
+
result = DelayedPromise.all([p1, 2])
|
551
|
+
|
552
|
+
expect(result).to be_pending
|
553
|
+
p1.fulfill(1.0)
|
554
|
+
expect(result.sync).to eq([1.0, 2])
|
555
|
+
end
|
495
556
|
end
|
496
557
|
end
|
497
558
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: promise.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Gierth
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|