promise.rb 0.4.0 → 0.5.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/.travis.yml +2 -1
- data/CHANGELOG.md +6 -0
- data/README.md +26 -3
- data/Rakefile +1 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +1 -1
- data/lib/promise.rb +8 -2
- data/lib/promise/version.rb +1 -1
- data/spec/promise_spec.rb +36 -8
- metadata +2 -2
data/.travis.yml
CHANGED
@@ -2,7 +2,7 @@ language: ruby
|
|
2
2
|
rvm:
|
3
3
|
- 1.9.3
|
4
4
|
- 2.0.0
|
5
|
-
- jruby
|
5
|
+
- jruby
|
6
6
|
- rbx
|
7
7
|
- ruby-head
|
8
8
|
- jruby-head
|
@@ -10,4 +10,5 @@ matrix:
|
|
10
10
|
allow_failures:
|
11
11
|
- rvm: ruby-head
|
12
12
|
- rvm: jruby-head
|
13
|
+
fast_finish: true
|
13
14
|
script: bundle exec rake -t metrics:coverage ci
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# promise.rb changelog
|
2
2
|
|
3
|
+
## 0.5.0 (December 16, 2013)
|
4
|
+
|
5
|
+
* Fulfillment value and rejection reason are no longer being frozen
|
6
|
+
* Rejection reason always gets a sensible backtrace now
|
7
|
+
* Have pending specs for deviations from the A+ spec
|
8
|
+
|
3
9
|
## 0.4.0 (December 13, 2013)
|
4
10
|
|
5
11
|
* Disclaiming my copyright, promise.rb is now in the Public Domain
|
data/README.md
CHANGED
@@ -6,15 +6,14 @@ Ruby implementation of the [Promises/A+ spec](http://promisesaplus.com/).
|
|
6
6
|
Similar projects:
|
7
7
|
|
8
8
|
- [concurrent-ruby](https://github.com/jdantonio/concurrent-ruby/blob/master/md/promise.md), Promises/A(+) implementation, thread based
|
9
|
+
- [ruby-thread](https://github.com/meh/ruby-thread), thread/mutex/condition variable based, thread safe
|
9
10
|
- [promise](https://github.com/bhuga/promising-future), a.k.a. promising-future, classic promises and futures, thread based
|
10
11
|
- [celluloid-promise](https://github.com/cotag/celluloid-promise), inspired by Q, backed by a Celluloid actor
|
11
12
|
- [em-promise](https://github.com/cotag/em-promise), inspired by Q, backed by an EventMachine reactor
|
12
13
|
- [futuristic](https://github.com/seanlilmateus/futuristic), MacRuby bindings for Grand Central Dispatch
|
13
14
|
- [methodmissing/promise](https://github.com/methodmissing/promise), thread based, abandoned
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
- test with https://gist.github.com/joeljackson/5722487
|
16
|
+
*Note that promise.rb is probably not thread safe.*
|
18
17
|
|
19
18
|
## Installation
|
20
19
|
|
@@ -81,6 +80,8 @@ end
|
|
81
80
|
failing_stuff.then(proc { |value| }, proc { |reason| p reason })
|
82
81
|
```
|
83
82
|
|
83
|
+
### Waiting for fulfillment/rejection
|
84
|
+
|
84
85
|
promise.rb also comes with the utility method `Promise#sync`, which waits for
|
85
86
|
the promise to be fulfilled and returns the value, or for it to be rejected and
|
86
87
|
re-raises the reason. Using `#sync` requires you to implement `#wait`. You could
|
@@ -127,6 +128,28 @@ end.resume
|
|
127
128
|
promise.reject(MyError.new)
|
128
129
|
```
|
129
130
|
|
131
|
+
### Chaining promises
|
132
|
+
|
133
|
+
As per the A+ spec, every call to `#then` returns a new promise, which assumes
|
134
|
+
the first promise's state. That means it passes its `#fulfill` and `#reject`
|
135
|
+
methods to first promise's `#then`, shortcircuiting the two promises. In case
|
136
|
+
a callback returns a promise, it'll instead assume that promise's state.
|
137
|
+
|
138
|
+
Imagine the `#fulfill` and `#reject` calls in the following example happening
|
139
|
+
somewhere in a background Fiber or so.
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
require 'promise'
|
143
|
+
|
144
|
+
Promise.new
|
145
|
+
.tap(&:fulfill)
|
146
|
+
.then { Promise.new.tap(&:fulfill) }
|
147
|
+
.then { Promise.new.tap(&:reject) }
|
148
|
+
.then(nil, proc { |reason| p reason })
|
149
|
+
```
|
150
|
+
|
151
|
+
### Progress callbacks
|
152
|
+
|
130
153
|
Very simple progress callbacks, as per Promises/A, are supported as well. They have been dropped in A+, but I found them to be a useful mechanism - if kept simple. Callback dispatch happens immediately in the call to `#progress`, in the order of definition via `#on_progress`. Also note that `#on_progress` does not return a new promise for chaining - the progress mechanism is meant to be very lightweight, and ignores many of the constraints and guarantees of `then`.
|
131
154
|
|
132
155
|
```ruby
|
data/Rakefile
CHANGED
data/config/flay.yml
CHANGED
data/config/reek.yml
CHANGED
data/lib/promise.rb
CHANGED
@@ -49,7 +49,8 @@ class Promise
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
def reject(reason =
|
52
|
+
def reject(reason = nil)
|
53
|
+
reason = build_reason(reason || RuntimeError, caller)
|
53
54
|
dispatch(@on_reject, reason) do
|
54
55
|
@state = :rejected
|
55
56
|
@reason = reason
|
@@ -68,7 +69,6 @@ class Promise
|
|
68
69
|
def dispatch(callbacks, arg)
|
69
70
|
if pending?
|
70
71
|
yield
|
71
|
-
arg.freeze
|
72
72
|
callbacks.each { |callback| dispatch!(callback, arg) }
|
73
73
|
end
|
74
74
|
|
@@ -97,4 +97,10 @@ class Promise
|
|
97
97
|
def defer
|
98
98
|
yield
|
99
99
|
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
|
100
106
|
end
|
data/lib/promise/version.rb
CHANGED
data/spec/promise_spec.rb
CHANGED
@@ -7,11 +7,13 @@ describe Promise do
|
|
7
7
|
|
8
8
|
let(:value) { double('value') }
|
9
9
|
let(:other_value) { double('other_value') }
|
10
|
+
|
11
|
+
let(:backtrace) { caller }
|
10
12
|
let(:reason) do
|
11
|
-
StandardError.new('reason').tap { |ex| ex.set_backtrace(
|
13
|
+
StandardError.new('reason').tap { |ex| ex.set_backtrace(backtrace) }
|
12
14
|
end
|
13
15
|
let(:other_reason) do
|
14
|
-
StandardError.new('other_reason').tap { |ex| ex.set_backtrace(
|
16
|
+
StandardError.new('other_reason').tap { |ex| ex.set_backtrace(backtrace) }
|
15
17
|
end
|
16
18
|
|
17
19
|
describe '3.1.1 pending' do
|
@@ -33,14 +35,16 @@ describe Promise do
|
|
33
35
|
expect(subject).to be_fulfilled
|
34
36
|
end
|
35
37
|
|
36
|
-
it 'has
|
38
|
+
it 'has a value' do
|
37
39
|
subject.fulfill(value)
|
38
40
|
expect(subject.value).to eq(value)
|
39
41
|
|
40
42
|
subject.fulfill(other_value)
|
41
43
|
expect(subject.value).to eq(value)
|
44
|
+
end
|
42
45
|
|
43
|
-
|
46
|
+
it 'freezes the value' do
|
47
|
+
pending 'Dropped in 74da6e9'
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
@@ -51,14 +55,16 @@ describe Promise do
|
|
51
55
|
expect(subject).to be_rejected
|
52
56
|
end
|
53
57
|
|
54
|
-
it 'has
|
58
|
+
it 'has a reason' do
|
55
59
|
subject.reject(reason)
|
56
60
|
expect(subject.reason).to eq(reason)
|
57
61
|
|
58
62
|
subject.reject(other_reason)
|
59
63
|
expect(subject.reason).to eq(reason)
|
64
|
+
end
|
60
65
|
|
61
|
-
|
66
|
+
it 'freezes the reason' do
|
67
|
+
pending 'Dropped in 74da6e9'
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
@@ -164,7 +170,7 @@ describe Promise do
|
|
164
170
|
|
165
171
|
describe '3.2.4' do
|
166
172
|
it 'returns before on_fulfill or on_reject is called' do
|
167
|
-
pending
|
173
|
+
pending 'To be implemented by application code'
|
168
174
|
end
|
169
175
|
end
|
170
176
|
|
@@ -358,7 +364,29 @@ describe Promise do
|
|
358
364
|
|
359
365
|
it 'does not require a reason' do
|
360
366
|
subject.reject
|
361
|
-
expect(subject.reason).to
|
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)
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'sets the backtrace' do
|
376
|
+
subject.reject
|
377
|
+
expect(subject.reason.backtrace[0])
|
378
|
+
.to include(__FILE__ + ':' + (__LINE__ - 2).to_s)
|
379
|
+
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)
|
362
390
|
end
|
363
391
|
end
|
364
392
|
|
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.5.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-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|