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,23 +0,0 @@
1
- require 'functional/global_thread_pool'
2
-
3
- module Functional
4
-
5
- class EventMachineDeferProxy
6
- behavior(:global_thread_pool)
7
-
8
- def post(*args, &block)
9
- if args.empty?
10
- EventMachine.defer(block)
11
- else
12
- new_block = proc{ block.call(*args) }
13
- EventMachine.defer(new_block)
14
- end
15
- return true
16
- end
17
-
18
- def <<(block)
19
- EventMachine.defer(block)
20
- return self
21
- end
22
- end
23
- end
@@ -1,89 +0,0 @@
1
- require 'thread'
2
-
3
- require 'functional/thread_pool'
4
- require 'functional/event'
5
-
6
- module Functional
7
-
8
- def self.new_fixed_thread_pool(size)
9
- return FixedThreadPool.new(size)
10
- end
11
-
12
- class FixedThreadPool < ThreadPool
13
- behavior(:thread_pool)
14
-
15
- MIN_POOL_SIZE = 1
16
- MAX_POOL_SIZE = 1024
17
-
18
- def initialize(size)
19
- super()
20
- if size < MIN_POOL_SIZE || size > MAX_POOL_SIZE
21
- raise ArgumentError.new("size must be between #{MIN_POOL_SIZE} and #{MAX_POOL_SIZE}")
22
- end
23
-
24
- @pool = size.times.collect{ create_worker_thread }
25
- collect_garbage
26
- end
27
-
28
- def kill
29
- @status = :killed
30
- @pool.each{|t| Thread.kill(t) }
31
- end
32
-
33
- def size
34
- if running?
35
- return @pool.length
36
- else
37
- return 0
38
- end
39
- end
40
-
41
- def post(*args, &block)
42
- raise ArgumentError.new('no block given') unless block_given?
43
- if running?
44
- @queue << [args, block]
45
- return true
46
- else
47
- return false
48
- end
49
- end
50
-
51
- # @private
52
- def status # :nodoc:
53
- @pool.collect{|t| t.status }
54
- end
55
-
56
- private
57
-
58
- # @private
59
- def create_worker_thread # :nodoc:
60
- Thread.new do
61
- loop do
62
- task = @queue.pop
63
- if task == :stop
64
- break
65
- else
66
- task.last.call(*task.first)
67
- end
68
- end
69
- @pool.delete(Thread.current)
70
- if @pool.empty?
71
- @termination.set
72
- @status = :shutdown unless killed?
73
- end
74
- end
75
- end
76
-
77
- # @private
78
- def collect_garbage # :nodoc:
79
- @collector = Thread.new do
80
- sleep(1)
81
- @pool.size.times do |i|
82
- if @pool[i].status.nil?
83
- @pool[i] = create_worker_thread
84
- end
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,42 +0,0 @@
1
- require 'thread'
2
-
3
- require 'functional/obligation'
4
- require 'functional/global_thread_pool'
5
-
6
- module Functional
7
-
8
- class Future
9
- include Obligation
10
- behavior(:future)
11
-
12
- def initialize(*args)
13
-
14
- unless block_given?
15
- @state = :fulfilled
16
- else
17
- @value = nil
18
- @state = :pending
19
- $GLOBAL_THREAD_POOL.post do
20
- semaphore.synchronize do
21
- Thread.pass
22
- begin
23
- @value = yield(*args)
24
- @state = :fulfilled
25
- rescue Exception => ex
26
- @state = :rejected
27
- @reason = ex
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
35
-
36
- module Kernel
37
-
38
- def future(*args, &block)
39
- return Functional::Future.new(*args, &block)
40
- end
41
- module_function :future
42
- end
@@ -1,3 +0,0 @@
1
- require 'functional/cached_thread_pool'
2
-
3
- $GLOBAL_THREAD_POOL ||= Functional::CachedThreadPool.new
@@ -1,121 +0,0 @@
1
- require 'thread'
2
- require 'timeout'
3
-
4
- require 'functional/behavior'
5
-
6
- behavior_info(:future,
7
- state: 0,
8
- value: -1,
9
- reason: 0,
10
- pending?: 0,
11
- fulfilled?: 0,
12
- rejected?: 0)
13
-
14
- behavior_info(:promise,
15
- state: 0,
16
- value: -1,
17
- reason: 0,
18
- pending?: 0,
19
- fulfilled?: 0,
20
- rejected?: 0,
21
- then: 0,
22
- rescue: -1)
23
-
24
- module Functional
25
-
26
- module Obligation
27
-
28
- attr_reader :state
29
- attr_reader :reason
30
-
31
- # Has the obligation been fulfilled?
32
- # @return [Boolean]
33
- def fulfilled?() return(@state == :fulfilled); end
34
- alias_method :realized?, :fulfilled?
35
-
36
- # Is obligation completion still pending?
37
- # @return [Boolean]
38
- def pending?() return(!(fulfilled? || rejected?)); end
39
-
40
- def value(timeout = nil)
41
- if !pending? || timeout == 0
42
- return @value
43
- elsif timeout.nil?
44
- return semaphore.synchronize { @value }
45
- else
46
- begin
47
- return Timeout::timeout(timeout.to_f) {
48
- semaphore.synchronize { @value }
49
- }
50
- rescue Timeout::Error => ex
51
- return nil
52
- end
53
- end
54
- end
55
- alias_method :deref, :value
56
-
57
- # Has the promise been rejected?
58
- # @return [Boolean]
59
- def rejected?() return(@state == :rejected); end
60
-
61
- protected
62
-
63
- def semaphore
64
- @semaphore ||= Mutex.new
65
- end
66
- end
67
- end
68
-
69
- module Kernel
70
-
71
- def deref(obligation, timeout = nil)
72
- if obligation.respond_to?(:deref)
73
- return obligation.deref(timeout)
74
- elsif obligation.respond_to?(:value)
75
- return obligation.deref(timeout)
76
- else
77
- return nil
78
- end
79
- end
80
- module_function :deref
81
-
82
- def pending?(obligation)
83
- if obligation.respond_to?(:pending?)
84
- return obligation.pending?
85
- else
86
- return false
87
- end
88
- end
89
- module_function :pending?
90
-
91
- def fulfilled?(obligation)
92
- if obligation.respond_to?(:fulfilled?)
93
- return obligation.fulfilled?
94
- elsif obligation.respond_to?(:realized?)
95
- return obligation.realized?
96
- else
97
- return false
98
- end
99
- end
100
- module_function :fulfilled?
101
-
102
- def realized?(obligation)
103
- if obligation.respond_to?(:realized?)
104
- return obligation.realized?
105
- elsif obligation.respond_to?(:fulfilled?)
106
- return obligation.fulfilled?
107
- else
108
- return false
109
- end
110
- end
111
- module_function :realized?
112
-
113
- def rejected?(obligation)
114
- if obligation.respond_to?(:rejected?)
115
- return obligation.rejected?
116
- else
117
- return false
118
- end
119
- end
120
- module_function :rejected?
121
- end
@@ -1,194 +0,0 @@
1
- require 'thread'
2
-
3
- require 'functional/obligation'
4
- require 'functional/global_thread_pool'
5
-
6
- module Functional
7
-
8
- class Promise
9
- include Obligation
10
- behavior(:future)
11
- behavior(:promise)
12
-
13
- # Creates a new promise object. "A promise represents the eventual
14
- # value returned from the single completion of an operation."
15
- # Promises can be chained in a tree structure where each promise
16
- # has zero or more children. Promises are resolved asynchronously
17
- # in the order they are added to the tree. Parents are guaranteed
18
- # to be resolved before their children. The result of each promise
19
- # is passed to each of its children upon resolution. When
20
- # a promise is rejected all its children will be summarily rejected.
21
- # A promise that is neither resolved or rejected is pending.
22
- #
23
- # @param args [Array] zero or more arguments for the block
24
- # @param block [Proc] the block to call when attempting fulfillment
25
- #
26
- # @see http://wiki.commonjs.org/wiki/Promises/A
27
- # @see http://promises-aplus.github.io/promises-spec/
28
- def initialize(*args, &block)
29
- if args.first.is_a?(Promise)
30
- @parent = args.first
31
- else
32
- @parent = nil
33
- @chain = [self]
34
- end
35
-
36
- @mutex = Mutex.new
37
- @handler = block || Proc.new{|result| result }
38
- @state = :pending
39
- @value = nil
40
- @reason = nil
41
- @children = []
42
- @rescuers = []
43
-
44
- realize(*args) if root?
45
- end
46
-
47
- # Create a new child Promise. The block argument for the child will
48
- # be the result of fulfilling its parent. If the child will
49
- # immediately be rejected if the parent has already been rejected.
50
- #
51
- # @param block [Proc] the block to call when attempting fulfillment
52
- #
53
- # @return [Promise] the new promise
54
- def then(&block)
55
- child = @mutex.synchronize do
56
- block = Proc.new{|result| result } unless block_given?
57
- @children << Promise.new(self, &block)
58
- @children.last.on_reject(@reason) if rejected?
59
- push(@children.last)
60
- @children.last
61
- end
62
- return child
63
- end
64
-
65
- # Add a rescue handler to be run if the promise is rejected (via raised
66
- # exception). Multiple rescue handlers may be added to a Promise.
67
- # Rescue blocks will be checked in order and the first one with a
68
- # matching Exception class will be processed. The block argument
69
- # will be the exception that caused the rejection.
70
- #
71
- # @param clazz [Class] The class of exception to rescue
72
- # @param block [Proc] the block to call if the rescue is matched
73
- #
74
- # @return [self] so that additional chaining can occur
75
- def rescue(clazz = Exception, &block)
76
- @mutex.synchronize do
77
- @rescuers << Rescuer.new(clazz, block) if block_given?
78
- end
79
- return self
80
- end
81
- alias_method :catch, :rescue
82
- alias_method :on_error, :rescue
83
-
84
- protected
85
-
86
- attr_reader :parent
87
- attr_reader :handler
88
- attr_reader :rescuers
89
-
90
- # @private
91
- Rescuer = Struct.new(:clazz, :block)
92
-
93
- # @private
94
- def root # :nodoc:
95
- current = self
96
- current = current.parent until current.root?
97
- return current
98
- end
99
-
100
- # @private
101
- def root? # :nodoc:
102
- @parent.nil?
103
- end
104
-
105
- # @private
106
- def push(promise) # :nodoc:
107
- if root?
108
- @chain << promise
109
- else
110
- @parent.push(promise)
111
- end
112
- end
113
-
114
- # @private
115
- def on_fulfill(value) # :nodoc:
116
- @mutex.synchronize do
117
- if pending?
118
- @value = @handler.call(value)
119
- @state = :fulfilled
120
- @reason = nil
121
- end
122
- end
123
- return @value
124
- end
125
-
126
- # @private
127
- def on_reject(reason) # :nodoc:
128
- @mutex.synchronize do
129
- if pending?
130
- @state = :rejected
131
- @reason = reason
132
- self.try_rescue(reason)
133
- @value = nil
134
- end
135
- @children.each{|child| child.on_reject(reason) }
136
- end
137
- end
138
-
139
- # @private
140
- def try_rescue(ex) # :nodoc:
141
- rescuer = @rescuers.find{|r| ex.is_a?(r.clazz) }
142
- rescuer.block.call(ex) if rescuer
143
- rescue Exception => e
144
- # supress
145
- end
146
-
147
- # @private
148
- def realize(*args) # :nodoc:
149
- $GLOBAL_THREAD_POOL.post(@chain, @mutex, args) do |chain, mutex, args|
150
- result = args.length == 1 ? args.first : args
151
- index = 0
152
- loop do
153
- Thread.pass
154
- current = mutex.synchronize{ chain[index] }
155
- unless current.rejected?
156
- current.semaphore.synchronize do
157
- begin
158
- result = current.on_fulfill(result)
159
- rescue Exception => ex
160
- current.on_reject(ex)
161
- end
162
- end
163
- end
164
- index += 1
165
- sleep while index >= chain.length
166
- end
167
- end
168
- end
169
- end
170
- end
171
-
172
- module Kernel
173
-
174
- # Creates a new promise object. "A promise represents the eventual
175
- # value returned from the single completion of an operation."
176
- # Promises can be chained in a tree structure where each promise
177
- # has zero or more children. Promises are resolved asynchronously
178
- # in the order they are added to the tree. Parents are guaranteed
179
- # to be resolved before their children. The result of each promise
180
- # is passes to each of its children when the child resolves. When
181
- # a promise is rejected all its children will be summarily rejected.
182
- # A promise added to a rejected promise will immediately be rejected.
183
- # A promise that is neither resolved or rejected is pending.
184
- #
185
- # @param args [Array] zero or more arguments for the block
186
- # @param block [Proc] the block to call when attempting fulfillment
187
- #
188
- # @see Promise
189
- # @see http://wiki.commonjs.org/wiki/Promises/A
190
- def promise(*args, &block)
191
- return Functional::Promise.new(*args, &block)
192
- end
193
- module_function :promise
194
- end