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 +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile +1 -0
- data/README.md +213 -48
- data/bin/title_fonts +6 -0
- data/exe/whirled_peas +7 -0
- data/lib/whirled_peas.rb +12 -30
- data/lib/whirled_peas/command_line.rb +270 -0
- data/lib/whirled_peas/config.rb +21 -0
- data/lib/whirled_peas/errors.rb +5 -0
- data/lib/whirled_peas/frame.rb +0 -7
- data/lib/whirled_peas/frame/event_loop.rb +91 -0
- data/lib/whirled_peas/frame/print_consumer.rb +33 -0
- data/lib/whirled_peas/frame/producer.rb +35 -31
- data/lib/whirled_peas/template.rb +5 -0
- data/lib/whirled_peas/template/element.rb +230 -0
- data/lib/whirled_peas/{ui → template}/settings.rb +28 -10
- data/lib/whirled_peas/ui.rb +1 -3
- data/lib/whirled_peas/ui/canvas.rb +37 -4
- data/lib/whirled_peas/ui/painter.rb +22 -18
- data/lib/whirled_peas/ui/screen.rb +24 -23
- data/lib/whirled_peas/utils.rb +5 -0
- data/lib/whirled_peas/utils/ansi.rb +103 -0
- data/lib/whirled_peas/{ui/ansi.rb → utils/color.rb} +23 -76
- data/lib/whirled_peas/utils/title_font.rb +75 -0
- data/lib/whirled_peas/version.rb +1 -1
- data/whirled_peas.gemspec +4 -2
- metadata +22 -18
- data/lib/whirled_peas/frame/consumer.rb +0 -61
- data/lib/whirled_peas/frame/loop.rb +0 -56
- data/lib/whirled_peas/ui/element.rb +0 -199
- data/lib/whirled_peas/ui/stroke.rb +0 -29
- data/sandbox/auto.rb +0 -13
- data/sandbox/box.rb +0 -19
- data/sandbox/grid.rb +0 -13
- data/sandbox/sandbox.rb +0 -17
- data/sandbox/text.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 563cc06007d90aa0599b61c5366a5d9b2c5224ec2973367b6c358cad95ddaedc
|
4
|
+
data.tar.gz: fa798abdfb8a8e4fd229f08b964c8ee4f4d480939ece9ddaeb5731c171985504
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1be24e74d75f87a001b26f216e320d4a4680c48513e48c42047d2c2168a9404471704731df97ced5954facdc4ca7316580f6078db5670e45067c7e8ed3ee4cd3
|
7
|
+
data.tar.gz: 86f3b995575fc7ff8b6bb83adf368182a684faece1584cffb7b1150bd47ce082adaba5b0d4ce0b83d19d2ec2d795bd31db19d35c990e6d0065698c050161bcd0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -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
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[
|
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.
|
51
|
+
producer.send_frame('starting', args: { name: 'World' })
|
41
52
|
# ...
|
42
53
|
end
|
43
54
|
end
|
44
55
|
|
45
|
-
WhirledPeas.
|
56
|
+
WhirledPeas.configure do |config|
|
57
|
+
config.driver = Driver.new
|
58
|
+
config.template_factory = TemplateFactory.new
|
59
|
+
end
|
46
60
|
```
|
47
61
|
|
48
|
-
|
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
|
-
|
51
|
-
|
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
|
74
|
-
|
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.
|
126
|
+
producer.send_frame('load-numbers', duration: 3, args: { numbers: numbers })
|
89
127
|
numbers.sort!
|
90
|
-
producer.
|
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.
|
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.
|
137
|
+
producer.send_frame('too-low', args: { low: low, high: high, sum: sum })
|
100
138
|
low += 1
|
101
139
|
else
|
102
|
-
producer.
|
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.
|
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
|
202
|
+
g.add_text { i }
|
131
203
|
end
|
132
204
|
end
|
133
205
|
|
134
206
|
template.add_grid do |g|
|
135
|
-
100.times.map(&:
|
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
|
280
|
-
|
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
|
-
|
284
|
-
@numbers = args['numbers'] if args.key?('numbers')
|
381
|
+
private
|
285
382
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
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.
|
data/bin/title_fonts
ADDED