lab42_color 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +194 -1
- data/lib/lab42/color/color_definitions.rb +1 -1
- data/lib/lab42/color/encode.rb +52 -0
- data/lib/lab42/color/output.rb +21 -0
- data/lib/lab42/color/version.rb +1 -1
- data/lib/lab42/color.rb +7 -48
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf53e88057be590f2ca1a63b4d118e089c09ad1651962b34202aa4d62bd37f7f
|
|
4
|
+
data.tar.gz: 5ed83c3f5546323db78b3150e51dd223b9510b45e12118359872fb7bcf4cbfcf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fb2289c336f75f65982e40afa8758f15cc5cb357f22d50b6f1838d3c034dc4e320abecdfa0115193afd4ffde3b7352546548c5a0f3f7dd2a23348af1376beada
|
|
7
|
+
data.tar.gz: 60cf66004e616872d3d8bb9ab401ada8307db6e768e9239eff9ee80cced0284eda30df9c485895a3674f151757fffb5e9b729f21c97d9b59c4c4f1e06aa23343
|
data/README.md
CHANGED
|
@@ -4,7 +4,200 @@
|
|
|
4
4
|
|
|
5
5
|
# Lab42::Color
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
gem install lab42_color
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
With bundler
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem 'lab42_color'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
In your code
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require 'lab42/color'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Synopsis
|
|
26
|
+
|
|
27
|
+
The `lab42_color` gem exposes the `Lab42::Color` module.
|
|
28
|
+
|
|
29
|
+
It can be uses as a receiver or to extend a module or
|
|
30
|
+
to be included.
|
|
31
|
+
|
|
32
|
+
## Speculations (literate specs)
|
|
33
|
+
|
|
34
|
+
The following specs are executed with the [speculate about](https://github.com/RobertDober/speculate_about) gem.
|
|
35
|
+
|
|
36
|
+
These specs assume the following setup code
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
require 'lab42/color'
|
|
40
|
+
Color = Lab42::Color
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Context: using `Color` as receiver
|
|
44
|
+
|
|
45
|
+
Then we can get a color code, with or without the reset
|
|
46
|
+
```ruby
|
|
47
|
+
expect(Color.color(:red, "hello")).to eq("\e[31mhello\e[0m")
|
|
48
|
+
expect(Color.color(:color253, "hello", reset: false)).to eq("\e[38;5;253mhello")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
And we can combine different colors, fg and bg, as well as bold, dim, etc.
|
|
52
|
+
```ruby
|
|
53
|
+
expect(Color.color(:bg_red, :bold, :dark_sea_green, "text", reset: false))
|
|
54
|
+
.to eq("\e[41m\e[1m\e[38;2;135;175;135mtext")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
And we are also protected against typos
|
|
58
|
+
```ruby
|
|
59
|
+
expect {
|
|
60
|
+
Color.color(:bg_red, :bold, :dark_see_green, "text", reset: false)
|
|
61
|
+
}.to raise_error(Color::BadColorSpecification, "bad color specification: dark_see_green")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or just the ANSI SGC code
|
|
65
|
+
```ruby
|
|
66
|
+
expect(Color.sgc(31)).to eq("\e[31m")
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
And we can observe that the NO_COLOR environnement variable is respected
|
|
71
|
+
```ruby
|
|
72
|
+
original = ENV['NO_COLOR']
|
|
73
|
+
ENV['NO_COLOR'] = 'true'
|
|
74
|
+
# ENV['NO_COLOR'] is cached though
|
|
75
|
+
Lab42::Color.instance_variable_set("@___no_color__", true)
|
|
76
|
+
|
|
77
|
+
expect(Color.color(:red, "hello")).to eq("hello")
|
|
78
|
+
expect(Color.sgc(31)).to eq("")
|
|
79
|
+
|
|
80
|
+
ensure
|
|
81
|
+
ENV['NO_COLOR'] = original
|
|
82
|
+
Lab42::Color.instance_variable_set("@___no_color__", false)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
And we can also output directly
|
|
86
|
+
```ruby
|
|
87
|
+
expect { Color.putc(:yellow, 'yellow') }.to output("\e[33myellow").to_stdout
|
|
88
|
+
expect { Color.putcs(:green, 'green', device: $stderr) }.to output("\e[32mgreen\e[0m\n").to_stderr
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
And we can override the default `reset:` argument of the two output methods
|
|
92
|
+
```ruby
|
|
93
|
+
expect { Color.putcs(:green, 'green', device: $stderr, reset: false) }.to output("\e[32mgreen\n").to_stderr
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
And we can write to any IO device
|
|
97
|
+
```ruby
|
|
98
|
+
out = StringIO.new
|
|
99
|
+
Color.putc(:magenta, 'magenta', device: out, reset: true)
|
|
100
|
+
out.rewind
|
|
101
|
+
expect(out.string).to eq("\e[35mmagenta\e[0m")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Context: Including `Color`
|
|
105
|
+
|
|
106
|
+
Given a class including `Color`
|
|
107
|
+
```ruby
|
|
108
|
+
class IncludingColor
|
|
109
|
+
include Color
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
let(:colorizer) { IncludingColor.new }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then we can use the included methods
|
|
116
|
+
```ruby
|
|
117
|
+
expect(colorizer.color(:aquamarine1, "hello")).to eq("\e[38;2;95;255;215mhello\e[0m")
|
|
118
|
+
expect(colorizer.color(:light_coral, "hello", reset: false)).to eq("\e[38;2;255;135;135mhello")
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Context: Selective Inclusion
|
|
123
|
+
|
|
124
|
+
Given two classes with different selective inclusion
|
|
125
|
+
```ruby
|
|
126
|
+
class OnlyOutput
|
|
127
|
+
include Color::Output
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class OnlyEncode
|
|
131
|
+
include Color::Encode
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
let(:oo) { OnlyOutput.new }
|
|
135
|
+
let(:oe) { OnlyEncode.new }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Then we can use the representivly included methods
|
|
139
|
+
```ruby
|
|
140
|
+
expect { oo.putc('something') }.to output('something').to_stdout
|
|
141
|
+
expect { oo.putcs(:red, 'red', device: $stderr) }.to output("\e[31mred\e[0m\n").to_stderr
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
And also
|
|
145
|
+
```ruby
|
|
146
|
+
expect(oe.color(:dim)).to eq("\e[2m\e[0m")
|
|
147
|
+
expect(oe.sgc(45)).to eq("\e[45m")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
But we will see that
|
|
151
|
+
```ruby
|
|
152
|
+
expect {
|
|
153
|
+
oo.color(:bold)
|
|
154
|
+
}.to raise_error(NoMethodError, "undefined method 'color' for an instance of OnlyOutput")
|
|
155
|
+
expect {
|
|
156
|
+
oo.sgc(0)
|
|
157
|
+
}.to raise_error(NoMethodError, "undefined method 'sgc' for an instance of OnlyOutput")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
And about the same for `OnlyEncode` instances
|
|
161
|
+
```ruby
|
|
162
|
+
# Yeah this might need a name change because of possible interfearence with the private method
|
|
163
|
+
expect {
|
|
164
|
+
oe.putc("hello")
|
|
165
|
+
}.to raise_error(NoMethodError, "private method 'putc' called for an instance of OnlyEncode")
|
|
166
|
+
expect {
|
|
167
|
+
oe.putcs("hello", reset: false)
|
|
168
|
+
}.to raise_error(NoMethodError, "undefined method 'putcs' for an instance of OnlyEncode")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Context: But all I need is `#putcs`
|
|
172
|
+
|
|
173
|
+
Well that is easy, use `Forwardable`
|
|
174
|
+
|
|
175
|
+
Given a class only using `#putcs`
|
|
176
|
+
```ruby
|
|
177
|
+
class OnlyPutcs
|
|
178
|
+
extend Forwardable
|
|
179
|
+
def_delegators Color::Output, :putcs
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
let(:op) { OnlyPutcs.new }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Then we can call `#putcs` from an instance, of course
|
|
186
|
+
```ruby
|
|
187
|
+
expect {
|
|
188
|
+
op.putcs(:bold, "Greetings", reset: false)
|
|
189
|
+
}.to output("\e[1mGreetings\n").to_stdout
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
But we cannot use `#putc`
|
|
194
|
+
```ruby
|
|
195
|
+
# Yeah this might need a name change because of possible interfearence with the private method
|
|
196
|
+
expect {
|
|
197
|
+
op.putc("hello")
|
|
198
|
+
}.to raise_error(NoMethodError, "private method 'putc' called for an instance of OnlyPutcs")
|
|
199
|
+
|
|
200
|
+
```
|
|
8
201
|
|
|
9
202
|
## Author
|
|
10
203
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lab42
|
|
4
|
+
module Color
|
|
5
|
+
module Encode extend self
|
|
6
|
+
|
|
7
|
+
def color(*chunks, reset: true)
|
|
8
|
+
_colors(chunks.flatten) => {needs_reset:, colorized:}
|
|
9
|
+
colorized << _color_code(:reset) if needs_reset && reset
|
|
10
|
+
|
|
11
|
+
colorized.join
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def sgc(value)
|
|
15
|
+
return '' if _no_color
|
|
16
|
+
|
|
17
|
+
ColorDefinitions.sgc(value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def _colors(chunks) =
|
|
22
|
+
# need the [""] to get the correct encoding (WTH?)
|
|
23
|
+
chunks.inject(OpenStruct.new(needs_reset: false, colorized: [""])) do |acc, chunk|
|
|
24
|
+
_color(acc, chunk)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def _color(acc, chunk)
|
|
28
|
+
case chunk
|
|
29
|
+
when Symbol
|
|
30
|
+
acc.colorized << _color_code(chunk)
|
|
31
|
+
acc.update(needs_reset: chunk != :reset)
|
|
32
|
+
when String
|
|
33
|
+
acc.colorized << chunk
|
|
34
|
+
when 0
|
|
35
|
+
acc.colorized << _color_code(:reset)
|
|
36
|
+
acc.update(needs_reset: false)
|
|
37
|
+
else
|
|
38
|
+
raise ArgumentError, "need a colorspec by symbol, a string or 0 (short for :reset), but got #{chunk.inspect}"
|
|
39
|
+
end
|
|
40
|
+
acc
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def _color_code(color) = ColorDefinitions.color_code(color, _no_color)
|
|
44
|
+
|
|
45
|
+
def _no_color
|
|
46
|
+
@___no_color__ ||= ENV['NO_COLOR']
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'encode'
|
|
4
|
+
module Lab42
|
|
5
|
+
module Color
|
|
6
|
+
module Output extend self
|
|
7
|
+
|
|
8
|
+
def putc(*chunks, reset: false, device: $stdout)
|
|
9
|
+
output = Encode.color(*chunks, reset:)
|
|
10
|
+
device.print(output)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def putcs(*chunks, reset: true, device: $stdout)
|
|
14
|
+
output = Encode.color(*chunks, reset:)
|
|
15
|
+
device.puts(output)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
data/lib/lab42/color/version.rb
CHANGED
data/lib/lab42/color.rb
CHANGED
|
@@ -1,62 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'color/color_definitions'
|
|
4
|
+
require_relative 'color/output'
|
|
4
5
|
require 'forwardable'
|
|
5
6
|
require_relative 'ostruct'
|
|
6
7
|
|
|
7
8
|
module Lab42
|
|
8
9
|
module Color extend self
|
|
9
|
-
|
|
10
|
+
BadColorSpecification = Class.new(RuntimeError)
|
|
11
|
+
extend Encode
|
|
12
|
+
extend Output
|
|
10
13
|
|
|
11
|
-
def
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
colorized.join
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def putc(*chunks, reset: false, device: $stdout)
|
|
19
|
-
device.print(color(*chunks, reset:))
|
|
14
|
+
def self.included(into)
|
|
15
|
+
into.send(:include, Encode)
|
|
16
|
+
into.send(:include, Output)
|
|
20
17
|
end
|
|
21
|
-
|
|
22
|
-
def putcs(*chunks, reset: false, device: $stdout)
|
|
23
|
-
device.puts(color(*chunks, reset:))
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def sgc(value)
|
|
27
|
-
return '' if _no_color
|
|
28
18
|
|
|
29
|
-
ColorDefinitions.sgc(value)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
def _colors(chunks) =
|
|
34
|
-
# need the [""] to get the correct encoding (WTH?)
|
|
35
|
-
chunks.inject(OpenStruct.new(needs_reset: false, colorized: [""])) do |acc, chunk|
|
|
36
|
-
_color(acc, chunk)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def _color(acc, chunk)
|
|
40
|
-
case chunk
|
|
41
|
-
when Symbol
|
|
42
|
-
acc.colorized << _color_code(chunk)
|
|
43
|
-
acc.update(needs_reset: chunk != :reset)
|
|
44
|
-
when String
|
|
45
|
-
acc.colorized << chunk
|
|
46
|
-
when 0
|
|
47
|
-
acc.colorized << _color_code(:reset)
|
|
48
|
-
acc.update(needs_reset: false)
|
|
49
|
-
else
|
|
50
|
-
raise ArgumentError, "need a colorspec by symbol, a string or 0 (short for :reset), but got #{chunk.inspect}"
|
|
51
|
-
end
|
|
52
|
-
acc
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def _color_code(color) = ColorDefinitions.color_code(color, _no_color)
|
|
56
|
-
|
|
57
|
-
def _no_color
|
|
58
|
-
@___no_color__ ||= ENV['NO_COLOR']
|
|
59
|
-
end
|
|
60
19
|
end
|
|
61
20
|
end
|
|
62
21
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lab42_color
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Dober
|
|
@@ -34,6 +34,8 @@ files:
|
|
|
34
34
|
- lib/lab42.rb
|
|
35
35
|
- lib/lab42/color.rb
|
|
36
36
|
- lib/lab42/color/color_definitions.rb
|
|
37
|
+
- lib/lab42/color/encode.rb
|
|
38
|
+
- lib/lab42/color/output.rb
|
|
37
39
|
- lib/lab42/color/version.rb
|
|
38
40
|
- lib/lab42/ostruct.rb
|
|
39
41
|
homepage: https://codeberg.org/lab419/lab42_color.git
|