future_proof 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 15ccb48d47e099121a7d5f7244f78df5e3a9ac5a
4
+ data.tar.gz: a54c4d62b82764b907c03fb3882f2acb2fa194a8
5
+ SHA512:
6
+ metadata.gz: 3994b096ae89e3cfe08f0a7358ee336ca38606b161ae8b5ffd8232ccb0b8e0efc75e53fa0aa85952be798f6da6b2fd4771e8f021b5fa9a134e0f6f2f9300e23b
7
+ data.tar.gz: a2d931e05870443e7a741df982193d8499eaeae9fffc04f6641cef25fec39067f5d4807e94289743e9946dcfd31667a10e9de2a10a52e09e13c588cfd421398d
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
+ - 2.0.0
4
5
  - rbx-19mode
5
6
  script: bundle exec rspec spec
data/README.md CHANGED
@@ -22,23 +22,31 @@ Or install it yourself as:
22
22
 
23
23
  ### Future
24
24
 
25
- f = FutureProof::Future.new { |a, b| a + b }
25
+ future = FutureProof::Future.new { |a, b| a + b }
26
26
 
27
- f.call(1, 2) # => executing in a thread
27
+ future.call(1, 2) # => executes in a thread
28
28
 
29
- f.value # => returning value when ready
29
+ future.value # => returns value when ready
30
30
 
31
31
  ### ThreadPool
32
32
 
33
- tp = FutureProof::ThreadPool.new(5)
33
+ thread_pool = FutureProof::ThreadPool.new(5)
34
34
 
35
35
  10.times do |i|
36
- tp.submit(Proc.new { |a, b| a + b }, i, i + 1)
36
+ thread_pool.submit i, i + 1 do |a, b|
37
+ a + b
38
+ end
37
39
  end
38
40
 
39
- tp.perform # executing 10 procs in 5 threads
41
+ thread_pool.perform # executes 10 procs in 5 threads
40
42
 
41
- tp.values # releasing threads and returning values
43
+ thread_pool.values # releases threads and returns values
44
+
45
+ ## Exceptions
46
+
47
+ If exception happens inside a thread it doesn't affect the whole process.
48
+ Exception is raised when accessing specific value:
49
+ thread_pool.values[3] # => raises exception
42
50
 
43
51
  ## Contributing
44
52
 
@@ -0,0 +1,10 @@
1
+ module FutureProof
2
+ module Exceptionable
3
+
4
+ private
5
+
6
+ def raise_or_value(value)
7
+ value.is_a?(StandardError) ? raise(value) : value
8
+ end
9
+ end
10
+ end
@@ -1,23 +1,25 @@
1
- class FutureProof::Future
2
- def initialize(&block)
3
- @block = block
4
- end
1
+ module FutureProof
2
+ class Future
3
+ def initialize(&block)
4
+ @block = block
5
+ end
5
6
 
6
- def call(*args)
7
- thread(*args)
8
- end
7
+ def call(*args)
8
+ thread(*args)
9
+ end
9
10
 
10
- def value(*args)
11
- thread(*args).value
12
- end
11
+ def value(*args)
12
+ thread(*args).value
13
+ end
13
14
 
14
- def complete?
15
- !thread.alive?
16
- end
15
+ def complete?
16
+ !thread.alive?
17
+ end
17
18
 
18
- private
19
+ private
19
20
 
20
- def thread(*args)
21
- @thread ||= Thread.new { @block.call *args }
21
+ def thread(*args)
22
+ @thread ||= Thread.new { @block.call *args }
23
+ end
22
24
  end
23
- end
25
+ end
@@ -0,0 +1,28 @@
1
+ module FutureProof
2
+ class FutureArray
3
+ include Enumerable
4
+ include FutureProof::Exceptionable
5
+
6
+ def initialize(arg)
7
+ @arry = Array.new arg
8
+ end
9
+
10
+ def [](index)
11
+ raise_or_value @arry[index]
12
+ end
13
+
14
+ def all
15
+ map { |a| a }
16
+ end
17
+
18
+ [:first, :last, :empty?].each do |method|
19
+ define_method method do |*args, &block|
20
+ raise_or_value @arry.send(method, *args, &block)
21
+ end
22
+ end
23
+
24
+ def each
25
+ @arry.each { |a| yield raise_or_value(a) }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module FutureProof
2
+ class FutureProofException < Exception; end
3
+ end
@@ -0,0 +1,59 @@
1
+ module FutureProof
2
+ class FutureQueue < Queue
3
+
4
+ include FutureProof::Exceptionable
5
+
6
+ def initialize
7
+ @finished = false
8
+ super()
9
+ end
10
+
11
+ def push(*values, &block)
12
+ raise_future_proof_exception if finished?
13
+ value = if block_given?
14
+ begin
15
+ block.call(*values)
16
+ rescue => e
17
+ e
18
+ end
19
+ else
20
+ values.size == 1 ? values[0] : values
21
+ end
22
+ super(value)
23
+ end
24
+
25
+ def pop
26
+ raise_future_proof_exception if finished?
27
+ raise_or_value super
28
+ end
29
+
30
+ def values
31
+ raise_future_proof_exception unless finished?
32
+ @values ||= FutureProof::FutureArray.new(instance_variable_get(:@que).dup)
33
+ end
34
+
35
+ def [](index)
36
+ raise_future_proof_exception unless finished?
37
+ values[index]
38
+ end
39
+
40
+ def stop!
41
+ @finished = true
42
+ end
43
+
44
+ def start!
45
+ @values = nil
46
+ @finished = false
47
+ end
48
+
49
+ private
50
+
51
+ def finished?
52
+ @finished
53
+ end
54
+
55
+ def raise_future_proof_exception
56
+ raise FutureProof::FutureProofException.new 'Queu is not accessible in the state given!'
57
+ end
58
+ end
59
+ end
@@ -1,58 +1,57 @@
1
1
  require 'thread'
2
2
 
3
- class FutureProof::ThreadPool
4
- def initialize(size)
5
- @size = size
6
- @threads = []
7
- @queue = Queue.new
8
- @values = Queue.new
9
- end
3
+ module FutureProof
4
+ class ThreadPool
5
+ def initialize(size)
6
+ @size = size
7
+ @threads = []
8
+ @queue = Queue.new
9
+ @values = FutureProof::FutureQueue.new
10
+ end
10
11
 
11
- def submit(job, *args)
12
- @queue.push [job, args]
13
- end
12
+ def submit(*args, &block)
13
+ @queue.push [block, args]
14
+ end
14
15
 
15
- def perform
16
- unless @threads.any? { |t| t.alive? }
17
- @size.times do
18
- @threads << Thread.new do
19
- while job = @queue.pop
20
- if job == :END_OF_WORK
21
- break
22
- else
23
- result = begin
24
- job[0].call(*job[1])
25
- rescue => e
26
- e.to_s
16
+ def perform
17
+ unless @threads.any? { |t| t.alive? }
18
+ @values.start!
19
+ @size.times do
20
+ @threads << Thread.new do
21
+ while job = @queue.pop
22
+ if job == :END_OF_WORK
23
+ break
24
+ else
25
+ @values.push *job[1], &job[0]
27
26
  end
28
- @values.push result
29
27
  end
30
28
  end
31
29
  end
32
30
  end
33
31
  end
34
- end
35
32
 
36
- def finalize
37
- @size.times { @queue.push :END_OF_WORK }
38
- end
33
+ def finalize
34
+ @size.times { @queue.push :END_OF_WORK }
35
+ end
39
36
 
40
- def wait
41
- finalize
42
- @threads.map &:join
43
- end
37
+ def wait
38
+ finalize
39
+ @threads.map &:join
40
+ end
44
41
 
45
- def values
46
- wait
47
- @values.instance_variable_get(:@que)
48
- end
42
+ def values
43
+ wait
44
+ @values.stop!
45
+ @values.values
46
+ end
49
47
 
50
- def finalize
51
- @size.times { @queue.push :END_OF_WORK }
52
- end
48
+ def finalize
49
+ @size.times { @queue.push :END_OF_WORK }
50
+ end
53
51
 
54
- def finalize!
55
- @queue.clear
56
- finalize
52
+ def finalize!
53
+ @queue.clear
54
+ finalize
55
+ end
57
56
  end
58
- end
57
+ end
@@ -1,3 +1,3 @@
1
1
  module FutureProof
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
data/lib/future_proof.rb CHANGED
@@ -1,3 +1,7 @@
1
1
  require 'future_proof/version'
2
+ require 'future_proof/exceptionable'
2
3
  require 'future_proof/future'
3
- require 'future_proof/thread_pool'
4
+ require 'future_proof/thread_pool'
5
+ require 'future_proof/future_queue'
6
+ require 'future_proof/future_array'
7
+ require 'future_proof/future_proof_exception'
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe FutureProof::Exceptionable do
4
+ let(:exceptionable) do
5
+ class TestExceptionable
6
+ include FutureProof::Exceptionable
7
+ end
8
+ TestExceptionable.new
9
+ end
10
+
11
+ context 'without exception' do
12
+ it 'should return value' do
13
+ exceptionable.send(:raise_or_value, 25).should eq 25
14
+ end
15
+ end
16
+
17
+ context 'with exception' do
18
+ it 'should raise error' do
19
+ expect {
20
+ exceptionable.send(:raise_or_value, ZeroDivisionError.new)
21
+ }.to raise_error(ZeroDivisionError)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe FutureProof::FutureArray do
4
+ let(:future_array) do
5
+ FutureProof::FutureArray.new [1, 2, 3, 4, 5]
6
+ end
7
+
8
+ let(:future_array_with_exception) do
9
+ FutureProof::FutureArray.new [1, 2, 3, 4, ZeroDivisionError.new]
10
+ end
11
+
12
+ describe '#[]' do
13
+ context 'without exception' do
14
+ it 'should retrun value' do
15
+ future_array[4].should eq 5
16
+ end
17
+ end
18
+
19
+ context 'with_exception' do
20
+ it 'should raise exception' do
21
+ expect { future_array_with_exception[4] }.to raise_error(ZeroDivisionError)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#last' do
27
+ context 'without exception' do
28
+ it 'should retrun value' do
29
+ future_array.last.should eq 5
30
+ end
31
+ end
32
+
33
+ context 'with_exception' do
34
+ it 'should raise exception' do
35
+ expect { future_array_with_exception.last }.to raise_error(ZeroDivisionError)
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#select' do
41
+ context 'without exception' do
42
+ it 'should select value' do
43
+ future_array.select { |e| e.even? }.should eq [2, 4]
44
+ end
45
+ end
46
+
47
+ context 'with_exception' do
48
+ it 'should raise exception' do
49
+ expect { future_array_with_exception.select { |e| e.even? } }.to raise_error(ZeroDivisionError)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+
3
+ describe FutureProof::FutureQueue do
4
+ let(:future_queue) { FutureProof::FutureQueue.new }
5
+
6
+ describe '#push' do
7
+ context 'running' do
8
+ context 'without exception' do
9
+ it 'should push' do
10
+ expect {
11
+ future_queue.push 24, 2 do |a, b|
12
+ a / b
13
+ end
14
+ }.not_to raise_error
15
+ end
16
+ end
17
+
18
+ context 'with exception' do
19
+ it 'should push' do
20
+ expect {
21
+ future_queue.push 24, 0 do |a, b|
22
+ a / b
23
+ end
24
+ }.not_to raise_error
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'stoped' do
30
+ before { future_queue.stop! }
31
+ it 'should not push' do
32
+ expect { future_queue.push 5 }.to raise_error(FutureProof::FutureProofException)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#pop' do
38
+ context 'running' do
39
+ context 'without exception' do
40
+ before { future_queue.push 77 }
41
+ it 'should pop value' do
42
+ future_queue.pop.should eq 77
43
+ end
44
+ end
45
+
46
+ context 'with exception' do
47
+ before { future_queue.push ZeroDivisionError.new }
48
+ it 'should raise error' do
49
+ expect { future_queue.pop }.to raise_error(ZeroDivisionError)
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'stoped' do
55
+ before { future_queue.stop! }
56
+ it 'should raise error' do
57
+ expect { future_queue.pop }.to raise_error(FutureProof::FutureProofException)
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#values' do
63
+ context 'stoped' do
64
+ before { future_queue.stop! }
65
+ it 'should return instance of FutureArray' do
66
+ future_queue.values.should be_instance_of FutureProof::FutureArray
67
+ end
68
+ end
69
+
70
+ context 'running' do
71
+ it 'should not allow get values' do
72
+ expect { future_queue.values }.to raise_error(FutureProof::FutureProofException)
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#[]' do
78
+ context 'stoped' do
79
+ context 'single value' do
80
+ before do
81
+ future_queue.push 25
82
+ future_queue.stop!
83
+ end
84
+
85
+ it 'should return value' do
86
+ future_queue[0].should eq 25
87
+ end
88
+ end
89
+
90
+ context 'multiple values' do
91
+ before do
92
+ future_queue.push 25, 34
93
+ future_queue.stop!
94
+ end
95
+
96
+ it 'should return value' do
97
+ future_queue[0].should eq [25, 34]
98
+ end
99
+ end
100
+
101
+ context 'exception' do
102
+ before do
103
+ future_queue.push ZeroDivisionError.new
104
+ future_queue.stop!
105
+ end
106
+
107
+ it 'should raise exception' do
108
+ expect { future_queue[0] }.to raise_error(ZeroDivisionError)
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ context 'running' do
115
+ it 'should not allow acess value' do
116
+ expect { future_queue[0] }.to raise_error(FutureProof::FutureProofException)
117
+ end
118
+ end
119
+ end
120
+
121
+ describe '#stop and #start' do
122
+ it 'should allow stop and run queue multiple times' do
123
+ future_queue.push 1
124
+ future_queue.push 2
125
+ future_queue.push 3
126
+
127
+ future_queue.stop!
128
+
129
+ array_id = future_queue.values.object_id
130
+
131
+ array_id.should eq future_queue.values.object_id
132
+
133
+ future_queue.start!
134
+
135
+ future_queue.push 4
136
+ future_queue.push 5
137
+ future_queue.push 6
138
+
139
+ future_queue.pop.should eq 1
140
+ future_queue.pop.should eq 2
141
+ future_queue.pop.should eq 3
142
+
143
+ future_queue.stop!
144
+
145
+ array_id.should_not eq future_queue.values.object_id
146
+
147
+ future_queue.start!
148
+
149
+ future_queue.pop.should eq 4
150
+ future_queue.pop.should eq 5
151
+ future_queue.pop.should eq 6
152
+ end
153
+ end
154
+ end
@@ -7,7 +7,7 @@ describe FutureProof::ThreadPool do
7
7
  let(:thread_pool) { FutureProof::ThreadPool.new pool_size }
8
8
 
9
9
  describe '#submit' do
10
- before { thread_pool.submit(task, 24, 2) }
10
+ before { thread_pool.submit(24, 2, &task) }
11
11
 
12
12
  it 'task should be submitted to queue' do
13
13
  thread_pool.instance_variable_get(:@queue).size.should eq 1
@@ -27,13 +27,13 @@ describe FutureProof::ThreadPool do
27
27
  describe 'first task' do
28
28
  it 'should start executing' do
29
29
  thread_pool.finalize
30
- thread_pool.values.should eq [12]
30
+ thread_pool.values.all.should eq [12]
31
31
  end
32
32
  end
33
33
 
34
34
  describe 'rest tasks' do
35
35
  it 'should start executing on #submit' do
36
- thread_pool.submit task, 6, 3
36
+ thread_pool.submit 6, 3, &task
37
37
  sleep 3
38
38
  thread_pool.instance_variable_get(:@values).instance_variable_get(:@que).size.should eq 2
39
39
  end
@@ -43,34 +43,46 @@ describe FutureProof::ThreadPool do
43
43
 
44
44
  describe '#finalize' do
45
45
  it 'should allow all tasks to finish' do
46
- 5.times { thread_pool.submit(task, 24, 2) }
46
+ 5.times { thread_pool.submit(24, 2, &task) }
47
47
  thread_pool.finalize
48
48
  thread_pool.perform
49
- thread_pool.values.should eq [12, 12, 12, 12, 12]
49
+ thread_pool.values.all.should eq [12, 12, 12, 12, 12]
50
50
  end
51
- end
52
51
 
52
+ it 'should allow to restart pool' do
53
+ thread_pool.submit(24, 2, &task)
54
+ thread_pool.perform
55
+ thread_pool.values.count.should eq 1
56
+ thread_pool.submit(24, 2, &task)
57
+ thread_pool.values.count.should eq 1
58
+ thread_pool.perform
59
+ thread_pool.values.count.should eq 2
60
+ end
61
+ end
53
62
 
54
63
  describe '#finalize!' do
55
64
  it 'should not start executing not started tasks' do
56
- 5.times { thread_pool.submit(task, 24, 2) }
65
+ 5.times { thread_pool.submit(24, 2, &task) }
57
66
  thread_pool.perform
58
67
  sleep 0.5
59
68
  thread_pool.finalize!
60
- thread_pool.values.should eq [12, 12, 12]
69
+ thread_pool.values.all.should eq [12, 12, 12]
61
70
  end
62
71
  end
63
72
 
64
73
  describe 'exceptions' do
65
- before { thread_pool.submit(task, 24, 0) }
74
+ before { thread_pool.submit(24, 0, &task) }
66
75
 
67
- it 'should not raise exceptions' do
68
- expect { thread_pool.perform; thread_pool.wait }.not_to raise_error
76
+ context 'without asking for specific value' do
77
+ it 'should not raise exceptions' do
78
+ expect { thread_pool.perform; thread_pool.wait }.not_to raise_error
79
+ end
69
80
  end
70
81
 
71
- it 'should return name of an exception' do
72
- thread_pool.perform
73
- thread_pool.values.should eq ['divided by 0']
82
+ context 'with asking for specific value' do
83
+ it 'should raise an exception' do
84
+ expect { thread_pool.perform; thread_pool.values[0] }.to raise_error(ZeroDivisionError)
85
+ end
74
86
  end
75
87
  end
76
88
  end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: future_proof
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
- prerelease:
4
+ version: 0.0.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Nikita Cernovs
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-04 00:00:00.000000000 Z
11
+ date: 2013-05-18 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,33 +27,29 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rspec
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rake
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  description: Ruby concurrency extenstions
@@ -74,38 +67,47 @@ files:
74
67
  - Rakefile
75
68
  - future_proof.gemspec
76
69
  - lib/future_proof.rb
70
+ - lib/future_proof/exceptionable.rb
77
71
  - lib/future_proof/future.rb
72
+ - lib/future_proof/future_array.rb
73
+ - lib/future_proof/future_proof_exception.rb
74
+ - lib/future_proof/future_queue.rb
78
75
  - lib/future_proof/thread_pool.rb
79
76
  - lib/future_proof/version.rb
77
+ - spec/future_proof/exceptionable_spec.rb
78
+ - spec/future_proof/future_array_spec.rb
79
+ - spec/future_proof/future_queu_spec.rb
80
80
  - spec/future_proof/future_spec.rb
81
81
  - spec/future_proof/thread_pool_spec.rb
82
82
  - spec/spec_helper.rb
83
83
  homepage: http://nikitachernov.github.io/FutureProof
84
84
  licenses:
85
85
  - MIT
86
+ metadata: {}
86
87
  post_install_message:
87
88
  rdoc_options: []
88
89
  require_paths:
89
90
  - lib
90
91
  required_ruby_version: !ruby/object:Gem::Requirement
91
- none: false
92
92
  requirements:
93
- - - ! '>='
93
+ - - '>='
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
96
  required_rubygems_version: !ruby/object:Gem::Requirement
97
- none: false
98
97
  requirements:
99
- - - ! '>='
98
+ - - '>='
100
99
  - !ruby/object:Gem::Version
101
100
  version: '0'
102
101
  requirements: []
103
102
  rubyforge_project:
104
- rubygems_version: 1.8.25
103
+ rubygems_version: 2.0.3
105
104
  signing_key:
106
- specification_version: 3
105
+ specification_version: 4
107
106
  summary: Ruby concurrency extenstions
108
107
  test_files:
108
+ - spec/future_proof/exceptionable_spec.rb
109
+ - spec/future_proof/future_array_spec.rb
110
+ - spec/future_proof/future_queu_spec.rb
109
111
  - spec/future_proof/future_spec.rb
110
112
  - spec/future_proof/thread_pool_spec.rb
111
113
  - spec/spec_helper.rb