promise.rb 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +8 -0
- data/Gemfile +0 -1
- data/Gemfile.devtools +23 -11
- data/README.md +78 -18
- data/{LICENSE → UNLICENSE} +0 -0
- data/config/flay.yml +1 -1
- data/config/rubocop.yml +0 -9
- data/lib/promise.rb +4 -2
- data/lib/promise/progress.rb +4 -7
- data/lib/promise/version.rb +1 -1
- data/promise.rb.gemspec +2 -2
- data/spec/promise_spec.rb +11 -8
- metadata +6 -5
data/CHANGELOG.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# promise.rb changelog
|
2
|
+
|
3
|
+
## 0.4.0 (December 13, 2013)
|
4
|
+
|
5
|
+
* Disclaiming my copyright, promise.rb is now in the Public Domain
|
6
|
+
* on_fulfill argument to #then now takes precedence over block
|
7
|
+
* The rejection reason now defaults to RuntimeError, so that it can be re-raised by #sync
|
8
|
+
* `progress` and `on_progress` are now first class citizens
|
data/Gemfile
CHANGED
data/Gemfile.devtools
CHANGED
@@ -4,6 +4,10 @@ group :development do
|
|
4
4
|
gem 'rake', '~> 10.1.0'
|
5
5
|
gem 'rspec', '~> 2.14.1'
|
6
6
|
gem 'yard', '~> 0.8.7'
|
7
|
+
|
8
|
+
platform :rbx do
|
9
|
+
gem 'rubysl-singleton', '~> 2.0.0'
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
group :yard do
|
@@ -11,14 +15,13 @@ group :yard do
|
|
11
15
|
end
|
12
16
|
|
13
17
|
group :guard do
|
14
|
-
gem 'guard', '~>
|
15
|
-
gem 'guard-bundler', '~>
|
16
|
-
gem 'guard-rspec', '~>
|
17
|
-
gem 'guard-rubocop', '~> 0.
|
18
|
-
gem 'guard-mutant', '~> 0.0.1'
|
18
|
+
gem 'guard', '~> 2.2.4'
|
19
|
+
gem 'guard-bundler', '~> 2.0.0'
|
20
|
+
gem 'guard-rspec', '~> 4.0.4'
|
21
|
+
gem 'guard-rubocop', '~> 1.0.0'
|
19
22
|
|
20
23
|
# file system change event handling
|
21
|
-
gem 'listen', '~>
|
24
|
+
gem 'listen', '~> 2.2.0'
|
22
25
|
gem 'rb-fchange', '~> 0.0.6', require: false
|
23
26
|
gem 'rb-fsevent', '~> 0.9.3', require: false
|
24
27
|
gem 'rb-inotify', '~> 0.9.0', require: false
|
@@ -30,18 +33,27 @@ group :guard do
|
|
30
33
|
end
|
31
34
|
|
32
35
|
group :metrics do
|
33
|
-
gem 'coveralls', '~> 0.
|
36
|
+
gem 'coveralls', '~> 0.7.0'
|
34
37
|
gem 'flay', '~> 2.4.0'
|
35
|
-
gem 'flog', '~> 4.
|
38
|
+
gem 'flog', '~> 4.2.0'
|
36
39
|
gem 'reek', '~> 1.3.2'
|
37
|
-
gem 'rubocop', '~> 0.
|
38
|
-
gem 'simplecov', '~> 0.
|
40
|
+
gem 'rubocop', '~> 0.15.0'
|
41
|
+
gem 'simplecov', '~> 0.8.2'
|
39
42
|
gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
|
40
43
|
|
41
44
|
platforms :ruby_19, :ruby_20 do
|
42
|
-
gem 'mutant', git: 'https://github.com/mbj/mutant.git'
|
45
|
+
gem 'mutant', '~> 0.3.0.rc3', git: 'https://github.com/mbj/mutant.git'
|
46
|
+
gem 'unparser', '~> 0.1.5', git: 'https://github.com/mbj/unparser.git'
|
43
47
|
gem 'yard-spellcheck', '~> 0.1.5'
|
44
48
|
end
|
49
|
+
|
50
|
+
platform :rbx do
|
51
|
+
gem 'json', '~> 1.8.1'
|
52
|
+
gem 'racc', '~> 1.4.10'
|
53
|
+
gem 'rubysl-logger', '~> 2.0.0'
|
54
|
+
gem 'rubysl-open-uri', '~> 2.0.0'
|
55
|
+
gem 'rubysl-prettyprint', '~> 2.0.2'
|
56
|
+
end
|
45
57
|
end
|
46
58
|
|
47
59
|
group :benchmarks do
|
data/README.md
CHANGED
@@ -1,7 +1,20 @@
|
|
1
1
|
# promise.rb [![Build Status](https://travis-ci.org/lgierth/promise.rb.png?branch=master)](https://travis-ci.org/lgierth/promise.rb) [![Code Climate](https://codeclimate.com/github/lgierth/promise.rb.png)](https://codeclimate.com/github/lgierth/promise.rb) [![Coverage Status](https://coveralls.io/repos/lgierth/promise.rb/badge.png?branch=master)](https://coveralls.io/r/lgierth/promise.rb?branch=master)
|
2
2
|
|
3
3
|
Ruby implementation of the [Promises/A+ spec](http://promisesaplus.com/).
|
4
|
-
100% mutation coverage, tested on 1.9, 2.0, Rubinius, and JRuby.
|
4
|
+
100% [mutation coverage](https://github.com/mbj/mutant), tested on 1.9, 2.0, Rubinius, and JRuby.
|
5
|
+
|
6
|
+
Similar projects:
|
7
|
+
|
8
|
+
- [concurrent-ruby](https://github.com/jdantonio/concurrent-ruby/blob/master/md/promise.md), Promises/A(+) implementation, thread based
|
9
|
+
- [promise](https://github.com/bhuga/promising-future), a.k.a. promising-future, classic promises and futures, thread based
|
10
|
+
- [celluloid-promise](https://github.com/cotag/celluloid-promise), inspired by Q, backed by a Celluloid actor
|
11
|
+
- [em-promise](https://github.com/cotag/em-promise), inspired by Q, backed by an EventMachine reactor
|
12
|
+
- [futuristic](https://github.com/seanlilmateus/futuristic), MacRuby bindings for Grand Central Dispatch
|
13
|
+
- [methodmissing/promise](https://github.com/methodmissing/promise), thread based, abandoned
|
14
|
+
|
15
|
+
## Todo
|
16
|
+
|
17
|
+
- test with https://gist.github.com/joeljackson/5722487
|
5
18
|
|
6
19
|
## Installation
|
7
20
|
|
@@ -19,19 +32,11 @@ Or install it yourself as:
|
|
19
32
|
|
20
33
|
## Usage
|
21
34
|
|
22
|
-
|
35
|
+
This guide assumes that you are familiar with the [Promises/A+ spec](http://promisesaplus.com/). It's a quick read, though.
|
23
36
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
class MyPromise < Promise
|
28
|
-
def defer(callback, arg)
|
29
|
-
callback.dispatch(arg)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
```
|
33
|
-
|
34
|
-
The above scheduling mechanism violates the following section of the spec:
|
37
|
+
promise.rb comes with a very primitive way of scheduling callback dispatch. It
|
38
|
+
immediately executes the callback, instead of scheduling it for execution
|
39
|
+
*after* `Promise#fulfill` or `Promise#reject`, as demanded by the spec:
|
35
40
|
|
36
41
|
> onFulfilled or onRejected must not be called until the execution context
|
37
42
|
> stack contains only platform code.
|
@@ -44,8 +49,8 @@ require 'promise'
|
|
44
49
|
require 'eventmachine'
|
45
50
|
|
46
51
|
class MyPromise < Promise
|
47
|
-
def defer
|
48
|
-
EM.next_tick {
|
52
|
+
def defer
|
53
|
+
EM.next_tick { yield }
|
49
54
|
end
|
50
55
|
end
|
51
56
|
```
|
@@ -76,10 +81,65 @@ end
|
|
76
81
|
failing_stuff.then(proc { |value| }, proc { |reason| p reason })
|
77
82
|
```
|
78
83
|
|
79
|
-
|
84
|
+
promise.rb also comes with the utility method `Promise#sync`, which waits for
|
85
|
+
the promise to be fulfilled and returns the value, or for it to be rejected and
|
86
|
+
re-raises the reason. Using `#sync` requires you to implement `#wait`. You could
|
87
|
+
for example cooperatively schedule fibers waiting for different promises:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
require 'promise'
|
91
|
+
require 'eventmachine'
|
92
|
+
|
93
|
+
class MyPromise < Promise
|
94
|
+
def defer
|
95
|
+
EM.next_tick { yield }
|
96
|
+
end
|
97
|
+
|
98
|
+
def wait
|
99
|
+
fiber = Fiber.current
|
100
|
+
resume = proc do |arg|
|
101
|
+
defer { fiber.resume(arg) }
|
102
|
+
end
|
103
|
+
|
104
|
+
self.then(resume, resume)
|
105
|
+
Fiber.yield
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
promise = MyPromise.new
|
110
|
+
Fiber.new { p promise.sync }.resume
|
111
|
+
promise.fulfill
|
112
|
+
```
|
113
|
+
|
114
|
+
Or have the rejection reason re-raised from `#sync`:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
promise = MyPromise.new
|
118
|
+
|
119
|
+
Fiber.new do
|
120
|
+
begin
|
121
|
+
promise.sync
|
122
|
+
rescue MyError
|
123
|
+
p $!
|
124
|
+
end
|
125
|
+
end.resume
|
126
|
+
|
127
|
+
promise.reject(MyError.new)
|
128
|
+
```
|
129
|
+
|
130
|
+
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
|
+
|
132
|
+
```ruby
|
133
|
+
promise = MyPromise.new
|
134
|
+
promise.on_progress { |status| p status }
|
135
|
+
promise.progress(:anything)
|
136
|
+
```
|
137
|
+
|
138
|
+
## Unlicense
|
80
139
|
|
81
|
-
|
82
|
-
|
140
|
+
promise.rb is free and unencumbered public domain software. For more
|
141
|
+
information, see [unlicense.org](http://unlicense.org/) or the accompanying
|
142
|
+
UNLICENSE file.
|
83
143
|
|
84
144
|
## Contributing
|
85
145
|
|
data/{LICENSE → UNLICENSE}
RENAMED
File without changes
|
data/config/flay.yml
CHANGED
data/config/rubocop.yml
CHANGED
@@ -24,15 +24,6 @@ CollectionMethods:
|
|
24
24
|
find: 'detect'
|
25
25
|
find_all: 'select'
|
26
26
|
|
27
|
-
# Do not force public/protected/private keyword to be indented at the same
|
28
|
-
# level as the def keyword. My personal preference is to outdent these keywords
|
29
|
-
# because I think when scanning code it makes it easier to identify the
|
30
|
-
# sections of code and visually separate them. When the keyword is at the same
|
31
|
-
# level I think it sort of blends in with the def keywords and makes it harder
|
32
|
-
# to scan the code and see where the sections are.
|
33
|
-
AccessControl:
|
34
|
-
Enabled: false
|
35
|
-
|
36
27
|
# Limit line length
|
37
28
|
LineLength:
|
38
29
|
Max: 79
|
data/lib/promise.rb
CHANGED
@@ -6,6 +6,8 @@ require 'promise/callback'
|
|
6
6
|
require 'promise/progress'
|
7
7
|
|
8
8
|
class Promise
|
9
|
+
include Promise::Progress
|
10
|
+
|
9
11
|
attr_reader :value, :reason
|
10
12
|
|
11
13
|
def initialize
|
@@ -27,7 +29,7 @@ class Promise
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def then(on_fulfill = nil, on_reject = nil, &block)
|
30
|
-
on_fulfill
|
32
|
+
on_fulfill ||= block
|
31
33
|
next_promise = add_callbacks(on_fulfill, on_reject)
|
32
34
|
|
33
35
|
maybe_dispatch(@on_fulfill.last, @on_reject.last)
|
@@ -47,7 +49,7 @@ class Promise
|
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
50
|
-
def reject(reason =
|
52
|
+
def reject(reason = RuntimeError)
|
51
53
|
dispatch(@on_reject, reason) do
|
52
54
|
@state = :rejected
|
53
55
|
@reason = reason
|
data/lib/promise/progress.rb
CHANGED
@@ -2,18 +2,15 @@
|
|
2
2
|
|
3
3
|
class Promise
|
4
4
|
module Progress
|
5
|
-
def initialize
|
6
|
-
super
|
7
|
-
@on_progress = []
|
8
|
-
end
|
9
|
-
|
10
5
|
def on_progress(&block)
|
11
|
-
@on_progress
|
6
|
+
@on_progress ||= []
|
7
|
+
@on_progress << block if block_given?
|
8
|
+
@on_progress
|
12
9
|
end
|
13
10
|
|
14
11
|
def progress(status)
|
15
12
|
if pending?
|
16
|
-
|
13
|
+
on_progress.each { |block| block.call(status) }
|
17
14
|
end
|
18
15
|
end
|
19
16
|
end
|
data/lib/promise/version.rb
CHANGED
data/promise.rb.gemspec
CHANGED
@@ -10,9 +10,9 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ['Lars Gierth']
|
11
11
|
spec.email = ['lars.gierth@gmail.com']
|
12
12
|
spec.description = %q{Promises/A+ for Ruby}
|
13
|
-
spec.summary = %q{Promises/A+
|
13
|
+
spec.summary = %q{Ruby implementation of the Promises/A+ spec}
|
14
14
|
spec.homepage = 'https://github.com/lgierth/promise'
|
15
|
-
spec.license = '
|
15
|
+
spec.license = 'Public Domain'
|
16
16
|
|
17
17
|
spec.files = `git ls-files`.split($/)
|
18
18
|
spec.test_files = spec.files.grep(%r{^spec/})
|
data/spec/promise_spec.rb
CHANGED
@@ -117,6 +117,14 @@ describe Promise do
|
|
117
117
|
subject.fulfill(value)
|
118
118
|
expect(result).to eq(value)
|
119
119
|
end
|
120
|
+
|
121
|
+
it 'takes precedence over block' do
|
122
|
+
result = nil
|
123
|
+
subject.then(proc { |_| result = :arg }) { |_| result = :block }
|
124
|
+
|
125
|
+
subject.fulfill(value)
|
126
|
+
expect(result).to be(:arg)
|
127
|
+
end
|
120
128
|
end
|
121
129
|
|
122
130
|
describe '3.2.3 on_reject' do
|
@@ -220,7 +228,7 @@ describe Promise do
|
|
220
228
|
end
|
221
229
|
|
222
230
|
it 'rejects promise2 with error raised by on_fulfill' do
|
223
|
-
promise2 = subject.then(proc { |_|
|
231
|
+
promise2 = subject.then(proc { |_| fail(error) })
|
224
232
|
expect { subject.fulfill(value) }.to raise_error(error)
|
225
233
|
|
226
234
|
expect(promise2).to be_rejected
|
@@ -228,7 +236,7 @@ describe Promise do
|
|
228
236
|
end
|
229
237
|
|
230
238
|
it 'rejects promise2 with error raised by on_reject' do
|
231
|
-
promise2 = subject.then(nil, proc { |_|
|
239
|
+
promise2 = subject.then(nil, proc { |_| fail(error) })
|
232
240
|
expect { subject.reject(reason) }.to raise_error(error)
|
233
241
|
|
234
242
|
expect(promise2).to be_rejected
|
@@ -305,11 +313,6 @@ describe Promise do
|
|
305
313
|
end
|
306
314
|
|
307
315
|
describe '#progress' do
|
308
|
-
let(:klass) do
|
309
|
-
Class.new(Promise) { include Promise::Progress }
|
310
|
-
end
|
311
|
-
let(:subject) { klass.new }
|
312
|
-
|
313
316
|
let(:status) { double('status') }
|
314
317
|
|
315
318
|
it 'calls the callbacks in the order of calls to #on_progress' do
|
@@ -355,7 +358,7 @@ describe Promise do
|
|
355
358
|
|
356
359
|
it 'does not require a reason' do
|
357
360
|
subject.reject
|
358
|
-
expect(subject.reason).to be(
|
361
|
+
expect(subject.reason).to be(RuntimeError)
|
359
362
|
end
|
360
363
|
end
|
361
364
|
|
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.4.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-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -37,11 +37,12 @@ files:
|
|
37
37
|
- .gitignore
|
38
38
|
- .rspec
|
39
39
|
- .travis.yml
|
40
|
+
- CHANGELOG.md
|
40
41
|
- Gemfile
|
41
42
|
- Gemfile.devtools
|
42
|
-
- LICENSE
|
43
43
|
- README.md
|
44
44
|
- Rakefile
|
45
|
+
- UNLICENSE
|
45
46
|
- config/devtools.yml
|
46
47
|
- config/flay.yml
|
47
48
|
- config/flog.yml
|
@@ -58,7 +59,7 @@ files:
|
|
58
59
|
- spec/spec_helper.rb
|
59
60
|
homepage: https://github.com/lgierth/promise
|
60
61
|
licenses:
|
61
|
-
-
|
62
|
+
- Public Domain
|
62
63
|
post_install_message:
|
63
64
|
rdoc_options: []
|
64
65
|
require_paths:
|
@@ -80,7 +81,7 @@ rubyforge_project:
|
|
80
81
|
rubygems_version: 1.8.23
|
81
82
|
signing_key:
|
82
83
|
specification_version: 3
|
83
|
-
summary: Promises/A+
|
84
|
+
summary: Ruby implementation of the Promises/A+ spec
|
84
85
|
test_files:
|
85
86
|
- spec/promise_spec.rb
|
86
87
|
- spec/spec_helper.rb
|