promise.rb 0.5.0 → 0.6.0
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.
- data/CHANGELOG.md +9 -0
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/reek.yml +4 -3
- data/config/rubocop.yml +4 -0
- data/lib/promise.rb +23 -47
- data/lib/promise/callback.rb +25 -33
- data/lib/promise/version.rb +1 -1
- data/spec/promise_spec.rb +20 -20
- metadata +2 -2
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# promise.rb changelog
|
2
2
|
|
3
|
+
## 0.6.0 (December 21, 2013)
|
4
|
+
|
5
|
+
* Most of Promise and Callback have been rewritten. Less code.
|
6
|
+
* The rejection reason isn't overloaded with the promise's backtrace anymore as
|
7
|
+
introduced in 0.5.0. Instead, Promise#backtrace will be populated with the
|
8
|
+
originating call to #fulfill or #reject.
|
9
|
+
* The backtrace no longer guarantees that the actual caller is its first
|
10
|
+
element (thank you JRuby).
|
11
|
+
|
3
12
|
## 0.5.0 (December 16, 2013)
|
4
13
|
|
5
14
|
* Fulfillment value and rejection reason are no longer being frozen
|
data/config/flay.yml
CHANGED
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 10.8
|
data/config/reek.yml
CHANGED
@@ -20,7 +20,8 @@ DuplicateMethodCall:
|
|
20
20
|
enabled: true
|
21
21
|
exclude: []
|
22
22
|
max_calls: 1
|
23
|
-
allow_calls:
|
23
|
+
allow_calls:
|
24
|
+
- source.backtrace
|
24
25
|
FeatureEnvy:
|
25
26
|
enabled: false
|
26
27
|
exclude: []
|
@@ -49,7 +50,7 @@ NilCheck:
|
|
49
50
|
RepeatedConditional:
|
50
51
|
enabled: true
|
51
52
|
exclude: []
|
52
|
-
max_ifs:
|
53
|
+
max_ifs: 3
|
53
54
|
TooManyInstanceVariables:
|
54
55
|
enabled: true
|
55
56
|
exclude: []
|
@@ -57,7 +58,7 @@ TooManyInstanceVariables:
|
|
57
58
|
TooManyMethods:
|
58
59
|
enabled: true
|
59
60
|
exclude: []
|
60
|
-
max_methods:
|
61
|
+
max_methods: 12
|
61
62
|
TooManyStatements:
|
62
63
|
enabled: true
|
63
64
|
exclude:
|
data/config/rubocop.yml
CHANGED
data/lib/promise.rb
CHANGED
@@ -8,12 +8,11 @@ require 'promise/progress'
|
|
8
8
|
class Promise
|
9
9
|
include Promise::Progress
|
10
10
|
|
11
|
-
attr_reader :value, :reason
|
11
|
+
attr_reader :state, :value, :reason, :backtrace
|
12
12
|
|
13
13
|
def initialize
|
14
14
|
@state = :pending
|
15
|
-
@
|
16
|
-
@on_reject = []
|
15
|
+
@callbacks = []
|
17
16
|
end
|
18
17
|
|
19
18
|
def pending?
|
@@ -30,77 +29,54 @@ class Promise
|
|
30
29
|
|
31
30
|
def then(on_fulfill = nil, on_reject = nil, &block)
|
32
31
|
on_fulfill ||= block
|
33
|
-
next_promise =
|
32
|
+
next_promise = Promise.new
|
34
33
|
|
35
|
-
|
34
|
+
add_callback { Callback.new(on_fulfill, on_reject, next_promise) }
|
36
35
|
next_promise
|
37
36
|
end
|
38
37
|
|
38
|
+
def add_callback(&generator)
|
39
|
+
if pending?
|
40
|
+
@callbacks << generator
|
41
|
+
else
|
42
|
+
dispatch!(generator.call)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
39
46
|
def sync
|
40
47
|
wait if pending?
|
41
|
-
|
48
|
+
raise reason if rejected?
|
42
49
|
value
|
43
50
|
end
|
44
51
|
|
45
|
-
def fulfill(value = nil)
|
46
|
-
dispatch(
|
52
|
+
def fulfill(value = nil, backtrace = nil)
|
53
|
+
dispatch(backtrace) do
|
47
54
|
@state = :fulfilled
|
48
55
|
@value = value
|
49
56
|
end
|
50
57
|
end
|
51
58
|
|
52
|
-
def reject(reason = nil)
|
53
|
-
|
54
|
-
dispatch(@on_reject, reason) do
|
59
|
+
def reject(reason = RuntimeError, backtrace = nil)
|
60
|
+
dispatch(backtrace) do
|
55
61
|
@state = :rejected
|
56
62
|
@reason = reason
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
60
|
-
|
61
|
-
|
62
|
-
def add_callbacks(on_fulfill, on_reject)
|
63
|
-
next_promise = self.class.new
|
64
|
-
@on_fulfill << FulfillCallback.new(on_fulfill, next_promise)
|
65
|
-
@on_reject << RejectCallback.new(on_reject, next_promise)
|
66
|
-
next_promise
|
67
|
-
end
|
68
|
-
|
69
|
-
def dispatch(callbacks, arg)
|
66
|
+
def dispatch(backtrace)
|
70
67
|
if pending?
|
71
68
|
yield
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# Callback#assume_state uses #dispatch as returned_promise's on_fulfill
|
76
|
-
# and on_reject callback. Without an explicit return value, the implicit
|
77
|
-
# return value might be the callbacks array, and thus returned_promise
|
78
|
-
# would be fulfilled (or rejected) with that array. It would be frozen
|
79
|
-
# from then on, letting further calls to #then crash.
|
80
|
-
nil
|
81
|
-
end
|
82
|
-
|
83
|
-
def maybe_dispatch(fulfill_callback, reject_callback)
|
84
|
-
if fulfilled?
|
85
|
-
dispatch!(fulfill_callback, value)
|
86
|
-
end
|
87
|
-
|
88
|
-
if rejected?
|
89
|
-
dispatch!(reject_callback, reason)
|
69
|
+
@backtrace = backtrace || caller
|
70
|
+
@callbacks.each { |generator| dispatch!(generator.call) }
|
71
|
+
nil
|
90
72
|
end
|
91
73
|
end
|
92
74
|
|
93
|
-
def dispatch!(callback
|
94
|
-
defer { callback.dispatch(
|
75
|
+
def dispatch!(callback)
|
76
|
+
defer { callback.dispatch(self) }
|
95
77
|
end
|
96
78
|
|
97
79
|
def defer
|
98
80
|
yield
|
99
81
|
end
|
100
|
-
|
101
|
-
def build_reason(reason, backtrace)
|
102
|
-
reason = reason.new if reason.respond_to?(:new)
|
103
|
-
reason.set_backtrace(backtrace) unless reason.backtrace
|
104
|
-
reason
|
105
|
-
end
|
106
82
|
end
|
data/lib/promise/callback.rb
CHANGED
@@ -2,55 +2,47 @@
|
|
2
2
|
|
3
3
|
class Promise
|
4
4
|
class Callback
|
5
|
-
def initialize(
|
6
|
-
@
|
5
|
+
def initialize(on_fulfill, on_reject, next_promise)
|
6
|
+
@on_fulfill, @on_reject = on_fulfill, on_reject
|
7
7
|
@next_promise = next_promise
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
def block_for(promise)
|
11
|
+
promise.fulfilled? ? @on_fulfill : @on_reject
|
12
|
+
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
-
rescue => error
|
15
|
-
@next_promise.reject(error)
|
16
|
-
raise
|
14
|
+
def param_for(promise)
|
15
|
+
promise.fulfilled? ? promise.value : promise.reason
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
if
|
21
|
-
|
18
|
+
def dispatch(promise)
|
19
|
+
if (block = block_for(promise))
|
20
|
+
handle_result(promise) { execute(promise, block) }
|
22
21
|
else
|
23
|
-
@next_promise
|
22
|
+
assume_state(promise, @next_promise)
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
def execute(promise, block)
|
27
|
+
block.call(param_for(promise))
|
28
|
+
rescue => ex
|
29
|
+
@next_promise.reject(ex, promise.backtrace)
|
30
|
+
raise
|
32
31
|
end
|
33
|
-
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
result = execute(value)
|
39
|
-
handle_result(result)
|
33
|
+
def handle_result(promise)
|
34
|
+
if Promise === (result = yield)
|
35
|
+
assume_state(result, @next_promise)
|
40
36
|
else
|
41
|
-
|
37
|
+
@next_promise.fulfill(result, promise.backtrace)
|
42
38
|
end
|
43
39
|
end
|
44
|
-
end
|
45
40
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
else
|
52
|
-
@next_promise.reject(reason)
|
53
|
-
end
|
41
|
+
def assume_state(source, target)
|
42
|
+
on_fulfill = proc { target.fulfill(source.value, source.backtrace) }
|
43
|
+
on_reject = proc { target.reject(source.reason, source.backtrace) }
|
44
|
+
|
45
|
+
source.then(on_fulfill, on_reject)
|
54
46
|
end
|
55
47
|
end
|
56
48
|
end
|
data/lib/promise/version.rb
CHANGED
data/spec/promise_spec.rb
CHANGED
@@ -223,6 +223,7 @@ describe Promise do
|
|
223
223
|
|
224
224
|
expect(promise2).to be_fulfilled
|
225
225
|
expect(promise2.value).to eq(other_value)
|
226
|
+
expect(promise2.backtrace).to be(subject.backtrace)
|
226
227
|
end
|
227
228
|
|
228
229
|
it 'fulfills promise2 with value returned by on_reject' do
|
@@ -231,22 +232,25 @@ describe Promise do
|
|
231
232
|
|
232
233
|
expect(promise2).to be_fulfilled
|
233
234
|
expect(promise2.value).to eq(other_value)
|
235
|
+
expect(promise2.backtrace).to be(subject.backtrace)
|
234
236
|
end
|
235
237
|
|
236
238
|
it 'rejects promise2 with error raised by on_fulfill' do
|
237
|
-
promise2 = subject.then(proc { |_|
|
239
|
+
promise2 = subject.then(proc { |_| raise error })
|
238
240
|
expect { subject.fulfill(value) }.to raise_error(error)
|
239
241
|
|
240
242
|
expect(promise2).to be_rejected
|
241
243
|
expect(promise2.reason).to eq(error)
|
244
|
+
expect(promise2.backtrace).to be(subject.backtrace)
|
242
245
|
end
|
243
246
|
|
244
247
|
it 'rejects promise2 with error raised by on_reject' do
|
245
|
-
promise2 = subject.then(nil, proc { |_|
|
248
|
+
promise2 = subject.then(nil, proc { |_| raise error })
|
246
249
|
expect { subject.reject(reason) }.to raise_error(error)
|
247
250
|
|
248
251
|
expect(promise2).to be_rejected
|
249
252
|
expect(promise2.reason).to eq(error)
|
253
|
+
expect(promise2.backtrace).to be(subject.backtrace)
|
250
254
|
end
|
251
255
|
|
252
256
|
describe 'on_fulfill returns promise' do
|
@@ -259,6 +263,7 @@ describe Promise do
|
|
259
263
|
returned_promise.fulfill(other_value)
|
260
264
|
expect(promise2).to be_fulfilled
|
261
265
|
expect(promise2.value).to eq(other_value)
|
266
|
+
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
262
267
|
end
|
263
268
|
|
264
269
|
it 'makes promise2 assume rejected state of returned promise' do
|
@@ -270,6 +275,7 @@ describe Promise do
|
|
270
275
|
returned_promise.reject(other_reason)
|
271
276
|
expect(promise2).to be_rejected
|
272
277
|
expect(promise2.reason).to eq(other_reason)
|
278
|
+
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
273
279
|
end
|
274
280
|
end
|
275
281
|
|
@@ -283,6 +289,7 @@ describe Promise do
|
|
283
289
|
returned_promise.fulfill(other_value)
|
284
290
|
expect(promise2).to be_fulfilled
|
285
291
|
expect(promise2.value).to eq(other_value)
|
292
|
+
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
286
293
|
end
|
287
294
|
|
288
295
|
it 'makes promise2 assume rejected state of returned promise' do
|
@@ -294,6 +301,7 @@ describe Promise do
|
|
294
301
|
returned_promise.reject(other_reason)
|
295
302
|
expect(promise2).to be_rejected
|
296
303
|
expect(promise2.reason).to eq(other_reason)
|
304
|
+
expect(promise2.backtrace).to be(returned_promise.backtrace)
|
297
305
|
end
|
298
306
|
end
|
299
307
|
|
@@ -304,6 +312,7 @@ describe Promise do
|
|
304
312
|
|
305
313
|
expect(promise2).to be_fulfilled
|
306
314
|
expect(promise2.value).to eq(value)
|
315
|
+
expect(promise2.backtrace).to be(subject.backtrace)
|
307
316
|
end
|
308
317
|
end
|
309
318
|
|
@@ -314,6 +323,7 @@ describe Promise do
|
|
314
323
|
|
315
324
|
expect(promise2).to be_rejected
|
316
325
|
expect(promise2.reason).to eq(reason)
|
326
|
+
expect(promise2.backtrace).to be(subject.backtrace)
|
317
327
|
end
|
318
328
|
end
|
319
329
|
end
|
@@ -355,6 +365,12 @@ describe Promise do
|
|
355
365
|
subject.fulfill
|
356
366
|
expect(subject.value).to be(nil)
|
357
367
|
end
|
368
|
+
|
369
|
+
it 'sets the backtrace' do
|
370
|
+
subject.fulfill
|
371
|
+
expect(subject.backtrace.join)
|
372
|
+
.to include(__FILE__ + ':' + (__LINE__ - 2).to_s)
|
373
|
+
end
|
358
374
|
end
|
359
375
|
|
360
376
|
describe '#reject' do
|
@@ -364,30 +380,14 @@ describe Promise do
|
|
364
380
|
|
365
381
|
it 'does not require a reason' do
|
366
382
|
subject.reject
|
367
|
-
expect(subject.reason).to
|
368
|
-
end
|
369
|
-
|
370
|
-
it 'allows nil reason' do
|
371
|
-
subject.reject(nil)
|
372
|
-
expect(subject.reason).to be_a(RuntimeError)
|
383
|
+
expect(subject.reason).to be(RuntimeError)
|
373
384
|
end
|
374
385
|
|
375
386
|
it 'sets the backtrace' do
|
376
387
|
subject.reject
|
377
|
-
expect(subject.
|
388
|
+
expect(subject.backtrace.join)
|
378
389
|
.to include(__FILE__ + ':' + (__LINE__ - 2).to_s)
|
379
390
|
end
|
380
|
-
|
381
|
-
it 'does not override backtrace' do
|
382
|
-
subject.reject(reason)
|
383
|
-
expect(subject.reason.backtrace).to be(backtrace)
|
384
|
-
end
|
385
|
-
|
386
|
-
it 'builds exception object for custom error class' do
|
387
|
-
cls = Class.new(StandardError)
|
388
|
-
subject.reject(cls)
|
389
|
-
expect(subject.reason).to be_a(cls)
|
390
|
-
end
|
391
391
|
end
|
392
392
|
|
393
393
|
describe '#sync' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: promise.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-12-
|
12
|
+
date: 2013-12-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|