tone 0.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: 0d3017ae0f1faf02c19825100422d443e068b901b29e6568c0b07d912452970f
4
+ data.tar.gz: 4d86df41de0b4f81cfac5924e065f7f17fa5e73e484e86c7071fcc5218fcb906
5
+ SHA512:
6
+ metadata.gz: b5701133e078fa303fdeb3fa3be36a2b7e0059fe0f0b42f7dae2a5320ed6744dc51021ea19d0d2ec107ed15e312a0c1a8c0e0a8eef295f84afa600941e1a972d
7
+ data.tar.gz: 2f18052fcd993427dd8e0accf3df23ca0cfbfdb59b38c29f72930864025bb01c2837c9c7401312d69759a32235c536f5ccfaa4eaeea574a37b9245e6fa669582
checksums.yaml.gz.sig ADDED
Binary file
data/LICENSE.adoc ADDED
@@ -0,0 +1,134 @@
1
+ = Hippocratic License
2
+
3
+ Version: 2.1.0.
4
+
5
+ Purpose. The purpose of this License is for the Licensor named above to
6
+ permit the Licensee (as defined below) broad permission, if consistent
7
+ with Human Rights Laws and Human Rights Principles (as each is defined
8
+ below), to use and work with the Software (as defined below) within the
9
+ full scope of Licensor’s copyright and patent rights, if any, in the
10
+ Software, while ensuring attribution and protecting the Licensor from
11
+ liability.
12
+
13
+ Permission and Conditions. The Licensor grants permission by this
14
+ license ("License"), free of charge, to the extent of Licensor’s
15
+ rights under applicable copyright and patent law, to any person or
16
+ entity (the "Licensee") obtaining a copy of this software and
17
+ associated documentation files (the "Software"), to do everything with
18
+ the Software that would otherwise infringe (i) the Licensor’s copyright
19
+ in the Software or (ii) any patent claims to the Software that the
20
+ Licensor can license or becomes able to license, subject to all of the
21
+ following terms and conditions:
22
+
23
+ * Acceptance. This License is automatically offered to every person and
24
+ entity subject to its terms and conditions. Licensee accepts this
25
+ License and agrees to its terms and conditions by taking any action with
26
+ the Software that, absent this License, would infringe any intellectual
27
+ property right held by Licensor.
28
+ * Notice. Licensee must ensure that everyone who gets a copy of any part
29
+ of this Software from Licensee, with or without changes, also receives
30
+ the License and the above copyright notice (and if included by the
31
+ Licensor, patent, trademark and attribution notice). Licensee must cause
32
+ any modified versions of the Software to carry prominent notices stating
33
+ that Licensee changed the Software. For clarity, although Licensee is
34
+ free to create modifications of the Software and distribute only the
35
+ modified portion created by Licensee with additional or different terms,
36
+ the portion of the Software not modified must be distributed pursuant to
37
+ this License. If anyone notifies Licensee in writing that Licensee has
38
+ not complied with this Notice section, Licensee can keep this License by
39
+ taking all practical steps to comply within 30 days after the notice. If
40
+ Licensee does not do so, Licensee’s License (and all rights licensed
41
+ hereunder) shall end immediately.
42
+ * Compliance with Human Rights Principles and Human Rights Laws.
43
+ [arabic]
44
+ . Human Rights Principles.
45
+ [loweralpha]
46
+ .. Licensee is advised to consult the articles of the United Nations
47
+ Universal Declaration of Human Rights and the United Nations Global
48
+ Compact that define recognized principles of international human rights
49
+ (the "Human Rights Principles"). Licensee shall use the Software in a
50
+ manner consistent with Human Rights Principles.
51
+ .. Unless the Licensor and Licensee agree otherwise, any dispute,
52
+ controversy, or claim arising out of or relating to (i) Section 1(a)
53
+ regarding Human Rights Principles, including the breach of Section 1(a),
54
+ termination of this License for breach of the Human Rights Principles,
55
+ or invalidity of Section 1(a) or (ii) a determination of whether any Law
56
+ is consistent or in conflict with Human Rights Principles pursuant to
57
+ Section 2, below, shall be settled by arbitration in accordance with the
58
+ Hague Rules on Business and Human Rights Arbitration (the "Rules");
59
+ provided, however, that Licensee may elect not to participate in such
60
+ arbitration, in which event this License (and all rights licensed
61
+ hereunder) shall end immediately. The number of arbitrators shall be one
62
+ unless the Rules require otherwise.
63
+ +
64
+ Unless both the Licensor and Licensee agree to the contrary: (1) All
65
+ documents and information concerning the arbitration shall be public and
66
+ may be disclosed by any party; (2) The repository referred to under
67
+ Article 43 of the Rules shall make available to the public in a timely
68
+ manner all documents concerning the arbitration which are communicated
69
+ to it, including all submissions of the parties, all evidence admitted
70
+ into the record of the proceedings, all transcripts or other recordings
71
+ of hearings and all orders, decisions and awards of the arbitral
72
+ tribunal, subject only to the arbitral tribunal’s powers to take such
73
+ measures as may be necessary to safeguard the integrity of the arbitral
74
+ process pursuant to Articles 18, 33, 41 and 42 of the Rules; and (3)
75
+ Article 26(6) of the Rules shall not apply.
76
+ . Human Rights Laws. The Software shall not be used by any person or
77
+ entity for any systems, activities, or other uses that violate any Human
78
+ Rights Laws. "Human Rights Laws" means any applicable laws,
79
+ regulations, or rules (collectively, "Laws") that protect human,
80
+ civil, labor, privacy, political, environmental, security, economic, due
81
+ process, or similar rights; provided, however, that such Laws are
82
+ consistent and not in conflict with Human Rights Principles (a dispute
83
+ over the consistency or a conflict between Laws and Human Rights
84
+ Principles shall be determined by arbitration as stated above). Where
85
+ the Human Rights Laws of more than one jurisdiction are applicable or in
86
+ conflict with respect to the use of the Software, the Human Rights Laws
87
+ that are most protective of the individuals or groups harmed shall
88
+ apply.
89
+ . Indemnity. Licensee shall hold harmless and indemnify Licensor (and
90
+ any other contributor) against all losses, damages, liabilities,
91
+ deficiencies, claims, actions, judgments, settlements, interest, awards,
92
+ penalties, fines, costs, or expenses of whatever kind, including
93
+ Licensor’s reasonable attorneys’ fees, arising out of or relating to
94
+ Licensee’s use of the Software in violation of Human Rights Laws or
95
+ Human Rights Principles.
96
+ * Failure to Comply. Any failure of Licensee to act according to the
97
+ terms and conditions of this License is both a breach of the License and
98
+ an infringement of the intellectual property rights of the Licensor
99
+ (subject to exceptions under Laws, e.g., fair use). In the event of a
100
+ breach or infringement, the terms and conditions of this License may be
101
+ enforced by Licensor under the Laws of any jurisdiction to which
102
+ Licensee is subject. Licensee also agrees that the Licensor may enforce
103
+ the terms and conditions of this License against Licensee through
104
+ specific performance (or similar remedy under Laws) to the extent
105
+ permitted by Laws. For clarity, except in the event of a breach of this
106
+ License, infringement, or as otherwise stated in this License, Licensor
107
+ may not terminate this License with Licensee.
108
+ * Enforceability and Interpretation. If any term or provision of this
109
+ License is determined to be invalid, illegal, or unenforceable by a
110
+ court of competent jurisdiction, then such invalidity, illegality, or
111
+ unenforceability shall not affect any other term or provision of this
112
+ License or invalidate or render unenforceable such term or provision in
113
+ any other jurisdiction; provided, however, subject to a court
114
+ modification pursuant to the immediately following sentence, if any term
115
+ or provision of this License pertaining to Human Rights Laws or Human
116
+ Rights Principles is deemed invalid, illegal, or unenforceable against
117
+ Licensee by a court of competent jurisdiction, all rights in the
118
+ Software granted to Licensee shall be deemed null and void as between
119
+ Licensor and Licensee. Upon a determination that any term or provision
120
+ is invalid, illegal, or unenforceable, to the extent permitted by Laws,
121
+ the court may modify this License to affect the original purpose that
122
+ the Software be used in compliance with Human Rights Principles and
123
+ Human Rights Laws as closely as possible. The language in this License
124
+ shall be interpreted as to its fair meaning and not strictly for or
125
+ against any party.
126
+ * Disclaimer. TO THE FULL EXTENT ALLOWED BY LAW, THIS SOFTWARE COMES
127
+ "AS IS," WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, AND LICENSOR AND
128
+ ANY OTHER CONTRIBUTOR SHALL NOT BE LIABLE TO ANYONE FOR ANY DAMAGES OR
129
+ OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE
130
+ OR THIS LICENSE, UNDER ANY KIND OF LEGAL CLAIM.
131
+
132
+ This Hippocratic License is an link:https://ethicalsource.dev[Ethical Source license] and is offered
133
+ for use by licensors and licensees at their own risk, on an "AS IS" basis, and with no warranties
134
+ express or implied, to the maximum extent permitted by Laws.
data/README.adoc ADDED
@@ -0,0 +1,453 @@
1
+ :toc: macro
2
+ :toclevels: 5
3
+ :figure-caption!:
4
+
5
+ :amazing_print_link: link:https://github.com/amazing-print/amazing_print[Amazing Print]
6
+ :pattern_matching_link: link:https://alchemists.io/articles/ruby_pattern_matching[pattern matching]
7
+
8
+ = Tone
9
+
10
+ Provides a customizable link:https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences[ANSI] text colorizer for your terminal. This allows you to encode plain text that renders as colorized output. This is great for enhancing Command Line Interfaces (CLIs) and terminal output in general.
11
+
12
+ toc::[]
13
+
14
+ == Features
15
+
16
+ * Provides default styles with multiple options for unique combinations.
17
+ * Provides a single object for encoding and decoding colorized text.
18
+ * Provides aliasing of color configurations.
19
+ * Provides quick access to default and aliased styles.
20
+
21
+ == Requirements
22
+
23
+ . link:https://www.ruby-lang.org[Ruby].
24
+
25
+ == Setup
26
+
27
+ To install, run:
28
+
29
+ [source,ruby]
30
+ ----
31
+ gem install tone
32
+ ----
33
+
34
+ You can also add the gem directly to your project (preferred):
35
+
36
+ [source,ruby]
37
+ ----
38
+ bundle add tone
39
+ ----
40
+
41
+ Once the gem is installed, you only need to require it:
42
+
43
+ ```
44
+ require "tone"
45
+ ```
46
+
47
+ == Usage
48
+
49
+ Basic usage is as follows:
50
+
51
+ [source,ruby]
52
+ ----
53
+ tone = Tone.new
54
+ tone["Success!", :green] # "\e[32mSuccess!\e[0m"
55
+ ----
56
+
57
+ There is a lot more you can do with this gem so the following sections will delve into the specifics.
58
+
59
+ === Encode
60
+
61
+ As you saw earlier, you can encode plain text as colorized text using `+#[]+`. Use of the `+#[]+` method is an _alias_ to the longer `#encode` method. This allows you to use minimal syntax to create colorized text. Here's a few more examples:
62
+
63
+ [source,ruby]
64
+ ----
65
+ tone = Tone.new
66
+
67
+ # With symbols.
68
+ tone["Success", :black, :on_green] # "\e[30;42mSuccess\e[0m"
69
+
70
+ # With strings.
71
+ tone["Success", "black", "on_green"] # "\e[30;42mSuccess\e[0m"
72
+
73
+ # With no styles.
74
+ tone["Success"] # "Success"
75
+
76
+ # With any object that responds to `#to_str` or `#to_s`.
77
+ tone[Object.new, :green] # "\e[32m#<Object:0x000000010f095668>\e[0m"
78
+
79
+ # With nil.
80
+ tone[nil] # ""
81
+
82
+ # With interspersed nils (nils are ignored).
83
+ tone["Success", nil, :green, nil] # "\e[32mSuccess\e[0m"
84
+ ----
85
+
86
+ The first argument is the text you want to encode/colorize. This can be a word, phrase, paragraph, or entire document. All arguments that follow after the first argument are _style_ arguments which allow you to style the color of your text as you see fit. In this case, the `"Success"` text will use a _black foreground_ on a _green background_. The styles available for you to use will be explained shortly, though. For now, know that `+#[]+` is shorthand for `#encode` so any of the above examples could be replaced with `#encode` messages. Example:
87
+
88
+ [source,ruby]
89
+ ----
90
+ tone = Tone.new
91
+ tone.encode "Success", :black, :on_green # "\e[30;42mSuccess\e[0m"
92
+ ----
93
+
94
+ Both methods are available to use depending on your preference.
95
+
96
+ === Decode
97
+
98
+ Once your text has been encoded with colors, it can be nice to decode the colorized text back to plain text along with additional metadata. This is helpful -- as an example -- for testing purposes since you might not always want to deal with the hard to read escape characters. If we build upon the examples from the _Encode_ section, we can decode our colorized text into plain text with extra metadata:
99
+
100
+ [source,ruby]
101
+ ----
102
+ tone = Tone.new
103
+
104
+ tone.decode "\e[30;42mSuccess\e[0m" # [["Success", :black, :on_green]]
105
+ tone.decode "\e[37;41mFailure\e[0m" # [["Failure", :white, :on_red]]
106
+ ----
107
+
108
+ Notice we get an array of sub arrays which mimic the original arguments passed to `#encode`. This allows you to encode and decode with minimal effort. Here's a more complex example where a sentence is used and formatted with the {amazing_print_link} gem:
109
+
110
+ [source,ruby]
111
+ ----
112
+ tone = Tone.new
113
+ ap tone.decode("We turned a \e[37;41mfailure\e[0m into a \e[30;42msuccess\e[0m!")
114
+
115
+ # [
116
+ # [
117
+ # "We turned a "
118
+ # ],
119
+ # [
120
+ # "failure",
121
+ # :white,
122
+ # :on_red
123
+ # ],
124
+ # [
125
+ # " into a "
126
+ # ],
127
+ # [
128
+ # "success",
129
+ # :black,
130
+ # :on_green
131
+ # ],
132
+ # [
133
+ # "!"
134
+ # ]
135
+ # ]
136
+ ----
137
+
138
+ For plain text, you get a single element array but for colorized text, it will be broken down into an array of arguments. This allows you to easily iterate over this structure for parsing, transformation, or {pattern_matching_link} purposes.
139
+
140
+ Here's another example where a paragraph is used:
141
+
142
+ [source,ruby]
143
+ ----
144
+ tone = Tone.new
145
+
146
+ paragraph = <<~CONTENT.strip
147
+ Yesterday \e[30;42mwent well\e[0m
148
+ but tomorrow will be \e[37;41mmore challenging\e[0m.
149
+ CONTENT
150
+
151
+ ap tone.decode(paragraph)
152
+
153
+ # [
154
+ # [
155
+ # "Yesterday "
156
+ # ],
157
+ # [
158
+ # "went well",
159
+ # :black,
160
+ # :on_green
161
+ # ],
162
+ # [
163
+ # "\nbut tomorrow will be "
164
+ # ],
165
+ # [
166
+ # "more challenging",
167
+ # :white,
168
+ # :on_red
169
+ # ],
170
+ # [
171
+ # "."
172
+ # ]
173
+ # ]
174
+ ----
175
+
176
+ === Defaults
177
+
178
+ To display defaults, use:
179
+
180
+ ``` ruby
181
+ tone = Tone.new
182
+ tone.defaults
183
+ ```
184
+
185
+ The above will output something similar to what you see below (minus the categorization) of key and value which will allow you to pick and choose the style or combination of styles you desire.
186
+
187
+ * *Styles*
188
+ ** `clear`
189
+ ** `bold`
190
+ ** `dim`
191
+ ** `italic`
192
+ ** `underline`
193
+ ** `inverse`
194
+ ** `hidden`
195
+ ** `strikethrough`
196
+ * *Foregrounds*
197
+ ** `black`
198
+ ** `red`
199
+ ** `green`
200
+ ** `yellow`
201
+ ** `blue`
202
+ ** `purple`
203
+ ** `cyan`
204
+ ** `white`
205
+ ** `bright_black`
206
+ ** `bright_red`
207
+ ** `bright_green`
208
+ ** `bright_yellow`
209
+ ** `bright_blue`
210
+ ** `bright_purple`
211
+ ** `bright_cyan`
212
+ ** `bright_white`
213
+ * *Backgrounds*
214
+ ** `on_black`
215
+ ** `on_red`
216
+ ** `on_green`
217
+ ** `on_yellow`
218
+ ** `on_blue`
219
+ ** `on_purple`
220
+ ** `on_cyan`
221
+ ** `on_white`
222
+ ** `on_bright_black`
223
+ ** `on_bright_red`
224
+ ** `on_bright_green`
225
+ ** `on_bright_yellow`
226
+ ** `on_bright_blue`
227
+ ** `on_bright_purple`
228
+ ** `on_bright_cyan`
229
+ ** `on_bright_white`
230
+
231
+ These are the defaults for which you can mix-n-match as desired to produce colorful output. For example, if you want black text on a green background with an underline, you could use:
232
+
233
+ ```
234
+ tone = Tone.new
235
+ puts tone["Success!", :black, :on_green, :strikethrough]
236
+ ```
237
+
238
+ ==== Codes
239
+
240
+ For situations where you'd like to find a code (or codes) for a symbol you can use the following:
241
+
242
+ [source,ruby]
243
+ ----
244
+ tone = Tone.new
245
+
246
+ tone.find_code :green # 32
247
+ tone.find_code :bogus # nil
248
+ tone.find_codes :green # [32]
249
+ tone.find_codes :red, :green, :blue # [31, 32, 34]
250
+ tone.find_codes :bogus, :invalid # [nil, nil]
251
+ ----
252
+
253
+ ==== Symbols
254
+
255
+ Much like with the codes, mentioned above, you can find a symbol (or symbols) for a code too:
256
+
257
+ [source,ruby]
258
+ ----
259
+ tone = Tone.new
260
+
261
+ tone.find_symbol 32 # :green
262
+ tone.find_symbol 666 # nil
263
+ tone.find_symbols 32 # [:green]
264
+ tone.find_symbols 31, 32, 34 # [:red, :green, :blue]
265
+ tone.find_symbols 666, 999 # [nil, nil]
266
+ ----
267
+
268
+ === Aliases
269
+
270
+ You can alias combinations of default styles with a descriptive name for shorthand reuse. This allows you to reduce duplicated effort and speed up your workflow. Here are a few examples:
271
+
272
+ [source,ruby]
273
+ ----
274
+ tone = Tone.new
275
+ tone.add_alias :success, :black, :on_green
276
+ tone.add_alias :failure, :white, :on_red
277
+
278
+ tone["Success!", :success] # "\e[30;42mSuccess!\e[0m"
279
+ tone["Failure!", :failure] # "\e[37;41mFailure!\e[0m"
280
+ ----
281
+
282
+ Notice that the first argument is your alias and _all arguments after the first argument_ is the list of styles. Once added, both the `:success` and `:failure` aliases can immediately be used. You can also add multiple aliases, at once, by chaining your messages:
283
+
284
+ ``` ruby
285
+ tone = Tone.new
286
+ .add_alias(:success, :black, :on_green)
287
+ .add_alias :failure, :white, :on_red
288
+
289
+ tone["Success!", :success] # "\e[30;42mSuccess!\e[0m"
290
+ tone["Failure!", :failure] # "\e[37;41mFailure!\e[0m"
291
+ ```
292
+
293
+ Aliases -- and associated styles -- can be symbols or strings. The following, despite using strings, is identical to the above:
294
+
295
+ [source,ruby]
296
+ ----
297
+ tone = Tone.new
298
+ .add_alias("success", "black", "on_green")
299
+ .add_alias "failure", "white", "on_red"
300
+
301
+ tone["Success!", :success] # "\e[30;42mSuccess!\e[0m"
302
+ tone["Failure!", :failure] # "\e[37;41mFailure!\e[0m"
303
+ ----
304
+
305
+ To see the list of all aliases added, use:
306
+
307
+ [source,ruby]
308
+ ----
309
+ tone = Tone.new.add_alias(:success, :black, :on_green).add_alias :failure, :white, :on_red
310
+ ap tone.aliases
311
+
312
+ # {
313
+ # :success => [
314
+ # :black,
315
+ # :on_green
316
+ # ],
317
+ # :failure => [
318
+ # :white,
319
+ # :on_red
320
+ # ]
321
+ # }
322
+ ----
323
+
324
+ To get a specific alias, use:
325
+
326
+ [source,ruby]
327
+ ----
328
+ tone = Tone.new.add_alias :success, :black, :on_green
329
+ tone.get_alias :success
330
+
331
+ # [:black, :on_green]
332
+ ----
333
+
334
+ In the case of a default, you'll only get back the given key:
335
+
336
+ [source,ruby]
337
+ ----
338
+ Tone.new.get_alias :green # :green
339
+ ----
340
+
341
+ === Errors
342
+
343
+ There are several checks performed which might result in a `Tone::Error` if not properly used. Here's a few examples of what you might see.
344
+
345
+ [source,ruby]
346
+ ----
347
+ tone = Tone.new
348
+
349
+ tone.add_alias :bogus
350
+ # Alias must have styles: :bogus. (Tone::Error)
351
+
352
+ tone.add_alias :bogus, nil
353
+ # Alias must have styles: :bogus. (Tone::Error)
354
+
355
+ tone.add_alias :red, :red
356
+ # Alias mustn't duplicate (override) default: :red. (Tone::Error)
357
+
358
+ tone.add_alias :bogus, :invalid
359
+ # Invalid style (:invalid) for key (:bogus). (Tone::Error)
360
+
361
+ tone.add_alias :success, :black, :on_green
362
+ tone.add_alias :success, :black, :on_green
363
+ # Duplicate alias detected (already exists): :success. (Tone::Error)
364
+
365
+ tone.get_alias nil
366
+ # Invalid alias or default: nil. (Tone::Error)
367
+
368
+ tone.get_alias :bogus
369
+ # Invalid alias or default: :bogus. (Tone::Error)
370
+ ----
371
+
372
+ === Guidelines
373
+
374
+ The following are worth considering, when using this gem, to help keep your implementation consistent.
375
+
376
+ Order your arguments by style, foreground, and background when encoding:
377
+
378
+ [source,ruby]
379
+ ----
380
+ # No
381
+ tone["test, :underline, :on_black, :white]
382
+ tone["test, :white, :underline, :on_black]
383
+ tone["test, :on_black, :white, :underline]
384
+
385
+ # Yes
386
+ tone["test, :underline, :white, :on_black]
387
+ ----
388
+
389
+ Order your arguments by style, foreground, and background when adding aliases:
390
+
391
+ [source,ruby]
392
+ ----
393
+ # No
394
+ tone.add_alias :demo, :underline, :on_black, :white
395
+ tone.add_alias :demo, :white, :underline, :on_black
396
+ tone.add_alias :demo, :on_black, :white, :underline
397
+
398
+ # Yes
399
+ tone.add_alias :demo, :underline, :white, :on_black
400
+ ----
401
+
402
+ These are not hard requirements but these little touches will help improve readability. 🎉
403
+
404
+ == Development
405
+
406
+ To contribute, run:
407
+
408
+ [source,bash]
409
+ ----
410
+ git clone https://github.com/bkuhlmann/tone
411
+ cd tone
412
+ bin/setup
413
+ ----
414
+
415
+ You use the IRB console for direct access to all objects:
416
+
417
+ [source,bash]
418
+ ----
419
+ bin/console
420
+ ----
421
+
422
+ Lastly, there is a `bin/show` script which can be run to display all default styles for quick visual convenience:
423
+
424
+ [source,bash]
425
+ ----
426
+ bin/show
427
+ ----
428
+
429
+ == Tests
430
+
431
+ To test, run:
432
+
433
+ [source,bash]
434
+ ----
435
+ bin/rake
436
+ ----
437
+
438
+ == link:https://alchemists.io/policies/license[License]
439
+
440
+ == link:https://alchemists.io/policies/security[Security]
441
+
442
+ == link:https://alchemists.io/policies/code_of_conduct[Code of Conduct]
443
+
444
+ == link:https://alchemists.io/policies/contributions[Contributions]
445
+
446
+ == link:https://alchemists.io/projects/tone/versions[Versions]
447
+
448
+ == link:https://alchemists.io/community[Community]
449
+
450
+ == Credits
451
+
452
+ * Built with link:https://alchemists.io/projects/gemsmith[Gemsmith].
453
+ * Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann].
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/arrays"
4
+
5
+ module Tone
6
+ # Allows storage of custom custom which can be referenced when colorizing text.
7
+ class Aliaser
8
+ using Refinements::Arrays
9
+
10
+ attr_reader :defaults
11
+
12
+ def initialize defaults: DEFAULTS
13
+ @defaults = defaults
14
+ @custom = {}
15
+ end
16
+
17
+ def get key
18
+ symbol = String(key).to_sym
19
+
20
+ custom.fetch symbol do
21
+ return key if defaults.key? symbol
22
+
23
+ usage = defaults.keys.append(*custom.keys).map(&:inspect).to_sentence conjunction: "and/or"
24
+
25
+ fail Error, "Invalid alias or default: #{key.inspect}. Use: #{usage}."
26
+ end
27
+ end
28
+
29
+ def add(key, *styles)
30
+ fail Error, "Alias must have styles: #{key.inspect}." if styles.tap(&:compact!).empty?
31
+
32
+ custom[key.to_sym] = validate key, styles.map(&:to_sym)
33
+ self
34
+ end
35
+
36
+ def to_h = custom.dup
37
+
38
+ private
39
+
40
+ attr_reader :custom
41
+
42
+ def validate key, styles
43
+ check_duplicate key
44
+ styles.each { |style| check_style key, style }
45
+ styles
46
+ end
47
+
48
+ def check_duplicate key
49
+ kind = key.inspect
50
+
51
+ fail Error, "Duplicate alias detected (already exists): #{kind}." if custom.key? key
52
+ fail Error, "Alias mustn't duplicate (override) default: #{kind}." if defaults.key? key
53
+ end
54
+
55
+ def check_style key, style
56
+ defaults.fetch style do
57
+ usage = defaults.keys.map(&:inspect).to_sentence conjunction: "and/or"
58
+
59
+ fail Error, "Invalid style (#{style.inspect}) for key (#{key.inspect}). Use: #{usage}."
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tone
4
+ # The primary interface for working with colorized text.
5
+ class Client
6
+ def initialize enabled: $stdout.tty?, container: Tone::CONTAINER
7
+ @aliaser = container.fetch(:aliaser).new defaults: container.fetch(:defaults)
8
+ @encoder = container.fetch(:encoder).new(aliaser:, enabled:)
9
+ @decoder = container.fetch(:decoder).new defaults: aliaser.defaults
10
+ end
11
+
12
+ def defaults = aliaser.defaults
13
+
14
+ def aliases = aliaser.to_h
15
+
16
+ def add_alias(...)
17
+ aliaser.add(...)
18
+ self
19
+ end
20
+
21
+ def get_alias(...) = aliaser.get(...)
22
+
23
+ def encode(...) = encoder.call(...)
24
+
25
+ alias [] encode
26
+
27
+ def decode(...) = decoder.call(...)
28
+
29
+ def find_code(key) = defaults[key]
30
+
31
+ def find_codes(*keys) = defaults.values_at(*keys)
32
+
33
+ def find_symbol(code) = defaults.invert[code]
34
+
35
+ def find_symbols(*codes) = defaults.invert.values_at(*codes)
36
+
37
+ private
38
+
39
+ attr_reader :aliaser, :encoder, :decoder
40
+ end
41
+ end
@@ -0,0 +1,44 @@
1
+ clear: 0
2
+ bold: 1
3
+ dim: 2
4
+ italic: 3
5
+ underline: 4
6
+ inverse: 7
7
+ hidden: 8
8
+ strikethrough: 9
9
+
10
+ black: 30
11
+ red: 31
12
+ green: 32
13
+ yellow: 33
14
+ blue: 34
15
+ purple: 35
16
+ cyan: 36
17
+ white: 37
18
+
19
+ bright_black: 90
20
+ bright_red: 91
21
+ bright_green: 92
22
+ bright_yellow: 93
23
+ bright_blue: 94
24
+ bright_purple: 95
25
+ bright_cyan: 96
26
+ bright_white: 97
27
+
28
+ on_black: 40
29
+ on_red: 41
30
+ on_green: 42
31
+ on_yellow: 43
32
+ on_blue: 44
33
+ on_purple: 45
34
+ on_cyan: 46
35
+ on_white: 47
36
+
37
+ on_bright_black: 100
38
+ on_bright_red: 101
39
+ on_bright_green: 102
40
+ on_bright_yellow: 103
41
+ on_bright_blue: 104
42
+ on_bright_purple: 105
43
+ on_bright_cyan: 106
44
+ on_bright_white: 107
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Tone
6
+ module Configuration
7
+ # Loads the default configuration into memory as a frozen hash.
8
+ class Loader
9
+ def initialize path: Pathname("#{__dir__}/defaults.yml")
10
+ @path = path
11
+ end
12
+
13
+ def call = YAML.safe_load(path.read, symbolize_names: true).freeze
14
+
15
+ private
16
+
17
+ attr_reader :path
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+
5
+ module Tone
6
+ # Decodes color encoded text into metadata (hash).
7
+ class Decoder
8
+ PATTERN = /
9
+ \e\[ # Start prefix.
10
+ (?<codes>[\d;]+) # Style codes.
11
+ m # Start suffix.
12
+ (?<text>.+?) # Lazy text.
13
+ \e\[0m # Stop.
14
+ /mx
15
+
16
+ def initialize pattern: PATTERN, defaults: DEFAULTS, client: StringScanner
17
+ @pattern = pattern
18
+ @defaults = defaults
19
+ @client = client
20
+ @prefix_pattern = /.*#{pattern}/m
21
+ end
22
+
23
+ def call(text) = scan client.new(String(text))
24
+
25
+ private
26
+
27
+ attr_reader :pattern, :defaults, :client, :prefix_pattern
28
+
29
+ def scan scanner, collection: []
30
+ until scanner.eos?
31
+ result = scanner.scan_until pattern
32
+
33
+ return collection.append [scanner.string[scanner.rest]] unless result
34
+
35
+ normal = scanner.pre_match.sub prefix_pattern, ""
36
+
37
+ collection << [normal] unless normal.empty?
38
+ collection << extract_captures(scanner)
39
+ end
40
+
41
+ collection
42
+ end
43
+
44
+ def extract_captures scanner
45
+ codes, text = scanner.captures
46
+ [text, *symbolize(codes)]
47
+ end
48
+
49
+ def symbolize(codes) = defaults.invert.values_at(*String(codes).split(";").map(&:to_i))
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/arrays"
4
+
5
+ module Tone
6
+ # Encodes plain text as colorized text.
7
+ class Encoder
8
+ using Refinements::Arrays
9
+
10
+ def initialize aliaser: Aliaser.new, enabled: $stdout.tty?
11
+ @aliaser = aliaser
12
+ @enabled = enabled
13
+ end
14
+
15
+ def call(text, *styles)
16
+ return "" if String(text).empty?
17
+
18
+ !enabled || styles.tap(&:compact!).empty? ? text : "#{start(*styles)}#{text}#{stop}"
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :aliaser, :enabled
24
+
25
+ def start(*styles) = %(\e[#{escape(*styles)}m)
26
+
27
+ def stop = "\e[#{defaults.fetch :clear}m"
28
+
29
+ def escape(*styles)
30
+ styles.reduce([]) { |expansion, key| expansion.append(*aliaser.get(key)) }
31
+ .map { |key| defaults[key.to_sym] }
32
+ .join ";"
33
+ end
34
+
35
+ def defaults = aliaser.defaults
36
+ end
37
+ end
data/lib/tone/error.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tone
4
+ # Root error class for gem.
5
+ class Error < StandardError
6
+ end
7
+ end
data/lib/tone.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+
5
+ Zeitwerk::Loader.for_gem.setup
6
+
7
+ # Main namespace.
8
+ module Tone
9
+ DEFAULTS = Configuration::Loader.new.call
10
+ CONTAINER = {defaults: DEFAULTS, aliaser: Aliaser, encoder: Encoder, decoder: Decoder}.freeze
11
+
12
+ def self.new(...) = Client.new(...)
13
+ end
data/tone.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "tone"
5
+ spec.version = "0.0.0"
6
+ spec.authors = ["Brooke Kuhlmann"]
7
+ spec.email = ["brooke@alchemists.io"]
8
+ spec.homepage = "https://alchemists.io/projects/tone"
9
+ spec.summary = "A customizable ANSI text colorizer for your terminal."
10
+ spec.license = "Hippocratic-2.1"
11
+
12
+ spec.metadata = {
13
+ "bug_tracker_uri" => "https://github.com/bkuhlmann/tone/issues",
14
+ "changelog_uri" => "https://alchemists.io/projects/tone/versions",
15
+ "documentation_uri" => "https://alchemists.io/projects/tone",
16
+ "funding_uri" => "https://github.com/sponsors/bkuhlmann",
17
+ "label" => "Tone",
18
+ "rubygems_mfa_required" => "true",
19
+ "source_code_uri" => "https://github.com/bkuhlmann/tone"
20
+ }
21
+
22
+ spec.signing_key = Gem.default_key_path
23
+ spec.cert_chain = [Gem.default_cert_path]
24
+
25
+ spec.required_ruby_version = "~> 3.2"
26
+ spec.add_dependency "refinements", "~> 10.0"
27
+ spec.add_dependency "zeitwerk", "~> 2.6"
28
+
29
+ spec.extra_rdoc_files = Dir["README*", "LICENSE*"]
30
+ spec.files = Dir["*.gemspec", "lib/**/*"]
31
+ end
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brooke Kuhlmann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIEeDCCAuCgAwIBAgIBATANBgkqhkiG9w0BAQsFADBBMQ8wDQYDVQQDDAZicm9v
14
+ a2UxGjAYBgoJkiaJk/IsZAEZFgphbGNoZW1pc3RzMRIwEAYKCZImiZPyLGQBGRYC
15
+ aW8wHhcNMjMwMzIyMTYxNDQxWhcNMjUwMzIxMTYxNDQxWjBBMQ8wDQYDVQQDDAZi
16
+ cm9va2UxGjAYBgoJkiaJk/IsZAEZFgphbGNoZW1pc3RzMRIwEAYKCZImiZPyLGQB
17
+ GRYCaW8wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCro8tj5/E1Hg88
18
+ f4qfiwPVd2zJQHvdYt4GHVvuHRRgx4HGhJuNp+4BId08RBn7V6V1MW6MY3kezRBs
19
+ M+7QOQ4b1xNLTvY7FYQB1wGK5a4x7TTokDrPYQxDB2jmsdDYCzVbIMrAvUfcecRi
20
+ khyGZCdByiiCl4fKv77P12tTT+NfsvXkLt/AYCGwjOUyGKTQ01Z6eC09T27GayPH
21
+ QQvIkakyFgcJtzSyGzs8bzK5q9u7wQ12MNTjJoXzW69lqp0oNvDylu81EiSUb5S6
22
+ QzzPxZBiRB1sgtbt1gUbVI262ZDq1gR+HxPFmp+Cgt7ZLIJZAtesQvtcMzseXpfn
23
+ hpmm0Sw22KGhRAy/mqHBRhDl5HqS1SJp2Ko3lcnpXeFResp0HNlt8NSu13vhC08j
24
+ GUHU9MyIXbFOsnp3K3ADrAVjPWop8EZkmUR3MV/CUm00w2cZHCSGiXl1KMpiVKvk
25
+ Ywr1gd2ZME4QLSo+EXUtLxDUa/W3xnBS8dBOuMMz02FPWYr3PN8CAwEAAaN7MHkw
26
+ CQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFAFgmv0tYMZnItuPycSM
27
+ F5wykJEVMB8GA1UdEQQYMBaBFGJyb29rZUBhbGNoZW1pc3RzLmlvMB8GA1UdEgQY
28
+ MBaBFGJyb29rZUBhbGNoZW1pc3RzLmlvMA0GCSqGSIb3DQEBCwUAA4IBgQAX+EGY
29
+ 9RLYGxF1VLZz+G1ACQc4uyrCB6kXwI06kzUa5dF9tPXqTX9ffnz3/W8ck2IQhKzu
30
+ MKO2FVijzbDWTsZeZGglS4E+4Jxpau1lU9HhOIcKolv6LeC6UdALTFudY+GLb8Xw
31
+ REXgaJkjzzhkUSILmEnRwEbY08dVSl7ZAaxVI679vfI2yapLlIwpbBgmQTiTvPr3
32
+ qyyLUno9flYEOv9fmGHunSrM+gE0/0niGTXa5GgXBXYGS2he4LQGgSBfGp/cTwMU
33
+ rDKJRcusZ12lNBeDfgqACz/BBJF8FLodgk6rGMRZz7+ZmjjHEmpG5bQpR6Q2BuWL
34
+ XMtYk/QzaWuhiR7pWjiF8jbdd7RO6or0ohq7iFkokz/5xrtQ/vPzU2RQ3Qc6YaKw
35
+ 3n5C8/6Zh9DYTkpcwPSuIfAga6wf4nXc9m6JAw8AuMLaiWN/r/2s4zJsUHYERJEu
36
+ gZGm4JqtuSg8pYjPeIJxS960owq+SfuC+jxqmRA54BisFCv/0VOJi7tiJVY=
37
+ -----END CERTIFICATE-----
38
+ date: 2023-04-01 00:00:00.000000000 Z
39
+ dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: refinements
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '10.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: zeitwerk
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.6'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.6'
68
+ description:
69
+ email:
70
+ - brooke@alchemists.io
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files:
74
+ - README.adoc
75
+ - LICENSE.adoc
76
+ files:
77
+ - LICENSE.adoc
78
+ - README.adoc
79
+ - lib/tone.rb
80
+ - lib/tone/aliaser.rb
81
+ - lib/tone/client.rb
82
+ - lib/tone/configuration/defaults.yml
83
+ - lib/tone/configuration/loader.rb
84
+ - lib/tone/decoder.rb
85
+ - lib/tone/encoder.rb
86
+ - lib/tone/error.rb
87
+ - tone.gemspec
88
+ homepage: https://alchemists.io/projects/tone
89
+ licenses:
90
+ - Hippocratic-2.1
91
+ metadata:
92
+ bug_tracker_uri: https://github.com/bkuhlmann/tone/issues
93
+ changelog_uri: https://alchemists.io/projects/tone/versions
94
+ documentation_uri: https://alchemists.io/projects/tone
95
+ funding_uri: https://github.com/sponsors/bkuhlmann
96
+ label: Tone
97
+ rubygems_mfa_required: 'true'
98
+ source_code_uri: https://github.com/bkuhlmann/tone
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '3.2'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.4.10
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: A customizable ANSI text colorizer for your terminal.
118
+ test_files: []
metadata.gz.sig ADDED
Binary file