promise.rb 0.7.1 → 0.7.2
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 +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +8 -1
- data/Gemfile +7 -1
- data/README.md +1 -1
- data/config/reek.yml +2 -2
- data/config/rubocop.yml +3 -0
- data/lib/promise.rb +43 -12
- data/lib/promise/callback.rb +23 -19
- data/lib/promise/group.rb +9 -1
- data/lib/promise/version.rb +1 -1
- data/promise.rb.gemspec +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/promise_loader.rb +13 -0
- data/spec/unit/promise_spec.rb +76 -0
- metadata +11 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1c5730ef1d6a9d4aca9152d2d0b0978d3599bf9
|
4
|
+
data.tar.gz: 7bf7f65107907530efb5befd130d8257c95b1ebd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b91773b745efae988afbcdd56080fac09398f72e781266e92b755128a3f606fc09f63299849a729c2b6e1ced9cc1cdc46581beb6880949c7ea2748516994cff3
|
7
|
+
data.tar.gz: 7ca9447a4c2766299ccc9d941dd2a25d68a65a1626ff3407726966ed85157bc0c25873bf53c5eecd2a6e683c093a6217f354be5712c38602d5d02f7e7f8bb3c5
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
# promise.rb changelog
|
2
2
|
|
3
|
+
## 0.7.2 (November 15, 2016)
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
* Add support for calling sync on the result of Promise.all (pull #24)
|
8
|
+
* Add Promise.sync to unwrap an object that may be a promise. (#25)
|
9
|
+
|
3
10
|
## 0.7.1 (June 15, 2016)
|
4
11
|
|
5
12
|
### Features
|
6
13
|
|
7
|
-
* Add Promise.map_value for chaining a promise or plain value
|
14
|
+
* Add Promise.map_value for chaining a promise or plain value (pull #17)
|
8
15
|
|
9
16
|
## 0.7.0 (February 24, 2016)
|
10
17
|
|
data/Gemfile
CHANGED
@@ -4,6 +4,12 @@ source 'https://rubygems.org'
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
+
if Gem.ruby_version < Gem::Version.new('2.0')
|
8
|
+
# gems that no longer support ruby 1.9.3
|
9
|
+
gem 'json', '~> 1.8.3'
|
10
|
+
gem 'tins', '~> 1.6.0'
|
11
|
+
gem 'term-ansicolor', '~> 1.3.2'
|
12
|
+
end
|
7
13
|
if Gem.ruby_version >= Gem::Version.new('2.1')
|
8
14
|
gem 'devtools', '~> 0.1.4'
|
9
15
|
end
|
@@ -11,7 +17,7 @@ gem 'fuubar', '~> 2.0.0'
|
|
11
17
|
gem 'awesome_print'
|
12
18
|
|
13
19
|
gem 'rake'
|
14
|
-
gem 'rspec', '~> 3.
|
20
|
+
gem 'rspec', '~> 3.5'
|
15
21
|
gem 'rspec-its'
|
16
22
|
gem 'coveralls', '~> 0.8.9'
|
17
23
|
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ tested on MRI 1.9, 2.0, 2.1, 2.2, Rubinius, and JRuby.
|
|
6
6
|
|
7
7
|
Similar projects:
|
8
8
|
|
9
|
-
- [concurrent-ruby](https://github.com/jdantonio/concurrent-ruby), Promises/A(+) implementation, thread based
|
9
|
+
- [concurrent-ruby](https://github.com/jdantonio/concurrent-ruby), Promises/A(+) inspired implementation, thread based
|
10
10
|
- [ruby-thread](https://github.com/meh/ruby-thread), thread/mutex/condition variable based, thread safe
|
11
11
|
- [promise](https://github.com/bhuga/promising-future), a.k.a. promising-future, classic promises and futures, thread based
|
12
12
|
- [celluloid-promise](https://github.com/cotag/celluloid-promise), inspired by Q, backed by a Celluloid actor
|
data/config/reek.yml
CHANGED
@@ -52,7 +52,7 @@ NilCheck:
|
|
52
52
|
RepeatedConditional:
|
53
53
|
enabled: true
|
54
54
|
exclude: []
|
55
|
-
max_ifs:
|
55
|
+
max_ifs: 4
|
56
56
|
TooManyInstanceVariables:
|
57
57
|
enabled: true
|
58
58
|
exclude: []
|
@@ -64,7 +64,7 @@ TooManyStatements:
|
|
64
64
|
exclude:
|
65
65
|
- initialize
|
66
66
|
- each
|
67
|
-
max_statements:
|
67
|
+
max_statements: 6
|
68
68
|
UncommunicativeMethodName:
|
69
69
|
enabled: true
|
70
70
|
exclude: []
|
data/config/rubocop.yml
CHANGED
data/lib/promise.rb
CHANGED
@@ -8,9 +8,11 @@ require 'promise/group'
|
|
8
8
|
|
9
9
|
class Promise
|
10
10
|
Error = Class.new(RuntimeError)
|
11
|
+
BrokenError = Class.new(Error)
|
11
12
|
|
12
13
|
include Promise::Progress
|
13
14
|
|
15
|
+
attr_accessor :source
|
14
16
|
attr_reader :state, :value, :reason
|
15
17
|
|
16
18
|
def self.resolve(obj)
|
@@ -30,6 +32,10 @@ class Promise
|
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
35
|
+
def self.sync(obj)
|
36
|
+
obj.is_a?(Promise) ? obj.sync : obj
|
37
|
+
end
|
38
|
+
|
33
39
|
def initialize
|
34
40
|
@state = :pending
|
35
41
|
@callbacks = []
|
@@ -51,7 +57,7 @@ class Promise
|
|
51
57
|
on_fulfill ||= block
|
52
58
|
next_promise = self.class.new
|
53
59
|
|
54
|
-
add_callback(Callback.new(
|
60
|
+
add_callback(Callback.new(on_fulfill, on_reject, next_promise))
|
55
61
|
next_promise
|
56
62
|
end
|
57
63
|
|
@@ -61,17 +67,21 @@ class Promise
|
|
61
67
|
alias_method :catch, :rescue
|
62
68
|
|
63
69
|
def sync
|
64
|
-
|
70
|
+
if pending?
|
71
|
+
wait
|
72
|
+
raise BrokenError if pending?
|
73
|
+
end
|
65
74
|
raise reason if rejected?
|
66
75
|
value
|
67
76
|
end
|
68
77
|
|
69
78
|
def fulfill(value = nil)
|
70
79
|
if Promise === value
|
71
|
-
|
80
|
+
value.add_callback(self)
|
72
81
|
else
|
73
82
|
dispatch do
|
74
83
|
@state = :fulfilled
|
84
|
+
@source = nil
|
75
85
|
@value = value
|
76
86
|
end
|
77
87
|
end
|
@@ -81,14 +91,37 @@ class Promise
|
|
81
91
|
def reject(reason = nil)
|
82
92
|
dispatch do
|
83
93
|
@state = :rejected
|
94
|
+
@source = nil
|
84
95
|
@reason = reason_coercion(reason || Error)
|
85
96
|
end
|
86
97
|
end
|
87
98
|
|
99
|
+
# Override to support sync on a promise without a source or to wait
|
100
|
+
# for deferred callbacks on the source
|
101
|
+
def wait
|
102
|
+
while source
|
103
|
+
saved_source = source
|
104
|
+
saved_source.wait
|
105
|
+
break if saved_source.equal?(source)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
# Override to defer calling the callback for Promises/A+ spec compliance
|
88
112
|
def defer
|
89
113
|
yield
|
90
114
|
end
|
91
115
|
|
116
|
+
def add_callback(callback)
|
117
|
+
if pending?
|
118
|
+
@callbacks << callback
|
119
|
+
callback.source = self
|
120
|
+
else
|
121
|
+
dispatch!(callback)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
92
125
|
private
|
93
126
|
|
94
127
|
def reason_coercion(reason)
|
@@ -101,14 +134,6 @@ class Promise
|
|
101
134
|
reason
|
102
135
|
end
|
103
136
|
|
104
|
-
def add_callback(callback)
|
105
|
-
if pending?
|
106
|
-
@callbacks << callback
|
107
|
-
else
|
108
|
-
dispatch!(callback)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
137
|
def dispatch
|
113
138
|
if pending?
|
114
139
|
yield
|
@@ -118,6 +143,12 @@ class Promise
|
|
118
143
|
end
|
119
144
|
|
120
145
|
def dispatch!(callback)
|
121
|
-
defer
|
146
|
+
defer do
|
147
|
+
if fulfilled?
|
148
|
+
callback.fulfill(value)
|
149
|
+
else
|
150
|
+
callback.reject(reason)
|
151
|
+
end
|
152
|
+
end
|
122
153
|
end
|
123
154
|
end
|
data/lib/promise/callback.rb
CHANGED
@@ -2,38 +2,42 @@
|
|
2
2
|
|
3
3
|
class Promise
|
4
4
|
class Callback
|
5
|
-
|
6
|
-
on_fulfill = target.method(:fulfill)
|
7
|
-
on_reject = target.method(:reject)
|
8
|
-
source.then(on_fulfill, on_reject)
|
9
|
-
end
|
5
|
+
attr_accessor :source
|
10
6
|
|
11
|
-
def initialize(
|
12
|
-
@promise = promise
|
7
|
+
def initialize(on_fulfill, on_reject, next_promise)
|
13
8
|
@on_fulfill = on_fulfill
|
14
9
|
@on_reject = on_reject
|
15
10
|
@next_promise = next_promise
|
11
|
+
@next_promise.source = self
|
16
12
|
end
|
17
13
|
|
18
|
-
def
|
19
|
-
if @
|
20
|
-
call_block(@on_fulfill,
|
14
|
+
def fulfill(value)
|
15
|
+
if @on_fulfill
|
16
|
+
call_block(@on_fulfill, value)
|
21
17
|
else
|
22
|
-
|
18
|
+
@next_promise.fulfill(value)
|
23
19
|
end
|
24
20
|
end
|
25
21
|
|
26
|
-
def
|
27
|
-
if
|
28
|
-
|
29
|
-
@next_promise.fulfill(block.call(param))
|
30
|
-
rescue => ex
|
31
|
-
@next_promise.reject(ex)
|
32
|
-
end
|
22
|
+
def reject(reason)
|
23
|
+
if @on_reject
|
24
|
+
call_block(@on_reject, reason)
|
33
25
|
else
|
34
|
-
|
26
|
+
@next_promise.reject(reason)
|
35
27
|
end
|
36
28
|
end
|
29
|
+
|
30
|
+
def wait
|
31
|
+
source.wait
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def call_block(block, param)
|
37
|
+
@next_promise.fulfill(block.call(param))
|
38
|
+
rescue => ex
|
39
|
+
@next_promise.reject(ex)
|
40
|
+
end
|
37
41
|
end
|
38
42
|
private_constant :Callback
|
39
43
|
end
|
data/lib/promise/group.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
class Promise
|
2
2
|
class Group
|
3
|
+
attr_accessor :source
|
3
4
|
attr_reader :promise
|
4
5
|
|
5
6
|
def initialize(result_promise, inputs)
|
@@ -9,15 +10,22 @@ class Promise
|
|
9
10
|
if @remaining.zero?
|
10
11
|
promise.fulfill(inputs)
|
11
12
|
else
|
13
|
+
promise.source = self
|
12
14
|
chain_inputs
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
18
|
+
def wait
|
19
|
+
each_promise do |input_promise|
|
20
|
+
input_promise.wait if input_promise.pending?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
16
24
|
private
|
17
25
|
|
18
26
|
def chain_inputs
|
19
27
|
on_fulfill = method(:on_fulfill)
|
20
|
-
on_reject = promise.
|
28
|
+
on_reject = promise.public_method(:reject)
|
21
29
|
each_promise do |input_promise|
|
22
30
|
input_promise.then(on_fulfill, on_reject)
|
23
31
|
end
|
data/lib/promise/version.rb
CHANGED
data/promise.rb.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'promise/version'
|
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'promise.rb'
|
9
|
-
spec.version = Promise::VERSION
|
9
|
+
spec.version = Promise::VERSION.dup
|
10
10
|
spec.authors = ['Lars Gierth']
|
11
11
|
spec.email = ['lars.gierth@gmail.com']
|
12
12
|
spec.description = %q{Promises/A+ for Ruby}
|
data/spec/spec_helper.rb
CHANGED
data/spec/unit/promise_spec.rb
CHANGED
@@ -476,6 +476,58 @@ describe Promise do
|
|
476
476
|
expect(subject).not_to receive(:wait)
|
477
477
|
expect(subject.sync).to be(value)
|
478
478
|
end
|
479
|
+
|
480
|
+
it 'waits for source by default' do
|
481
|
+
PromiseLoader.lazy_load(subject) { subject.fulfill(1) }
|
482
|
+
p2 = subject.then { |v| v + 1 }
|
483
|
+
expect(p2).to be_pending
|
484
|
+
expect(p2.sync).to eq(2)
|
485
|
+
expect(p2.source).to eq(nil)
|
486
|
+
end
|
487
|
+
|
488
|
+
it 'waits for source that is fulfilled with a promise' do
|
489
|
+
PromiseLoader.lazy_load(subject) { subject.fulfill(1) }
|
490
|
+
p2 = subject.then do |v|
|
491
|
+
Promise.new.tap do |p3|
|
492
|
+
PromiseLoader.lazy_load(p3) { p3.fulfill(v + 1) }
|
493
|
+
end
|
494
|
+
end
|
495
|
+
expect(p2).to be_pending
|
496
|
+
expect(p2.sync).to eq(2)
|
497
|
+
expect(p2.source).to eq(nil)
|
498
|
+
end
|
499
|
+
|
500
|
+
it 'waits for source rejection' do
|
501
|
+
PromiseLoader.lazy_load(subject) { subject.reject(reason) }
|
502
|
+
p2 = subject.then { |v| v + 1 }
|
503
|
+
expect { p2.sync }.to raise_error(reason)
|
504
|
+
expect(p2.source).to eq(nil)
|
505
|
+
end
|
506
|
+
|
507
|
+
it 'raises for promise without a source by default' do
|
508
|
+
expect { subject.sync }.to raise_error(Promise::BrokenError)
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'raises if source.wait leaves promise pending' do
|
512
|
+
PromiseLoader.lazy_load(subject) {}
|
513
|
+
expect { subject.sync }.to raise_error(Promise::BrokenError)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
describe '.sync' do
|
518
|
+
it 'returns non-promise argument' do
|
519
|
+
expect(Promise.sync(42)).to eq(42)
|
520
|
+
end
|
521
|
+
|
522
|
+
it 'calls sync on promise argument' do
|
523
|
+
PromiseLoader.lazy_load(subject) { subject.fulfill(123) }
|
524
|
+
expect(Promise.sync(subject)).to eq(123)
|
525
|
+
end
|
526
|
+
|
527
|
+
it 'calls sync on promise of another class' do
|
528
|
+
promise = Class.new(Promise).resolve('a')
|
529
|
+
expect(Class.new(Promise).sync(promise)).to eq('a')
|
530
|
+
end
|
479
531
|
end
|
480
532
|
|
481
533
|
describe '.resolve' do
|
@@ -575,6 +627,30 @@ describe Promise do
|
|
575
627
|
p1.fulfill(1.0)
|
576
628
|
expect(result.sync).to eq([1.0, 2])
|
577
629
|
end
|
630
|
+
|
631
|
+
it 'returns a promise that can sync promises of another class' do
|
632
|
+
p1 = DelayedPromise.new
|
633
|
+
DelayedPromise.deferred << -> { p1.fulfill('a') }
|
634
|
+
|
635
|
+
result = Promise.all([p1, Promise.resolve(:b), 3])
|
636
|
+
|
637
|
+
expect(result).to be_pending
|
638
|
+
expect(result.sync).to eq(['a', :b, 3])
|
639
|
+
end
|
640
|
+
|
641
|
+
it 'sync on result does not call wait on resolved promises' do
|
642
|
+
p1 = Class.new(Promise) do
|
643
|
+
def wait
|
644
|
+
raise 'wait not expected'
|
645
|
+
end
|
646
|
+
end.resolve(:one)
|
647
|
+
p2 = DelayedPromise.new
|
648
|
+
DelayedPromise.deferred << -> { p2.fulfill(:two) }
|
649
|
+
|
650
|
+
result = Promise.all([p1, p2])
|
651
|
+
|
652
|
+
expect(result.sync).to eq([:one, :two])
|
653
|
+
end
|
578
654
|
end
|
579
655
|
|
580
656
|
describe '.map_value' do
|
metadata
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: promise.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Gierth
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - ">="
|
17
18
|
- !ruby/object:Gem::Version
|
18
19
|
version: '0'
|
19
|
-
name: rspec
|
20
|
-
prerelease: false
|
21
20
|
type: :development
|
21
|
+
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
@@ -54,12 +54,13 @@ files:
|
|
54
54
|
- promise.rb.gemspec
|
55
55
|
- spec/spec_helper.rb
|
56
56
|
- spec/support/delayed_promise.rb
|
57
|
+
- spec/support/promise_loader.rb
|
57
58
|
- spec/unit/promise_spec.rb
|
58
59
|
homepage: https://github.com/lgierth/promise
|
59
60
|
licenses:
|
60
61
|
- Public Domain
|
61
62
|
metadata: {}
|
62
|
-
post_install_message:
|
63
|
+
post_install_message:
|
63
64
|
rdoc_options: []
|
64
65
|
require_paths:
|
65
66
|
- lib
|
@@ -74,13 +75,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
75
|
- !ruby/object:Gem::Version
|
75
76
|
version: '0'
|
76
77
|
requirements: []
|
77
|
-
rubyforge_project:
|
78
|
-
rubygems_version: 2.
|
79
|
-
signing_key:
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.5.1
|
80
|
+
signing_key:
|
80
81
|
specification_version: 4
|
81
82
|
summary: Ruby implementation of the Promises/A+ spec
|
82
83
|
test_files:
|
83
84
|
- spec/spec_helper.rb
|
84
85
|
- spec/support/delayed_promise.rb
|
86
|
+
- spec/support/promise_loader.rb
|
85
87
|
- spec/unit/promise_spec.rb
|
86
|
-
has_rdoc:
|