promise.rb 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 10
3
- total_score: 50
3
+ total_score: 55
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 7.7
2
+ threshold: 10.8
@@ -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: 2
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: 14
61
+ max_methods: 12
61
62
  TooManyStatements:
62
63
  enabled: true
63
64
  exclude:
@@ -47,3 +47,7 @@ ConstantName:
47
47
  # Not all trivial readers/writers can be defined with attr_* methods
48
48
  TrivialAccessors:
49
49
  Enabled: false
50
+
51
+ SignalException:
52
+ # Valid values are: semantic, only_raise and only_fail
53
+ EnforcedStyle: only_raise
@@ -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
- @on_fulfill = []
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 = add_callbacks(on_fulfill, on_reject)
32
+ next_promise = Promise.new
34
33
 
35
- maybe_dispatch(@on_fulfill.last, @on_reject.last)
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
- fail reason if rejected?
48
+ raise reason if rejected?
42
49
  value
43
50
  end
44
51
 
45
- def fulfill(value = nil)
46
- dispatch(@on_fulfill, value) do
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
- reason = build_reason(reason || RuntimeError, caller)
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
- private
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
- callbacks.each { |callback| dispatch!(callback, arg) }
73
- end
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, arg)
94
- defer { callback.dispatch(arg) }
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
@@ -2,55 +2,47 @@
2
2
 
3
3
  class Promise
4
4
  class Callback
5
- def initialize(block, next_promise)
6
- @block = block
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
- private
10
+ def block_for(promise)
11
+ promise.fulfilled? ? @on_fulfill : @on_reject
12
+ end
11
13
 
12
- def execute(value)
13
- @block.call(value)
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 handle_result(result)
20
- if Promise === result
21
- assume_state(result)
18
+ def dispatch(promise)
19
+ if (block = block_for(promise))
20
+ handle_result(promise) { execute(promise, block) }
22
21
  else
23
- @next_promise.fulfill(result)
22
+ assume_state(promise, @next_promise)
24
23
  end
25
24
  end
26
25
 
27
- def assume_state(returned_promise)
28
- on_fulfill = @next_promise.method(:fulfill)
29
- on_reject = @next_promise.method(:reject)
30
-
31
- returned_promise.then(on_fulfill, on_reject)
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
- class FulfillCallback < Callback
36
- def dispatch(value)
37
- if @block
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
- handle_result(value)
37
+ @next_promise.fulfill(result, promise.backtrace)
42
38
  end
43
39
  end
44
- end
45
40
 
46
- class RejectCallback < Callback
47
- def dispatch(reason)
48
- if @block
49
- result = execute(reason)
50
- handle_result(result)
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
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class Promise
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -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 { |_| fail(error) })
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 { |_| fail(error) })
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 be_a(RuntimeError)
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.reason.backtrace[0])
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.5.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-16 00:00:00.000000000 Z
12
+ date: 2013-12-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec