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.
@@ -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