Skip to content

Commit d8f3907

Browse files
Apply code review suggestions
- Changed the API to `stylize("string").bold.blue.on_white` - Moved magic numbers to constants - Improve code reuse and legibility
1 parent 11cb3fa commit d8f3907

File tree

6 files changed

+265
-192
lines changed

6 files changed

+265
-192
lines changed

docsite/source/styling-your-output.html.md

+26-26
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,32 @@ module StylesDemo
2020
# rubocop:disable Metrics/AbcSize
2121
def call
2222
demo = <<~DEMO
23-
`bold` #{bold("This is bold")}
24-
`dim` #{dim("This is dim")}
25-
`italic` #{italic("This is italic")}
26-
`underline` #{underline("This is underline")}
27-
`blink` #{blink("This blinks")}
28-
`reverse` #{reverse("This was reversed")}
29-
`invisible` #{invisible("This is invisible")} (you can't see it, right?)
30-
`black` #{black("This is black")}
31-
`red` #{red("This is red")}
32-
`green` #{green("This is green")}
33-
`yellow` #{yellow("This is yellow")}
34-
`blue` #{blue("This is blue")}
35-
`magenta` #{magenta("This is magenta")}
36-
`cyan` #{cyan("This is cyan")}
37-
`white` #{white("This is white")}
38-
`on_black` #{on_black("This is black")}
39-
`on_red` #{on_red("This is red")}
40-
`on_green` #{on_green("This is green")}
41-
`on_yellow` #{on_yellow("This is yellow")}
42-
`on_blue` #{on_blue("This is blue")}
43-
`on_magenta` #{on_magenta("This is magenta")}
44-
`on_cyan` #{on_cyan("This is cyan")}
45-
`on_white` #{on_white("This is white")}
46-
`bold`+`red`: #{bold(red("This is bold red"))}
47-
`bold`+`on_green`: #{bold(on_green("This is bold on green"))}
48-
`bold`+`red`+`on_green`: #{bold(red(on_green("This is bold red on green")))}
23+
`stylize("This is bold").bold` #=> #{stylize("This is bold").bold}
24+
`stylize("This is dim").dim` #=> #{stylize("This is dim").dim}
25+
`stylize("This is italic").italic` #=> #{stylize("This is italic").italic}
26+
`stylize("This is underline").underline` #=> #{stylize("This is underline").underline}
27+
`stylize("This blinks").blink` #=> #{stylize("This blinks").blink}
28+
`stylize("This was reversed").reverse` #=> #{stylize("This was reversed").reverse}
29+
`stylize("This is invisible").invisible` #=> #{stylize("This is invisible").invisible} (you can't see it, right?)
30+
`stylize("This is black").black` #=> #{stylize("This is black").black}
31+
`stylize("This is red").red` #=> #{stylize("This is red").red}
32+
`stylize("This is green").green` #=> #{stylize("This is green").green}
33+
`stylize("This is yellow").yellow` #=> #{stylize("This is yellow").yellow}
34+
`stylize("This is blue").blue` #=> #{stylize("This is blue").blue}
35+
`stylize("This is magenta").magenta` #=> #{stylize("This is magenta").magenta}
36+
`stylize("This is cyan").cyan` #=> #{stylize("This is cyan").cyan}
37+
`stylize("This is white").white` #=> #{stylize("This is white").white}
38+
`stylize("This is black").on_black` #=> #{stylize("This is black").on_black}
39+
`stylize("This is red").on_red` #=> #{stylize("This is red").on_red}
40+
`stylize("This is green").on_green` #=> #{stylize("This is green").on_green}
41+
`stylize("This is yellow").on_yellow` #=> #{stylize("This is yellow").on_yellow}
42+
`stylize("This is blue").on_blue` #=> #{stylize("This is blue").on_blue}
43+
`stylize("This is magenta").on_magenta` #=> #{stylize("This is magenta").on_magenta}
44+
`stylize("This is cyan").on_cyan` #=> #{stylize("This is cyan").on_cyan}
45+
`stylize("This is white").on_white` #=> #{stylize("This is white").on_white}
46+
`stylize("This is bold red").bold.red #=> #{stylize("This is bold red").bold.red}
47+
`stylize("This is bold on green").bold.on_green` #=> #{stylize("This is bold on green").bold.on_green}
48+
`stylize("This is bold red on green").bold.red.on_green` #=> #{stylize("This is bold red on green").bold.red.on_green}
4949
DEMO
5050
puts demo
5151
end

lib/dry/cli/styles.rb

+178-122
Original file line numberDiff line numberDiff line change
@@ -2,134 +2,190 @@
22

33
module Dry
44
class CLI
5-
# Collection of functions to style text.
5+
# Collection of functions to style text
66
#
77
# @since 1.3.0
88
module Styles
9-
# since 1.3.0
10-
def bold(text)
11-
ensure_clean_sequence("\e[1m#{text}")
12-
end
13-
14-
# since 1.3.0
15-
def dim(text)
16-
ensure_clean_sequence("\e[2m#{text}")
17-
end
18-
19-
# since 1.3.0
20-
def italic(text)
21-
ensure_clean_sequence("\e[3m#{text}")
22-
end
23-
24-
# since 1.3.0
25-
def underline(text)
26-
ensure_clean_sequence("\e[4m#{text}")
27-
end
28-
29-
# since 1.3.0
30-
def blink(text)
31-
ensure_clean_sequence("\e[5m#{text}")
32-
end
33-
34-
# since 1.3.0
35-
def reverse(text)
36-
ensure_clean_sequence("\e[7m#{text}")
37-
end
38-
39-
# since 1.3.0
40-
def invisible(text)
41-
ensure_clean_sequence("\e[8m#{text}")
42-
end
43-
44-
# since 1.3.0
45-
def black(text)
46-
ensure_clean_sequence("\e[30m#{text}")
47-
end
48-
49-
# since 1.3.0
50-
def red(text)
51-
ensure_clean_sequence("\e[31m#{text}")
52-
end
53-
54-
# since 1.3.0
55-
def green(text)
56-
ensure_clean_sequence("\e[32m#{text}")
57-
end
58-
59-
# since 1.3.0
60-
def yellow(text)
61-
ensure_clean_sequence("\e[33m#{text}")
62-
end
63-
64-
# since 1.3.0
65-
def blue(text)
66-
ensure_clean_sequence("\e[34m#{text}")
67-
end
68-
69-
# since 1.3.0
70-
def magenta(text)
71-
ensure_clean_sequence("\e[35m#{text}")
72-
end
73-
74-
# since 1.3.0
75-
def cyan(text)
76-
ensure_clean_sequence("\e[36m#{text}")
77-
end
78-
79-
# since 1.3.0
80-
def white(text)
81-
ensure_clean_sequence("\e[37m#{text}")
82-
end
83-
84-
# since 1.3.0
85-
def on_black(text)
86-
ensure_clean_sequence("\e[40m#{text}")
87-
end
88-
89-
# since 1.3.0
90-
def on_red(text)
91-
ensure_clean_sequence("\e[41m#{text}")
92-
end
93-
94-
# since 1.3.0
95-
def on_green(text)
96-
ensure_clean_sequence("\e[42m#{text}")
97-
end
98-
99-
# since 1.3.0
100-
def on_yellow(text)
101-
ensure_clean_sequence("\e[43m#{text}")
102-
end
103-
104-
# since 1.3.0
105-
def on_blue(text)
106-
ensure_clean_sequence("\e[44m#{text}")
107-
end
108-
109-
# since 1.3.0
110-
def on_magenta(text)
111-
ensure_clean_sequence("\e[45m#{text}")
112-
end
113-
114-
# since 1.3.0
115-
def on_cyan(text)
116-
ensure_clean_sequence("\e[46m#{text}")
117-
end
118-
119-
# since 1.3.0
120-
def on_white(text)
121-
ensure_clean_sequence("\e[47m#{text}")
9+
RESET = 0
10+
BOLD = 1
11+
DIM = 2
12+
ITALIC = 3
13+
UNDERLINE = 4
14+
BLINK = 5
15+
REVERSE = 7
16+
INVISIBLE = 8
17+
BLACK = 30
18+
RED = 31
19+
GREEN = 32
20+
YELLOW = 33
21+
BLUE = 34
22+
MAGENTA = 35
23+
CYAN = 36
24+
WHITE = 37
25+
ON_BLACK = 40
26+
ON_RED = 41
27+
ON_GREEN = 42
28+
ON_YELLOW = 43
29+
ON_BLUE = 44
30+
ON_MAGENTA = 45
31+
ON_CYAN = 46
32+
ON_WHITE = 47
33+
34+
# Returns a text that can be styled
35+
#
36+
# @param text [String] text to be styled
37+
#
38+
# @since 1.3.0
39+
def stylize(text)
40+
StyledText.new(text)
12241
end
12342

124-
private
125-
43+
# Styled text
44+
#
12645
# @since 1.3.0
127-
# @api private
128-
def ensure_clean_sequence(text)
129-
clen_text = text
130-
clear = "\e[0m"
131-
clen_text += clear unless text.end_with?(clear)
132-
clen_text
46+
class StyledText
47+
def initialize(text)
48+
@text = text
49+
end
50+
51+
# Makes `StyledText` printable
52+
#
53+
# @since 1.3.0
54+
def to_s
55+
text + select_graphic_rendition(RESET)
56+
end
57+
58+
# since 1.3.0
59+
def bold
60+
chainable_update!(BOLD, text)
61+
end
62+
63+
# since 1.3.0
64+
def dim
65+
chainable_update!(DIM, text)
66+
end
67+
68+
# since 1.3.0
69+
def italic
70+
chainable_update!(ITALIC, text)
71+
end
72+
73+
# since 1.3.0
74+
def underline
75+
chainable_update!(UNDERLINE, text)
76+
end
77+
78+
# since 1.3.0
79+
def blink
80+
chainable_update!(BLINK, text)
81+
end
82+
83+
# since 1.3.0
84+
def reverse
85+
chainable_update!(REVERSE, text)
86+
end
87+
88+
# since 1.3.0
89+
def invisible
90+
chainable_update!(INVISIBLE, text)
91+
end
92+
93+
# since 1.3.0
94+
def black
95+
chainable_update!(BLACK, text)
96+
end
97+
98+
# since 1.3.0
99+
def red
100+
chainable_update!(RED, text)
101+
end
102+
103+
# since 1.3.0
104+
def green
105+
chainable_update!(GREEN, text)
106+
end
107+
108+
# since 1.3.0
109+
def yellow
110+
chainable_update!(YELLOW, text)
111+
end
112+
113+
# since 1.3.0
114+
def blue
115+
chainable_update!(BLUE, text)
116+
end
117+
118+
# since 1.3.0
119+
def magenta
120+
chainable_update!(MAGENTA, text)
121+
end
122+
123+
# since 1.3.0
124+
def cyan
125+
chainable_update!(CYAN, text)
126+
end
127+
128+
# since 1.3.0
129+
def white
130+
chainable_update!(WHITE, text)
131+
end
132+
133+
# since 1.3.0
134+
def on_black
135+
chainable_update!(ON_BLACK, text)
136+
end
137+
138+
# since 1.3.0
139+
def on_red
140+
chainable_update!(ON_RED, text)
141+
end
142+
143+
# since 1.3.0
144+
def on_green
145+
chainable_update!(ON_GREEN, text)
146+
end
147+
148+
# since 1.3.0
149+
def on_yellow
150+
chainable_update!(ON_YELLOW, text)
151+
end
152+
153+
# since 1.3.0
154+
def on_blue
155+
chainable_update!(ON_BLUE, text)
156+
end
157+
158+
# since 1.3.0
159+
def on_magenta
160+
chainable_update!(ON_MAGENTA, text)
161+
end
162+
163+
# since 1.3.0
164+
def on_cyan
165+
chainable_update!(ON_CYAN, text)
166+
end
167+
168+
# since 1.3.0
169+
def on_white
170+
chainable_update!(ON_WHITE, text)
171+
end
172+
173+
private
174+
175+
attr_reader :text
176+
177+
# @since 1.3.0
178+
# @api private
179+
def chainable_update!(style, new_text)
180+
@text = select_graphic_rendition(style) + new_text
181+
self
182+
end
183+
184+
# @since 1.3.0
185+
# @api private
186+
def select_graphic_rendition(code)
187+
"\e[#{code}m"
188+
end
133189
end
134190
end
135191
end

0 commit comments

Comments
 (0)