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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +154 -562
  3. data/lib/functional/agent.rb +130 -0
  4. data/lib/functional/all.rb +9 -1
  5. data/lib/functional/behavior.rb +72 -39
  6. data/lib/functional/cached_thread_pool.rb +122 -0
  7. data/lib/functional/concurrency.rb +32 -24
  8. data/lib/functional/core.rb +2 -62
  9. data/lib/functional/event.rb +53 -0
  10. data/lib/functional/event_machine_defer_proxy.rb +23 -0
  11. data/lib/functional/fixed_thread_pool.rb +89 -0
  12. data/lib/functional/future.rb +42 -0
  13. data/lib/functional/global_thread_pool.rb +3 -0
  14. data/lib/functional/obligation.rb +121 -0
  15. data/lib/functional/promise.rb +194 -0
  16. data/lib/functional/thread_pool.rb +61 -0
  17. data/lib/functional/utilities.rb +114 -0
  18. data/lib/functional/version.rb +1 -1
  19. data/lib/functional.rb +1 -0
  20. data/lib/functional_ruby.rb +1 -0
  21. data/md/behavior.md +147 -0
  22. data/md/concurrency.md +465 -0
  23. data/md/future.md +32 -0
  24. data/md/obligation.md +32 -0
  25. data/md/pattern_matching.md +512 -0
  26. data/md/promise.md +220 -0
  27. data/md/utilities.md +53 -0
  28. data/spec/functional/agent_spec.rb +405 -0
  29. data/spec/functional/behavior_spec.rb +12 -33
  30. data/spec/functional/cached_thread_pool_spec.rb +112 -0
  31. data/spec/functional/concurrency_spec.rb +55 -0
  32. data/spec/functional/event_machine_defer_proxy_spec.rb +246 -0
  33. data/spec/functional/event_spec.rb +114 -0
  34. data/spec/functional/fixed_thread_pool_spec.rb +84 -0
  35. data/spec/functional/future_spec.rb +115 -0
  36. data/spec/functional/obligation_shared.rb +121 -0
  37. data/spec/functional/pattern_matching_spec.rb +10 -8
  38. data/spec/functional/promise_spec.rb +310 -0
  39. data/spec/functional/thread_pool_shared.rb +209 -0
  40. data/spec/functional/utilities_spec.rb +149 -0
  41. data/spec/spec_helper.rb +2 -0
  42. metadata +55 -5
data/md/promise.md ADDED
@@ -0,0 +1,220 @@
1
+ # Promises, promises...
2
+
3
+ > A promise represents the eventual value returned from the single completion of an operation.
4
+
5
+ Promises have become an extremely important async technique in JavaScript. A promise is an
6
+ an operation that is performed asynchronously and is guaranteed to either succeed and return
7
+ a value or fail with a reason. What makes promises distinctly different from futures is
8
+ that promises can be chained such that the result of onw promise is passed to zero or
9
+ more children. Order of execution is guaranteed based on the order the promises are
10
+ created and parent promises are guaranteed to be complete before their children. Once a
11
+ promise has been fulfilled or rejected the corresponding value/reason can be retrieved.
12
+
13
+ ## The shoulders of giants
14
+
15
+ Inspiration for this implementation came from the CommonJS
16
+ [Promises/A](http://wiki.commonjs.org/wiki/Promises/A) proposal and the
17
+ [Promises/A+](http://promises-aplus.github.io/promises-spec/) specification.
18
+ This implementation is specifically tailored to the idioms and practices of
19
+ Ruby. It is not 100% compliant with either of the aforementioned specifications.
20
+
21
+ ## Usage
22
+
23
+
24
+ Start by requiring promises
25
+
26
+ ```ruby
27
+ require 'functional/promise'
28
+ ```
29
+
30
+ Then create one
31
+
32
+ ```ruby
33
+ p = Promise.new("Jerry", "D'Antonio") do |first, last|
34
+ "#{last}, #{first}"
35
+ end
36
+
37
+ # -or-
38
+
39
+ p = promise(10){|x| x * x * x }
40
+ ```
41
+
42
+ Promises can be chained using the `then` method. The `then` method
43
+ accepts a block but no arguments. The result of the each promise is
44
+ passed as the block argument to chained promises
45
+
46
+
47
+ ```ruby
48
+ p = promise(10){|x| x * 2}.then{|result| result - 10 }
49
+ ```
50
+
51
+ And so on, and so on, and so on...
52
+
53
+ ```ruby
54
+ p = promise(10){|x| x * 2}.
55
+ then{|result| result - 10 }.
56
+ then{|result| result * 3 }.
57
+ then{|result| result % 5 }
58
+ ```
59
+
60
+ Promises are executed asynchronously so a newly-created promise
61
+ *should* always be in the pending state
62
+
63
+
64
+ ```ruby
65
+ p = promise{ "Hello, world!" }
66
+ p.state #=> :pending
67
+ p.pending? #=> true
68
+ ```
69
+
70
+ Wait a little bit, and the promise will resolve and provide a value
71
+
72
+
73
+ ```ruby
74
+ p = promise{ "Hello, world!" }
75
+ sleep(0.1)
76
+
77
+ p.state #=> :fulfilled
78
+ p.fulfilled? #=> true
79
+
80
+ p.value #=> "Hello, world!"
81
+
82
+ ```
83
+
84
+ If an exception occurs, the promise will be rejected and will provide
85
+ a reason for the rejection
86
+
87
+
88
+ ```ruby
89
+ p = promise{ raise StandardError.new("Here comes the Boom!") }
90
+ sleep(0.1)
91
+
92
+ p.state #=> :rejected
93
+ p.rejected? #=> true
94
+
95
+ p.reason=> #=> "#<StandardError: Here comes the Boom!>"
96
+ ```
97
+
98
+ ### Rejection
99
+
100
+ Much like the economy, rejection exhibits a trickle-down effect. When
101
+ a promise is rejected all its children will be rejected
102
+
103
+ ```ruby
104
+ p = [ promise{ Thread.pass; raise StandardError } ]
105
+
106
+ 10.times{|i| p << p.first.then{ i } }
107
+ sleep(0.1)
108
+
109
+ p.length #=> 11
110
+ p.first.state #=> :rejected
111
+ p.last.state #=> :rejected
112
+ ```
113
+
114
+ Once a promise is rejected it will not accept any children. Calls
115
+ to `then` will continually return `self`
116
+
117
+ ```ruby
118
+ p = promise{ raise StandardError }
119
+ sleep(0.1)
120
+
121
+ p.object_id #=> 32960556
122
+ p.then{}.object_id #=> 32960556
123
+ p.then{}.object_id #=> 32960556
124
+ ```
125
+
126
+ ### Error Handling
127
+
128
+ Promises support error handling callbacks is a style mimicing Ruby's
129
+ own exception handling mechanism, namely `rescue`
130
+
131
+
132
+ ```ruby
133
+ promise{ "dangerous operation..." }.rescue{|ex| puts "Bam!" }
134
+
135
+ # -or- (for the Java/C# crowd)
136
+ promise{ "dangerous operation..." }.catch{|ex| puts "Boom!" }
137
+
138
+ # -or- (for the hipsters)
139
+ promise{ "dangerous operation..." }.on_error{|ex| puts "Pow!" }
140
+ ```
141
+
142
+ As with Ruby's `rescue` mechanism, a promise's `rescue` method can
143
+ accept an optional Exception class argument (defaults to `Exception`
144
+ when not specified)
145
+
146
+
147
+ ```ruby
148
+ promise{ "dangerous operation..." }.rescue(ArgumentError){|ex| puts "Bam!" }
149
+ ```
150
+
151
+ Calls to `rescue` can also be chained
152
+
153
+ ```ruby
154
+ promise{ "dangerous operation..." }.
155
+ rescue(ArgumentError){|ex| puts "Bam!" }.
156
+ rescue(NoMethodError){|ex| puts "Boom!" }.
157
+ rescue(StandardError){|ex| puts "Pow!" }
158
+ ```
159
+
160
+ When there are multiple `rescue` handlers the first one to match the thrown
161
+ exception will be triggered
162
+
163
+ ```ruby
164
+ promise{ raise NoMethodError }.
165
+ rescue(ArgumentError){|ex| puts "Bam!" }.
166
+ rescue(NoMethodError){|ex| puts "Boom!" }.
167
+ rescue(StandardError){|ex| puts "Pow!" }
168
+
169
+ sleep(0.1)
170
+
171
+ #=> Boom!
172
+ ```
173
+
174
+ Trickle-down rejection also applies to rescue handlers. When a promise is rejected,
175
+ for any reason, its rescue handlers will be triggered. Rejection of the parent counts.
176
+
177
+ ```ruby
178
+ promise{ Thread.pass; raise StandardError }.
179
+ then{ true }.rescue{ puts 'Boom!' }.
180
+ then{ true }.rescue{ puts 'Boom!' }.
181
+ then{ true }.rescue{ puts 'Boom!' }.
182
+ then{ true }.rescue{ puts 'Boom!' }.
183
+ then{ true }.rescue{ puts 'Boom!' }
184
+ sleep(0.1)
185
+
186
+ #=> Boom!
187
+ #=> Boom!
188
+ #=> Boom!
189
+ #=> Boom!
190
+ #=> Boom!
191
+ ```
192
+
193
+ ## Copyright
194
+
195
+ *Functional Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
196
+ It is free software and may be redistributed under the terms specified in the LICENSE file.
197
+
198
+ ## License
199
+
200
+ Released under the MIT license.
201
+
202
+ http://www.opensource.org/licenses/mit-license.php
203
+
204
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
205
+ > of this software and associated documentation files (the "Software"), to deal
206
+ > in the Software without restriction, including without limitation the rights
207
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
208
+ > copies of the Software, and to permit persons to whom the Software is
209
+ > furnished to do so, subject to the following conditions:
210
+ >
211
+ > The above copyright notice and this permission notice shall be included in
212
+ > all copies or substantial portions of the Software.
213
+ >
214
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
215
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
216
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
217
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
218
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
219
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
220
+ > THE SOFTWARE.
data/md/utilities.md ADDED
@@ -0,0 +1,53 @@
1
+ # Utility Functions
2
+
3
+ Convenience functions are not imported by default. They need a separate `require` statement:
4
+
5
+ ```ruby
6
+ require 'functional/utilities'
7
+ ```
8
+
9
+ This gives you access to a few constants and functions:
10
+
11
+ ```ruby
12
+ Infinity #=> Infinity
13
+ NaN #=> NaN
14
+
15
+ repl? #=> true when called under irb, pry, bundle console, or rails console
16
+
17
+ safe(1, 2){|a, b| a + b} #=> 3
18
+ safe{ eval 'puts "Hello World!"' } #=> SecurityError: Insecure operation
19
+
20
+ pp_s [1,2,3,4] #=> "[1, 2, 3, 4]\n" props to Rha7
21
+
22
+ delta(-1, 1) #=> 2
23
+ delta({count: -1}, {count: 1}){|item| item[:count]} #=> 2
24
+ ```
25
+
26
+ ## Copyright
27
+
28
+ *Functional Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
29
+ It is free software and may be redistributed under the terms specified in the LICENSE file.
30
+
31
+ ## License
32
+
33
+ Released under the MIT license.
34
+
35
+ http://www.opensource.org/licenses/mit-license.php
36
+
37
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ > of this software and associated documentation files (the "Software"), to deal
39
+ > in the Software without restriction, including without limitation the rights
40
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ > copies of the Software, and to permit persons to whom the Software is
42
+ > furnished to do so, subject to the following conditions:
43
+ >
44
+ > The above copyright notice and this permission notice shall be included in
45
+ > all copies or substantial portions of the Software.
46
+ >
47
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53
+ > THE SOFTWARE.
@@ -0,0 +1,405 @@
1
+ require 'spec_helper'
2
+
3
+ module Functional
4
+
5
+ describe Agent do
6
+
7
+ subject { Agent.new(0) }
8
+
9
+ let(:observer) do
10
+ Class.new do
11
+ attr_reader :value
12
+ define_method(:update) do |time, value|
13
+ @value = value
14
+ end
15
+ end.new
16
+ end
17
+
18
+ before(:each) do
19
+ $GLOBAL_THREAD_POOL = CachedThreadPool.new
20
+ end
21
+
22
+ context '#initialize' do
23
+
24
+ it 'sets the value to the given initial state' do
25
+ Agent.new(10).value.should eq 10
26
+ end
27
+
28
+ it 'sets the timeout to the given value' do
29
+ Agent.new(0, 5).timeout.should eq 5
30
+ end
31
+
32
+ it 'sets the timeout to the default when nil' do
33
+ Agent.new(0).timeout.should eq Agent::TIMEOUT
34
+ end
35
+
36
+ it 'sets the length to zero' do
37
+ Agent.new(10).length.should eq 0
38
+ end
39
+
40
+ it 'spawns the worker thread' do
41
+ $GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
42
+ Agent.new(0)
43
+ end
44
+ end
45
+
46
+ context '#rescue' do
47
+
48
+ it 'returns self when a block is given' do
49
+ a1 = subject
50
+ a2 = a1.rescue{}
51
+ a1.object_id.should eq a2.object_id
52
+ end
53
+
54
+ it 'returns self when no block is given' do
55
+ a1 = subject
56
+ a2 = a1.rescue
57
+ a1.object_id.should eq a2.object_id
58
+ end
59
+
60
+ it 'accepts an exception class as the first parameter' do
61
+ lambda {
62
+ subject.rescue(StandardError){}
63
+ }.should_not raise_error(ArgumentError)
64
+ end
65
+
66
+ it 'ignores rescuers without a block' do
67
+ subject.rescue
68
+ subject.instance_variable_get(:@rescuers).should be_empty
69
+ end
70
+ end
71
+
72
+ context '#validate' do
73
+
74
+ it 'returns self when a block is given' do
75
+ a1 = subject
76
+ a2 = a1.validate{}
77
+ a1.object_id.should eq a2.object_id
78
+ end
79
+
80
+ it 'returns self when no block is given' do
81
+ a1 = subject
82
+ a2 = a1.validate
83
+ a1.object_id.should eq a2.object_id
84
+ end
85
+
86
+ it 'ignores validators without a block' do
87
+ subject.validate
88
+ subject.instance_variable_get(:@validator).should be_nil
89
+ end
90
+ end
91
+
92
+ context '#post' do
93
+
94
+ it 'adds the given block to the queue' do
95
+ before = subject.length
96
+ subject.post{ nil }
97
+ subject.post{ nil }
98
+ subject.length.should eq before+2
99
+ end
100
+
101
+ it 'does not add to the queue when no block is given' do
102
+ before = subject.length
103
+ subject.post
104
+ subject.post{ nil }
105
+ subject.length.should eq before+1
106
+ end
107
+ end
108
+
109
+ context '#length' do
110
+
111
+ it 'should be zero for a new agent' do
112
+ subject.length.should eq 0
113
+ end
114
+
115
+ it 'should increase by one for each #post' do
116
+ subject.post{ sleep }
117
+ subject.post{ sleep }
118
+ subject.post{ sleep }
119
+ subject.length.should eq 3
120
+ end
121
+
122
+ it 'should decrease by one each time a handler is run' do
123
+ subject.post{ nil }
124
+ subject.post{ sleep }
125
+ subject.post{ sleep }
126
+ sleep(0.1)
127
+ subject.length.should eq 1
128
+ end
129
+ end
130
+
131
+ context 'fulfillment' do
132
+
133
+ it 'process each block in the queue' do
134
+ @expected = []
135
+ subject.post{ @expected << 1 }
136
+ subject.post{ @expected << 2 }
137
+ subject.post{ @expected << 3 }
138
+ sleep(0.1)
139
+ @expected.should eq [1,2,3]
140
+ end
141
+
142
+ it 'passes the current value to the handler' do
143
+ @expected = nil
144
+ Agent.new(10).post{|i| @expected = i }
145
+ sleep(0.1)
146
+ @expected.should eq 10
147
+ end
148
+
149
+ it 'sets the value to the handler return value on success' do
150
+ subject.post{ 100 }
151
+ sleep(0.1)
152
+ subject.value.should eq 100
153
+ end
154
+
155
+ it 'rejects the handler after timeout reached' do
156
+ agent = Agent.new(0, 0.1)
157
+ agent.post{ sleep(1); 10 }
158
+ agent.value.should eq 0
159
+ end
160
+ end
161
+
162
+ context 'validation' do
163
+
164
+ it 'processes the validator when present' do
165
+ @expected = nil
166
+ subject.validate{ @expected = 10; true }
167
+ subject.post{ nil }
168
+ sleep(0.1)
169
+ @expected.should eq 10
170
+ end
171
+
172
+ it 'passes the new value to the validator' do
173
+ @expected = nil
174
+ subject.validate{|v| @expected = v; true }
175
+ subject.post{ 10 }
176
+ sleep(0.1)
177
+ @expected.should eq 10
178
+ end
179
+
180
+ it 'sets the new value when the validator returns true' do
181
+ agent = Agent.new(0).validate{ true }
182
+ agent.post{ 10 }
183
+ sleep(0.1)
184
+ agent.value.should eq 10
185
+ end
186
+
187
+ it 'does not change the value when the validator returns false' do
188
+ agent = Agent.new(0).validate{ false }
189
+ agent.post{ 10 }
190
+ sleep(0.1)
191
+ agent.value.should eq 0
192
+ end
193
+
194
+ it 'does not change the value when the validator raises an exception' do
195
+ agent = Agent.new(0).validate{ raise StandardError }
196
+ agent.post{ 10 }
197
+ sleep(0.1)
198
+ agent.value.should eq 0
199
+ end
200
+ end
201
+
202
+ context 'rejection' do
203
+
204
+ it 'calls the first exception block with a matching class' do
205
+ @expected = nil
206
+ subject.
207
+ rescue(StandardError){|ex| @expected = 1 }.
208
+ rescue(StandardError){|ex| @expected = 2 }.
209
+ rescue(StandardError){|ex| @expected = 3 }
210
+ subject.post{ raise StandardError }
211
+ sleep(0.1)
212
+ @expected.should eq 1
213
+ end
214
+
215
+ it 'matches all with a rescue with no class given' do
216
+ @expected = nil
217
+ subject.
218
+ rescue(LoadError){|ex| @expected = 1 }.
219
+ rescue{|ex| @expected = 2 }.
220
+ rescue(StandardError){|ex| @expected = 3 }
221
+ subject.post{ raise NoMethodError }
222
+ sleep(0.1)
223
+ @expected.should eq 2
224
+ end
225
+
226
+ it 'searches associated rescue handlers in order' do
227
+ @expected = nil
228
+ subject.
229
+ rescue(ArgumentError){|ex| @expected = 1 }.
230
+ rescue(LoadError){|ex| @expected = 2 }.
231
+ rescue(Exception){|ex| @expected = 3 }
232
+ subject.post{ raise ArgumentError }
233
+ sleep(0.1)
234
+ @expected.should eq 1
235
+
236
+ @expected = nil
237
+ subject.
238
+ rescue(ArgumentError){|ex| @expected = 1 }.
239
+ rescue(LoadError){|ex| @expected = 2 }.
240
+ rescue(Exception){|ex| @expected = 3 }
241
+ subject.post{ raise LoadError }
242
+ sleep(0.1)
243
+ @expected.should eq 2
244
+
245
+ @expected = nil
246
+ subject.
247
+ rescue(ArgumentError){|ex| @expected = 1 }.
248
+ rescue(LoadError){|ex| @expected = 2 }.
249
+ rescue(Exception){|ex| @expected = 3 }
250
+ subject.post{ raise StandardError }
251
+ sleep(0.1)
252
+ @expected.should eq 3
253
+ end
254
+
255
+ it 'passes the exception object to the matched block' do
256
+ @expected = nil
257
+ subject.
258
+ rescue(ArgumentError){|ex| @expected = ex }.
259
+ rescue(LoadError){|ex| @expected = ex }.
260
+ rescue(Exception){|ex| @expected = ex }
261
+ subject.post{ raise StandardError }
262
+ sleep(0.1)
263
+ @expected.should be_a(StandardError)
264
+ end
265
+
266
+ it 'ignores rescuers without a block' do
267
+ @expected = nil
268
+ subject.
269
+ rescue(StandardError).
270
+ rescue(StandardError){|ex| @expected = ex }.
271
+ rescue(Exception){|ex| @expected = ex }
272
+ subject.post{ raise StandardError }
273
+ sleep(0.1)
274
+ @expected.should be_a(StandardError)
275
+ end
276
+
277
+ it 'supresses the exception if no rescue matches' do
278
+ lambda {
279
+ subject.
280
+ rescue(ArgumentError){|ex| @expected = ex }.
281
+ rescue(StandardError){|ex| @expected = ex }.
282
+ rescue(Exception){|ex| @expected = ex }
283
+ subject.post{ raise StandardError }
284
+ sleep(0.1)
285
+ }.should_not raise_error
286
+ end
287
+
288
+ it 'supresses exceptions thrown from rescue handlers' do
289
+ lambda {
290
+ subject.rescue(Exception){ raise StandardError }
291
+ subject.post{ raise ArgumentError }
292
+ sleep(0.1)
293
+ }.should_not raise_error(StandardError)
294
+ end
295
+ end
296
+
297
+ context 'observation' do
298
+
299
+ it 'notifies all observers when the value changes' do
300
+ agent = Agent.new(0)
301
+ agent.add_observer(observer)
302
+ agent.post{ 10 }
303
+ sleep(0.1)
304
+ observer.value.should eq 10
305
+ end
306
+
307
+ it 'does not notify observers when validation fails' do
308
+ agent = Agent.new(0)
309
+ agent.validate{ false }
310
+ agent.add_observer(observer)
311
+ agent.post{ 10 }
312
+ sleep(0.1)
313
+ observer.value.should be_nil
314
+ end
315
+
316
+ it 'does not notify observers when the handler raises an exception' do
317
+ agent = Agent.new(0)
318
+ agent.add_observer(observer)
319
+ agent.post{ raise StandardError }
320
+ sleep(0.1)
321
+ observer.value.should be_nil
322
+ end
323
+ end
324
+
325
+ context 'aliases' do
326
+
327
+ it 'aliases #deref for #value' do
328
+ Agent.new(10).deref.should eq 10
329
+ end
330
+
331
+ it 'aliases #validates for :validate' do
332
+ @expected = nil
333
+ subject.validates{|v| @expected = v }
334
+ subject.post{ 10 }
335
+ sleep(0.1)
336
+ @expected.should eq 10
337
+ end
338
+
339
+ it 'aliases #validate_with for :validate' do
340
+ @expected = nil
341
+ subject.validate_with{|v| @expected = v }
342
+ subject.post{ 10 }
343
+ sleep(0.1)
344
+ @expected.should eq 10
345
+ end
346
+
347
+ it 'aliases #validates_with for :validate' do
348
+ @expected = nil
349
+ subject.validates_with{|v| @expected = v }
350
+ subject.post{ 10 }
351
+ sleep(0.1)
352
+ @expected.should eq 10
353
+ end
354
+
355
+ it 'aliases #catch for #rescue' do
356
+ @expected = nil
357
+ subject.catch{ @expected = true }
358
+ subject.post{ raise StandardError }
359
+ sleep(0.1)
360
+ @expected.should be_true
361
+ end
362
+
363
+ it 'aliases #on_error for #rescue' do
364
+ @expected = nil
365
+ subject.on_error{ @expected = true }
366
+ subject.post{ raise StandardError }
367
+ sleep(0.1)
368
+ @expected.should be_true
369
+ end
370
+
371
+ it 'aliases #add_watch for #add_observer' do
372
+ agent = Agent.new(0)
373
+ agent.add_watch(observer)
374
+ agent.post{ 10 }
375
+ sleep(0.1)
376
+ observer.value.should eq 10
377
+ end
378
+
379
+ it 'aliases #<< for Agent#post' do
380
+ subject << proc{ 100 }
381
+ sleep(0.1)
382
+ subject.value.should eq 100
383
+
384
+ subject << lambda{ 100 }
385
+ sleep(0.1)
386
+ subject.value.should eq 100
387
+ end
388
+
389
+ it 'aliases Kernel#agent for Agent.new' do
390
+ agent(10).should be_a(Agent)
391
+ end
392
+
393
+ it 'aliases Kernel#deref for #deref' do
394
+ deref(Agent.new(10)).should eq 10
395
+ deref(Agent.new(10), 10).should eq 10
396
+ end
397
+
398
+ it 'aliases Kernel:post for Agent#post' do
399
+ post(subject){ 100 }
400
+ sleep(0.1)
401
+ subject.value.should eq 100
402
+ end
403
+ end
404
+ end
405
+ end