Skip to content

Commit 580234a

Browse files
committed
Extract print methods to seperate classes
The printing methods are already pretty large, making it difficult to add extra functionality. Extracting them to seperate classes allows refactoring them for easier maintainability. A lot of the functionality of calculating the terminal width can be extracted to a separate object as well.
1 parent f98f4a9 commit 580234a

File tree

7 files changed

+217
-144
lines changed

7 files changed

+217
-144
lines changed

lib/thor/shell/basic.rb

Lines changed: 11 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
require_relative "column_printer"
2+
require_relative "table_printer"
3+
require_relative "wrapped_printer"
4+
15
class Thor
26
module Shell
37
class Basic
4-
DEFAULT_TERMINAL_WIDTH = 80
5-
68
attr_accessor :base
79
attr_reader :padding
810

@@ -161,16 +163,8 @@ def no?(statement, color = nil)
161163
# Array[String, String, ...]
162164
#
163165
def print_in_columns(array)
164-
return if array.empty?
165-
colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
166-
array.each_with_index do |value, index|
167-
# Don't output trailing spaces when printing the last column
168-
if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
169-
stdout.puts value
170-
else
171-
stdout.printf("%-#{colwidth}s", value)
172-
end
173-
end
166+
printer = ColumnPrinter.new(stdout)
167+
printer.print(array)
174168
end
175169

176170
# Prints a table.
@@ -183,56 +177,8 @@ def print_in_columns(array)
183177
# colwidth<Integer>:: Force the first column to colwidth spaces wide.
184178
#
185179
def print_table(array, options = {}) # rubocop:disable Metrics/MethodLength
186-
return if array.empty?
187-
188-
formats = []
189-
indent = options[:indent].to_i
190-
colwidth = options[:colwidth]
191-
options[:truncate] = terminal_width if options[:truncate] == true
192-
193-
formats << "%-#{colwidth + 2}s".dup if colwidth
194-
start = colwidth ? 1 : 0
195-
196-
colcount = array.max { |a, b| a.size <=> b.size }.size
197-
198-
maximas = []
199-
200-
start.upto(colcount - 1) do |index|
201-
maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
202-
maximas << maxima
203-
formats << if index == colcount - 1
204-
# Don't output 2 trailing spaces when printing the last column
205-
"%-s".dup
206-
else
207-
"%-#{maxima + 2}s".dup
208-
end
209-
end
210-
211-
formats[0] = formats[0].insert(0, " " * indent)
212-
formats << "%s"
213-
214-
array.each do |row|
215-
sentence = "".dup
216-
217-
row.each_with_index do |column, index|
218-
maxima = maximas[index]
219-
220-
f = if column.is_a?(Numeric)
221-
if index == row.size - 1
222-
# Don't output 2 trailing spaces when printing the last column
223-
"%#{maxima}s"
224-
else
225-
"%#{maxima}s "
226-
end
227-
else
228-
formats[index]
229-
end
230-
sentence << f % column.to_s
231-
end
232-
233-
sentence = truncate(sentence, options[:truncate]) if options[:truncate]
234-
stdout.puts sentence
235-
end
180+
printer = TablePrinter.new(stdout, options)
181+
printer.print(array)
236182
end
237183

238184
# Prints a long string, word-wrapping the text to the current width of the
@@ -245,33 +191,8 @@ def print_table(array, options = {}) # rubocop:disable Metrics/MethodLength
245191
# indent<Integer>:: Indent each line of the printed paragraph by indent value.
246192
#
247193
def print_wrapped(message, options = {})
248-
indent = options[:indent] || 0
249-
width = terminal_width - indent
250-
paras = message.split("\n\n")
251-
252-
paras.map! do |unwrapped|
253-
words = unwrapped.split(" ")
254-
counter = words.first.length
255-
words.inject do |memo, word|
256-
word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n")
257-
counter = 0 if word.include? "\n"
258-
if (counter + word.length + 1) < width
259-
memo = "#{memo} #{word}"
260-
counter += (word.length + 1)
261-
else
262-
memo = "#{memo}\n#{word}"
263-
counter = word.length
264-
end
265-
memo
266-
end
267-
end.compact!
268-
269-
paras.each do |para|
270-
para.split("\n").each do |line|
271-
stdout.puts line.insert(0, " " * indent)
272-
end
273-
stdout.puts unless para == paras.last
274-
end
194+
printer = WrappedPrinter.new(stdout, options)
195+
printer.print(message)
275196
end
276197

277198
# Deals with file collision and returns true if the file should be
@@ -321,19 +242,6 @@ def file_collision(destination)
321242
end
322243
end
323244

324-
# This code was copied from Rake, available under MIT-LICENSE
325-
# Copyright (c) 2003, 2004 Jim Weirich
326-
def terminal_width
327-
result = if ENV["THOR_COLUMNS"]
328-
ENV["THOR_COLUMNS"].to_i
329-
else
330-
unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
331-
end
332-
result < 10 ? DEFAULT_TERMINAL_WIDTH : result
333-
rescue
334-
DEFAULT_TERMINAL_WIDTH
335-
end
336-
337245
# Called if something goes wrong during the execution. This is used by Thor
338246
# internally and should not be used inside your scripts. If something went
339247
# wrong, you can always raise an exception. If you raise a Thor::Error, it
@@ -416,46 +324,8 @@ def quiet? #:nodoc:
416324
mute? || (base && base.options[:quiet])
417325
end
418326

419-
# Calculate the dynamic width of the terminal
420-
def dynamic_width
421-
@dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
422-
end
423-
424-
def dynamic_width_stty
425-
`stty size 2>/dev/null`.split[1].to_i
426-
end
427-
428-
def dynamic_width_tput
429-
`tput cols 2>/dev/null`.to_i
430-
end
431-
432327
def unix?
433-
RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris)/i
434-
end
435-
436-
def truncate(string, width)
437-
as_unicode do
438-
chars = string.chars.to_a
439-
if chars.length <= width
440-
chars.join
441-
else
442-
chars[0, width - 3].join + "..."
443-
end
444-
end
445-
end
446-
447-
if "".respond_to?(:encode)
448-
def as_unicode
449-
yield
450-
end
451-
else
452-
def as_unicode
453-
old = $KCODE
454-
$KCODE = "U"
455-
yield
456-
ensure
457-
$KCODE = old
458-
end
328+
Terminal.unix?
459329
end
460330

461331
def ask_simply(statement, color, options)

lib/thor/shell/column_printer.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require_relative "terminal"
2+
3+
class Thor
4+
module Shell
5+
class ColumnPrinter
6+
attr_reader :stdout, :options
7+
8+
def initialize(stdout, options = {})
9+
@stdout = stdout
10+
@options = options
11+
@indent = options[:indent].to_i
12+
end
13+
14+
def print(array)
15+
return if array.empty?
16+
colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
17+
array.each_with_index do |value, index|
18+
# Don't output trailing spaces when printing the last column
19+
if ((((index + 1) % (Terminal.terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
20+
stdout.puts value
21+
else
22+
stdout.printf("%-#{colwidth}s", value)
23+
end
24+
end
25+
end
26+
end
27+
end
28+
end
29+

lib/thor/shell/table_printer.rb

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
require_relative "column_printer"
2+
require_relative "terminal"
3+
4+
class Thor
5+
module Shell
6+
class TablePrinter < ColumnPrinter
7+
def initialize(stdout, options = {})
8+
super
9+
@formats = []
10+
@maximas = []
11+
@colwidth = options[:colwidth]
12+
@truncate = options[:truncate] == true ? Terminal.terminal_width : options[:truncate]
13+
end
14+
15+
def print(array)
16+
return if array.empty?
17+
18+
prepare(array)
19+
20+
array.each do |row|
21+
sentence = "".dup
22+
23+
row.each_with_index do |column, index|
24+
maxima = @maximas[index]
25+
26+
f = if column.is_a?(Numeric)
27+
if index == row.size - 1
28+
# Don't output 2 trailing spaces when printing the last column
29+
"%#{maxima}s"
30+
else
31+
"%#{maxima}s "
32+
end
33+
else
34+
@formats[index]
35+
end
36+
sentence << f % column.to_s
37+
end
38+
39+
sentence = truncate(sentence)
40+
stdout.puts sentence
41+
end
42+
end
43+
44+
private
45+
46+
def prepare(array)
47+
@formats << "%-#{@colwidth + 2}s".dup if @colwidth
48+
start = @colwidth ? 1 : 0
49+
50+
colcount = array.max { |a, b| a.size <=> b.size }.size
51+
52+
start.upto(colcount - 1) do |index|
53+
maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
54+
@maximas << maxima
55+
@formats << if index == colcount - 1
56+
# Don't output 2 trailing spaces when printing the last column
57+
"%-s".dup
58+
else
59+
"%-#{maxima + 2}s".dup
60+
end
61+
end
62+
63+
@formats[0] = @formats[0].insert(0, " " * @indent)
64+
@formats << "%s"
65+
end
66+
67+
def truncate(string)
68+
return string unless @truncate
69+
as_unicode do
70+
chars = string.chars.to_a
71+
if chars.length <= @truncate
72+
chars.join
73+
else
74+
chars[0, @truncate - 3].join + "..."
75+
end
76+
end
77+
end
78+
79+
if "".respond_to?(:encode)
80+
def as_unicode
81+
yield
82+
end
83+
else
84+
def as_unicode
85+
old = $KCODE # rubocop:disable Style/GlobalVars
86+
$KCODE = "U" # rubocop:disable Style/GlobalVars
87+
yield
88+
ensure
89+
$KCODE = old # rubocop:disable Style/GlobalVars
90+
end
91+
end
92+
end
93+
end
94+
end
95+

lib/thor/shell/terminal.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
class Thor
2+
module Shell
3+
module Terminal
4+
DEFAULT_TERMINAL_WIDTH = 80
5+
6+
class << self
7+
# This code was copied from Rake, available under MIT-LICENSE
8+
# Copyright (c) 2003, 2004 Jim Weirich
9+
def terminal_width
10+
result = if ENV["THOR_COLUMNS"]
11+
ENV["THOR_COLUMNS"].to_i
12+
else
13+
unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
14+
end
15+
result < 10 ? DEFAULT_TERMINAL_WIDTH : result
16+
rescue
17+
DEFAULT_TERMINAL_WIDTH
18+
end
19+
20+
def unix?
21+
RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris)/i
22+
end
23+
24+
private
25+
26+
# Calculate the dynamic width of the terminal
27+
def dynamic_width
28+
@dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
29+
end
30+
31+
def dynamic_width_stty
32+
`stty size 2>/dev/null`.split[1].to_i
33+
end
34+
35+
def dynamic_width_tput
36+
`tput cols 2>/dev/null`.to_i
37+
end
38+
39+
end
40+
end
41+
end
42+
end

0 commit comments

Comments
 (0)