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
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: functional-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
description: |2
|
28
|
-
A gem for adding Erlang, Clojure, and Go inspired
|
28
|
+
A gem for adding Erlang, Clojure, and Go inspired functional programming tools to Ruby.
|
29
29
|
email: jerry.dantonio@gmail.com
|
30
30
|
executables: []
|
31
31
|
extensions: []
|
@@ -35,46 +35,19 @@ extra_rdoc_files:
|
|
35
35
|
files:
|
36
36
|
- README.md
|
37
37
|
- LICENSE
|
38
|
-
- lib/functional/agent.rb
|
39
|
-
- lib/functional/all.rb
|
40
38
|
- lib/functional/behavior.rb
|
41
39
|
- lib/functional/behaviour.rb
|
42
|
-
- lib/functional/cached_thread_pool.rb
|
43
|
-
- lib/functional/concurrency.rb
|
44
|
-
- lib/functional/core.rb
|
45
|
-
- lib/functional/event.rb
|
46
|
-
- lib/functional/event_machine_defer_proxy.rb
|
47
|
-
- lib/functional/fixed_thread_pool.rb
|
48
|
-
- lib/functional/future.rb
|
49
|
-
- lib/functional/global_thread_pool.rb
|
50
|
-
- lib/functional/obligation.rb
|
51
40
|
- lib/functional/pattern_matching.rb
|
52
|
-
- lib/functional/promise.rb
|
53
|
-
- lib/functional/thread_pool.rb
|
54
41
|
- lib/functional/utilities.rb
|
55
42
|
- lib/functional/version.rb
|
56
43
|
- lib/functional.rb
|
57
44
|
- lib/functional_ruby.rb
|
58
45
|
- md/behavior.md
|
59
|
-
- md/concurrency.md
|
60
|
-
- md/future.md
|
61
|
-
- md/obligation.md
|
62
46
|
- md/pattern_matching.md
|
63
|
-
- md/promise.md
|
64
47
|
- md/utilities.md
|
65
|
-
- spec/functional/agent_spec.rb
|
66
48
|
- spec/functional/behavior_spec.rb
|
67
|
-
- spec/functional/cached_thread_pool_spec.rb
|
68
|
-
- spec/functional/concurrency_spec.rb
|
69
|
-
- spec/functional/event_machine_defer_proxy_spec.rb
|
70
|
-
- spec/functional/event_spec.rb
|
71
|
-
- spec/functional/fixed_thread_pool_spec.rb
|
72
|
-
- spec/functional/future_spec.rb
|
73
49
|
- spec/functional/integration_spec.rb
|
74
|
-
- spec/functional/obligation_shared.rb
|
75
50
|
- spec/functional/pattern_matching_spec.rb
|
76
|
-
- spec/functional/promise_spec.rb
|
77
|
-
- spec/functional/thread_pool_shared.rb
|
78
51
|
- spec/functional/utilities_spec.rb
|
79
52
|
- spec/spec_helper.rb
|
80
53
|
homepage: https://github.com/jdantonio/functional-ruby/
|
@@ -111,18 +84,8 @@ signing_key:
|
|
111
84
|
specification_version: 4
|
112
85
|
summary: Erlang and Clojure inspired functional programming tools for Ruby.
|
113
86
|
test_files:
|
114
|
-
- spec/functional/agent_spec.rb
|
115
87
|
- spec/functional/behavior_spec.rb
|
116
|
-
- spec/functional/cached_thread_pool_spec.rb
|
117
|
-
- spec/functional/concurrency_spec.rb
|
118
|
-
- spec/functional/event_machine_defer_proxy_spec.rb
|
119
|
-
- spec/functional/event_spec.rb
|
120
|
-
- spec/functional/fixed_thread_pool_spec.rb
|
121
|
-
- spec/functional/future_spec.rb
|
122
88
|
- spec/functional/integration_spec.rb
|
123
|
-
- spec/functional/obligation_shared.rb
|
124
89
|
- spec/functional/pattern_matching_spec.rb
|
125
|
-
- spec/functional/promise_spec.rb
|
126
|
-
- spec/functional/thread_pool_shared.rb
|
127
90
|
- spec/functional/utilities_spec.rb
|
128
91
|
- spec/spec_helper.rb
|
data/lib/functional/agent.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
require 'observer'
|
2
|
-
require 'thread'
|
3
|
-
|
4
|
-
require 'functional/global_thread_pool'
|
5
|
-
|
6
|
-
module Functional
|
7
|
-
|
8
|
-
# An agent is a single atomic value that represents an identity. The current value
|
9
|
-
# of the agent can be requested at any time (#deref). Each agent has a work queue and operates on
|
10
|
-
# the global thread pool. Consumers can #post code blocks to the agent. The code block (function)
|
11
|
-
# will receive the current value of the agent as its sole parameter. The return value of the block
|
12
|
-
# will become the new value of the agent. Agents support two error handling modes: fail and continue.
|
13
|
-
# A good example of an agent is a shared incrementing counter, such as the score in a video game.
|
14
|
-
class Agent
|
15
|
-
include Observable
|
16
|
-
|
17
|
-
TIMEOUT = 5
|
18
|
-
|
19
|
-
attr_reader :initial
|
20
|
-
attr_reader :timeout
|
21
|
-
|
22
|
-
def initialize(initial, timeout = TIMEOUT)
|
23
|
-
@value = initial
|
24
|
-
@timeout = timeout
|
25
|
-
@rescuers = []
|
26
|
-
@validator = nil
|
27
|
-
@queue = Queue.new
|
28
|
-
|
29
|
-
$GLOBAL_THREAD_POOL << proc{ work }
|
30
|
-
end
|
31
|
-
|
32
|
-
def value(timeout = 0) return @value; end
|
33
|
-
alias_method :deref, :value
|
34
|
-
|
35
|
-
def rescue(clazz = Exception, &block)
|
36
|
-
@rescuers << Rescuer.new(clazz, block) if block_given?
|
37
|
-
return self
|
38
|
-
end
|
39
|
-
alias_method :catch, :rescue
|
40
|
-
alias_method :on_error, :rescue
|
41
|
-
|
42
|
-
def validate(&block)
|
43
|
-
@validator = block if block_given?
|
44
|
-
return self
|
45
|
-
end
|
46
|
-
alias_method :validates, :validate
|
47
|
-
alias_method :validate_with, :validate
|
48
|
-
alias_method :validates_with, :validate
|
49
|
-
|
50
|
-
def post(&block)
|
51
|
-
return @queue.length unless block_given?
|
52
|
-
@queue << block
|
53
|
-
return @queue.length
|
54
|
-
end
|
55
|
-
|
56
|
-
def <<(block)
|
57
|
-
self.post(&block)
|
58
|
-
return self
|
59
|
-
end
|
60
|
-
|
61
|
-
def length
|
62
|
-
@queue.length
|
63
|
-
end
|
64
|
-
alias_method :size, :length
|
65
|
-
alias_method :count, :length
|
66
|
-
|
67
|
-
alias_method :add_watch, :add_observer
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
# @private
|
72
|
-
Rescuer = Struct.new(:clazz, :block)
|
73
|
-
|
74
|
-
# @private
|
75
|
-
def try_rescue(ex) # :nodoc:
|
76
|
-
rescuer = @rescuers.find{|r| ex.is_a?(r.clazz) }
|
77
|
-
rescuer.block.call(ex) if rescuer
|
78
|
-
rescue Exception => e
|
79
|
-
# supress
|
80
|
-
end
|
81
|
-
|
82
|
-
# @private
|
83
|
-
def work # :nodoc:
|
84
|
-
loop do
|
85
|
-
Thread.pass
|
86
|
-
handler = @queue.pop
|
87
|
-
begin
|
88
|
-
result = Timeout.timeout(@timeout){
|
89
|
-
handler.call(@value)
|
90
|
-
}
|
91
|
-
if @validator.nil? || @validator.call(result)
|
92
|
-
@value = result
|
93
|
-
changed
|
94
|
-
notify_observers(Time.now, @value)
|
95
|
-
end
|
96
|
-
rescue Exception => ex
|
97
|
-
try_rescue(ex)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
module Kernel
|
105
|
-
|
106
|
-
def agent(initial, timeout = Functional::Agent::TIMEOUT)
|
107
|
-
return Functional::Agent.new(initial, timeout)
|
108
|
-
end
|
109
|
-
module_function :agent
|
110
|
-
|
111
|
-
def deref(agent, timeout = nil)
|
112
|
-
if agent.respond_to?(:deref)
|
113
|
-
return agent.deref(timeout)
|
114
|
-
elsif agent.respond_to?(:value)
|
115
|
-
return agent.deref(timeout)
|
116
|
-
else
|
117
|
-
return nil
|
118
|
-
end
|
119
|
-
end
|
120
|
-
module_function :deref
|
121
|
-
|
122
|
-
def post(agent, &block)
|
123
|
-
if agent.respond_to?(:post)
|
124
|
-
return agent.post(&block)
|
125
|
-
else
|
126
|
-
return nil
|
127
|
-
end
|
128
|
-
end
|
129
|
-
module_function :deref
|
130
|
-
end
|
data/lib/functional/all.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'functional/agent'
|
2
|
-
require 'functional/behavior'
|
3
|
-
require 'functional/cached_thread_pool'
|
4
|
-
require 'functional/concurrency'
|
5
|
-
require 'functional/event'
|
6
|
-
require 'functional/fixed_thread_pool'
|
7
|
-
require 'functional/future'
|
8
|
-
require 'functional/obligation'
|
9
|
-
require 'functional/pattern_matching'
|
10
|
-
require 'functional/promise'
|
11
|
-
require 'functional/thread_pool'
|
12
|
-
require 'functional/utilities'
|
13
|
-
require 'functional/version'
|
@@ -1,122 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'functional/thread_pool'
|
4
|
-
require 'functional/utilities'
|
5
|
-
|
6
|
-
module Functional
|
7
|
-
|
8
|
-
def self.new_cached_thread_pool
|
9
|
-
return CachedThreadPool.new
|
10
|
-
end
|
11
|
-
|
12
|
-
class CachedThreadPool < ThreadPool
|
13
|
-
behavior(:thread_pool)
|
14
|
-
|
15
|
-
DEFAULT_GC_INTERVAL = 60
|
16
|
-
DEFAULT_THREAD_IDLETIME = 60
|
17
|
-
|
18
|
-
attr_reader :working
|
19
|
-
|
20
|
-
def initialize(opts = {})
|
21
|
-
@gc_interval = opts[:gc_interval] || DEFAULT_GC_INTERVAL
|
22
|
-
@thread_idletime = opts[:thread_idletime] || DEFAULT_THREAD_IDLETIME
|
23
|
-
super()
|
24
|
-
@working = 0
|
25
|
-
@mutex = Mutex.new
|
26
|
-
end
|
27
|
-
|
28
|
-
def kill
|
29
|
-
@status = :killed
|
30
|
-
@mutex.synchronize do
|
31
|
-
@pool.each{|t| Thread.kill(t.thread) }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def size
|
36
|
-
return @pool.length
|
37
|
-
end
|
38
|
-
|
39
|
-
def post(*args, &block)
|
40
|
-
raise ArgumentError.new('no block given') unless block_given?
|
41
|
-
if running?
|
42
|
-
collect_garbage if @pool.empty?
|
43
|
-
@mutex.synchronize do
|
44
|
-
if @working >= @pool.length
|
45
|
-
create_worker_thread
|
46
|
-
end
|
47
|
-
@queue << [args, block]
|
48
|
-
end
|
49
|
-
return true
|
50
|
-
else
|
51
|
-
return false
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# @private
|
56
|
-
def status # :nodoc:
|
57
|
-
@mutex.synchronize do
|
58
|
-
@pool.collect do |worker|
|
59
|
-
[
|
60
|
-
worker.status,
|
61
|
-
worker.status == :idle ? delta(worker.idletime, timestamp) : nil,
|
62
|
-
worker.thread.status
|
63
|
-
]
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
Worker = Struct.new(:status, :idletime, :thread)
|
71
|
-
|
72
|
-
# @private
|
73
|
-
def create_worker_thread # :nodoc:
|
74
|
-
worker = Worker.new(:idle, timestamp, nil)
|
75
|
-
|
76
|
-
worker.thread = Thread.new(worker) do |me|
|
77
|
-
|
78
|
-
loop do
|
79
|
-
task = @queue.pop
|
80
|
-
|
81
|
-
@working += 1
|
82
|
-
me.status = :working
|
83
|
-
|
84
|
-
if task == :stop
|
85
|
-
me.status = :stopping
|
86
|
-
break
|
87
|
-
else
|
88
|
-
task.last.call(*task.first)
|
89
|
-
@working -= 1
|
90
|
-
me.status = :idle
|
91
|
-
me.idletime = timestamp
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
@pool.delete(me)
|
96
|
-
if @pool.empty?
|
97
|
-
@termination.set
|
98
|
-
@status = :shutdown unless killed?
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
@pool << worker
|
103
|
-
end
|
104
|
-
|
105
|
-
# @private
|
106
|
-
def collect_garbage # :nodoc:
|
107
|
-
@collector = Thread.new do
|
108
|
-
loop do
|
109
|
-
sleep(@gc_interval)
|
110
|
-
@mutex.synchronize do
|
111
|
-
@pool.reject! do |worker|
|
112
|
-
worker.thread.status.nil? ||
|
113
|
-
(worker.status == :idle && @thread_idletime >= delta(worker.idletime, timestamp))
|
114
|
-
end
|
115
|
-
end
|
116
|
-
@working = @pool.count{|worker| worker.status == :working}
|
117
|
-
break if @pool.empty?
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'functional/agent'
|
4
|
-
require 'functional/future'
|
5
|
-
require 'functional/promise'
|
6
|
-
|
7
|
-
require 'functional/thread_pool'
|
8
|
-
require 'functional/cached_thread_pool'
|
9
|
-
require 'functional/fixed_thread_pool'
|
10
|
-
|
11
|
-
require 'functional/global_thread_pool'
|
12
|
-
|
13
|
-
require 'functional/event_machine_defer_proxy' if defined?(EventMachine)
|
14
|
-
|
15
|
-
module Kernel
|
16
|
-
|
17
|
-
# Post the given agruments and block to the Global Thread Pool.
|
18
|
-
#
|
19
|
-
# @param args [Array] zero or more arguments for the block
|
20
|
-
# @param block [Proc] operation to be performed concurrently
|
21
|
-
#
|
22
|
-
# @return [true,false] success/failre of thread creation
|
23
|
-
#
|
24
|
-
# @note Althought based on Go's goroutines and Erlang's spawn/1,
|
25
|
-
# Ruby has a vastly different runtime. Threads aren't nearly as
|
26
|
-
# efficient in Ruby. Use this function appropriately.
|
27
|
-
#
|
28
|
-
# @see http://golang.org/doc/effective_go.html#goroutines
|
29
|
-
# @see https://gobyexample.com/goroutines
|
30
|
-
def go(*args, &block)
|
31
|
-
return false unless block_given?
|
32
|
-
$GLOBAL_THREAD_POOL.post(*args, &block)
|
33
|
-
end
|
34
|
-
module_function :go
|
35
|
-
end
|
data/lib/functional/core.rb
DELETED
data/lib/functional/event.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'timeout'
|
3
|
-
|
4
|
-
module Functional
|
5
|
-
|
6
|
-
class Event
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@set = false
|
10
|
-
@notifier = Queue.new
|
11
|
-
@mutex = Mutex.new
|
12
|
-
@waiting = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def set?
|
16
|
-
return @set == true
|
17
|
-
end
|
18
|
-
|
19
|
-
def set
|
20
|
-
return true if set?
|
21
|
-
@mutex.synchronize {
|
22
|
-
@set = true
|
23
|
-
while @waiting > 0
|
24
|
-
@notifier << :set
|
25
|
-
@waiting -= 1
|
26
|
-
end
|
27
|
-
}
|
28
|
-
end
|
29
|
-
|
30
|
-
def reset
|
31
|
-
@mutex.synchronize {
|
32
|
-
@set = false
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
def wait(timeout = nil)
|
37
|
-
return true if set?
|
38
|
-
|
39
|
-
if timeout.nil?
|
40
|
-
@waiting += 1
|
41
|
-
@notifier.pop
|
42
|
-
else
|
43
|
-
Timeout::timeout(timeout) do
|
44
|
-
@waiting += 1
|
45
|
-
@notifier.pop
|
46
|
-
end
|
47
|
-
end
|
48
|
-
return true
|
49
|
-
rescue Timeout::Error
|
50
|
-
return false
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|