functional-ruby 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -126
  3. data/lib/functional.rb +4 -1
  4. data/lib/functional/utilities.rb +46 -0
  5. data/lib/functional/version.rb +1 -1
  6. data/lib/functional_ruby.rb +1 -1
  7. data/md/utilities.md +2 -0
  8. data/spec/functional/behavior_spec.rb +2 -2
  9. data/spec/functional/pattern_matching_spec.rb +2 -2
  10. data/spec/functional/utilities_spec.rb +131 -43
  11. data/spec/spec_helper.rb +1 -3
  12. metadata +3 -40
  13. data/lib/functional/agent.rb +0 -130
  14. data/lib/functional/all.rb +0 -13
  15. data/lib/functional/cached_thread_pool.rb +0 -122
  16. data/lib/functional/concurrency.rb +0 -35
  17. data/lib/functional/core.rb +0 -2
  18. data/lib/functional/event.rb +0 -53
  19. data/lib/functional/event_machine_defer_proxy.rb +0 -23
  20. data/lib/functional/fixed_thread_pool.rb +0 -89
  21. data/lib/functional/future.rb +0 -42
  22. data/lib/functional/global_thread_pool.rb +0 -3
  23. data/lib/functional/obligation.rb +0 -121
  24. data/lib/functional/promise.rb +0 -194
  25. data/lib/functional/thread_pool.rb +0 -61
  26. data/md/concurrency.md +0 -465
  27. data/md/future.md +0 -32
  28. data/md/obligation.md +0 -32
  29. data/md/promise.md +0 -220
  30. data/spec/functional/agent_spec.rb +0 -405
  31. data/spec/functional/cached_thread_pool_spec.rb +0 -112
  32. data/spec/functional/concurrency_spec.rb +0 -55
  33. data/spec/functional/event_machine_defer_proxy_spec.rb +0 -246
  34. data/spec/functional/event_spec.rb +0 -114
  35. data/spec/functional/fixed_thread_pool_spec.rb +0 -84
  36. data/spec/functional/future_spec.rb +0 -115
  37. data/spec/functional/obligation_shared.rb +0 -121
  38. data/spec/functional/promise_spec.rb +0 -310
  39. data/spec/functional/thread_pool_shared.rb +0 -209
@@ -1,84 +0,0 @@
1
- require 'spec_helper'
2
- require_relative 'thread_pool_shared'
3
-
4
- module Functional
5
-
6
- describe FixedThreadPool do
7
-
8
- subject { FixedThreadPool.new(5) }
9
-
10
- it_should_behave_like 'Thread Pool'
11
-
12
- context '#initialize' do
13
-
14
- it 'raises an exception when the pool size is less than one' do
15
- lambda {
16
- FixedThreadPool.new(0)
17
- }.should raise_error(ArgumentError)
18
- end
19
-
20
- it 'raises an exception when the pool size is greater than 1024' do
21
- lambda {
22
- FixedThreadPool.new(1025)
23
- }.should raise_error(ArgumentError)
24
- end
25
-
26
- it 'creates a thread pool of the given size' do
27
- thread = mock('thread')
28
- # add one for the garbage collector
29
- Thread.should_receive(:new).exactly(5+1).times.and_return(thread)
30
- pool = FixedThreadPool.new(5)
31
- pool.size.should eq 5
32
- end
33
-
34
- it 'aliases Functional#new_fixed_thread_pool' do
35
- pool = Functional.new_fixed_thread_pool(5)
36
- pool.should be_a(FixedThreadPool)
37
- pool.size.should eq 5
38
- end
39
- end
40
-
41
- context '#kill' do
42
-
43
- it 'kills all threads' do
44
- Thread.should_receive(:kill).exactly(5).times
45
- pool = FixedThreadPool.new(5)
46
- pool.kill
47
- sleep(0.1)
48
- end
49
- end
50
-
51
- context '#size' do
52
-
53
- let(:pool_size) { 3 }
54
- subject { FixedThreadPool.new(pool_size) }
55
-
56
- it 'returns the size of the subject when running' do
57
- subject.size.should eq pool_size
58
- end
59
-
60
- it 'returns zero while shutting down' do
61
- subject.post{ sleep(1) }
62
- subject.shutdown
63
- subject.size.should eq 0
64
- end
65
-
66
- it 'returns zero once shut down' do
67
- subject.shutdown
68
- subject.size.should eq 0
69
- end
70
- end
71
-
72
- context 'exception handling' do
73
-
74
- it 'restarts threads that experience exception' do
75
- pool = FixedThreadPool.new(5)
76
- 3.times{ pool << proc{ raise StandardError } }
77
- sleep(2)
78
- pool.size.should eq 5
79
- pool.status.should_not include(nil)
80
- #pool.status.include?(nil).should be_false
81
- end
82
- end
83
- end
84
- end
@@ -1,115 +0,0 @@
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
@@ -1,121 +0,0 @@
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
@@ -1,310 +0,0 @@
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