evt-poll 0.1.0.0

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
+ SHA256:
3
+ metadata.gz: 89cf5f449b8dcf01f48c82d39a139434ea8ac185c7178bac749c4394de174bf7
4
+ data.tar.gz: d24ea1791c7bf0400b32b33a83fa07963f700623ed3169dff10967ed417f18f3
5
+ SHA512:
6
+ metadata.gz: 0f875934c022770e5b14eed4b066326ec0c0faf60bb48d8e09450d508e72d20c398dbda654124a0337a8ca484c05ea16f9b1ae392fcac0f1b1220f8d821c55f7
7
+ data.tar.gz: ea00a5edf1896470713a6cb76cd516ae97f19a99e8832f7c54f63aa7cc2525b96521f2b976173d431119587c46c17eee8b7db3397e1aab98a41e35b74d163821
data/lib/poll.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'log'
2
+ require 'telemetry'
3
+ require 'clock'
4
+
5
+ require 'poll/log'
6
+ require 'poll/cycle'
7
+ require 'poll/defaults'
@@ -0,0 +1,3 @@
1
+ require 'clock/controls'
2
+
3
+ require 'poll/controls/time'
@@ -0,0 +1,5 @@
1
+ class Poll
2
+ module Controls
3
+ Time = Clock::Controls::Time
4
+ end
5
+ end
data/lib/poll/cycle.rb ADDED
@@ -0,0 +1,179 @@
1
+ class Poll
2
+ Error = Class.new(RuntimeError)
3
+
4
+ include Log::Dependency
5
+
6
+ dependency :clock, Clock::UTC
7
+ dependency :telemetry, Telemetry
8
+
9
+ attr_writer :interval_milliseconds
10
+ def interval_milliseconds
11
+ @interval_milliseconds ||= Defaults.interval_milliseconds
12
+ end
13
+
14
+ attr_accessor :timeout_milliseconds
15
+
16
+ attr_writer :delay_condition
17
+ def delay_condition
18
+ @delay_condition ||= Defaults.delay_condition
19
+ end
20
+
21
+ def self.build(interval_milliseconds: nil, timeout_milliseconds: nil, delay_condition: nil)
22
+ instance = new
23
+
24
+ instance.interval_milliseconds = interval_milliseconds
25
+ instance.timeout_milliseconds = timeout_milliseconds
26
+ instance.delay_condition = delay_condition
27
+
28
+ instance.configure
29
+
30
+ instance
31
+ end
32
+
33
+ def self.configure(receiver, attr_name: nil, interval_milliseconds: nil, timeout_milliseconds: nil, delay_condition: nil, cycle: nil)
34
+ attr_name ||= :cycle
35
+
36
+ if !cycle.nil?
37
+ instance = cycle
38
+ else
39
+ instance = build(interval_milliseconds: interval_milliseconds, timeout_milliseconds: timeout_milliseconds, delay_condition: delay_condition)
40
+ end
41
+
42
+ receiver.public_send "#{attr_name}=", instance
43
+ end
44
+
45
+ def self.none
46
+ None.build
47
+ end
48
+
49
+ def configure
50
+ Clock::UTC.configure self
51
+ ::Telemetry.configure self
52
+ end
53
+
54
+ def self.call(interval_milliseconds: nil, timeout_milliseconds: nil, delay_condition: nil, &action)
55
+ instance = build(interval_milliseconds: interval_milliseconds, timeout_milliseconds: timeout_milliseconds, delay_condition: delay_condition)
56
+ instance.call(&action)
57
+ end
58
+
59
+ def call(&action)
60
+ stop_time = nil
61
+ stop_time_iso8601 = nil
62
+ if !timeout_milliseconds.nil?
63
+ stop_time = clock.now + (timeout_milliseconds.to_f / 1000.0)
64
+ stop_time_iso8601 = clock.iso8601(stop_time, precision: 5)
65
+ end
66
+
67
+ logger.trace { "Cycling (Interval Milliseconds: #{interval_milliseconds}, Timeout Milliseconds: #{timeout_milliseconds.inspect}, Stop Time: #{stop_time_iso8601})" }
68
+
69
+ cycle = -1
70
+ result = nil
71
+ loop do
72
+ cycle += 1
73
+ telemetry.record :cycle, cycle
74
+
75
+ result, elapsed_milliseconds = invoke(cycle, &action)
76
+
77
+ if delay_condition.(result)
78
+ logger.debug { "Got no results from action (Cycle: #{cycle})" }
79
+ delay(elapsed_milliseconds)
80
+ else
81
+ logger.debug { "Got results from action (Cycle: #{cycle})" }
82
+ telemetry.record :got_result
83
+ break
84
+ end
85
+
86
+ if !timeout_milliseconds.nil?
87
+ now = clock.now
88
+ if now >= stop_time
89
+ logger.debug { "Timeout has lapsed (Cycle: #{cycle}, Stop Time: #{stop_time_iso8601}, Timeout Milliseconds: #{timeout_milliseconds})" }
90
+ telemetry.record :timed_out, now
91
+ break
92
+ end
93
+ end
94
+ end
95
+
96
+ logger.debug { "Cycled (Iterations: #{cycle + 1}, Interval Milliseconds: #{interval_milliseconds}, Timeout Milliseconds: #{timeout_milliseconds.inspect}, Stop Time: #{stop_time_iso8601})" }
97
+
98
+ return result
99
+ end
100
+
101
+ def invoke(cycle, &action)
102
+ if action.nil?
103
+ raise Error, "Poll must be actuated with a block"
104
+ end
105
+
106
+ action_start_time = clock.now
107
+
108
+ logger.trace { "Invoking action (Cycle: #{cycle}, Start Time: #{clock.iso8601(action_start_time, precision: 5)})" }
109
+
110
+ result = action.call(cycle)
111
+
112
+ action_end_time = clock.now
113
+ elapsed_milliseconds = clock.elapsed_milliseconds(action_start_time, action_end_time)
114
+
115
+ telemetry.record :invoked_action, elapsed_milliseconds
116
+
117
+ logger.debug { "Invoked action (Cycle: #{cycle}, Elapsed Milliseconds: #{elapsed_milliseconds}, Start Time: #{clock.iso8601(action_start_time, precision: 5)}, End Time: #{clock.iso8601(action_end_time, precision: 5)})" }
118
+
119
+ [result, elapsed_milliseconds]
120
+ end
121
+
122
+ def delay(elapsed_milliseconds)
123
+ delay_milliseconds = interval_milliseconds - elapsed_milliseconds
124
+
125
+ logger.trace { "Delaying (Delay Milliseconds: #{delay_milliseconds}, Interval Milliseconds: #{interval_milliseconds}, Elapsed Milliseconds: #{elapsed_milliseconds})" }
126
+
127
+ if delay_milliseconds <= 0
128
+ logger.debug { "Elapsed time exceeds or equals interval. Not delayed. (Delay Milliseconds: #{delay_milliseconds}, Interval Milliseconds: #{interval_milliseconds}, Elapsed Milliseconds: #{elapsed_milliseconds})" }
129
+ return
130
+ end
131
+
132
+ delay_seconds = (delay_milliseconds.to_f / 1000.0)
133
+
134
+ sleep delay_seconds
135
+
136
+ telemetry.record :delayed, delay_milliseconds
137
+
138
+ logger.debug { "Finished delaying (Delay Milliseconds: #{delay_milliseconds}, Interval Milliseconds: #{interval_milliseconds}, Elapsed Milliseconds: #{elapsed_milliseconds})" }
139
+ end
140
+
141
+ def self.register_telemetry_sink(cycle)
142
+ sink = Telemetry.sink
143
+ cycle.telemetry.register(sink)
144
+ sink
145
+ end
146
+
147
+ module Telemetry
148
+ class Sink
149
+ include ::Telemetry::Sink
150
+
151
+ record :cycle
152
+ record :invoked_action
153
+ record :got_result
154
+ record :delayed
155
+ record :timed_out
156
+ end
157
+
158
+ def self.sink
159
+ Sink.new
160
+ end
161
+ end
162
+
163
+ module Substitute
164
+ def self.build
165
+ instance = Poll.build(timeout_milliseconds: 0)
166
+
167
+ sink = Poll.register_telemetry_sink(instance)
168
+ instance.telemetry_sink = sink
169
+
170
+ instance.configure
171
+
172
+ instance
173
+ end
174
+
175
+ class Poll < ::Poll
176
+ attr_accessor :telemetry_sink
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,17 @@
1
+ class Poll
2
+ module Defaults
3
+ def self.interval_milliseconds
4
+ 0
5
+ end
6
+
7
+ def self.delay_condition
8
+ lambda do |result|
9
+ if result.respond_to?(:empty?)
10
+ result.empty?
11
+ else
12
+ result.nil?
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/poll/log.rb ADDED
@@ -0,0 +1,9 @@
1
+ class Poll
2
+ class Log < ::Log
3
+ def tag!(tags)
4
+ tags << :cycle
5
+ tags << :library
6
+ tags << :verbose
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: evt-poll
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - The Eventide Project
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: evt-log
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: evt-telemetry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: evt-clock
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test_bench
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: " "
70
+ email: opensource@eventide-project.org
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/poll.rb
76
+ - lib/poll/controls.rb
77
+ - lib/poll/controls/time.rb
78
+ - lib/poll/cycle.rb
79
+ - lib/poll/defaults.rb
80
+ - lib/poll/log.rb
81
+ homepage: https://github.com/eventide-project/cycle
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '2.4'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.7.3
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Generalized implementation of polling
105
+ test_files: []