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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0b8cb37c77ec6ca8b6f3d8119778886fb61f257be00459d7f3bea5326b775a2
4
- data.tar.gz: c19106ec0eae163b6d9dfa7eda10e17767fdc40f2ea061f4d764898c2b75e278
3
+ metadata.gz: cf53e88057be590f2ca1a63b4d118e089c09ad1651962b34202aa4d62bd37f7f
4
+ data.tar.gz: 5ed83c3f5546323db78b3150e51dd223b9510b45e12118359872fb7bcf4cbfcf
5
5
  SHA512:
6
- metadata.gz: 7c406f8f45aef6377f6ccb3c517b68570b18f3358bcdce249403c2abcc0412c077709a923cb7611337c11ca60cdd29f47d81db0730a07edf9af4d79d50c9fca0
7
- data.tar.gz: 1e113daaeee941c804ea98a0c427503e396da3a8b0b516285b8dc8bae3681536c97945730b4e63aa834460bab2a0bb0ec87208ff86a655b51c17b92fbc636256
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
- Describing how this gem works with some [speculations](https://github.com/RobertDober/speculate_about)...
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
 
@@ -257,7 +257,7 @@ module Lab42
257
257
  in [:color, col]
258
258
  sgc("38;5;#{col}")
259
259
  in nil
260
- raise ArgumentError, "bad color definition #{color}"
260
+ raise BadColorSpecification, "bad color specification: #{color}"
261
261
  in value
262
262
  sgc(value)
263
263
  end
@@ -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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Lab42
4
4
  module Color
5
- VERSION = '0.1.1'
5
+ VERSION = '0.1.2'
6
6
  end
7
7
  end
8
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
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
- extend Forwardable
10
+ BadColorSpecification = Class.new(RuntimeError)
11
+ extend Encode
12
+ extend Output
10
13
 
11
- def color(*chunks, reset: true)
12
- _colors(chunks.flatten) => {needs_reset:, colorized:}
13
- colorized << _color_code(:reset) if needs_reset && reset
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.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