thomas_utils 0.1.18 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7d8863b06f5f048eab689b989c66dd14e4d29b4f
4
- data.tar.gz: c5d8484310812422078cf2e147ef9caa1eb5c845
3
+ metadata.gz: 1020f2a07e22d03f1be48e6c97fdc68b4c03631b
4
+ data.tar.gz: 35873e679f97bab51c4cad7b613efeca95733a60
5
5
  SHA512:
6
- metadata.gz: f7a8d86e988a34be705829a487d9404e8e385a03f6eb7bf8c06dec02f7a95cfb6da37fb80eac00eb4efb6e2074aa88e8718e9086005e701f7b447ae586018218
7
- data.tar.gz: 99dc2ba2d5513d7148963fa76edebe30280900673158468ae393fdd6fbe392297f78c84e8ca9a06c378c7269dd80fdcbce6fb622658e6cd433dd09af97302703
6
+ metadata.gz: 2cd4e5f2b799142ea45e3ad1026fd748cfa2e2520072d3a0ca836b61b98dd39e3db42d61f3a88f72f1ec70168fe34d94e6ceb8f020e6a36f54715d057789d029
7
+ data.tar.gz: a511baf55abad9d03c3f8f70ce972fad75e3defd6c2b46c1b58235c940450d7e15e74c262062d5f76f02444097202eb9f5b46ba61cc0b468d59495db5ad15c96
@@ -25,4 +25,7 @@ require 'thomas_utils/multi_future_wrapper'
25
25
  require 'thomas_utils/key_comparer'
26
26
  require 'thomas_utils/key_indexer'
27
27
  require 'thomas_utils/key_child'
28
+ require 'thomas_utils/executor_service'
29
+ require 'thomas_utils/constant_var'
30
+ require 'thomas_utils/observation'
28
31
  require 'thomas_utils/future'
@@ -0,0 +1,47 @@
1
+ module ThomasUtils
2
+ class ConstantVar < Struct.new(:time, :value, :reason)
3
+
4
+ def self.value(value)
5
+ new(Time.now, value, nil)
6
+ end
7
+
8
+ def self.none
9
+ new(Time.now, nil, nil)
10
+ end
11
+
12
+ def self.error(error)
13
+ new(Time.now, nil, error)
14
+ end
15
+
16
+ def value!
17
+ raise reason if reason
18
+ value
19
+ end
20
+
21
+ def add_observer(observer = nil, func = :update, &block)
22
+ if block
23
+ observer = block
24
+ func = :call
25
+ end
26
+ observer.public_send(func, time, value, reason)
27
+ end
28
+
29
+ def with_observer(observer = nil, func = :update, &block)
30
+ add_observer(observer, func, &block)
31
+ self
32
+ end
33
+
34
+ def delete_observer(_)
35
+ raise NotImplementedError
36
+ end
37
+
38
+ def delete_observers
39
+ raise NotImplementedError
40
+ end
41
+
42
+ def count_observers
43
+ raise NotImplementedError
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,7 @@
1
+ module Concurrent
2
+ module ExecutorService
3
+ def execute(*args, &block)
4
+ post(*args, &block)
5
+ end
6
+ end
7
+ end
@@ -1,42 +1,53 @@
1
1
  module ThomasUtils
2
- class Future
2
+ class Future < Observation
3
3
  DEFAULT_EXECUTOR = ::Concurrent::CachedThreadPool.new
4
+ IMMEDIATE_EXECUTOR = ::Concurrent::ImmediateExecutor.new
4
5
 
5
- def initialize(options = {})
6
- options[:executor] ||= DEFAULT_EXECUTOR
7
-
8
- @mutex = Mutex.new
9
- @future = ::Concurrent::Future.execute(executor: options[:executor]) do
10
- begin
11
- @result = yield
12
- @result = @result.get if @result.is_a?(FutureWrapper)
13
- @mutex.synchronize { @success_callback.call(@result) if @success_callback }
14
- @result
15
- rescue => e
16
- @error = e
17
- @mutex.synchronize { @failure_callback.call(e) if @failure_callback }
18
- end
19
- end
6
+ def self.value(value)
7
+ Observation.new(IMMEDIATE_EXECUTOR, ConstantVar.value(value))
8
+ end
9
+
10
+ def self.immediate(&block)
11
+ none.then(&block)
20
12
  end
21
13
 
22
- def get
23
- result = @future.value
24
- raise @error if @error
25
- result
14
+ def self.none
15
+ Observation.new(IMMEDIATE_EXECUTOR, ConstantVar.none)
26
16
  end
27
17
 
28
- def join
29
- get rescue nil
18
+ def self.error(error)
19
+ Observation.new(IMMEDIATE_EXECUTOR, ConstantVar.error(error))
30
20
  end
31
21
 
32
- def on_success(&block)
33
- @mutex.synchronize { @success_callback = block }
34
- @success_callback.call(@result) if @future.fulfilled? && !@error
22
+ def self.all(observations)
23
+ observable = Concurrent::IVar.new
24
+ left = observations.count
25
+ buffer = [nil] * left
26
+ mutex = Mutex.new
27
+ observations.each_with_index do |observation, index|
28
+ observation.on_complete do |value, error|
29
+ if error
30
+ observable.fail(error)
31
+ else
32
+ buffer[index] = value
33
+ done = false
34
+ mutex.synchronize do
35
+ left -= 1
36
+ done = !!left.zero?
37
+ end
38
+ observable.set(buffer) if done
39
+ end
40
+ end
41
+ end
42
+ Observation.new(DEFAULT_EXECUTOR, observable)
35
43
  end
36
44
 
37
- def on_failure(&block)
38
- @mutex.synchronize { @failure_callback = block }
39
- @failure_callback.call(@error) if @future.fulfilled? && @error
45
+ def initialize(options = {}, &block)
46
+ executor = options.fetch(:executor) { DEFAULT_EXECUTOR }
47
+ observable = Concurrent::Future.execute(executor: executor, &block)
48
+ super(executor, observable)
40
49
  end
50
+
41
51
  end
42
- end
52
+ end
53
+
@@ -0,0 +1,167 @@
1
+ module ThomasUtils
2
+ class Observation
3
+ extend Forwardable
4
+
5
+ def_delegator :@observable, :value!, :get
6
+
7
+ def initialize(executor, observable)
8
+ @executor = executor
9
+ @observable = observable
10
+ end
11
+
12
+ def on_success
13
+ @observable.add_observer do |_, value, error|
14
+ @executor.post do
15
+ yield value unless error
16
+ end
17
+ end
18
+ self
19
+ end
20
+
21
+ def on_failure
22
+ @observable.add_observer do |_, _, error|
23
+ @executor.post do
24
+ yield error if error
25
+ end
26
+ end
27
+ self
28
+ end
29
+
30
+ def on_complete
31
+ @observable.add_observer do |_, value, error|
32
+ @executor.post do
33
+ yield value, error
34
+ end
35
+ end
36
+ self
37
+ end
38
+
39
+ def join
40
+ @observable.value
41
+ self
42
+ end
43
+
44
+ def then(&block)
45
+ successive(:on_complete_then, &block)
46
+ end
47
+
48
+ def none_fallback
49
+ self.then do |result|
50
+ result || yield
51
+ end
52
+ end
53
+
54
+ def fallback(&block)
55
+ successive(:on_complete_fallback, &block)
56
+ end
57
+
58
+ def ensure(&block)
59
+ successive(:on_complete_ensure, &block)
60
+ end
61
+
62
+ def on_success_ensure(&block)
63
+ self.then do |result|
64
+ child_result = block.call(result)
65
+ if result_is_observation?(child_result)
66
+ child_result.then { result }
67
+ else
68
+ result
69
+ end
70
+ end
71
+ end
72
+
73
+ def on_failure_ensure(&block)
74
+ self.fallback do |error|
75
+ result = block.call(error)
76
+ error_observation = Observation.new(@executor, ConstantVar.error(error))
77
+ if result_is_observation?(result)
78
+ result.then { error_observation }
79
+ else
80
+ error_observation
81
+ end
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def successive(method, &block)
88
+ observable = Concurrent::IVar.new
89
+ send(method, observable, &block)
90
+ Observation.new(@executor, observable)
91
+ end
92
+
93
+ def on_complete_then(observable, &block)
94
+ on_complete do |value, error|
95
+ if error
96
+ observable.fail(error)
97
+ else
98
+ on_success_then(observable, value, &block)
99
+ end
100
+ end
101
+ end
102
+
103
+ def on_complete_fallback(observable, &block)
104
+ on_complete do |value, error|
105
+ if error
106
+ on_failure_fallback(observable, error, &block)
107
+ else
108
+ observable.set(value)
109
+ end
110
+ end
111
+ end
112
+
113
+ def on_success_then(observable, value)
114
+ result = yield value
115
+ if result_is_observation?(result)
116
+ result.on_complete do |child_result, child_error|
117
+ ensure_then(child_error, observable, child_result)
118
+ end
119
+ else
120
+ observable.set(result)
121
+ end
122
+ rescue Exception => error
123
+ observable.fail(error)
124
+ end
125
+
126
+ alias :on_failure_fallback :on_success_then
127
+
128
+ def on_complete_ensure(observable)
129
+ on_complete do |value, error|
130
+ begin
131
+ result = yield value, error
132
+ if result_is_observation?(result)
133
+ ensure_complete_then(error, observable, result, value)
134
+ else
135
+ ensure_then(error, observable, value)
136
+ end
137
+ rescue Exception => child_error
138
+ observable.fail(child_error)
139
+ end
140
+ end
141
+ end
142
+
143
+ def ensure_complete_then(error, observable, result, value)
144
+ result.on_complete do |_, child_error|
145
+ if child_error
146
+ observable.fail(child_error)
147
+ elsif error
148
+ observable.fail(error)
149
+ else
150
+ observable.set(value)
151
+ end
152
+ end
153
+ end
154
+
155
+ def ensure_then(error, observable, value)
156
+ if error
157
+ observable.fail(error)
158
+ else
159
+ observable.set(value)
160
+ end
161
+ end
162
+
163
+ def result_is_observation?(result)
164
+ result.respond_to?(:on_complete) && result.respond_to?(:then)
165
+ end
166
+ end
167
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thomas_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.18
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas RM Rogers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-14 00:00:00.000000000 Z
11
+ date: 2016-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '0.8'
19
+ version: 1.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '0.8'
26
+ version: 1.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: workers
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -50,6 +50,8 @@ files:
50
50
  - LICENSE.txt
51
51
  - README.md
52
52
  - lib/thomas_utils.rb
53
+ - lib/thomas_utils/constant_var.rb
54
+ - lib/thomas_utils/executor_service.rb
53
55
  - lib/thomas_utils/future.rb
54
56
  - lib/thomas_utils/future_wrapper.rb
55
57
  - lib/thomas_utils/key_child.rb
@@ -57,6 +59,7 @@ files:
57
59
  - lib/thomas_utils/key_indexer.rb
58
60
  - lib/thomas_utils/multi_future_wrapper.rb
59
61
  - lib/thomas_utils/object_stream.rb
62
+ - lib/thomas_utils/observation.rb
60
63
  - lib/thomas_utils/periodic_flusher.rb
61
64
  - lib/thomas_utils/symbol_helpers.rb
62
65
  homepage: https://www.github.com/thomasrogers03/thomas_utils