functional-ruby 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.
- checksums.yaml +4 -4
- data/README.md +154 -562
- data/lib/functional/agent.rb +130 -0
- data/lib/functional/all.rb +9 -1
- data/lib/functional/behavior.rb +72 -39
- data/lib/functional/cached_thread_pool.rb +122 -0
- data/lib/functional/concurrency.rb +32 -24
- data/lib/functional/core.rb +2 -62
- data/lib/functional/event.rb +53 -0
- data/lib/functional/event_machine_defer_proxy.rb +23 -0
- data/lib/functional/fixed_thread_pool.rb +89 -0
- data/lib/functional/future.rb +42 -0
- data/lib/functional/global_thread_pool.rb +3 -0
- data/lib/functional/obligation.rb +121 -0
- data/lib/functional/promise.rb +194 -0
- data/lib/functional/thread_pool.rb +61 -0
- data/lib/functional/utilities.rb +114 -0
- data/lib/functional/version.rb +1 -1
- data/lib/functional.rb +1 -0
- data/lib/functional_ruby.rb +1 -0
- data/md/behavior.md +147 -0
- data/md/concurrency.md +465 -0
- data/md/future.md +32 -0
- data/md/obligation.md +32 -0
- data/md/pattern_matching.md +512 -0
- data/md/promise.md +220 -0
- data/md/utilities.md +53 -0
- data/spec/functional/agent_spec.rb +405 -0
- data/spec/functional/behavior_spec.rb +12 -33
- data/spec/functional/cached_thread_pool_spec.rb +112 -0
- data/spec/functional/concurrency_spec.rb +55 -0
- data/spec/functional/event_machine_defer_proxy_spec.rb +246 -0
- data/spec/functional/event_spec.rb +114 -0
- data/spec/functional/fixed_thread_pool_spec.rb +84 -0
- data/spec/functional/future_spec.rb +115 -0
- data/spec/functional/obligation_shared.rb +121 -0
- data/spec/functional/pattern_matching_spec.rb +10 -8
- data/spec/functional/promise_spec.rb +310 -0
- data/spec/functional/thread_pool_shared.rb +209 -0
- data/spec/functional/utilities_spec.rb +149 -0
- data/spec/spec_helper.rb +2 -0
- metadata +55 -5
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'obligation_shared'
|
3
|
+
|
4
|
+
module Functional
|
5
|
+
|
6
|
+
describe Future do
|
7
|
+
|
8
|
+
let!(:fulfilled_value) { 10 }
|
9
|
+
let!(:rejected_reason) { StandardError.new('mojo jojo') }
|
10
|
+
|
11
|
+
let(:pending_subject) do
|
12
|
+
Future.new{ sleep(2) }
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:fulfilled_subject) do
|
16
|
+
Future.new{ fulfilled_value }.tap(){ sleep(0.1) }
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:rejected_subject) do
|
20
|
+
Future.new{ raise rejected_reason }.tap(){ sleep(0.1) }
|
21
|
+
end
|
22
|
+
|
23
|
+
before(:each) do
|
24
|
+
$GLOBAL_THREAD_POOL = CachedThreadPool.new
|
25
|
+
end
|
26
|
+
|
27
|
+
it_should_behave_like Obligation
|
28
|
+
|
29
|
+
context 'behavior' do
|
30
|
+
|
31
|
+
it 'implements :future behavior' do
|
32
|
+
lambda {
|
33
|
+
Future.new{ nil }
|
34
|
+
}.should_not raise_error(BehaviorError)
|
35
|
+
|
36
|
+
Future.new{ nil }.behaves_as?(:future).should be_true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context '#initialize' do
|
41
|
+
|
42
|
+
it 'spawns a new thread when a block is given' do
|
43
|
+
$GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
|
44
|
+
Future.new{ nil }
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not spawns a new thread when no block given' do
|
48
|
+
Thread.should_not_receive(:new).with(any_args())
|
49
|
+
Future.new
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'immediately sets the state to :fulfilled when no block given' do
|
53
|
+
Future.new.should be_fulfilled
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'immediately sets the value to nil when no block given' do
|
57
|
+
Future.new.value.should be_nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'fulfillment' do
|
62
|
+
|
63
|
+
it 'passes all arguments to handler' do
|
64
|
+
@a = @b = @c = nil
|
65
|
+
f = Future.new(1, 2, 3) do |a, b, c|
|
66
|
+
@a, @b, @c = a, b, c
|
67
|
+
end
|
68
|
+
sleep(0.1)
|
69
|
+
[@a, @b, @c].should eq [1, 2, 3]
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'sets the value to the result of the handler' do
|
73
|
+
f = Future.new(10){|a| a * 2 }
|
74
|
+
sleep(0.1)
|
75
|
+
f.value.should eq 20
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'sets the state to :fulfilled when the block completes' do
|
79
|
+
f = Future.new(10){|a| a * 2 }
|
80
|
+
sleep(0.1)
|
81
|
+
f.should be_fulfilled
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'sets the value to nil when the handler raises an exception' do
|
85
|
+
f = Future.new{ raise StandardError }
|
86
|
+
sleep(0.1)
|
87
|
+
f.value.should be_nil
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'sets the state to :rejected when the handler raises an exception' do
|
91
|
+
f = Future.new{ raise StandardError }
|
92
|
+
sleep(0.1)
|
93
|
+
f.should be_rejected
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'aliases' do
|
97
|
+
|
98
|
+
it 'aliases #realized? for #fulfilled?' do
|
99
|
+
fulfilled_subject.should be_realized
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'aliases #deref for #value' do
|
103
|
+
fulfilled_subject.deref.should eq fulfilled_value
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'aliases Kernel#future for Future.new' do
|
107
|
+
future().should be_a(Future)
|
108
|
+
future(){ nil }.should be_a(Future)
|
109
|
+
future(1, 2, 3).should be_a(Future)
|
110
|
+
future(1, 2, 3){ nil }.should be_a(Future)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Functional
|
4
|
+
|
5
|
+
share_examples_for Obligation do
|
6
|
+
|
7
|
+
context '#state' do
|
8
|
+
|
9
|
+
it 'is :pending when first created' do
|
10
|
+
f = pending_subject
|
11
|
+
f.state.should == :pending
|
12
|
+
f.should be_pending
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'is :fulfilled when the handler completes' do
|
16
|
+
f = fulfilled_subject
|
17
|
+
f.state.should == :fulfilled
|
18
|
+
f.should be_fulfilled
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'is :rejected when the handler raises an exception' do
|
22
|
+
f = rejected_subject
|
23
|
+
f.state.should == :rejected
|
24
|
+
f.should be_rejected
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context '#value' do
|
29
|
+
|
30
|
+
it 'blocks the caller when :pending and timeout is nil' do
|
31
|
+
f = pending_subject
|
32
|
+
sleep(0.1)
|
33
|
+
f.value.should be_true
|
34
|
+
f.should be_fulfilled
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns nil when reaching the optional timeout value' do
|
38
|
+
f = pending_subject
|
39
|
+
sleep(0.1)
|
40
|
+
f.value(0.1).should be_nil
|
41
|
+
f.should be_pending
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns immediately when timeout is zero' do
|
45
|
+
Timeout.should_not_receive(:timeout).with(any_args())
|
46
|
+
f = pending_subject
|
47
|
+
sleep(0.1)
|
48
|
+
f.value(0).should be_nil
|
49
|
+
f.should be_pending
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'is nil when :pending' do
|
53
|
+
expected = pending_subject.value
|
54
|
+
expected.should be_nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'is nil when :rejected' do
|
58
|
+
expected = rejected_subject.value
|
59
|
+
expected.should be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'is set to the return value of the block when :fulfilled' do
|
63
|
+
expected = fulfilled_subject.value
|
64
|
+
expected.should eq fulfilled_value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context '#reason' do
|
69
|
+
|
70
|
+
it 'is nil when :pending' do
|
71
|
+
pending_subject.reason.should be_nil
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'is nil when :fulfilled' do
|
75
|
+
fulfilled_subject.reason.should be_nil
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'is set to error object of the exception when :rejected' do
|
79
|
+
rejected_subject.reason.should be_a(Exception)
|
80
|
+
rejected_subject.reason.to_s.should =~ /#{rejected_reason}/
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'Kernel aliases' do
|
85
|
+
|
86
|
+
it 'aliases Kernel#deref for #deref' do
|
87
|
+
deref(fulfilled_subject).should eq fulfilled_value
|
88
|
+
deref(fulfilled_subject, 0).should eq fulfilled_value
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'aliases Kernel#pending? for #pending?' do
|
92
|
+
#NOTE: was structured like others but was incorrectly failing
|
93
|
+
# on fulfilled_subject
|
94
|
+
fulfilled_subject.should_receive(:pending?).once
|
95
|
+
pending?(fulfilled_subject)
|
96
|
+
pending_subject.should_receive(:pending?).once
|
97
|
+
pending?(pending_subject)
|
98
|
+
rejected_subject.should_receive(:pending?).once
|
99
|
+
pending?(rejected_subject)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'aliases Kernel#fulfilled? for #fulfilled?' do
|
103
|
+
fulfilled?(fulfilled_subject).should be_true
|
104
|
+
fulfilled?(pending_subject).should be_false
|
105
|
+
fulfilled?(rejected_subject).should be_false
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'aliases Kernel#realized? for #realized?' do
|
109
|
+
realized?(fulfilled_subject).should be_true
|
110
|
+
realized?(pending_subject).should be_false
|
111
|
+
realized?(rejected_subject).should be_false
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'aliases Kernel#rejected? for #rejected?' do
|
115
|
+
rejected?(rejected_subject).should be_true
|
116
|
+
rejected?(fulfilled_subject).should be_false
|
117
|
+
rejected?(pending_subject).should be_false
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -44,14 +44,16 @@ describe PatternMatching do
|
|
44
44
|
|
45
45
|
it 'can pattern match the constructor' do
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
unless RUBY_VERSION = '1.9.2'
|
48
|
+
subject.defn(:initialize, PatternMatching::UNBOUND, PatternMatching::UNBOUND, PatternMatching::UNBOUND) { 'three args' }
|
49
|
+
subject.defn(:initialize, PatternMatching::UNBOUND, PatternMatching::UNBOUND) { 'two args' }
|
50
|
+
subject.defn(:initialize, PatternMatching::UNBOUND) { 'one arg' }
|
51
|
+
|
52
|
+
lambda { subject.new(1) }.should_not raise_error
|
53
|
+
lambda { subject.new(1, 2) }.should_not raise_error
|
54
|
+
lambda { subject.new(1, 2, 3) }.should_not raise_error
|
55
|
+
lambda { subject.new(1, 2, 3, 4) }.should raise_error
|
56
|
+
end
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
@@ -0,0 +1,310 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'obligation_shared'
|
3
|
+
|
4
|
+
module Functional
|
5
|
+
|
6
|
+
describe Promise do
|
7
|
+
|
8
|
+
let!(:fulfilled_value) { 10 }
|
9
|
+
let!(:rejected_reason) { StandardError.new('mojo jojo') }
|
10
|
+
|
11
|
+
let(:pending_subject) do
|
12
|
+
Promise.new{ sleep(1) }
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:fulfilled_subject) do
|
16
|
+
Promise.new{ fulfilled_value }.tap(){ sleep(0.1) }
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:rejected_subject) do
|
20
|
+
Promise.new{ raise rejected_reason }.
|
21
|
+
rescue{ nil }.tap(){ sleep(0.1) }
|
22
|
+
end
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
$GLOBAL_THREAD_POOL = CachedThreadPool.new
|
26
|
+
end
|
27
|
+
|
28
|
+
it_should_behave_like Obligation
|
29
|
+
|
30
|
+
context 'behavior' do
|
31
|
+
|
32
|
+
it 'implements :promise behavior' do
|
33
|
+
lambda {
|
34
|
+
Promise.new{ nil }
|
35
|
+
}.should_not raise_error(BehaviorError)
|
36
|
+
|
37
|
+
Promise.new{ nil }.behaves_as?(:promise).should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'implements :future behavior' do
|
41
|
+
lambda {
|
42
|
+
Promise.new{ nil }
|
43
|
+
}.should_not raise_error(BehaviorError)
|
44
|
+
|
45
|
+
Promise.new{ nil }.behaves_as?(:future).should be_true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context '#then' do
|
50
|
+
|
51
|
+
it 'returns a new Promise when :pending' do
|
52
|
+
p1 = pending_subject
|
53
|
+
p2 = p1.then{}
|
54
|
+
p2.should be_a(Promise)
|
55
|
+
p1.should_not eq p2
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns a new Promise when :fulfilled' do
|
59
|
+
p1 = fulfilled_subject
|
60
|
+
p2 = p1.then{}
|
61
|
+
p2.should be_a(Promise)
|
62
|
+
p1.should_not eq p2
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns a new Promise when :rejected' do
|
66
|
+
p1 = rejected_subject
|
67
|
+
p2 = p1.then{}
|
68
|
+
p2.should be_a(Promise)
|
69
|
+
p1.should_not eq p2
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'immediately rejects new promises when self has been rejected' do
|
73
|
+
p = rejected_subject
|
74
|
+
p.then.should be_rejected
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'accepts a nil block' do
|
78
|
+
lambda {
|
79
|
+
pending_subject.then
|
80
|
+
}.should_not raise_error
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'can be called more than once' do
|
84
|
+
p = pending_subject
|
85
|
+
p1 = p.then{}
|
86
|
+
p2 = p.then{}
|
87
|
+
p1.object_id.should_not eq p2.object_id
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context '#rescue' do
|
92
|
+
|
93
|
+
it 'returns self when a block is given' do
|
94
|
+
p1 = pending_subject
|
95
|
+
p2 = p1.rescue{}
|
96
|
+
p1.object_id.should eq p2.object_id
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'returns self when no block is given' do
|
100
|
+
p1 = pending_subject
|
101
|
+
p2 = p1.rescue
|
102
|
+
p1.object_id.should eq p2.object_id
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'accepts an exception class as the first parameter' do
|
106
|
+
lambda {
|
107
|
+
pending_subject.rescue(StandardError){}
|
108
|
+
}.should_not raise_error
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'fulfillment' do
|
113
|
+
|
114
|
+
it 'passes all arguments to the first promise in the chain' do
|
115
|
+
@a = @b = @c = nil
|
116
|
+
p = Promise.new(1, 2, 3) do |a, b, c|
|
117
|
+
@a, @b, @c = a, b, c
|
118
|
+
end
|
119
|
+
sleep(0.1)
|
120
|
+
[@a, @b, @c].should eq [1, 2, 3]
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'passes the result of each block to all its children' do
|
124
|
+
@expected = nil
|
125
|
+
Promise.new(10){|a| a * 2 }.then{|result| @expected = result}
|
126
|
+
sleep(0.1)
|
127
|
+
@expected.should eq 20
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'sets the promise value to the result if its block' do
|
131
|
+
p = Promise.new(10){|a| a * 2 }.then{|result| result * 2}
|
132
|
+
sleep(0.1)
|
133
|
+
p.value.should eq 40
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'sets the promise state to :fulfilled if the block completes' do
|
137
|
+
p = Promise.new(10){|a| a * 2 }.then{|result| result * 2}
|
138
|
+
sleep(0.1)
|
139
|
+
p.should be_fulfilled
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'passes the last result through when a promise has no block' do
|
143
|
+
@expected = nil
|
144
|
+
Promise.new(10){|a| a * 2 }.then.then{|result| @expected = result}
|
145
|
+
sleep(0.1)
|
146
|
+
@expected.should eq 20
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'rejection' do
|
151
|
+
|
152
|
+
it 'sets the promise reason the error object on exception' do
|
153
|
+
p = Promise.new{ raise StandardError.new('Boom!') }
|
154
|
+
sleep(0.1)
|
155
|
+
p.reason.should be_a(Exception)
|
156
|
+
p.reason.should.to_s =~ /Boom!/
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'sets the promise state to :rejected on exception' do
|
160
|
+
p = Promise.new{ raise StandardError.new('Boom!') }
|
161
|
+
sleep(0.1)
|
162
|
+
p.should be_rejected
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'recursively rejects all children' do
|
166
|
+
p = Promise.new{ Thread.pass; raise StandardError.new('Boom!') }
|
167
|
+
promises = 10.times.collect{ p.then{ true } }
|
168
|
+
sleep(0.1)
|
169
|
+
|
170
|
+
10.times.each{|i| promises[i].should be_rejected }
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'skips processing rejected promises' do
|
174
|
+
p = Promise.new{ raise StandardError.new('Boom!') }
|
175
|
+
promises = 3.times.collect{ p.then{ true } }
|
176
|
+
sleep(0.1)
|
177
|
+
promises.each{|p| p.value.should_not be_true }
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'calls the first exception block with a matching class' do
|
181
|
+
@expected = nil
|
182
|
+
Promise.new{ raise StandardError }.
|
183
|
+
rescue(StandardError){|ex| @expected = 1 }.
|
184
|
+
rescue(StandardError){|ex| @expected = 2 }.
|
185
|
+
rescue(StandardError){|ex| @expected = 3 }
|
186
|
+
sleep(0.1)
|
187
|
+
@expected.should eq 1
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'matches all with a rescue with no class given' do
|
191
|
+
@expected = nil
|
192
|
+
Promise.new{ raise NoMethodError }.
|
193
|
+
rescue(LoadError){|ex| @expected = 1 }.
|
194
|
+
rescue{|ex| @expected = 2 }.
|
195
|
+
rescue(StandardError){|ex| @expected = 3 }
|
196
|
+
sleep(0.1)
|
197
|
+
@expected.should eq 2
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'searches associated rescue handlers in order' do
|
201
|
+
@expected = nil
|
202
|
+
Promise.new{ raise ArgumentError }.
|
203
|
+
rescue(ArgumentError){|ex| @expected = 1 }.
|
204
|
+
rescue(LoadError){|ex| @expected = 2 }.
|
205
|
+
rescue(Exception){|ex| @expected = 3 }
|
206
|
+
sleep(0.1)
|
207
|
+
@expected.should eq 1
|
208
|
+
|
209
|
+
@expected = nil
|
210
|
+
Promise.new{ raise LoadError }.
|
211
|
+
rescue(ArgumentError){|ex| @expected = 1 }.
|
212
|
+
rescue(LoadError){|ex| @expected = 2 }.
|
213
|
+
rescue(Exception){|ex| @expected = 3 }
|
214
|
+
sleep(0.1)
|
215
|
+
@expected.should eq 2
|
216
|
+
|
217
|
+
@expected = nil
|
218
|
+
Promise.new{ raise StandardError }.
|
219
|
+
rescue(ArgumentError){|ex| @expected = 1 }.
|
220
|
+
rescue(LoadError){|ex| @expected = 2 }.
|
221
|
+
rescue(Exception){|ex| @expected = 3 }
|
222
|
+
sleep(0.1)
|
223
|
+
@expected.should eq 3
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'passes the exception object to the matched block' do
|
227
|
+
@expected = nil
|
228
|
+
Promise.new{ raise StandardError }.
|
229
|
+
rescue(ArgumentError){|ex| @expected = ex }.
|
230
|
+
rescue(LoadError){|ex| @expected = ex }.
|
231
|
+
rescue(Exception){|ex| @expected = ex }
|
232
|
+
sleep(0.1)
|
233
|
+
@expected.should be_a(StandardError)
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'ignores rescuers without a block' do
|
237
|
+
@expected = nil
|
238
|
+
Promise.new{ raise StandardError }.
|
239
|
+
rescue(StandardError).
|
240
|
+
rescue(StandardError){|ex| @expected = ex }.
|
241
|
+
rescue(Exception){|ex| @expected = ex }
|
242
|
+
sleep(0.1)
|
243
|
+
@expected.should be_a(StandardError)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'supresses the exception if no rescue matches' do
|
247
|
+
lambda {
|
248
|
+
Promise.new{ raise StandardError }.
|
249
|
+
rescue(ArgumentError){|ex| @expected = ex }.
|
250
|
+
rescue(StandardError){|ex| @expected = ex }.
|
251
|
+
rescue(Exception){|ex| @expected = ex }
|
252
|
+
sleep(0.1)
|
253
|
+
}.should_not raise_error
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'supresses exceptions thrown from rescue handlers' do
|
257
|
+
lambda {
|
258
|
+
Promise.new{ raise ArgumentError }.
|
259
|
+
rescue(Exception){ raise StandardError }
|
260
|
+
sleep(0.1)
|
261
|
+
}.should_not raise_error(StandardError)
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'calls matching rescue handlers on all children' do
|
265
|
+
@expected = []
|
266
|
+
Promise.new{ Thread.pass; raise StandardError }.
|
267
|
+
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
268
|
+
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
269
|
+
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
270
|
+
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
271
|
+
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }
|
272
|
+
sleep(0.1)
|
273
|
+
|
274
|
+
@expected.length.should eq 5
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context 'aliases' do
|
279
|
+
|
280
|
+
it 'aliases #realized? for #fulfilled?' do
|
281
|
+
fulfilled_subject.should be_realized
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'aliases #deref for #value' do
|
285
|
+
fulfilled_subject.deref.should eq fulfilled_value
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'aliases #catch for #rescue' do
|
289
|
+
@expected = nil
|
290
|
+
Promise.new{ raise StandardError }.catch{ @expected = true }
|
291
|
+
sleep(0.1)
|
292
|
+
@expected.should be_true
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'aliases #on_error for #rescue' do
|
296
|
+
@expected = nil
|
297
|
+
Promise.new{ raise StandardError }.on_error{ @expected = true }
|
298
|
+
sleep(0.1)
|
299
|
+
@expected.should be_true
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'aliases Kernel#promise for Promise.new' do
|
303
|
+
promise().should be_a(Promise)
|
304
|
+
promise(){ nil }.should be_a(Promise)
|
305
|
+
promise(1, 2, 3).should be_a(Promise)
|
306
|
+
promise(1, 2, 3){ nil }.should be_a(Promise)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|