whirled_peas 0.1.0 → 0.1.1
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/CHANGELOG.md +9 -0
- data/README.md +69 -27
- data/lib/whirled_peas.rb +8 -4
- data/lib/whirled_peas/frame/consumer.rb +24 -18
- data/lib/whirled_peas/frame/{loop.rb → event_loop.rb} +13 -9
- data/lib/whirled_peas/frame/producer.rb +14 -7
- data/lib/whirled_peas/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6608e1e6c9505eefea5409bcdc0f8af117b5bcf390564d163315f05b9fe73323
|
4
|
+
data.tar.gz: 713b7ff1c83c462b137e1a9203282ad74f8dc71e660237da35b40d472ae1d486
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81d5ae8c86907fa5a32025be54fd4649d1c75c93726d0f581f0f63cd7182f8381285451cef1c2350b223c378ba98c00682b2a96ac65b31cabc05b5871cbaf1fc
|
7
|
+
data.tar.gz: 52cdda8685d24cd1ad89c5a2acc70b7fb77c22cf6c21cb3d3eb20dce5df2660bc2e9d285e04fbb13018189f9b2f9700203e3915c13f678ecb4d92f2ce173bfab
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## v0.1.1 - 2021-01-20
|
4
|
+
|
5
|
+
- [3852c8c](https://github.com/tcollier/whirled_peas/tree/3852c8c700c2e8fb92e65bbca1c99be74304c6d0): Improve error handling
|
6
|
+
|
7
|
+
## v0.1.0 - 2021-01-20
|
8
|
+
|
9
|
+
- [f343434](https://github.com/tcollier/whirled_peas/tree/f34343458097da04d5846ab13533e8226ba04d75): Inaugural release
|
data/README.md
CHANGED
@@ -122,6 +122,36 @@ A `ComposableElement` provides the following methods to add child elements
|
|
122
122
|
- `add_grid` - yields a `ComposableElement` and a `GridSettings`, which will be added to the parent's children
|
123
123
|
- `add_text` - yields `nil` and a `TextSettings`, which will be added to the parent's children
|
124
124
|
|
125
|
+
E.g.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
WhirledPeas.template do |template, template_settings|
|
129
|
+
template_settings.bg_color = :blue
|
130
|
+
template.add_grid do |grid, grid_settings|
|
131
|
+
grid_settings.num_cols = 10
|
132
|
+
100.times do |i|
|
133
|
+
grid.add_text { i.to_s }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
The above template can also be broken down into more manageable methods, e.g.
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
def number_grid(grid, settings)
|
143
|
+
settings.num_cols = 10
|
144
|
+
100.times do |i|
|
145
|
+
grid.add_text { i.to_s }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
WhirledPeas.template do |template, settings|
|
150
|
+
settings.bg_color = :blue
|
151
|
+
template.add_grid(&method(:number_grid))
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
125
155
|
Additionally, if no child element is explicitly added to a `GridElement`, but the block returns an array of strings or numbers, those will be converted to `TextElements` and added as children to the `GridElement`. For example, these are identical ways to create a grid of strings
|
126
156
|
|
127
157
|
```ruby
|
@@ -276,39 +306,51 @@ Many of these also have a "bright" option:
|
|
276
306
|
|
277
307
|
```ruby
|
278
308
|
class TemplateFactory
|
279
|
-
def
|
280
|
-
|
309
|
+
def build(frame, args)
|
310
|
+
set_state(frame, args)
|
311
|
+
WhirledPeas.template do |t|
|
312
|
+
t.add_box(&method(:body))
|
313
|
+
end
|
281
314
|
end
|
282
315
|
|
283
|
-
|
284
|
-
@numbers = args['numbers'] if args.key?('numbers')
|
316
|
+
private
|
285
317
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
318
|
+
def set_state(frame, args)
|
319
|
+
@frame = frame
|
320
|
+
@numbers = args.key?('numbers') ? args['numbers'] || []
|
321
|
+
@sum = args['sum'] if args.key?('sum')
|
322
|
+
@low = args['low'] if args.key?('low')
|
323
|
+
@high = args['high'] if args.key?('high')
|
324
|
+
end
|
325
|
+
|
326
|
+
def title(_elem, settings)
|
327
|
+
settings.underline = true
|
328
|
+
"Pair Finder"
|
329
|
+
end
|
330
|
+
|
331
|
+
def sum(_elem, settings)
|
332
|
+
settings.color = @frame == 'found-pair' ? :green : :red
|
333
|
+
@sum ? "Sum: #{@sum}" : 'N/A'
|
334
|
+
end
|
335
|
+
|
336
|
+
def number_grid(elem, settings)
|
337
|
+
settings.full_border
|
338
|
+
@numbers.each.with_index do |num, index|
|
339
|
+
g.add_text do |_, settings|
|
340
|
+
settings.bg_color = (@low == index || @high == index) ? :cyan : :white
|
341
|
+
num.to_s
|
309
342
|
end
|
310
343
|
end
|
311
344
|
end
|
345
|
+
|
346
|
+
def body(elem, settings)
|
347
|
+
settings.flow = :l2r
|
348
|
+
settings.auto_margin = true
|
349
|
+
|
350
|
+
elem.add_box(&method(:title))
|
351
|
+
elem.add_box(&method(:sum))
|
352
|
+
elem.add_grid(&method(:number_grid))
|
353
|
+
end
|
312
354
|
end
|
313
355
|
```
|
314
356
|
|
data/lib/whirled_peas.rb
CHANGED
@@ -11,6 +11,7 @@ module WhirledPeas
|
|
11
11
|
DEFAULT_PORT = 8765
|
12
12
|
DEFAULT_REFRESH_RATE = 30
|
13
13
|
|
14
|
+
LOGGER_ID = 'MAIN'
|
14
15
|
|
15
16
|
def self.start(driver, template_factory, log_level: Logger::INFO, refresh_rate: DEFAULT_REFRESH_RATE, host: DEFAULT_HOST, port: DEFAULT_PORT)
|
16
17
|
logger = Logger.new(File.open('whirled_peas.log', 'a'))
|
@@ -20,16 +21,19 @@ module WhirledPeas
|
|
20
21
|
end
|
21
22
|
|
22
23
|
consumer = Frame::Consumer.new(template_factory, refresh_rate, logger)
|
23
|
-
consumer_thread = Thread.new
|
24
|
+
consumer_thread = Thread.new do
|
25
|
+
Thread.current.report_on_exception = false
|
26
|
+
consumer.start(host: host, port: port)
|
27
|
+
end
|
24
28
|
|
25
29
|
Frame::Producer.start(logger: logger, host: host, port: port) do |producer|
|
26
30
|
begin
|
27
31
|
driver.start(producer)
|
28
32
|
producer.stop
|
29
33
|
rescue => e
|
30
|
-
logger.warn(
|
31
|
-
logger.error(
|
32
|
-
logger.error(
|
34
|
+
logger.warn(LOGGER_ID) { 'Driver exited with error, terminating producer...' }
|
35
|
+
logger.error(LOGGER_ID) { e }
|
36
|
+
logger.error(LOGGER_ID) { e.backtrace.join("\n") }
|
33
37
|
producer.terminate
|
34
38
|
raise
|
35
39
|
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'json'
|
3
3
|
|
4
|
-
require_relative '
|
4
|
+
require_relative 'event_loop'
|
5
5
|
|
6
6
|
module WhirledPeas
|
7
7
|
module Frame
|
8
8
|
class Consumer
|
9
|
+
LOGGER_ID = 'CONSUMER'
|
10
|
+
|
9
11
|
def initialize(template_factory, refresh_rate, logger=NullLogger.new)
|
10
|
-
@
|
12
|
+
@event_loop = EventLoop.new(template_factory, refresh_rate, logger)
|
11
13
|
@logger = logger
|
12
14
|
@running = false
|
13
15
|
@mutex = Mutex.new
|
@@ -15,10 +17,13 @@ module WhirledPeas
|
|
15
17
|
|
16
18
|
def start(host:, port:)
|
17
19
|
mutex.synchronize { @running = true }
|
18
|
-
loop_thread = Thread.new
|
20
|
+
loop_thread = Thread.new do
|
21
|
+
Thread.current.report_on_exception = false
|
22
|
+
event_loop.start
|
23
|
+
end
|
19
24
|
socket = TCPSocket.new(host, port)
|
20
|
-
logger.info(
|
21
|
-
while @running
|
25
|
+
logger.info(LOGGER_ID) { "Connected to #{host}:#{port}" }
|
26
|
+
while @running && event_loop.running?
|
22
27
|
line = socket.gets
|
23
28
|
if line.nil?
|
24
29
|
sleep(0.001)
|
@@ -27,35 +32,36 @@ module WhirledPeas
|
|
27
32
|
args = JSON.parse(line)
|
28
33
|
name = args.delete('name')
|
29
34
|
if [Frame::EOF, Frame::TERMINATE].include?(name)
|
30
|
-
logger.info(
|
31
|
-
|
35
|
+
logger.info(LOGGER_ID) { "Received #{name} event, stopping..." }
|
36
|
+
event_loop.stop if name == Frame::TERMINATE
|
32
37
|
@running = false
|
33
38
|
else
|
34
39
|
duration = args.delete('duration')
|
35
|
-
|
40
|
+
event_loop.enqueue(name, duration, args)
|
36
41
|
end
|
37
42
|
end
|
38
|
-
logger.info(
|
43
|
+
logger.info(LOGGER_ID) { 'Exited normally' }
|
44
|
+
logger.info(LOGGER_ID) { 'Waiting for loop thread to exit' }
|
45
|
+
loop_thread.join
|
39
46
|
rescue => e
|
40
|
-
|
41
|
-
logger.
|
42
|
-
logger.error(
|
43
|
-
|
47
|
+
event_loop.stop if event_loop.running?
|
48
|
+
logger.warn(LOGGER_ID) { 'Exited with error' }
|
49
|
+
logger.error(LOGGER_ID) { e.message }
|
50
|
+
logger.error(LOGGER_ID) { e.backtrace.join("\n") }
|
51
|
+
raise
|
44
52
|
ensure
|
45
|
-
logger.info(
|
46
|
-
loop_thread.join
|
47
|
-
logger.info('CONSUMER') { "Closing socket" }
|
53
|
+
logger.info(LOGGER_ID) { 'Closing socket' }
|
48
54
|
socket.close if socket
|
49
55
|
end
|
50
56
|
|
51
57
|
def stop
|
52
|
-
logger.info(
|
58
|
+
logger.info(LOGGER_ID) { 'Stopping...' }
|
53
59
|
mutex.synchronize { @running = false }
|
54
60
|
end
|
55
61
|
|
56
62
|
private
|
57
63
|
|
58
|
-
attr_reader :
|
64
|
+
attr_reader :event_loop, :logger, :mutex
|
59
65
|
end
|
60
66
|
end
|
61
67
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module WhirledPeas
|
2
2
|
module Frame
|
3
|
-
class
|
3
|
+
class EventLoop
|
4
4
|
def initialize(template_factory, refresh_rate, logger=NullLogger.new)
|
5
5
|
@template_factory = template_factory
|
6
6
|
@queue = Queue.new
|
@@ -12,8 +12,12 @@ module WhirledPeas
|
|
12
12
|
queue.push([name, duration, args])
|
13
13
|
end
|
14
14
|
|
15
|
+
def running?
|
16
|
+
@running
|
17
|
+
end
|
18
|
+
|
15
19
|
def start
|
16
|
-
logger.info('EVENT LOOP') {
|
20
|
+
logger.info('EVENT LOOP') { 'Starting' }
|
17
21
|
@running = true
|
18
22
|
screen = UI::Screen.new
|
19
23
|
sleep(0.01) while queue.empty? # Wait for the first event
|
@@ -33,17 +37,17 @@ module WhirledPeas
|
|
33
37
|
end
|
34
38
|
sleep(next_frame_at - Time.now)
|
35
39
|
end
|
36
|
-
logger.info('EVENT LOOP') {
|
37
|
-
rescue
|
38
|
-
logger.warn('EVENT LOOP') {
|
39
|
-
|
40
|
-
|
40
|
+
logger.info('EVENT LOOP') { 'Exiting normally' }
|
41
|
+
rescue
|
42
|
+
logger.warn('EVENT LOOP') { 'Exiting with error' }
|
43
|
+
@running = false
|
44
|
+
raise
|
41
45
|
ensure
|
42
46
|
screen.finalize if screen
|
43
47
|
end
|
44
48
|
|
45
49
|
def stop
|
46
|
-
logger.info('EVENT LOOP') {
|
50
|
+
logger.info('EVENT LOOP') { 'Stopping...' }
|
47
51
|
@running = false
|
48
52
|
end
|
49
53
|
|
@@ -51,6 +55,6 @@ module WhirledPeas
|
|
51
55
|
|
52
56
|
attr_reader :template_factory, :queue, :refresh_rate, :logger
|
53
57
|
end
|
54
|
-
private_constant :
|
58
|
+
private_constant :EventLoop
|
55
59
|
end
|
56
60
|
end
|
@@ -4,19 +4,26 @@ require 'json'
|
|
4
4
|
module WhirledPeas
|
5
5
|
module Frame
|
6
6
|
class Producer
|
7
|
+
LOGGER_ID = 'PRODUCER'
|
8
|
+
|
7
9
|
def self.start(logger: NullLogger.new, host:, port:, &block)
|
8
10
|
server = TCPServer.new(host, port)
|
9
11
|
client = server.accept
|
10
|
-
logger.info(
|
12
|
+
logger.info(LOGGER_ID) { "Connected to #{host}:#{port}" }
|
11
13
|
producer = new(client, logger)
|
12
14
|
yield producer
|
13
|
-
logger.info(
|
15
|
+
logger.info(LOGGER_ID) { 'Exited normally' }
|
14
16
|
rescue => e
|
15
|
-
|
16
|
-
logger.
|
17
|
-
logger.error(
|
17
|
+
producer.terminate
|
18
|
+
logger.warn(LOGGER_ID) { 'Exited with error' }
|
19
|
+
logger.error(LOGGER_ID) { e.message }
|
20
|
+
logger.error(LOGGER_ID) { e.backtrace.join("\n") }
|
21
|
+
raise
|
18
22
|
ensure
|
19
|
-
|
23
|
+
if client
|
24
|
+
logger.info(LOGGER_ID) { 'Closing connection'}
|
25
|
+
client.close
|
26
|
+
end
|
20
27
|
end
|
21
28
|
|
22
29
|
def initialize(client, logger=NullLogger.new)
|
@@ -27,7 +34,7 @@ module WhirledPeas
|
|
27
34
|
|
28
35
|
def send(name, duration: nil, args: {})
|
29
36
|
client.puts(JSON.generate('name' => name, 'duration' => duration, **args))
|
30
|
-
logger.debug(
|
37
|
+
logger.debug(LOGGER_ID) { "Sending frame: #{name}" }
|
31
38
|
end
|
32
39
|
|
33
40
|
def enqueue(name, duration: nil, args: {})
|
data/lib/whirled_peas/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: whirled_peas
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Collier
|
@@ -73,7 +73,7 @@ files:
|
|
73
73
|
- lib/whirled_peas.rb
|
74
74
|
- lib/whirled_peas/frame.rb
|
75
75
|
- lib/whirled_peas/frame/consumer.rb
|
76
|
-
- lib/whirled_peas/frame/
|
76
|
+
- lib/whirled_peas/frame/event_loop.rb
|
77
77
|
- lib/whirled_peas/frame/producer.rb
|
78
78
|
- lib/whirled_peas/null_logger.rb
|
79
79
|
- lib/whirled_peas/ui.rb
|