future_proof 0.0.2 → 0.0.3

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 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