whirled_peas 0.1.0 → 0.4.1

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
  SHA256:
3
- metadata.gz: 85262725d00d4055dceb88c2672afc6a7f5027626a3038998706b5d8e2d1a3aa
4
- data.tar.gz: 15bc9278fd2945d0ea5cf6a5436ba11e00724e71f128ec487f5569c3689bfaeb
3
+ metadata.gz: 563cc06007d90aa0599b61c5366a5d9b2c5224ec2973367b6c358cad95ddaedc
4
+ data.tar.gz: fa798abdfb8a8e4fd229f08b964c8ee4f4d480939ece9ddaeb5731c171985504
5
5
  SHA512:
6
- metadata.gz: 7270704f5ff2adc5d225dd7c612f489a968c9d5efc2fef399b65a8ad7320fcad2920ec355645f961744577df740efbfcf4947f477e13224702764bf64931a52b
7
- data.tar.gz: a46f83016c3038275fccc907894c0ea7fc3ce0553c5da343d77fbfbbdf9f619ca169346e11b5862cf502e7785372a750a0ab668bfffb3198b1d529a75cd198a5
6
+ metadata.gz: 1be24e74d75f87a001b26f216e320d4a4680c48513e48c42047d2c2168a9404471704731df97ced5954facdc4ca7316580f6078db5670e45067c7e8ed3ee4cd3
7
+ data.tar.gz: 86f3b995575fc7ff8b6bb83adf368182a684faece1584cffb7b1150bd47ce082adaba5b0d4ce0b83d19d2ec2d795bd31db19d35c990e6d0065698c050161bcd0
@@ -3,4 +3,6 @@ language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
5
  - 2.6.6
6
+ - 2.7.2
7
+ - 3.0.0
6
8
  before_install: gem install bundler -v 2.1.4
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ ## v0.4.0 - 2021-01-22
4
+
5
+ - [7fd6712](https://github.com/tcollier/whirled_peas/tree/7fd6712818c94cdbfd81828277ca67c705e01793): BREAKING: replace `WhirledPeas.start` with command line executable
6
+ - [2535342](https://github.com/tcollier/whirled_peas/tree/25353424f1ab4af4880f44eb7ddd28afefbbb9b2): Add support for loading screen
7
+ - [7388fc2](https://github.com/tcollier/whirled_peas/tree/7388fc2eacdc8045b725311c11d650d6b8654be8): Add support for title fonts
8
+ - [b345155](https://github.com/tcollier/whirled_peas/tree/b345155b1c212cabe73f9a2562ac8dbbedbbb6df): Add command to list title fonts to executable
9
+ - [d3a8324](https://github.com/tcollier/whirled_peas/tree/d3a832496c36985993217ff11b6d83dd4697c4ed): Add commands for debugging application to executable
10
+
11
+ ## v0.3.0 - 2021-01-21
12
+
13
+ - [617f802](https://github.com/tcollier/whirled_peas/tree/617f8027d6688a2ec81a3e594e529c94485cee85): BREAKING: send frames directly to EventLoop (`Producer#send` renamed to `Producer#send_frame`)
14
+
15
+ ## v0.2.0 - 2021-01-20
16
+
17
+ - [73eb326](https://github.com/tcollier/whirled_peas/tree/73eb326426f9814e91e3bc7a60dfd87be3d69f7e): Convert "primitive" data types to strings
18
+ - [f28b69d](https://github.com/tcollier/whirled_peas/tree/f28b69df8b6cfc973da2ebc0b8da29b278f23433): Give elements names
19
+ - [1ae1c929](https://github.com/tcollier/whirled_peas/tree/1ae1c929429c2f8520054d33a064c2b6d71955fe): Consistently format exceptions in logs
20
+ - [4c2114f](https://github.com/tcollier/whirled_peas/tree/4c2114fd360fd98c65e6e32f905a377f09b919ee): Fix bug with negative sleep times
21
+ - [627bf12](https://github.com/tcollier/whirled_peas/tree/627bf126dd7f9c845f65105e0826d14a35a0a953): Don't reraise pipe error
22
+
23
+ ## v0.1.1 - 2021-01-20
24
+
25
+ - [3852c8c](https://github.com/tcollier/whirled_peas/tree/3852c8c700c2e8fb92e65bbca1c99be74304c6d0): Improve error handling
26
+
27
+ ## v0.1.0 - 2021-01-20
28
+
29
+ - [f343434](https://github.com/tcollier/whirled_peas/tree/f34343458097da04d5846ab13533e8226ba04d75): Inaugural release
data/Gemfile CHANGED
@@ -6,3 +6,4 @@ gemspec
6
6
  gem 'rake', '~> 12.0'
7
7
  gem 'rspec', '~> 3.0'
8
8
  gem 'pry-byebug'
9
+ gem 'tty-cursor', '~> 0.7'
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.com/tcollier/whirled_peas.svg?branch=main)](https://travis-ci.com/tcollier/whirled_peas)
2
+
1
3
  # WhirledPeas
2
4
 
3
5
  Visualize your code's execution with Whirled Peas!
@@ -20,15 +22,24 @@ Or install it yourself as:
20
22
 
21
23
  ## Usage
22
24
 
25
+ A Whirled Peas application consists of the following pieces
26
+
27
+ - The driver (required) - the code that is to be visualized, it emits lightweight frame events through a producer
28
+ - The main template factory (required) - builds templates to convert frame events from the driver into terminal graphics
29
+ - A loading screen template factory (optional) - builds templates to display while content is loading
30
+
31
+ These pieces are configured as following
32
+
23
33
  ```ruby
34
+ # visualize.rb
24
35
  require 'whirled_peas'
25
36
 
26
37
  class TemplateFactory
27
38
  def build(frame, args)
28
39
  WhirledPeas.template do |body|
29
- body.add_box do |_, settings|
40
+ body.add_box('Title') do |_, settings|
30
41
  settings.underline = true
31
- "Hello #{args['name']}"
42
+ "Hello #{args[:name]}"
32
43
  end
33
44
  # ...
34
45
  end
@@ -37,18 +48,44 @@ end
37
48
 
38
49
  class Driver
39
50
  def start(producer)
40
- producer.send('starting', args: { 'name' => 'World' })
51
+ producer.send_frame('starting', args: { name: 'World' })
41
52
  # ...
42
53
  end
43
54
  end
44
55
 
45
- WhirledPeas.start(Driver.new, TemplateFactory.new)
56
+ WhirledPeas.configure do |config|
57
+ config.driver = Driver.new
58
+ config.template_factory = TemplateFactory.new
59
+ end
46
60
  ```
47
61
 
48
- A Whirled Peas application consists of two pieces
62
+ Then the visualizer is started on the command line with
63
+
64
+ ```
65
+ $ whirled_peas start visualize.rb
66
+ ```
67
+
68
+ The optional loading screen can be configured like
69
+
70
+ ```ruby
71
+ class LoadingTemplateFactory
72
+ def build
73
+ WhirledPeas.template do |t|
74
+ t.add_box('Loading') do |box, settings|
75
+ settings.set_margin(top: 15)
76
+ settings.auto_margin = true
77
+ settings.full_border(color: :blue, style: :double)
78
+ "Loading..."
79
+ end
80
+ end
81
+ end
82
+ end
49
83
 
50
- 1. The driver, which emits lightweight frame events
51
- 1. The template factory, which builds templates to convert frame events from the driver into terminal graphics
84
+ WhirledPeas.configure do |config|
85
+ # ...
86
+ config.loading_template_factory = LoadingTemplateFactory.new
87
+ end
88
+ ```
52
89
 
53
90
  ### Driver
54
91
 
@@ -70,13 +107,14 @@ The producer provides a single method
70
107
  #
71
108
  # @param name [String] application defined name for the frame. The template factory will be provided this name
72
109
  # @param duration [Number] time in seconds this frame should be displayed for (defaults to 1 frame)
73
- # @param args [Hash] key value pairs to send as arguments to the template factory, these values will be
74
- # serialized/deserialized
75
- def send(name, duration:, args:)
110
+ # @param args [Hash<Symbol, Object>] key value pairs to send as arguments to the template factory
111
+ def send_frame(name, duration:, args:)
76
112
  # implementation
77
113
  end
78
114
  ```
79
115
 
116
+ **IMPORTANT**: the keys for arguments must be symbols.
117
+
80
118
  #### Example
81
119
 
82
120
  Simple application that loads a set of numbers and looks for a pair that adds up to 1,000
@@ -85,25 +123,25 @@ Simple application that loads a set of numbers and looks for a pair that adds up
85
123
  class Driver
86
124
  def start(producer)
87
125
  numbers = File.readlines('/path/to/numbers.txt').map(&:to_i)
88
- producer.send('load-numbers', duration: 3, args: { numbers: numbers })
126
+ producer.send_frame('load-numbers', duration: 3, args: { numbers: numbers })
89
127
  numbers.sort!
90
- producer.send('sort-numbers', duration: 3, args: { numbers: numbers })
128
+ producer.send_frame('sort-numbers', duration: 3, args: { numbers: numbers })
91
129
  low = 0
92
130
  high = numbers.length - 1
93
131
  while low < high
94
132
  sum = numbers[low] + numbers[high]
95
133
  if sum == 1000
96
- producer.send('found-pair', duration: 5, args: { low: low, high: high, sum: sum })
134
+ producer.send_frame('found-pair', duration: 5, args: { low: low, high: high, sum: sum })
97
135
  return
98
136
  elsif sum < 1000
99
- producer.send('too-low', args: { low: low, high: high, sum: sum })
137
+ producer.send_frame('too-low', args: { low: low, high: high, sum: sum })
100
138
  low += 1
101
139
  else
102
- producer.send('too-high', args: { low: low, high: high, sum: sum })
140
+ producer.send_frame('too-high', args: { low: low, high: high, sum: sum })
103
141
  high -= 1
104
142
  end
105
143
  end
106
- producer.send('no-solution', duration: 5)
144
+ producer.send_frame('no-solution', duration: 5)
107
145
  end
108
146
  end
109
147
  ```
@@ -112,27 +150,61 @@ end
112
150
 
113
151
  To render the frame events sent by the driver, the application requires a template factory. This factory will be called for each frame event, with the frame name and the arguments supplied by the driver. A template factory can be a simple ruby class and thus can maintain state. Whirled Peas provides a few basic building blocks to make simple, yet elegant terminal-based UIs.
114
152
 
153
+ #### Loading Template Factory
154
+
155
+ `WhirledPeas.start` takes an optional template facotry to build a loading screen. This instance must implement `#build` (taking no arguments). The template returned by that method will be painted while the event loop is waiting for frames. The factory method will be called once per refresh cycle, so it's possible to implement animation.
156
+
115
157
  #### Building Blocks
116
158
 
117
159
  A template is created with `WhirledPeas.template`, which yields a `Template` object and `TemplateSettings`. This template object is a `ComposableElement`, which allows for attaching child elements and setting layout options. `GridElement` and `BoxElement` are two other composable elements and `TextElement` is a simple element that can hold a text/number value and has layout options, but cannot have any child elements.
118
160
 
119
- A `ComposableElement` provides the following methods to add child elements
161
+ A `ComposableElement` provides the following methods to add child elements, each of these takes an optional string argument that is set as the name of the element (which can be useful when debugging).
120
162
 
121
163
  - `add_box` - yields a `ComposableElement` and a `BoxSettings`, which will be added to the parent's children
122
164
  - `add_grid` - yields a `ComposableElement` and a `GridSettings`, which will be added to the parent's children
123
165
  - `add_text` - yields `nil` and a `TextSettings`, which will be added to the parent's children
124
166
 
167
+ E.g.
168
+
169
+ ```ruby
170
+ WhirledPeas.template do |template, template_settings|
171
+ template_settings.bg_color = :blue
172
+ template.add_grid do |grid, grid_settings|
173
+ grid_settings.num_cols = 10
174
+ 100.times do |i|
175
+ grid.add_text { i }
176
+ end
177
+ end
178
+ end
179
+ ```
180
+
181
+ The above template can also be broken down into more manageable methods, e.g.
182
+
183
+ ```ruby
184
+ def number_grid(grid, settings)
185
+ settings.num_cols = 10
186
+ 100.times do |i|
187
+ grid.add_text { i }
188
+ end
189
+ end
190
+
191
+ WhirledPeas.template do |template, settings|
192
+ settings.bg_color = :blue
193
+ template.add_grid(&method(:number_grid))
194
+ end
195
+ ```
196
+
125
197
  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
198
 
127
199
  ```ruby
128
200
  template.add_grid do |g|
129
201
  100.times do |i|
130
- g.add_text { i.to_s }
202
+ g.add_text { i }
131
203
  end
132
204
  end
133
205
 
134
206
  template.add_grid do |g|
135
- 100.times.map(&:to_s)
207
+ 100.times.map(&:itself)
136
208
  end
137
209
  ```
138
210
 
@@ -164,6 +236,7 @@ The available settigs are
164
236
  | `flow` | Flow to display child elements (see [Display Flow](#display-flow)) | `:l2r` | `Box` | Yes |
165
237
  | `margin` | Set the (left, top, right, bottom) margin of the element | `0` | `Box`, `Grid` | Yes |
166
238
  | `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Box`, `Grid` | Yes |
239
+ | `title_font` | Font used to create "large" text (see [Large Text](#large-text)) | | `Text` |
167
240
  | `transpose` | Display grid elements top-to-bottom, then left-to-right | `false` | `Grid` | No |
168
241
  | `underline` | `true` underlines the font | `false` | `Box`, `Grid`, `Template`, `Text` | Yes |
169
242
  | `width` | Override the calculated with of an element | | `Box`, `Grid`, `Text` | No |
@@ -172,15 +245,16 @@ The available settigs are
172
245
 
173
246
  Margin and padding settings allow for setting the spacing on each of the 4 sides of the element independently. The set these values, use
174
247
 
248
+ - `clear_margin` - sets all margin values to 0
175
249
  - `set_margin(left:, top:, right:, bottom:)`
250
+ - `clear_padding` - sets all margin values to 0
176
251
  - `set_padding(left:, top:, right:, bottom:)`
177
252
 
178
- Any argument value not provided will result in that value being 0.
179
-
180
253
  ##### Border
181
254
 
182
255
  The border settings consist of 6 boolean values (border are either width 1 or not shown), the 4 obvious values (`left`, `top`, `right`, and `bottom`) along with 2 other values for inner borders (`inner_horiz` and `inner_vert`) in a grid. A border also has a foreground color (defaults to `:white`) and a style. The background color is determined by the `bg_color` of the element. Border values can be set with
183
256
 
257
+ - `clear_border` - sets all border positions to `false`
184
258
  - `set_border(left:, top:, right:, bottom:, inner_horiz:, inner_vert:, color:, style:)`
185
259
 
186
260
  Available border styles are
@@ -272,46 +346,137 @@ Many of these also have a "bright" option:
272
346
  - `:bright_red`
273
347
  - `:bright_yellow`
274
348
 
349
+ ##### Large Text
350
+
351
+ The `title_font` setting for `TextElement`s converts the standard terminal font into a large block font. The available fonts vary from system to system. Every system will have a `:default` font available, this font could look like
352
+
353
+ ```
354
+ ██████╗ ███████╗███████╗ █████╗ ██╗ ██╗██╗ ████████╗
355
+ ██╔══██╗██╔════╝██╔════╝██╔══██╗██║ ██║██║ ╚══██╔══╝
356
+ ██║ ██║█████╗ █████╗ ███████║██║ ██║██║ ██║
357
+ ██║ ██║██╔══╝ ██╔══╝ ██╔══██║██║ ██║██║ ██║
358
+ ██████╔╝███████╗██║ ██║ ██║╚██████╔╝███████╗ ██║
359
+ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
360
+ ```
361
+
362
+ To print out a list of all available fonts as well as sample text in that font, run
363
+
364
+ ```
365
+ $ whirled_peas title_fonts
366
+ ```
367
+
368
+ Note: when using a title font with WhirledPeas for the first time on a system, the gem loads all fonts to check which ones are available. This can be a slow process and may cause a noticeable delay when running a visualization. Running the command above will cache the results and thus when a WhirledPeas visualization is run, there will be no lag from loading fonts.
369
+
275
370
  ### Example
276
371
 
277
372
  ```ruby
278
373
  class TemplateFactory
279
- def initialize
280
- @numbers = []
374
+ def build(frame, args)
375
+ set_state(frame, args)
376
+ WhirledPeas.template do |t|
377
+ t.add_box('Body', &method(:body))
378
+ end
281
379
  end
282
380
 
283
- def build(name, args)
284
- @numbers = args['numbers'] if args.key?('numbers')
381
+ private
285
382
 
286
- WhirledPeas.template do |t|
287
- t.add_box do |body, settings|
288
- settings.flow = :l2r
289
- settings.auto_margin = true
290
- body.add_box do |title, settings|
291
- settings.underline = true
292
- "Pair Finder"
293
- end
294
- body.add_box do |_, settings|
295
- settings.color = name == 'found-pair' ? :green : :red
296
- args.key?('sum') ? "Sum: #{args['sum']}" : 'N/A'
297
- end
298
- body.add_grid do |g, settings|
299
- settings.full_border
300
- @numbers.each.with_index do |num, index|
301
- is_low = args.key?('low') && args['low'] == index
302
- is_high = args.key?('high') && args['high'] == index
303
- g.add_text do |_, settings|
304
- settings.bg_color = (is_low || is_high) ? :cyan : :white
305
- num.to_s
306
- end
307
- end
308
- end
383
+ def set_state(frame, args)
384
+ @frame = frame
385
+ @numbers = args.key?(:numbers) ? args[:numbers] || []
386
+ @sum = args[:sum] if args.key?(:sum)
387
+ @low = args[:low] if args.key?(:low)
388
+ @high = args[:high] if args.key?(:high)
389
+ end
390
+
391
+ def title(_elem, settings)
392
+ settings.underline = true
393
+ "Pair Finder"
394
+ end
395
+
396
+ def sum(_elem, settings)
397
+ settings.color = @frame == 'found-pair' ? :green : :red
398
+ @sum ? "Sum: #{@sum}" : 'N/A'
399
+ end
400
+
401
+ def number_grid(elem, settings)
402
+ settings.full_border
403
+ @numbers.each.with_index do |num, index|
404
+ g.add_text do |_, settings|
405
+ settings.bg_color = (@low == index || @high == index) ? :cyan : :white
406
+ num
309
407
  end
310
408
  end
311
409
  end
410
+
411
+ def body(elem, settings)
412
+ settings.flow = :l2r
413
+ settings.auto_margin = true
414
+
415
+ elem.add_box('Title', &method(:title))
416
+ elem.add_box('Sum', &method(:sum))
417
+ elem.add_grid('NumberGrid', &method(:number_grid))
418
+ end
312
419
  end
313
420
  ```
314
421
 
422
+ ### Debugging
423
+
424
+ The `whirled_peas` executable provides some commands that are helpful for debugging.
425
+
426
+ #### list_frames
427
+
428
+ List the frames sent by the driver
429
+
430
+ ```
431
+ $ whirled_peas <config file> list_frames
432
+ Frame 'start' displayed for 5 second(s)
433
+ Frame 'move' displayed for 1 frame ({:direction=>'N'})
434
+ ...
435
+ EOF frame detected
436
+ ```
437
+
438
+ #### play_frame
439
+
440
+ Displays a single frame for several seconds
441
+
442
+ ```
443
+ $ whirled_peas <config file> play_frame move '{"direction":"N"}'
444
+ ```
445
+
446
+ Adding the `--debug` flag will result in just printing out the template's debug information, e.g.
447
+
448
+ ```
449
+ $ whirled_peas <config file> play_frame move '{"direction":"N"}' --debug
450
+ + TEMPLATE [WhirledPeas::UI::Template]
451
+ - Settings
452
+ WhirledPeas::UI::TemplateSettings
453
+ <default>
454
+ - Children
455
+ + TitleContainer [WhirledPeas::UI::BoxElement]
456
+ ...
457
+ ```
458
+
459
+ #### loading
460
+
461
+ Displays the configured loading screen for several seconds
462
+
463
+ ```
464
+ $ whirled_peas <config file> loading
465
+ ```
466
+
467
+ Adding the `--debug` flag will result in just printing out the loading template's debug information, e.g.
468
+
469
+ ```
470
+ $ whirled_peas <config file> loading --debug
471
+ + TEMPLATE [WhirledPeas::UI::Template]
472
+ - Settings
473
+ WhirledPeas::UI::TemplateSettings
474
+ <default>
475
+ - Children
476
+ + TitleContainer [WhirledPeas::UI::BoxElement]
477
+ ...
478
+ ```
479
+
315
480
  ## Development
316
481
 
317
482
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'whirled_peas'
5
+
6
+ WhirledPeas.print_title_fonts
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'whirled_peas'
5
+ require 'whirled_peas/command_line'
6
+
7
+ WhirledPeas::CommandLine.new(ARGV).start