promise.rb 0.6.1 → 0.7.0.rc1
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.
- checksums.yaml +7 -0
- data/.travis.yml +8 -12
- data/CHANGELOG.md +11 -0
- data/Gemfile +9 -6
- data/README.md +27 -17
- data/Rakefile +18 -4
- data/config/devtools.yml +1 -1
- data/config/flay.yml +2 -2
- data/config/reek.yml +3 -1
- data/config/rubocop.yml +48 -4
- data/lib/promise.rb +14 -4
- data/lib/promise/callback.rb +2 -1
- data/lib/promise/group.rb +51 -0
- data/lib/promise/progress.rb +3 -3
- data/lib/promise/version.rb +1 -1
- data/spec/spec_helper.rb +3 -1
- data/spec/support/delayed_promise.rb +22 -0
- data/spec/{promise_spec.rb → unit/promise_spec.rb} +147 -60
- metadata +18 -20
- data/Gemfile.devtools +0 -69
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b98cd2e53ffcbfb4c387c41a85c9699b8c050f5e
|
4
|
+
data.tar.gz: 22cf6c2bd54e4490470520aca42aa9f15abb24b4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 990eedfbc0cfb55355f2ca0bc963e0cec24d3668b7a22e8e97fb643bb6abd71da10dec39c9e4846f243bc3bad51b5e6a544a270d04a600f7fbca2b3d060e7f96
|
7
|
+
data.tar.gz: dffd4ca2bd12d3ee92acb2dc4c719a7aacbe20a4563da016b21ad63d2909c1f22a5c68947b9369f3792b1440edf5e2b37f4b13d8b4e8aa4d14c0d4795ee83e25
|
data/.travis.yml
CHANGED
@@ -1,18 +1,14 @@
|
|
1
|
+
sudo: false
|
1
2
|
language: ruby
|
2
3
|
rvm:
|
3
|
-
- 1.9
|
4
|
-
- 2.0
|
5
|
-
- 2.1
|
4
|
+
- 1.9
|
5
|
+
- 2.0
|
6
|
+
- 2.1
|
7
|
+
- 2.2
|
6
8
|
- jruby
|
7
|
-
- rbx
|
8
|
-
- ruby-head
|
9
|
-
- jruby-head
|
9
|
+
- rbx-3.14
|
10
10
|
matrix:
|
11
|
-
allow_failures:
|
12
|
-
- rvm: 2.1.0 # https://github.com/dkubb/adamantium/issues/30
|
13
|
-
- rvm: ruby-head
|
14
|
-
- rvm: jruby-head
|
15
11
|
fast_finish: true
|
16
|
-
script: bundle exec rake -t metrics:coverage ci
|
17
12
|
before_install:
|
18
|
-
- gem install bundler
|
13
|
+
- gem install bundler
|
14
|
+
script: bundle exec rake -t
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# promise.rb changelog
|
2
2
|
|
3
|
+
## 0.7.0.rc1 (February 9, 2016)
|
4
|
+
|
5
|
+
### Bug Fixes
|
6
|
+
|
7
|
+
* Return instances of the custom promise class from its methods (pull #10)
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* Add Promise.resolve utility method (pull #8)
|
12
|
+
* Add Promise.all utility method (pull #9)
|
13
|
+
|
3
14
|
## 0.6.1 (January 14, 2014)
|
4
15
|
|
5
16
|
* The rejection reason now defaults to Promise::Error.
|
data/Gemfile
CHANGED
@@ -4,15 +4,18 @@ source 'https://rubygems.org'
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
|
8
|
-
gem '
|
9
|
-
|
7
|
+
if Gem.ruby_version >= Gem::Version.new('2.1')
|
8
|
+
gem 'devtools', '~> 0.1.4'
|
9
|
+
end
|
10
|
+
gem 'fuubar', '~> 2.0.0'
|
10
11
|
gem 'awesome_print'
|
11
12
|
|
13
|
+
gem 'rake'
|
14
|
+
gem 'rspec', '~> 3.4.0'
|
15
|
+
gem 'rspec-its'
|
16
|
+
gem 'coveralls', '~> 0.8.9'
|
17
|
+
|
12
18
|
platform :rbx do
|
13
19
|
gem 'rubysl', '~> 2.0'
|
14
20
|
gem 'rubinius', '~> 2.0'
|
15
21
|
end
|
16
|
-
|
17
|
-
# Added by devtools
|
18
|
-
eval_gemfile 'Gemfile.devtools'
|
data/README.md
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# promise.rb [](https://travis-ci.org/lgierth/promise.rb) [](https://codeclimate.com/github/lgierth/promise.rb) [](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](https://github.com/mbj/mutant),
|
4
|
+
100% [mutation coverage](https://github.com/mbj/mutant),
|
5
|
+
tested on MRI 1.9, 2.0, 2.1, 2.2, Rubinius, and JRuby.
|
5
6
|
|
6
7
|
Similar projects:
|
7
8
|
|
8
|
-
- [concurrent-ruby](https://github.com/jdantonio/concurrent-ruby
|
9
|
+
- [concurrent-ruby](https://github.com/jdantonio/concurrent-ruby), Promises/A(+) implementation, thread based
|
9
10
|
- [ruby-thread](https://github.com/meh/ruby-thread), thread/mutex/condition variable based, thread safe
|
10
11
|
- [promise](https://github.com/bhuga/promising-future), a.k.a. promising-future, classic promises and futures, thread based
|
11
12
|
- [celluloid-promise](https://github.com/cotag/celluloid-promise), inspired by Q, backed by a Celluloid actor
|
@@ -64,8 +65,10 @@ def nonblocking_stuff
|
|
64
65
|
promise
|
65
66
|
end
|
66
67
|
|
67
|
-
|
68
|
-
nonblocking_stuff.then
|
68
|
+
EM.run do
|
69
|
+
nonblocking_stuff.then { |value| p value }
|
70
|
+
nonblocking_stuff.then(proc { |value| p value })
|
71
|
+
end
|
69
72
|
```
|
70
73
|
|
71
74
|
Rejection works similarly:
|
@@ -77,7 +80,9 @@ def failing_stuff
|
|
77
80
|
promise
|
78
81
|
end
|
79
82
|
|
80
|
-
|
83
|
+
EM.run do
|
84
|
+
failing_stuff.then(proc { |value| }, proc { |reason| p reason })
|
85
|
+
end
|
81
86
|
```
|
82
87
|
|
83
88
|
### Waiting for fulfillment/rejection
|
@@ -88,6 +93,7 @@ re-raises the reason. Using `#sync` requires you to implement `#wait`. You could
|
|
88
93
|
for example cooperatively schedule fibers waiting for different promises:
|
89
94
|
|
90
95
|
```ruby
|
96
|
+
require 'fiber'
|
91
97
|
require 'promise'
|
92
98
|
require 'eventmachine'
|
93
99
|
|
@@ -107,25 +113,29 @@ class MyPromise < Promise
|
|
107
113
|
end
|
108
114
|
end
|
109
115
|
|
110
|
-
|
111
|
-
|
112
|
-
promise.
|
116
|
+
EM.run do
|
117
|
+
promise = MyPromise.new
|
118
|
+
Fiber.new { p promise.sync }.resume
|
119
|
+
promise.fulfill
|
120
|
+
end
|
113
121
|
```
|
114
122
|
|
115
123
|
Or have the rejection reason re-raised from `#sync`:
|
116
124
|
|
117
125
|
```ruby
|
118
|
-
|
126
|
+
EM.run do
|
127
|
+
promise = MyPromise.new
|
119
128
|
|
120
|
-
Fiber.new do
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end.resume
|
129
|
+
Fiber.new do
|
130
|
+
begin
|
131
|
+
promise.sync
|
132
|
+
rescue
|
133
|
+
p $!
|
134
|
+
end
|
135
|
+
end.resume
|
127
136
|
|
128
|
-
promise.reject(
|
137
|
+
promise.reject('reason')
|
138
|
+
end
|
129
139
|
```
|
130
140
|
|
131
141
|
### Chaining promises
|
data/Rakefile
CHANGED
@@ -1,7 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
if Gem.ruby_version >= Gem::Version.new('2.1')
|
4
|
+
require 'devtools'
|
5
|
+
Devtools.init_rake_tasks
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
tasks = %w[
|
8
|
+
metrics:coverage
|
9
|
+
metrics:yardstick:verify
|
10
|
+
metrics:rubocop
|
11
|
+
metrics:flog
|
12
|
+
metrics:reek
|
13
|
+
spec:integration
|
14
|
+
]
|
15
|
+
tasks << 'metrics:mutant' if RUBY_ENGINE == 'ruby'
|
16
|
+
task :default => tasks
|
17
|
+
else
|
18
|
+
require 'rspec/core/rake_task'
|
19
|
+
RSpec::Core::RakeTask.new(:spec)
|
20
|
+
task :default => :spec
|
21
|
+
end
|
data/config/devtools.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
unit_test_timeout: 0
|
2
|
+
unit_test_timeout: 5.0
|
data/config/flay.yml
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
3
|
-
total_score:
|
2
|
+
threshold: 9
|
3
|
+
total_score: 43
|
data/config/reek.yml
CHANGED
@@ -12,6 +12,7 @@ ControlParameter:
|
|
12
12
|
enabled: true
|
13
13
|
exclude:
|
14
14
|
- "Promise#reject"
|
15
|
+
- "Promise#dispatch"
|
15
16
|
DataClump:
|
16
17
|
enabled: true
|
17
18
|
exclude: []
|
@@ -63,8 +64,9 @@ TooManyMethods:
|
|
63
64
|
TooManyStatements:
|
64
65
|
enabled: true
|
65
66
|
exclude:
|
67
|
+
- initialize
|
66
68
|
- each
|
67
|
-
max_statements:
|
69
|
+
max_statements: 5
|
68
70
|
UncommunicativeMethodName:
|
69
71
|
enabled: true
|
70
72
|
exclude: []
|
data/config/rubocop.yml
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
AllCops:
|
2
|
-
|
2
|
+
Include:
|
3
3
|
- '**/*.rake'
|
4
4
|
- 'Gemfile'
|
5
5
|
- 'Gemfile.devtools'
|
6
|
-
|
6
|
+
Exclude:
|
7
7
|
- '**/vendor/**'
|
8
8
|
- '**/benchmarks/**'
|
9
9
|
|
10
10
|
# Avoid parameter lists longer than five parameters.
|
11
11
|
ParameterLists:
|
12
|
-
Max:
|
12
|
+
Max: 4
|
13
13
|
CountKeywordArgs: true
|
14
|
-
|
14
|
+
|
15
|
+
MethodLength:
|
16
|
+
CountComments: false
|
17
|
+
Max: 10
|
15
18
|
|
16
19
|
# Avoid more than `Max` levels of nesting.
|
17
20
|
BlockNesting:
|
@@ -25,6 +28,15 @@ CollectionMethods:
|
|
25
28
|
find: 'detect'
|
26
29
|
find_all: 'select'
|
27
30
|
|
31
|
+
# Do not force public/protected/private keyword to be indented at the same
|
32
|
+
# level as the def keyword. My personal preference is to outdent these keywords
|
33
|
+
# because I think when scanning code it makes it easier to identify the
|
34
|
+
# sections of code and visually separate them. When the keyword is at the same
|
35
|
+
# level I think it sort of blends in with the def keywords and makes it harder
|
36
|
+
# to scan the code and see where the sections are.
|
37
|
+
AccessModifierIndentation:
|
38
|
+
Enabled: false
|
39
|
+
|
28
40
|
# Limit line length
|
29
41
|
LineLength:
|
30
42
|
Max: 79
|
@@ -52,3 +64,35 @@ TrivialAccessors:
|
|
52
64
|
SignalException:
|
53
65
|
# Valid values are: semantic, only_raise and only_fail
|
54
66
|
EnforcedStyle: only_raise
|
67
|
+
|
68
|
+
# Do not prefer do/end over {} for multiline blocks
|
69
|
+
BlockDelimiters:
|
70
|
+
Enabled: false
|
71
|
+
|
72
|
+
# Allow empty lines around body
|
73
|
+
EmptyLinesAroundBlockBody:
|
74
|
+
Enabled: false
|
75
|
+
|
76
|
+
# Prefer String#% over Kernel#sprintf
|
77
|
+
FormatString:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
# Use square brackets for literal Array objects
|
81
|
+
PercentLiteralDelimiters:
|
82
|
+
PreferredDelimiters:
|
83
|
+
'%': ()
|
84
|
+
'%i': '[]'
|
85
|
+
'%q': ()
|
86
|
+
'%Q': ()
|
87
|
+
'%r': '{}'
|
88
|
+
'%s': ()
|
89
|
+
'%w': '[]'
|
90
|
+
'%W': '[]'
|
91
|
+
'%x': ()
|
92
|
+
|
93
|
+
# buggy in rubocop-0.21.0, fixed in 0.23.0
|
94
|
+
LineEndConcatenation:
|
95
|
+
Enabled: false
|
96
|
+
|
97
|
+
GuardClause:
|
98
|
+
Enabled: false
|
data/lib/promise.rb
CHANGED
@@ -4,6 +4,7 @@ require 'promise/version'
|
|
4
4
|
|
5
5
|
require 'promise/callback'
|
6
6
|
require 'promise/progress'
|
7
|
+
require 'promise/group'
|
7
8
|
|
8
9
|
class Promise
|
9
10
|
Error = Class.new(RuntimeError)
|
@@ -12,26 +13,35 @@ class Promise
|
|
12
13
|
|
13
14
|
attr_reader :state, :value, :reason, :backtrace
|
14
15
|
|
16
|
+
def self.resolve(obj)
|
17
|
+
return obj if obj.instance_of?(self)
|
18
|
+
new.tap { |promise| promise.fulfill(obj) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.all(enumerable)
|
22
|
+
Group.new(new, enumerable).promise
|
23
|
+
end
|
24
|
+
|
15
25
|
def initialize
|
16
26
|
@state = :pending
|
17
27
|
@callbacks = []
|
18
28
|
end
|
19
29
|
|
20
30
|
def pending?
|
21
|
-
|
31
|
+
state.equal?(:pending)
|
22
32
|
end
|
23
33
|
|
24
34
|
def fulfilled?
|
25
|
-
|
35
|
+
state.equal?(:fulfilled)
|
26
36
|
end
|
27
37
|
|
28
38
|
def rejected?
|
29
|
-
|
39
|
+
state.equal?(:rejected)
|
30
40
|
end
|
31
41
|
|
32
42
|
def then(on_fulfill = nil, on_reject = nil, &block)
|
33
43
|
on_fulfill ||= block
|
34
|
-
next_promise =
|
44
|
+
next_promise = self.class.new
|
35
45
|
|
36
46
|
add_callback { Callback.new(self, on_fulfill, on_reject, next_promise) }
|
37
47
|
next_promise
|
data/lib/promise/callback.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
class Promise
|
2
|
+
class Group
|
3
|
+
attr_reader :promise
|
4
|
+
|
5
|
+
def initialize(result_promise, inputs)
|
6
|
+
@promise = result_promise
|
7
|
+
@inputs = inputs
|
8
|
+
@remaining = count_promises
|
9
|
+
if @remaining.zero?
|
10
|
+
promise.fulfill(inputs)
|
11
|
+
else
|
12
|
+
chain_inputs
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def chain_inputs
|
19
|
+
on_fulfill = method(:on_fulfill)
|
20
|
+
on_reject = promise.method(:reject)
|
21
|
+
each_promise do |input_promise|
|
22
|
+
input_promise.then(on_fulfill, on_reject)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_fulfill(_result)
|
27
|
+
@remaining -= 1
|
28
|
+
if @remaining.zero?
|
29
|
+
result = @inputs.map { |obj| promise?(obj) ? obj.value : obj }
|
30
|
+
promise.fulfill(result)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def promise?(obj)
|
35
|
+
obj.instance_of?(Promise)
|
36
|
+
end
|
37
|
+
|
38
|
+
def count_promises
|
39
|
+
count = 0
|
40
|
+
each_promise { count += 1 }
|
41
|
+
count
|
42
|
+
end
|
43
|
+
|
44
|
+
def each_promise
|
45
|
+
@inputs.each do |obj|
|
46
|
+
yield obj if promise?(obj)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
private_constant :Group
|
51
|
+
end
|
data/lib/promise/progress.rb
CHANGED
data/lib/promise/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -18,6 +18,8 @@ if ENV['COVERAGE'] == 'true'
|
|
18
18
|
end
|
19
19
|
|
20
20
|
require 'promise'
|
21
|
+
require_relative 'support/delayed_promise'
|
21
22
|
|
22
23
|
require 'awesome_print'
|
23
|
-
require 'devtools/spec_helper'
|
24
|
+
require 'devtools/spec_helper' if Gem.ruby_version >= Gem::Version.new('2.1')
|
25
|
+
require 'rspec/its'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class DelayedPromise < Promise
|
2
|
+
BrokenPromise = Class.new(StandardError)
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def deferred
|
6
|
+
@deferred ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def call_deferred
|
10
|
+
deferred.shift.call until deferred.empty?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def wait
|
15
|
+
DelayedPromise.call_deferred
|
16
|
+
raise BrokenPromise if pending?
|
17
|
+
end
|
18
|
+
|
19
|
+
def defer(&block)
|
20
|
+
DelayedPromise.deferred << block
|
21
|
+
end
|
22
|
+
end
|
@@ -44,7 +44,7 @@ describe Promise do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'freezes the value' do
|
47
|
-
|
47
|
+
skip 'Dropped in 74da6e9'
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -64,7 +64,7 @@ describe Promise do
|
|
64
64
|
end
|
65
65
|
|
66
66
|
it 'freezes the reason' do
|
67
|
-
|
67
|
+
skip 'Dropped in 74da6e9'
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
@@ -170,7 +170,16 @@ describe Promise do
|
|
170
170
|
|
171
171
|
describe '3.2.4' do
|
172
172
|
it 'returns before on_fulfill or on_reject is called' do
|
173
|
-
|
173
|
+
called = false
|
174
|
+
p1 = DelayedPromise.new
|
175
|
+
p2 = p1.then { called = true }
|
176
|
+
|
177
|
+
p1.fulfill(42)
|
178
|
+
|
179
|
+
expect(called).to eq(false)
|
180
|
+
DelayedPromise.call_deferred
|
181
|
+
expect(called).to eq(true)
|
182
|
+
expect(p2).to be_fulfilled
|
174
183
|
end
|
175
184
|
end
|
176
185
|
|
@@ -328,83 +337,161 @@ describe Promise do
|
|
328
337
|
end
|
329
338
|
end
|
330
339
|
|
331
|
-
describe '
|
332
|
-
|
340
|
+
describe 'extras' do
|
341
|
+
describe '#progress' do
|
342
|
+
let(:status) { double('status') }
|
333
343
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
344
|
+
it 'calls the callbacks in the order of calls to #on_progress' do
|
345
|
+
order = []
|
346
|
+
block = proc do |i, stat|
|
347
|
+
order << i
|
348
|
+
expect(stat).to eq(status)
|
349
|
+
end
|
350
|
+
|
351
|
+
subject.on_progress(&block.curry[1])
|
352
|
+
subject.on_progress(&block.curry[2])
|
353
|
+
subject.on_progress(&block.curry[3])
|
354
|
+
subject.progress(status)
|
355
|
+
|
356
|
+
expect(order).to eq([1, 2, 3])
|
339
357
|
end
|
340
358
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
359
|
+
it 'does not call back unless pending' do
|
360
|
+
called = false
|
361
|
+
subject.on_progress { |_| called = true }
|
362
|
+
subject.fulfill(value)
|
345
363
|
|
346
|
-
|
364
|
+
subject.progress(status)
|
365
|
+
expect(called).to eq(false)
|
366
|
+
end
|
347
367
|
end
|
348
368
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
369
|
+
describe '#fulfill' do
|
370
|
+
it 'does not return anything' do
|
371
|
+
expect(subject.fulfill(nil)).to eq(nil)
|
372
|
+
end
|
353
373
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
374
|
+
it 'does not require a value' do
|
375
|
+
subject.fulfill
|
376
|
+
expect(subject.value).to be(nil)
|
377
|
+
end
|
358
378
|
|
359
|
-
|
360
|
-
|
361
|
-
|
379
|
+
it 'sets the backtrace' do
|
380
|
+
subject.fulfill
|
381
|
+
expect(subject.backtrace.join)
|
382
|
+
.to include(__FILE__ + ':' + (__LINE__ - 2).to_s)
|
383
|
+
end
|
362
384
|
end
|
363
385
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
386
|
+
describe '#reject' do
|
387
|
+
it 'does not return anything' do
|
388
|
+
expect(subject.reject(nil)).to eq(nil)
|
389
|
+
end
|
368
390
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
end
|
374
|
-
end
|
391
|
+
it 'does not require a reason' do
|
392
|
+
subject.reject
|
393
|
+
expect(subject.reason).to be(Promise::Error)
|
394
|
+
end
|
375
395
|
|
376
|
-
|
377
|
-
|
378
|
-
|
396
|
+
it 'sets the backtrace' do
|
397
|
+
subject.reject
|
398
|
+
expect(subject.backtrace.join)
|
399
|
+
.to include(__FILE__ + ':' + (__LINE__ - 2).to_s)
|
400
|
+
end
|
379
401
|
end
|
380
402
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
403
|
+
describe '#sync' do
|
404
|
+
it 'waits for fulfillment' do
|
405
|
+
allow(subject).to receive(:wait) { subject.fulfill(value) }
|
406
|
+
expect(subject.sync).to be(value)
|
407
|
+
end
|
385
408
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
end
|
391
|
-
end
|
409
|
+
it 'waits for rejection' do
|
410
|
+
allow(subject).to receive(:wait) { subject.reject(reason) }
|
411
|
+
expect { subject.sync }.to raise_error(reason)
|
412
|
+
end
|
392
413
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
414
|
+
it 'waits if pending' do
|
415
|
+
subject.fulfill(value)
|
416
|
+
expect(subject).not_to receive(:wait)
|
417
|
+
expect(subject.sync).to be(value)
|
418
|
+
end
|
397
419
|
end
|
398
420
|
|
399
|
-
|
400
|
-
|
401
|
-
|
421
|
+
describe '.resolve' do
|
422
|
+
it 'returns a fulfilled promise from a non-promise' do
|
423
|
+
promise = Promise.resolve(123)
|
424
|
+
expect(promise.fulfilled?).to eq(true)
|
425
|
+
expect(promise.value).to eq(123)
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'assumes the state of a given promise' do
|
429
|
+
promise = Promise.new
|
430
|
+
new_promise = Promise.resolve(promise)
|
431
|
+
expect(new_promise.pending?).to eq(true)
|
432
|
+
promise.fulfill(42)
|
433
|
+
expect(new_promise.fulfilled?).to eq(true)
|
434
|
+
expect(new_promise.value).to eq(42)
|
435
|
+
end
|
402
436
|
end
|
403
437
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
438
|
+
describe '.all' do
|
439
|
+
it 'returns a fulfilled promise for an array with no promises' do
|
440
|
+
obj = Object.new
|
441
|
+
promise = Promise.all([1, 'b', obj])
|
442
|
+
expect(promise.fulfilled?).to eq(true)
|
443
|
+
expect(promise.value).to eq([1, 'b', obj])
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'fulfills the result when all args are fulfilled' do
|
447
|
+
p1 = Promise.new
|
448
|
+
p2 = Promise.new
|
449
|
+
|
450
|
+
result = Promise.all([p1, p2, 3])
|
451
|
+
|
452
|
+
expect(result).to be_pending
|
453
|
+
p2.fulfill('b')
|
454
|
+
expect(result).to be_pending
|
455
|
+
p1.fulfill(:a)
|
456
|
+
expect(result).to be_fulfilled
|
457
|
+
expect(result.value).to eq([:a, 'b', 3])
|
458
|
+
end
|
459
|
+
|
460
|
+
it 'leaves result pending if only the first input arg is fulfilled' do
|
461
|
+
p1 = Promise.new
|
462
|
+
p1.fulfill('a')
|
463
|
+
p2 = Promise.new
|
464
|
+
|
465
|
+
result = Promise.all([p1, p2])
|
466
|
+
|
467
|
+
expect(result).to be_pending
|
468
|
+
p2.fulfill(:b)
|
469
|
+
expect(result).to be_fulfilled
|
470
|
+
expect(result.value).to eq(['a', :b])
|
471
|
+
end
|
472
|
+
|
473
|
+
it 'rejects the result when any args is rejected' do
|
474
|
+
p1 = Promise.new
|
475
|
+
p2 = Promise.new
|
476
|
+
reason = RuntimeError.new('p1 failed')
|
477
|
+
|
478
|
+
result = Promise.all([p1, p2])
|
479
|
+
|
480
|
+
expect(result).to be_pending
|
481
|
+
p1.reject(reason)
|
482
|
+
expect(result).to be_rejected
|
483
|
+
expect(result.reason).to eq(reason)
|
484
|
+
end
|
485
|
+
|
486
|
+
it 'returns an instance of the class it is called on' do
|
487
|
+
p1 = Promise.new
|
488
|
+
|
489
|
+
result = DelayedPromise.all([p1, 2])
|
490
|
+
|
491
|
+
expect(result).to be_an_instance_of(DelayedPromise)
|
492
|
+
p1.fulfill(1.0)
|
493
|
+
expect(result.sync).to eq([1.0, 2])
|
494
|
+
end
|
408
495
|
end
|
409
496
|
end
|
410
497
|
end
|
metadata
CHANGED
@@ -1,30 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: promise.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.7.0.rc1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Lars Gierth
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2016-02-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
description: Promises/A+ for Ruby
|
@@ -34,12 +31,11 @@ executables: []
|
|
34
31
|
extensions: []
|
35
32
|
extra_rdoc_files: []
|
36
33
|
files:
|
37
|
-
- .gitignore
|
38
|
-
- .rspec
|
39
|
-
- .travis.yml
|
34
|
+
- ".gitignore"
|
35
|
+
- ".rspec"
|
36
|
+
- ".travis.yml"
|
40
37
|
- CHANGELOG.md
|
41
38
|
- Gemfile
|
42
|
-
- Gemfile.devtools
|
43
39
|
- README.md
|
44
40
|
- Rakefile
|
45
41
|
- UNLICENSE
|
@@ -52,37 +48,39 @@ files:
|
|
52
48
|
- config/yardstick.yml
|
53
49
|
- lib/promise.rb
|
54
50
|
- lib/promise/callback.rb
|
51
|
+
- lib/promise/group.rb
|
55
52
|
- lib/promise/progress.rb
|
56
53
|
- lib/promise/version.rb
|
57
54
|
- promise.rb.gemspec
|
58
|
-
- spec/promise_spec.rb
|
59
55
|
- spec/spec_helper.rb
|
56
|
+
- spec/support/delayed_promise.rb
|
57
|
+
- spec/unit/promise_spec.rb
|
60
58
|
homepage: https://github.com/lgierth/promise
|
61
59
|
licenses:
|
62
60
|
- Public Domain
|
61
|
+
metadata: {}
|
63
62
|
post_install_message:
|
64
63
|
rdoc_options: []
|
65
64
|
require_paths:
|
66
65
|
- lib
|
67
66
|
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
-
none: false
|
69
67
|
requirements:
|
70
|
-
- -
|
68
|
+
- - ">="
|
71
69
|
- !ruby/object:Gem::Version
|
72
70
|
version: '0'
|
73
71
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
-
none: false
|
75
72
|
requirements:
|
76
|
-
- -
|
73
|
+
- - ">"
|
77
74
|
- !ruby/object:Gem::Version
|
78
|
-
version:
|
75
|
+
version: 1.3.1
|
79
76
|
requirements: []
|
80
77
|
rubyforge_project:
|
81
|
-
rubygems_version:
|
78
|
+
rubygems_version: 2.2.5
|
82
79
|
signing_key:
|
83
|
-
specification_version:
|
80
|
+
specification_version: 4
|
84
81
|
summary: Ruby implementation of the Promises/A+ spec
|
85
82
|
test_files:
|
86
|
-
- spec/promise_spec.rb
|
87
83
|
- spec/spec_helper.rb
|
84
|
+
- spec/support/delayed_promise.rb
|
85
|
+
- spec/unit/promise_spec.rb
|
88
86
|
has_rdoc:
|
data/Gemfile.devtools
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
group :development do
|
4
|
-
gem 'rake', '~> 10.1.0'
|
5
|
-
gem 'rspec', '~> 2.14.1'
|
6
|
-
gem 'yard', '~> 0.8.7'
|
7
|
-
|
8
|
-
platform :rbx do
|
9
|
-
gem 'rubysl-singleton', '~> 2.0.0'
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
group :yard do
|
14
|
-
gem 'kramdown', '~> 1.3.0'
|
15
|
-
end
|
16
|
-
|
17
|
-
group :guard do
|
18
|
-
gem 'guard', '~> 2.2.4'
|
19
|
-
gem 'guard-bundler', '~> 2.0.0'
|
20
|
-
gem 'guard-rspec', '~> 4.2.0'
|
21
|
-
gem 'guard-rubocop', '~> 1.0.0'
|
22
|
-
|
23
|
-
# file system change event handling
|
24
|
-
gem 'listen', '~> 2.4.0'
|
25
|
-
gem 'rb-fchange', '~> 0.0.6', require: false
|
26
|
-
gem 'rb-fsevent', '~> 0.9.3', require: false
|
27
|
-
gem 'rb-inotify', '~> 0.9.0', require: false
|
28
|
-
|
29
|
-
# notification handling
|
30
|
-
gem 'libnotify', '~> 0.8.0', require: false
|
31
|
-
gem 'rb-notifu', '~> 0.0.4', require: false
|
32
|
-
gem 'terminal-notifier-guard', '~> 1.5.3', require: false
|
33
|
-
end
|
34
|
-
|
35
|
-
group :metrics do
|
36
|
-
gem 'coveralls', '~> 0.7.0'
|
37
|
-
gem 'flay', '~> 2.4.0'
|
38
|
-
gem 'flog', '~> 4.2.0'
|
39
|
-
gem 'reek', '~> 1.3.2'
|
40
|
-
gem 'rubocop', '~> 0.16.0'
|
41
|
-
gem 'simplecov', '~> 0.8.2'
|
42
|
-
gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
|
43
|
-
|
44
|
-
platforms :mri do
|
45
|
-
gem 'mutant', '~> 0.3.4'
|
46
|
-
end
|
47
|
-
|
48
|
-
platforms :ruby_19, :ruby_20 do
|
49
|
-
gem 'yard-spellcheck', '~> 0.1.5'
|
50
|
-
end
|
51
|
-
|
52
|
-
platform :rbx do
|
53
|
-
gem 'json', '~> 1.8.1'
|
54
|
-
gem 'racc', '~> 1.4.10'
|
55
|
-
gem 'rubysl-logger', '~> 2.0.0'
|
56
|
-
gem 'rubysl-open-uri', '~> 2.0.0'
|
57
|
-
gem 'rubysl-prettyprint', '~> 2.0.2'
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
group :benchmarks do
|
62
|
-
gem 'rbench', '~> 0.2.3'
|
63
|
-
end
|
64
|
-
|
65
|
-
platform :jruby do
|
66
|
-
group :jruby do
|
67
|
-
gem 'jruby-openssl', '~> 0.8.5'
|
68
|
-
end
|
69
|
-
end
|