hackmac 1.11.0 → 2.0.0

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.
@@ -1,494 +0,0 @@
1
- class Hackmac::Graph
2
- # A terminal display class that manages a grid of colored cells with ANSI
3
- # styling support
4
- #
5
- # The Display class provides functionality for creating and managing
6
- # terminal-based visual displays It handles cell-level rendering with support
7
- # for colors, background colors, and text styles The class maintains internal
8
- # state for cursor position and formatting attributes while providing methods
9
- # to manipulate individual cells and render complete display grids to the
10
- # terminal
11
- #
12
- # @example
13
- # display = Hackmac::Graph::Display.new(24, 80)
14
- # # Creates a 24x80 character display grid for terminal rendering
15
- class Display
16
- # Shortcut for Term::ANSIColor module.
17
- ANSI = Term::ANSIColor
18
-
19
- # The initialize method sets up a Display instance by configuring its grid
20
- # dimensions and initializing internal state
21
- #
22
- # This method creates a display grid with the specified number of lines and
23
- # columns, establishing the boundaries for cell positioning. It initializes
24
- # the internal ranges for lines and columns and performs an initial clear
25
- # operation to set up the display buffer with default values
26
- #
27
- # @param lines [ Integer ] the number of lines (rows) in the display grid
28
- # @param columns [ Integer ] the number of columns (characters per line) in
29
- # the display grid
30
- # @param color [ Symbol ] the default text color for the display
31
- # @param on_color [ Symbol ] the default background color for the display
32
- def initialize(lines, columns, color: :white, on_color: :black)
33
- @lines_range = 1..lines
34
- @columns_range = 1..columns
35
- @orig_color = color
36
- @orig_on_color = on_color
37
- clear
38
- end
39
-
40
- # The clear method resets the display state by initializing cursor
41
- # position, color attributes, and cell contents to their default values
42
- #
43
- # This method prepares the terminal display for a fresh rendering by
44
- # resetting internal tracking variables such as cursor coordinates, color
45
- # settings, and text styles. It also reinitializes the two-dimensional
46
- # array of Cell objects that represent the display grid, filling each cell
47
- # with a space character and default styling attributes
48
- #
49
- # @param color [ Symbol ] the default text color for the display
50
- # @param on_color [ Symbol ] the default background color for the display
51
- #
52
- # @return [ Hackmac::Graph::Display ] returns the Display instance to allow
53
- # for method chaining
54
- def clear
55
- @x = 1
56
- @y = 1
57
- @color = @orig_color
58
- @on_color = @orig_on_color
59
- @styles = []
60
- @cells =
61
- Array.new(lines) {
62
- Array.new(columns) {
63
- Cell.new(' ', @color, @on_color, @styles)
64
- }
65
- }
66
- reset
67
- end
68
-
69
- # The reset method resets the display's color and style attributes to their
70
- # default values
71
- #
72
- # This method reinitializes the internal color tracking variables to their
73
- # default state, clearing any active styling and resetting the text color
74
- # to white (15) and background color to black (0). It also clears all
75
- # active text styles.
76
- #
77
- # @return [ Hackmac::Graph::Display ] returns the Display instance to allow
78
- # for method chaining
79
- def reset
80
- @color = @orig_color
81
- @on_color = @orig_on_color
82
- @styles = []
83
- self
84
- end
85
-
86
- # The each method iterates over all cells in the display grid
87
- #
88
- # This method provides an iterator interface for accessing each cell in the
89
- # terminal display grid by yielding the row, column, and cell object for
90
- # each position in the grid
91
- #
92
- # @yield [ y, x, cell ] yields the row coordinate, column coordinate, and cell object
93
- #
94
- # @return [ Enumerator ] an enumerator that allows iteration over all cells in the grid
95
- def each(&block)
96
- Enumerator.new do |enum|
97
- @lines_range.each do |y|
98
- @columns_range.each do |x|
99
- enum.yield y, x, @cells[y - 1][x - 1]
100
- end
101
- end
102
- end.each(&block)
103
- end
104
-
105
- # The - method calculates the difference between two display states by
106
- # comparing their cells and returns a string of ANSI escape sequences that
107
- # represent only the changes
108
- #
109
- # This method takes another Display instance and compares it with the
110
- # current display state to identify which cells have changed. It generates
111
- # a minimal set of ANSI cursor movement and character output commands to
112
- # redraw only the portions of the display that have changed, making
113
- # terminal updates more efficient
114
- #
115
- # @param old [ Hackmac::Graph::Display ] the previous display state to compare against
116
- #
117
- # @return [ String ] a string containing ANSI escape sequences for
118
- # rendering only the changed cells
119
- def -(old)
120
- dimensions != old.dimensions and raise ArgumentError,
121
- "old dimensions #{old.dimensions.inspect} don't match #{dimensions.inspect}"
122
- result = +''
123
- each.zip(old.each) do |(my, mx, me), (_, _, old)|
124
- if me != old
125
- result << ANSI.move_to(my, mx) << me.to_s
126
- end
127
- end
128
- result
129
- end
130
-
131
- # The to_s method generates a string representation of the display by
132
- # iterating over all cells and constructing a formatted terminal output
133
- # with ANSI escape sequences for positioning and styling
134
- #
135
- # @return [ String ] a complete terminal display string with all cells
136
- # rendered using their visual attributes and positioned according to
137
- # their coordinates in the grid
138
- def to_s
139
- each.inject(ANSI.clear_screen) do |s, (y, x, c)|
140
- s << ANSI.move_to(y, x) << c.to_s
141
- end
142
- end
143
-
144
- # The inspect method returns a string representation of the display object
145
- # that includes its class name along with the dimensions of the display
146
- # grid
147
- #
148
- # @return [ String ] a formatted string containing the class name and
149
- # display dimensions in the format "#<ClassName: columns×lines>"
150
- def inspect
151
- "#<#{self.class}: #{columns}×#{lines}>"
152
- end
153
-
154
- # The x reader method provides access to the x attribute that was set
155
- # during object initialization.
156
- #
157
- # This method returns the value of the x instance variable, which typically
158
- # represents the horizontal coordinate or position within the display grid.
159
- #
160
- # @return [ Integer ] the x coordinate value stored in the instance variable
161
- attr_reader :x
162
-
163
- # The y reader method provides access to the y attribute that was set
164
- # during object initialization.
165
- #
166
- # This method returns the value of the y instance variable, which typically
167
- # represents the vertical coordinate or position within the display grid.
168
- #
169
- # @return [ Integer ] the y coordinate value stored in the instance variable
170
- attr_reader :y
171
-
172
- # The lines method returns the number of lines in the display
173
- #
174
- # This method provides access to the vertical dimension of the graphical
175
- # display by returning the total number of rows available for rendering
176
- # content
177
- #
178
- # @return [ Integer ] the number of lines (rows) in the display object
179
- def lines
180
- @lines_range.end
181
- end
182
-
183
- # The columns method returns the number of columns in the display
184
- #
185
- # This method provides access to the horizontal dimension of the graphical
186
- # display by returning the total number of columns available for rendering
187
- # content
188
- #
189
- # @return [ Integer ] the number of columns (characters per line) in the
190
- # display object
191
- def columns
192
- @columns_range.end
193
- end
194
-
195
- # The dimensions method returns the size dimensions of the display grid
196
- #
197
- # This method provides access to the display's rectangular dimensions by
198
- # returning an array containing the number of lines (height) and columns (width)
199
- # that define the terminal display area
200
- #
201
- # @return [ Array<Integer> ] an array where the first element is the number of lines
202
- # and the second element is the number of columns in the display grid
203
- def dimensions
204
- [ lines, columns ]
205
- end
206
-
207
- # The styled method sets the text styles for subsequent character output
208
- #
209
- # This method configures the styling attributes that will be applied to
210
- # characters written to the display, such as bold, italic, underline,
211
- # and other text formatting options. It validates that all provided style
212
- # names are recognized before applying them.
213
- #
214
- # @param s [ Array<Symbol> ] an array of style symbols to apply
215
- #
216
- # @return [ Hackmac::Graph::Display ] returns the Display instance for method chaining
217
- #
218
- # @raise [ ArgumentError ] raised when any of the provided style symbols is not recognized
219
- def styled(*s)
220
- if nope = s.find { !style?(_1) }
221
- raise ArgumentError, "#{nope} is not a style"
222
- end
223
- @styles = s
224
- self
225
- end
226
-
227
- # The at method sets the cursor position within the display grid
228
- #
229
- # This method updates the internal y and x coordinates to the specified position,
230
- # validating that the coordinates fall within the defined grid boundaries before
231
- # making the change. It enables subsequent character output operations to occur
232
- # at the designated location.
233
- #
234
- # @param y [ Integer ] the vertical coordinate to move the cursor to
235
- # @param x [ Integer ] the horizontal coordinate to move the cursor to
236
- #
237
- # @return [ Hackmac::Graph::Display ] returns the Display instance for method chaining
238
- #
239
- # @raise [ ArgumentError ] raised when the y coordinate is outside the valid lines range
240
- # @raise [ ArgumentError ] raised when the x coordinate is outside the valid columns range
241
- def at(y, x)
242
- @lines_range.include?(y) or
243
- raise ArgumentError, "y #{y} out of lines range #@lines_range"
244
- @columns_range.include?(x) or
245
- raise ArgumentError, "x #{x} out of columns range #@columns_range"
246
- @y, @x = y, x
247
- self
248
- end
249
-
250
- # The put method assigns a character to the current cursor position in the
251
- # display grid
252
- #
253
- # This method stores a character cell at the current vertical and
254
- # horizontal coordinates within the terminal display grid. It validates
255
- # that the input is a single character before assigning it, ensuring that
256
- # only valid characters are placed in the grid.
257
- #
258
- # @param char [ String ] the single character to be placed at the current cursor position
259
- #
260
- # @return [ Hackmac::Graph::Display ] returns the Display instance to allow for method chaining
261
- #
262
- # @raise [ ArgumentError ] raised when the provided character is not a single character
263
- def put(char)
264
- char.size == 1 or
265
- raise ArgumentError, "#{char} is not single character"
266
- @cells[@y - 1][@x - 1] = Cell.new(char, @color, @on_color, @styles)
267
- self
268
- end
269
-
270
- # The write method outputs a string character by character within the
271
- # display grid
272
- #
273
- # This method takes a string and writes it to the current cursor position
274
- # in the terminal display, advancing the cursor position after each
275
- # character is written. It handles line wrapping by stopping when reaching
276
- # the end of the row.
277
- #
278
- # @param string [ String ] the string to be written to the display
279
- #
280
- # @return [ Hackmac::Graph::Display ] returns the Display instance to allow
281
- # for method chaining
282
- def write(string)
283
- string.each_char do |char|
284
- put(char)
285
- if x < columns - 1
286
- at(y, x + 1)
287
- else
288
- break
289
- end
290
- end
291
- self
292
- end
293
-
294
- # The top method moves the cursor position to the first line of the current
295
- # column
296
- #
297
- # This method sets the vertical cursor coordinate to the first line while
298
- # preserving the current horizontal position It is useful for positioning
299
- # text at the top of the current column in terminal displays
300
- #
301
- # @return [ Hackmac::Graph::Display ] returns the Display instance for
302
- # method chaining
303
- #
304
- def top
305
- at(1, x)
306
- end
307
-
308
- # The bottom method moves the cursor position to the last line of the
309
- # current column
310
- #
311
- # This method sets the vertical cursor coordinate to the last line while
312
- # preserving the current horizontal position. It is useful for positioning
313
- # text at the bottom of the current column in terminal displays.
314
- #
315
- # @return [ Hackmac::Graph::Display ] returns the Display instance for
316
- # method chaining
317
- def bottom
318
- at(lines, x)
319
- end
320
-
321
- # The left method moves the cursor position to the first column of the
322
- # current line
323
- #
324
- # This method sets the horizontal cursor coordinate to the first column while
325
- # preserving the current vertical position. It is useful for positioning text
326
- # at the beginning of the current line in terminal displays
327
- #
328
- # @return [ Hackmac::Graph::Display ] returns the Display instance for
329
- # method chaining
330
- def left
331
- at(y, 1)
332
- end
333
-
334
- # The right method moves the cursor position to the last column of the
335
- # current line
336
- #
337
- # This method sets the horizontal cursor coordinate to the final column
338
- # while maintaining the current vertical position. It is useful for
339
- # positioning text at the end of the current line in terminal displays
340
- #
341
- # @return [ Hackmac::Graph::Display ] returns the Display instance for
342
- # method chaining
343
- def right
344
- at(y, columns)
345
- end
346
-
347
- # The centered method moves the cursor position to the center of the
348
- # display grid
349
- #
350
- # This method calculates the midpoint coordinates of the terminal display
351
- # grid and positions the cursor at that location, making it convenient for
352
- # centering text or other content within the available terminal space
353
- #
354
- # @return [ Hackmac::Graph::Display ] returns the Display instance for
355
- # method chaining
356
- def centered
357
- at(lines / 2, columns / 2)
358
- end
359
-
360
- # The write_centered method positions the cursor at the horizontal center
361
- # of the display grid and writes a string there
362
- #
363
- # This method calculates the starting column position needed to center the
364
- # given string within the current display width, moves the cursor to that
365
- # position, and then writes the string character by character.
366
- # It is useful for creating centered text output in terminal displays.
367
- #
368
- # @param string [ String ] the text string to be written and centered on
369
- # the current line
370
- #
371
- # @return [ Hackmac::Graph::Display ] returns the Display instance to allow
372
- # for method chaining
373
- def write_centered(string)
374
- at(@y, (columns - string.size) / 2).write(string)
375
- end
376
-
377
- # The get method retrieves the cell object at the current cursor position
378
- #
379
- # This method accesses the internal two-dimensional array of Cell objects
380
- # using the current vertical and horizontal cursor coordinates to return
381
- # the specific cell that is currently pointed to by the cursor
382
- #
383
- # @return [ Hackmac::Graph::Display::Cell ] the cell object at the current
384
- # cursor position
385
- def get
386
- @cells[@y - 1][@x - 1]
387
- end
388
-
389
- # The color method sets the text color attribute for subsequent character
390
- # output
391
- #
392
- # This method configures the color that will be applied to characters
393
- # written to the display by updating the internal color tracking variable
394
- # with a validated color attribute value
395
- #
396
- # @param color [ Integer ] the color index to set for subsequent text output
397
- #
398
- # @return [ Hackmac::Graph::Display ] returns the Display instance to allow
399
- # for method chaining
400
- #
401
- # @raise [ ArgumentError ] raised when the provided color value is not a
402
- # valid color attribute
403
- def color(color)
404
- @color = attribute!(color)
405
- self
406
- end
407
-
408
- # The on_color method sets the background color attribute for subsequent
409
- # character output
410
- #
411
- # This method configures the background color that will be applied to
412
- # characters written to the display by updating the internal on_color
413
- # tracking variable with a validated color attribute value
414
- #
415
- # @param on_color [ Integer ] the background color index to set for
416
- # subsequent text output
417
- #
418
- # @return [ Hackmac::Graph::Display ] returns the Display instance to allow
419
- # for method chaining
420
- #
421
- # @raise [ ArgumentError ] raised when the provided color value is not a
422
- # valid color attribute
423
- def on_color(on_color)
424
- @on_color = attribute!(on_color)
425
- self
426
- end
427
-
428
- private
429
-
430
- # The style? method checks whether a given symbol is a valid text styling
431
- # attribute
432
- #
433
- # This method verifies if the provided symbol corresponds to one of the
434
- # supported text styles that can be applied to terminal output, such as
435
- # bold, italic, underline, and other formatting options
436
- #
437
- # @param s [ Symbol ] the symbol to check against the list of valid styles
438
- #
439
- # @return [ Boolean ] true if the symbol is a recognized text style, false
440
- # otherwise
441
- def style?(s)
442
- [
443
- :bold,
444
- :dark,
445
- :faint,
446
- :italic,
447
- :underline,
448
- :underscore,
449
- :blink,
450
- :rapid_blink,
451
- :reverse,
452
- :negative,
453
- :concealed,
454
- :conceal,
455
- :strikethrough,
456
- ].member?(s)
457
- end
458
-
459
- # The attribute? method checks if a given value is a valid ANSI color
460
- # attribute
461
- #
462
- # This method validates whether the provided input corresponds to a
463
- # recognized ANSI color attribute within the Term::ANSIColor::Attribute
464
- # namespace
465
- #
466
- # @param a [ Object ] the value to check against valid ANSI attributes
467
- #
468
- # @return [ Object, nil ] the attribute if valid, nil otherwise
469
- def attribute?(a)
470
- Term::ANSIColor::Attribute[a]
471
- end
472
-
473
- # The attribute! method validates a given value against known ANSI color
474
- # attributes and raises an error if invalid
475
- #
476
- # This method serves as a validation wrapper around the attribute? method,
477
- # ensuring that a provided value is a recognized ANSI color attribute. If
478
- # the validation fails, it raises an ArgumentError with a descriptive
479
- # message indicating which value was not accepted
480
- #
481
- # @param a [ Object ] the value to validate as an ANSI color attribute
482
- #
483
- # @return [ Object ] returns the validated attribute if it passes validation
484
- #
485
- # @raise [ ArgumentError ] raised when the provided value is not recognized
486
- # as a valid ANSI color attribute
487
- def attribute!(a)
488
- attribute?(a) or
489
- raise ArgumentError, "#{a.inspect} is not a color attribute"
490
- end
491
- end
492
- end
493
-
494
- require 'hackmac/graph/display/cell'
@@ -1,111 +0,0 @@
1
- class Hackmac::Graph
2
- # A module that provides various formatting methods for converting numeric
3
- # values into human-readable strings with appropriate units and display
4
- # formats.
5
- #
6
- # The Formatters module contains a collection of utility methods designed to
7
- # transform raw numeric data into formatted strings that are more suitable
8
- # for display purposes. These methods handle common formatting tasks such as
9
- # converting byte measurements, frequency values, temperature readings, and
10
- # percentages into clean, readable representations.
11
- #
12
- # Each formatter method in this module is intended to be used with data
13
- # visualization or reporting scenarios where presenting numerical information
14
- # in an easily understandable format is important. The module also includes
15
- # specialized functionality for deriving consistent color values based on
16
- # input strings, which can be useful for maintaining visual coherence when
17
- # displaying multiple data series.
18
- #
19
- # @example
20
- # include Hackmac::Graph::Formatters
21
- #
22
- # as_bytes(1024 * 1024) # => "1.000MB"
23
- # as_hertz(2500000000) # => "2.500GHz"
24
- # as_celsius(37.5) # => "37.5°"
25
- # as_percent(95.7) # => "95.7%"
26
- # as_default(42) # => "42"
27
- module Formatters
28
- # The as_bytes method formats a numeric value into a human-readable byte
29
- # representation
30
- #
31
- # This method takes a numeric input and converts it into a formatted string
32
- # representing the value in bytes with appropriate binary prefixes (KB,
33
- # MB, GB, etc.)
34
- #
35
- # @param value [ Numeric ] the numeric value to be formatted as bytes
36
- #
37
- # @return [ String ] the formatted byte representation with unit suffix
38
- def as_bytes(value)
39
- Tins::Unit.format(value, prefix: :uc, format: '%.3f%U', unit: 'B')
40
- end
41
-
42
- # The as_hertz method formats a numeric value into a human-readable
43
- # frequency representation
44
- #
45
- # This method takes a numeric input and converts it into a formatted string
46
- # representing the value in hertz with appropriate metric prefixes (kHz,
47
- # MHz, GHz, etc.)
48
- #
49
- # @param value [ Numeric ] the numeric frequency value to be formatted
50
- #
51
- # @return [ String ] the formatted frequency representation with unit suffix
52
- def as_hertz(value)
53
- Tins::Unit.format(value, prefix: :uc, format: '%.3f%U', unit: 'Hz')
54
- end
55
-
56
- # The as_celsius method formats a temperature value with a degree symbol
57
- #
58
- # This method takes a numeric temperature value and returns a string
59
- # representation with the degree Celsius symbol appended to it
60
- #
61
- # @param value [ Numeric ] the temperature value to be formatted
62
- #
63
- # @return [ String ] the temperature value formatted with a degree Celsius symbol
64
- def as_celsius(value)
65
- "#{value}°"
66
- end
67
-
68
- # The as_percent method formats a numeric value as a percentage string
69
- #
70
- # This method takes a numeric input and returns a string representation
71
- # with a percent sign appended to it, providing a simple way to format
72
- # numeric values as percentages for display purposes
73
- #
74
- # @param value [ Numeric ] the numeric value to be formatted as a percentage
75
- #
76
- # @return [ String ] the numeric value formatted as a percentage string
77
- def as_percent(value)
78
- "#{value}%"
79
- end
80
-
81
- # The as_default method converts a value to its string representation
82
- #
83
- # This method takes any input value and converts it to a string using the to_s method
84
- # It serves as a fallback formatting option when no specific formatting is required
85
- #
86
- # @param value [ Object ] the value to be converted to a string
87
- #
88
- # @return [ String ] the string representation of the input value
89
- def as_default(value)
90
- value.to_s
91
- end
92
-
93
- # The derive_color_from_string method calculates a color value based on the
94
- # input string by generating an MD5 hash and selecting from a predefined
95
- # set of dark colors
96
- #
97
- # @param string [ String ] the input string used to derive the color value
98
- #
99
- # @return [ Integer ] the derived color value from the set of dark ANSI attributes
100
- def derive_color_from_string(string)
101
- cs = (21..226).select { |d|
102
- Term::ANSIColor::Attribute[d].to_rgb_triple.to_hsl_triple.
103
- lightness < 40
104
- }
105
- s = Digest::MD5.digest(string).unpack('Q*')
106
- cs[ s.first % cs.size ]
107
- end
108
-
109
- self
110
- end
111
- end