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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b98cd2e53ffcbfb4c387c41a85c9699b8c050f5e
4
- data.tar.gz: 22cf6c2bd54e4490470520aca42aa9f15abb24b4
3
+ metadata.gz: 487aaf8c555487ea2f7b3bbdb77a5a63b0be1f38
4
+ data.tar.gz: 1a1ede2482f03dd9c55c0050b913bc718b077bea
5
5
  SHA512:
6
- metadata.gz: 990eedfbc0cfb55355f2ca0bc963e0cec24d3668b7a22e8e97fb643bb6abd71da10dec39c9e4846f243bc3bad51b5e6a544a270d04a600f7fbca2b3d060e7f96
7
- data.tar.gz: dffd4ca2bd12d3ee92acb2dc4c719a7aacbe20a4563da016b21ad63d2909c1f22a5c68947b9369f3792b1440edf5e2b37f4b13d8b4e8aa4d14c0d4795ee83e25
6
+ metadata.gz: 31b9d4895eca8ceab77fcce63730ad620b2d7f294b2bd53eb47c6da412557c714ce58bb99cc1a87995dac172e554e61ea4b0ae94ef6004d63746a3acc2ed8399
7
+ data.tar.gz: 6bf132f844d4f83b7369ffd79711aa9b772c2d285507908510993099a4c1a54a3aab66eb2de7d2d4271509c62bbea24c71229086deff567adf73f5867d3b4f54
@@ -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
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 10.8
2
+ threshold: 10.6
@@ -60,7 +60,7 @@ TooManyInstanceVariables:
60
60
  TooManyMethods:
61
61
  enabled: true
62
62
  exclude: []
63
- max_methods: 12
63
+ max_methods: 13
64
64
  TooManyStatements:
65
65
  enabled: true
66
66
  exclude:
@@ -11,10 +11,10 @@ class Promise
11
11
 
12
12
  include Promise::Progress
13
13
 
14
- attr_reader :state, :value, :reason, :backtrace
14
+ attr_reader :state, :value, :reason
15
15
 
16
16
  def self.resolve(obj)
17
- return obj if obj.instance_of?(self)
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 { Callback.new(self, on_fulfill, on_reject, next_promise) }
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, backtrace = nil)
65
- dispatch(backtrace) do
66
- @state = :fulfilled
67
- @value = value
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, backtrace = nil)
72
- dispatch(backtrace) do
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 dispatch(backtrace)
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
- @backtrace = backtrace || caller
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.dispatch }
89
- end
90
-
91
- def defer
92
- yield
108
+ defer { callback.call }
93
109
  end
94
110
  end
@@ -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 block
13
- @promise.fulfilled? ? @on_fulfill : @on_reject
14
- end
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
- assume_state(@promise, @next_promise)
22
+ call_block(@on_reject, @promise.reason)
25
23
  end
26
24
  end
27
25
 
28
- def execute
29
- block.call(param)
30
- rescue => ex
31
- @next_promise.reject(ex, @promise.backtrace)
32
- raise
33
- end
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
- @next_promise.fulfill(result, @promise.backtrace)
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
@@ -32,7 +32,7 @@ class Promise
32
32
  end
33
33
 
34
34
  def promise?(obj)
35
- obj.instance_of?(Promise)
35
+ obj.is_a?(Promise)
36
36
  end
37
37
 
38
38
  def count_promises
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class Promise
4
- VERSION = '0.7.0.rc1'
4
+ VERSION = '0.7.0.rc2'
5
5
  end
@@ -10,10 +10,10 @@ describe Promise do
10
10
 
11
11
  let(:backtrace) { caller }
12
12
  let(:reason) do
13
- StandardError.new('reason').tap { |ex| ex.set_backtrace(backtrace) }
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 { |ex| ex.set_backtrace(backtrace) }
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
- expect { subject.fulfill(value) }.to raise_error(error)
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
- expect { subject.reject(reason) }.to raise_error(error)
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 'sets the backtrace' do
380
- subject.fulfill
381
- expect(subject.backtrace.join)
382
- .to include(__FILE__ + ':' + (__LINE__ - 2).to_s)
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 be(Promise::Error)
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 'assumes the state of a given promise' do
466
+ it 'returns a given promise' do
429
467
  promise = Promise.new
430
468
  new_promise = Promise.resolve(promise)
431
- expect(new_promise.pending?).to eq(true)
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.fulfilled?).to eq(true)
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.rc1
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-09 00:00:00.000000000 Z
11
+ date: 2016-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec