functional-ruby 0.6.0 → 0.7.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 +14 -126
- data/lib/functional.rb +4 -1
- data/lib/functional/utilities.rb +46 -0
- data/lib/functional/version.rb +1 -1
- data/lib/functional_ruby.rb +1 -1
- data/md/utilities.md +2 -0
- data/spec/functional/behavior_spec.rb +2 -2
- data/spec/functional/pattern_matching_spec.rb +2 -2
- data/spec/functional/utilities_spec.rb +131 -43
- data/spec/spec_helper.rb +1 -3
- metadata +3 -40
- data/lib/functional/agent.rb +0 -130
- data/lib/functional/all.rb +0 -13
- data/lib/functional/cached_thread_pool.rb +0 -122
- data/lib/functional/concurrency.rb +0 -35
- data/lib/functional/core.rb +0 -2
- data/lib/functional/event.rb +0 -53
- data/lib/functional/event_machine_defer_proxy.rb +0 -23
- data/lib/functional/fixed_thread_pool.rb +0 -89
- data/lib/functional/future.rb +0 -42
- data/lib/functional/global_thread_pool.rb +0 -3
- data/lib/functional/obligation.rb +0 -121
- data/lib/functional/promise.rb +0 -194
- data/lib/functional/thread_pool.rb +0 -61
- data/md/concurrency.md +0 -465
- data/md/future.md +0 -32
- data/md/obligation.md +0 -32
- data/md/promise.md +0 -220
- data/spec/functional/agent_spec.rb +0 -405
- data/spec/functional/cached_thread_pool_spec.rb +0 -112
- data/spec/functional/concurrency_spec.rb +0 -55
- data/spec/functional/event_machine_defer_proxy_spec.rb +0 -246
- data/spec/functional/event_spec.rb +0 -114
- data/spec/functional/fixed_thread_pool_spec.rb +0 -84
- data/spec/functional/future_spec.rb +0 -115
- data/spec/functional/obligation_shared.rb +0 -121
- data/spec/functional/promise_spec.rb +0 -310
- data/spec/functional/thread_pool_shared.rb +0 -209
@@ -1,112 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative 'thread_pool_shared'
|
3
|
-
|
4
|
-
module Functional
|
5
|
-
|
6
|
-
describe CachedThreadPool do
|
7
|
-
|
8
|
-
subject { CachedThreadPool.new }
|
9
|
-
|
10
|
-
it_should_behave_like 'Thread Pool'
|
11
|
-
|
12
|
-
context '#initialize' do
|
13
|
-
it 'aliases Functional#new_cached_thread_pool' do
|
14
|
-
pool = Functional.new_cached_thread_pool
|
15
|
-
pool.should be_a(CachedThreadPool)
|
16
|
-
pool.size.should eq 0
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
context '#kill' do
|
21
|
-
|
22
|
-
it 'kills all threads' do
|
23
|
-
Thread.should_receive(:kill).exactly(5).times
|
24
|
-
pool = CachedThreadPool.new
|
25
|
-
5.times{ sleep(0.1); pool << proc{ sleep(1) } }
|
26
|
-
sleep(1)
|
27
|
-
pool.kill
|
28
|
-
sleep(0.1)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context '#size' do
|
33
|
-
|
34
|
-
it 'returns zero for a new thread pool' do
|
35
|
-
subject.size.should eq 0
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'returns the size of the subject when running' do
|
39
|
-
5.times{ sleep(0.1); subject << proc{ sleep(1) } }
|
40
|
-
subject.size.should eq 5
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'returns zero once shut down' do
|
44
|
-
subject.shutdown
|
45
|
-
subject.size.should eq 0
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context 'worker creation and caching' do
|
50
|
-
|
51
|
-
it 'creates new workers when there are none available' do
|
52
|
-
subject.size.should eq 0
|
53
|
-
5.times{ sleep(0.1); subject << proc{ sleep(1000) } }
|
54
|
-
sleep(1)
|
55
|
-
subject.size.should eq 5
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'uses existing idle threads' do
|
59
|
-
5.times{ sleep(0.05); subject << proc{ sleep(0.5) } }
|
60
|
-
sleep(1)
|
61
|
-
3.times{ sleep(0.1); subject << proc{ sleep(0.5) } }
|
62
|
-
subject.size.should eq 5
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context 'garbage collection' do
|
67
|
-
|
68
|
-
subject{ CachedThreadPool.new(gc_interval: 1, thread_idleime: 1) }
|
69
|
-
|
70
|
-
it 'starts when the first thread is added to the pool' do
|
71
|
-
subject.should_receive(:collect_garbage)
|
72
|
-
subject << proc{ nil }
|
73
|
-
sleep(0.1)
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'removes from pool any thread that has been idle too long' do
|
77
|
-
subject << proc{ nil }
|
78
|
-
subject.size.should eq 1
|
79
|
-
sleep(1.5)
|
80
|
-
subject.size.should eq 0
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'removed from pool any dead thread' do
|
84
|
-
subject << proc{ raise StandardError }
|
85
|
-
subject.size.should eq 1
|
86
|
-
sleep(1.5)
|
87
|
-
subject.size.should eq 0
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'resets the working count appropriately' do
|
91
|
-
subject << proc{ sleep(1000) }
|
92
|
-
sleep(0.1)
|
93
|
-
subject << proc{ raise StandardError }
|
94
|
-
sleep(0.1)
|
95
|
-
subject << proc{ nil }
|
96
|
-
|
97
|
-
sleep(0.1)
|
98
|
-
subject.working.should eq 2
|
99
|
-
|
100
|
-
sleep(1.5)
|
101
|
-
subject.working.should eq 1
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'stops collection when the pool size becomes zero' do
|
105
|
-
3.times{ sleep(0.1); subject << proc{ sleep(0.5) } }
|
106
|
-
subject.instance_variable_get(:@collector).status.should eq 'sleep'
|
107
|
-
sleep(1.5)
|
108
|
-
subject.instance_variable_get(:@collector).status.should be_false
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Functional
|
4
|
-
|
5
|
-
describe 'concurrency' do
|
6
|
-
|
7
|
-
context '#go' do
|
8
|
-
|
9
|
-
before(:each) do
|
10
|
-
$GLOBAL_THREAD_POOL = CachedThreadPool.new
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'passes all arguments to the block' do
|
14
|
-
@expected = nil
|
15
|
-
go(1, 2, 3){|a, b, c| @expected = [c, b, a] }
|
16
|
-
sleep(0.1)
|
17
|
-
@expected.should eq [3, 2, 1]
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'returns true if the thread is successfully created' do
|
21
|
-
$GLOBAL_THREAD_POOL.should_receive(:post).and_return(true)
|
22
|
-
go{ nil }.should be_true
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'returns false if the thread cannot be created' do
|
26
|
-
$GLOBAL_THREAD_POOL.should_receive(:post).and_return(false)
|
27
|
-
go{ nil }.should be_false
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'immediately returns false if no block is given' do
|
31
|
-
go().should be_false
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'does not create a thread if no block is given' do
|
35
|
-
$GLOBAL_THREAD_POOL.should_not_receive(:post)
|
36
|
-
go()
|
37
|
-
sleep(0.1)
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'supresses exceptions on the thread' do
|
41
|
-
lambda{
|
42
|
-
go{ raise StandardError }
|
43
|
-
sleep(0.1)
|
44
|
-
}.should_not raise_error
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'processes the block' do
|
48
|
-
@expected = false
|
49
|
-
go(1,2,3){|*args| @expected = args }
|
50
|
-
sleep(0.1)
|
51
|
-
@expected.should eq [1,2,3]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,246 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
require 'functional/agent'
|
4
|
-
require 'functional/future'
|
5
|
-
require 'functional/promise'
|
6
|
-
|
7
|
-
module Functional
|
8
|
-
|
9
|
-
describe EventMachineDeferProxy do
|
10
|
-
|
11
|
-
subject { EventMachineDeferProxy.new }
|
12
|
-
|
13
|
-
context '#post' do
|
14
|
-
|
15
|
-
it 'proxies a call without arguments' do
|
16
|
-
@expected = false
|
17
|
-
EventMachine.run do
|
18
|
-
subject.post{ @expected = true }
|
19
|
-
sleep(0.1)
|
20
|
-
EventMachine.stop
|
21
|
-
end
|
22
|
-
@expected.should eq true
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'proxies a call with arguments' do
|
26
|
-
@expected = []
|
27
|
-
EventMachine.run do
|
28
|
-
subject.post(1,2,3){|*args| @expected = args }
|
29
|
-
sleep(0.1)
|
30
|
-
EventMachine.stop
|
31
|
-
end
|
32
|
-
@expected.should eq [1,2,3]
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'aliases #<<' do
|
36
|
-
@expected = false
|
37
|
-
EventMachine.run do
|
38
|
-
subject << proc{ @expected = true }
|
39
|
-
sleep(0.1)
|
40
|
-
EventMachine.stop
|
41
|
-
end
|
42
|
-
@expected.should eq true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'operation' do
|
47
|
-
|
48
|
-
context 'goroutine' do
|
49
|
-
|
50
|
-
it 'passes all arguments to the block' do
|
51
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
52
|
-
|
53
|
-
EventMachine.run do
|
54
|
-
|
55
|
-
@expected = nil
|
56
|
-
go(1, 2, 3){|a, b, c| @expected = [c, b, a] }
|
57
|
-
sleep(0.1)
|
58
|
-
@expected.should eq [3, 2, 1]
|
59
|
-
|
60
|
-
EventMachine.stop
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context Agent do
|
66
|
-
|
67
|
-
subject { Agent.new(0) }
|
68
|
-
|
69
|
-
it 'supports fulfillment' do
|
70
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
71
|
-
|
72
|
-
EventMachine.run do
|
73
|
-
|
74
|
-
@expected = []
|
75
|
-
subject.post{ @expected << 1 }
|
76
|
-
subject.post{ @expected << 2 }
|
77
|
-
subject.post{ @expected << 3 }
|
78
|
-
sleep(0.1)
|
79
|
-
@expected.should eq [1,2,3]
|
80
|
-
|
81
|
-
EventMachine.stop
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'supports validation' do
|
86
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
87
|
-
|
88
|
-
EventMachine.run do
|
89
|
-
|
90
|
-
@expected = nil
|
91
|
-
subject.validate{ @expected = 10; true }
|
92
|
-
subject.post{ nil }
|
93
|
-
sleep(0.1)
|
94
|
-
@expected.should eq 10
|
95
|
-
|
96
|
-
EventMachine.stop
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
it 'supports rejection' do
|
101
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
102
|
-
|
103
|
-
EventMachine.run do
|
104
|
-
|
105
|
-
@expected = nil
|
106
|
-
subject.
|
107
|
-
on_error(StandardError){|ex| @expected = 1 }.
|
108
|
-
on_error(StandardError){|ex| @expected = 2 }.
|
109
|
-
on_error(StandardError){|ex| @expected = 3 }
|
110
|
-
subject.post{ raise StandardError }
|
111
|
-
sleep(0.1)
|
112
|
-
@expected.should eq 1
|
113
|
-
|
114
|
-
EventMachine.stop
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context Future do
|
120
|
-
|
121
|
-
it 'supports fulfillment' do
|
122
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
123
|
-
|
124
|
-
EventMachine.run do
|
125
|
-
|
126
|
-
@a = @b = @c = nil
|
127
|
-
f = Future.new(1, 2, 3) do |a, b, c|
|
128
|
-
@a, @b, @c = a, b, c
|
129
|
-
end
|
130
|
-
sleep(0.1)
|
131
|
-
[@a, @b, @c].should eq [1, 2, 3]
|
132
|
-
|
133
|
-
sleep(0.1)
|
134
|
-
EventMachine.stop
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
context Promise do
|
140
|
-
|
141
|
-
context 'fulfillment' do
|
142
|
-
|
143
|
-
it 'passes all arguments to the first promise in the chain' do
|
144
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
145
|
-
|
146
|
-
EventMachine.run do
|
147
|
-
|
148
|
-
@a = @b = @c = nil
|
149
|
-
p = Promise.new(1, 2, 3) do |a, b, c|
|
150
|
-
@a, @b, @c = a, b, c
|
151
|
-
end
|
152
|
-
sleep(0.1)
|
153
|
-
[@a, @b, @c].should eq [1, 2, 3]
|
154
|
-
|
155
|
-
sleep(0.1)
|
156
|
-
EventMachine.stop
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
it 'passes the result of each block to all its children' do
|
161
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
162
|
-
|
163
|
-
EventMachine.run do
|
164
|
-
@expected = nil
|
165
|
-
Promise.new(10){|a| a * 2 }.then{|result| @expected = result}
|
166
|
-
sleep(0.1)
|
167
|
-
@expected.should eq 20
|
168
|
-
|
169
|
-
sleep(0.1)
|
170
|
-
EventMachine.stop
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'sets the promise value to the result if its block' do
|
175
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
176
|
-
|
177
|
-
EventMachine.run do
|
178
|
-
|
179
|
-
p = Promise.new(10){|a| a * 2 }.then{|result| result * 2}
|
180
|
-
sleep(0.1)
|
181
|
-
p.value.should eq 40
|
182
|
-
|
183
|
-
sleep(0.1)
|
184
|
-
EventMachine.stop
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
context 'rejection' do
|
190
|
-
|
191
|
-
it 'sets the promise reason and error on exception' do
|
192
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
193
|
-
|
194
|
-
EventMachine.run do
|
195
|
-
|
196
|
-
p = Promise.new{ raise StandardError.new('Boom!') }
|
197
|
-
sleep(0.1)
|
198
|
-
p.reason.should be_a(Exception)
|
199
|
-
p.reason.should.to_s =~ /Boom!/
|
200
|
-
p.should be_rejected
|
201
|
-
|
202
|
-
sleep(0.1)
|
203
|
-
EventMachine.stop
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
it 'calls the first exception block with a matching class' do
|
208
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
209
|
-
|
210
|
-
EventMachine.run do
|
211
|
-
|
212
|
-
@expected = nil
|
213
|
-
Promise.new{ raise StandardError }.
|
214
|
-
on_error(StandardError){|ex| @expected = 1 }.
|
215
|
-
on_error(StandardError){|ex| @expected = 2 }.
|
216
|
-
on_error(StandardError){|ex| @expected = 3 }
|
217
|
-
sleep(0.1)
|
218
|
-
@expected.should eq 1
|
219
|
-
|
220
|
-
sleep(0.1)
|
221
|
-
EventMachine.stop
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
it 'passes the exception object to the matched block' do
|
226
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
227
|
-
|
228
|
-
EventMachine.run do
|
229
|
-
|
230
|
-
@expected = nil
|
231
|
-
Promise.new{ raise StandardError }.
|
232
|
-
on_error(ArgumentError){|ex| @expected = ex }.
|
233
|
-
on_error(LoadError){|ex| @expected = ex }.
|
234
|
-
on_error(Exception){|ex| @expected = ex }
|
235
|
-
sleep(0.1)
|
236
|
-
@expected.should be_a(StandardError)
|
237
|
-
|
238
|
-
sleep(0.1)
|
239
|
-
EventMachine.stop
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
@@ -1,114 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Functional
|
4
|
-
|
5
|
-
describe Event do
|
6
|
-
|
7
|
-
subject{ Event.new }
|
8
|
-
|
9
|
-
context '#initialize' do
|
10
|
-
|
11
|
-
it 'sets the state to unset' do
|
12
|
-
subject.should_not be_set
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
context '#set?' do
|
17
|
-
|
18
|
-
it 'returns true when the event has been set' do
|
19
|
-
subject.set
|
20
|
-
subject.should be_set
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'returns false if the event is unset' do
|
24
|
-
subject.reset
|
25
|
-
subject.should_not be_set
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context '#set' do
|
30
|
-
|
31
|
-
it 'triggers the event' do
|
32
|
-
subject.reset
|
33
|
-
@expected = false
|
34
|
-
Thread.new{ sleep(0.5); subject.wait; @expected = true }
|
35
|
-
subject.set
|
36
|
-
sleep(1)
|
37
|
-
@expected.should be_true
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'sets the state to set' do
|
41
|
-
subject.set
|
42
|
-
subject.should be_set
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context '#reset' do
|
47
|
-
|
48
|
-
it 'sets the state to unset' do
|
49
|
-
subject.set
|
50
|
-
subject.should be_set
|
51
|
-
subject.reset
|
52
|
-
subject.should_not be_set
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context '#wait' do
|
57
|
-
|
58
|
-
it 'returns immediately when the event has been set' do
|
59
|
-
subject.reset
|
60
|
-
@expected = false
|
61
|
-
subject.set
|
62
|
-
Thread.new{ subject.wait(1000); @expected = true}
|
63
|
-
sleep(1)
|
64
|
-
@expected.should be_true
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'returns true once the event is set' do
|
68
|
-
subject.set
|
69
|
-
subject.wait.should be_true
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'blocks indefinitely when the timer is nil' do
|
73
|
-
subject.reset
|
74
|
-
@expected = false
|
75
|
-
Thread.new{ subject.wait; @expected = true}
|
76
|
-
subject.set
|
77
|
-
sleep(1)
|
78
|
-
@expected.should be_true
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'stops waiting when the timer expires' do
|
82
|
-
subject.reset
|
83
|
-
@expected = false
|
84
|
-
Thread.new{ subject.wait(0.5); @expected = true}
|
85
|
-
sleep(1)
|
86
|
-
@expected.should be_true
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'returns false when the timer expires' do
|
90
|
-
subject.reset
|
91
|
-
subject.wait(1).should be_false
|
92
|
-
end
|
93
|
-
|
94
|
-
it 'triggers multiple waiting threads' do
|
95
|
-
subject.reset
|
96
|
-
@expected = []
|
97
|
-
5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
|
98
|
-
subject.set
|
99
|
-
sleep(1)
|
100
|
-
@expected.length.should eq 5
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'behaves appropriately if wait begins while #set is processing' do
|
104
|
-
subject.reset
|
105
|
-
@expected = []
|
106
|
-
5.times{ Thread.new{ subject.wait(5) } }
|
107
|
-
subject.set
|
108
|
-
5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
|
109
|
-
sleep(1)
|
110
|
-
@expected.length.should eq 5
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|