thor 1.2.2 → 1.4.0

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.
@@ -1,8 +1,10 @@
1
+ require_relative "column_printer"
2
+ require_relative "table_printer"
3
+ require_relative "wrapped_printer"
4
+
1
5
  class Thor
2
6
  module Shell
3
7
  class Basic
4
- DEFAULT_TERMINAL_WIDTH = 80
5
-
6
8
  attr_accessor :base
7
9
  attr_reader :padding
8
10
 
@@ -65,15 +67,15 @@ class Thor
65
67
  # Readline.
66
68
  #
67
69
  # ==== Example
68
- # ask("What is your name?")
70
+ # ask("What is your name?")
69
71
  #
70
- # ask("What is the planet furthest from the sun?", :default => "Pluto")
72
+ # ask("What is the planet furthest from the sun?", :default => "Neptune")
71
73
  #
72
- # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
74
+ # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
73
75
  #
74
- # ask("What is your password?", :echo => false)
76
+ # ask("What is your password?", :echo => false)
75
77
  #
76
- # ask("Where should the file be saved?", :path => true)
78
+ # ask("Where should the file be saved?", :path => true)
77
79
  #
78
80
  def ask(statement, *args)
79
81
  options = args.last.is_a?(Hash) ? args.pop : {}
@@ -91,7 +93,7 @@ class Thor
91
93
  # are passed straight to puts (behavior got from Highline).
92
94
  #
93
95
  # ==== Example
94
- # say("I know you knew that.")
96
+ # say("I know you knew that.")
95
97
  #
96
98
  def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
97
99
  return if quiet?
@@ -108,7 +110,7 @@ class Thor
108
110
  # are passed straight to puts (behavior got from Highline).
109
111
  #
110
112
  # ==== Example
111
- # say_error("error: something went wrong")
113
+ # say_error("error: something went wrong")
112
114
  #
113
115
  def say_error(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
114
116
  return if quiet?
@@ -141,18 +143,18 @@ class Thor
141
143
  stdout.flush
142
144
  end
143
145
 
144
- # Make a question the to user and returns true if the user replies "y" or
146
+ # Asks the user a question and returns true if the user replies "y" or
145
147
  # "yes".
146
148
  #
147
149
  def yes?(statement, color = nil)
148
- !!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
150
+ !!(ask(statement, color, add_to_history: false) =~ is?(:yes))
149
151
  end
150
152
 
151
- # Make a question the to user and returns true if the user replies "n" or
153
+ # Asks the user a question and returns true if the user replies "n" or
152
154
  # "no".
153
155
  #
154
156
  def no?(statement, color = nil)
155
- !!(ask(statement, color, :add_to_history => false) =~ is?(:no))
157
+ !!(ask(statement, color, add_to_history: false) =~ is?(:no))
156
158
  end
157
159
 
158
160
  # Prints values in columns
@@ -161,16 +163,8 @@ class Thor
161
163
  # Array[String, String, ...]
162
164
  #
163
165
  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)
174
168
  end
175
169
 
176
170
  # Prints a table.
@@ -181,58 +175,11 @@ class Thor
181
175
  # ==== Options
182
176
  # indent<Integer>:: Indent the first column by indent value.
183
177
  # colwidth<Integer>:: Force the first column to colwidth spaces wide.
178
+ # borders<Boolean>:: Adds ascii borders.
184
179
  #
185
180
  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
181
+ printer = TablePrinter.new(stdout, options)
182
+ printer.print(array)
236
183
  end
237
184
 
238
185
  # Prints a long string, word-wrapping the text to the current width of the
@@ -245,33 +192,8 @@ class Thor
245
192
  # indent<Integer>:: Indent each line of the printed paragraph by indent value.
246
193
  #
247
194
  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
195
+ printer = WrappedPrinter.new(stdout, options)
196
+ printer.print(message)
275
197
  end
276
198
 
277
199
  # Deals with file collision and returns true if the file should be
@@ -289,7 +211,7 @@ class Thor
289
211
  loop do
290
212
  answer = ask(
291
213
  %[Overwrite #{destination}? (enter "h" for help) #{options}],
292
- :add_to_history => false
214
+ add_to_history: false
293
215
  )
294
216
 
295
217
  case answer
@@ -316,24 +238,11 @@ class Thor
316
238
 
317
239
  say "Please specify merge tool to `THOR_MERGE` env."
318
240
  else
319
- say file_collision_help
241
+ say file_collision_help(block_given?)
320
242
  end
321
243
  end
322
244
  end
323
245
 
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
-
337
246
  # Called if something goes wrong during the execution. This is used by Thor
338
247
  # internally and should not be used inside your scripts. If something went
339
248
  # wrong, you can always raise an exception. If you raise a Thor::Error, it
@@ -384,23 +293,28 @@ class Thor
384
293
  end
385
294
  end
386
295
 
387
- def file_collision_help #:nodoc:
388
- <<-HELP
296
+ def file_collision_help(block_given) #:nodoc:
297
+ help = <<-HELP
389
298
  Y - yes, overwrite
390
299
  n - no, do not overwrite
391
300
  a - all, overwrite this and all others
392
301
  q - quit, abort
393
- d - diff, show the differences between the old and the new
394
302
  h - help, show this help
395
- m - merge, run merge tool
396
303
  HELP
304
+ if block_given
305
+ help << <<-HELP
306
+ d - diff, show the differences between the old and the new
307
+ m - merge, run merge tool
308
+ HELP
309
+ end
310
+ help
397
311
  end
398
312
 
399
313
  def show_diff(destination, content) #:nodoc:
400
314
  diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
401
315
 
402
316
  require "tempfile"
403
- Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
317
+ Tempfile.open(File.basename(destination), File.dirname(destination), binmode: true) do |temp|
404
318
  temp.write content
405
319
  temp.rewind
406
320
  system %(#{diff_cmd} "#{destination}" "#{temp.path}")
@@ -411,46 +325,8 @@ class Thor
411
325
  mute? || (base && base.options[:quiet])
412
326
  end
413
327
 
414
- # Calculate the dynamic width of the terminal
415
- def dynamic_width
416
- @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
417
- end
418
-
419
- def dynamic_width_stty
420
- `stty size 2>/dev/null`.split[1].to_i
421
- end
422
-
423
- def dynamic_width_tput
424
- `tput cols 2>/dev/null`.to_i
425
- end
426
-
427
328
  def unix?
428
- RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris)/i
429
- end
430
-
431
- def truncate(string, width)
432
- as_unicode do
433
- chars = string.chars.to_a
434
- if chars.length <= width
435
- chars.join
436
- else
437
- chars[0, width - 3].join + "..."
438
- end
439
- end
440
- end
441
-
442
- if "".respond_to?(:encode)
443
- def as_unicode
444
- yield
445
- end
446
- else
447
- def as_unicode
448
- old = $KCODE
449
- $KCODE = "U"
450
- yield
451
- ensure
452
- $KCODE = old
453
- end
329
+ Terminal.unix?
454
330
  end
455
331
 
456
332
  def ask_simply(statement, color, options)
@@ -496,16 +372,12 @@ class Thor
496
372
  Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
497
373
  temp.write content
498
374
  temp.rewind
499
- system %(#{merge_tool} "#{temp.path}" "#{destination}")
375
+ system(merge_tool, temp.path, destination)
500
376
  end
501
377
  end
502
378
 
503
379
  def merge_tool #:nodoc:
504
- @merge_tool ||= ENV["THOR_MERGE"] || git_merge_tool
505
- end
506
-
507
- def git_merge_tool #:nodoc:
508
- `git config merge.tool`.rstrip rescue ""
380
+ @merge_tool ||= ENV["THOR_MERGE"] || "git difftool --no-index"
509
381
  end
510
382
  end
511
383
  end
@@ -1,4 +1,5 @@
1
1
  require_relative "basic"
2
+ require_relative "lcs_diff"
2
3
 
3
4
  class Thor
4
5
  module Shell
@@ -6,6 +7,8 @@ class Thor
6
7
  # Thor::Shell::Basic to see all available methods.
7
8
  #
8
9
  class Color < Basic
10
+ include LCSDiff
11
+
9
12
  # Embed in a String to clear all previous ANSI sequences.
10
13
  CLEAR = "\e[0m"
11
14
  # The start of an ANSI bold sequence.
@@ -105,52 +108,7 @@ class Thor
105
108
  end
106
109
 
107
110
  def are_colors_disabled?
108
- !ENV['NO_COLOR'].nil? && !ENV['NO_COLOR'].empty?
109
- end
110
-
111
- # Overwrite show_diff to show diff with colors if Diff::LCS is
112
- # available.
113
- #
114
- def show_diff(destination, content) #:nodoc:
115
- if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
116
- actual = File.binread(destination).to_s.split("\n")
117
- content = content.to_s.split("\n")
118
-
119
- Diff::LCS.sdiff(actual, content).each do |diff|
120
- output_diff_line(diff)
121
- end
122
- else
123
- super
124
- end
125
- end
126
-
127
- def output_diff_line(diff) #:nodoc:
128
- case diff.action
129
- when "-"
130
- say "- #{diff.old_element.chomp}", :red, true
131
- when "+"
132
- say "+ #{diff.new_element.chomp}", :green, true
133
- when "!"
134
- say "- #{diff.old_element.chomp}", :red, true
135
- say "+ #{diff.new_element.chomp}", :green, true
136
- else
137
- say " #{diff.old_element.chomp}", nil, true
138
- end
139
- end
140
-
141
- # Check if Diff::LCS is loaded. If it is, use it to create pretty output
142
- # for diff.
143
- #
144
- def diff_lcs_loaded? #:nodoc:
145
- return true if defined?(Diff::LCS)
146
- return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
147
-
148
- @diff_lcs_loaded = begin
149
- require "diff/lcs"
150
- true
151
- rescue LoadError
152
- false
153
- end
111
+ !ENV["NO_COLOR"].nil? && !ENV["NO_COLOR"].empty?
154
112
  end
155
113
  end
156
114
  end
@@ -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
+
@@ -1,4 +1,5 @@
1
1
  require_relative "basic"
2
+ require_relative "lcs_diff"
2
3
 
3
4
  class Thor
4
5
  module Shell
@@ -6,6 +7,8 @@ class Thor
6
7
  # Thor::Shell::Basic to see all available methods.
7
8
  #
8
9
  class HTML < Basic
10
+ include LCSDiff
11
+
9
12
  # The start of an HTML bold sequence.
10
13
  BOLD = "font-weight: bold"
11
14
 
@@ -64,7 +67,7 @@ class Thor
64
67
  # Ask something to the user and receives a response.
65
68
  #
66
69
  # ==== Example
67
- # ask("What is your name?")
70
+ # ask("What is your name?")
68
71
  #
69
72
  # TODO: Implement #ask for Thor::Shell::HTML
70
73
  def ask(statement, color = nil)
@@ -76,51 +79,6 @@ class Thor
76
79
  def can_display_colors?
77
80
  true
78
81
  end
79
-
80
- # Overwrite show_diff to show diff with colors if Diff::LCS is
81
- # available.
82
- #
83
- def show_diff(destination, content) #:nodoc:
84
- if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
85
- actual = File.binread(destination).to_s.split("\n")
86
- content = content.to_s.split("\n")
87
-
88
- Diff::LCS.sdiff(actual, content).each do |diff|
89
- output_diff_line(diff)
90
- end
91
- else
92
- super
93
- end
94
- end
95
-
96
- def output_diff_line(diff) #:nodoc:
97
- case diff.action
98
- when "-"
99
- say "- #{diff.old_element.chomp}", :red, true
100
- when "+"
101
- say "+ #{diff.new_element.chomp}", :green, true
102
- when "!"
103
- say "- #{diff.old_element.chomp}", :red, true
104
- say "+ #{diff.new_element.chomp}", :green, true
105
- else
106
- say " #{diff.old_element.chomp}", nil, true
107
- end
108
- end
109
-
110
- # Check if Diff::LCS is loaded. If it is, use it to create pretty output
111
- # for diff.
112
- #
113
- def diff_lcs_loaded? #:nodoc:
114
- return true if defined?(Diff::LCS)
115
- return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
116
-
117
- @diff_lcs_loaded = begin
118
- require "diff/lcs"
119
- true
120
- rescue LoadError
121
- false
122
- end
123
- end
124
82
  end
125
83
  end
126
84
  end
@@ -0,0 +1,49 @@
1
+ module LCSDiff
2
+ protected
3
+
4
+ # Overwrite show_diff to show diff with colors if Diff::LCS is
5
+ # available.
6
+ def show_diff(destination, content) #:nodoc:
7
+ if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
8
+ actual = File.binread(destination).to_s.split("\n")
9
+ content = content.to_s.split("\n")
10
+
11
+ Diff::LCS.sdiff(actual, content).each do |diff|
12
+ output_diff_line(diff)
13
+ end
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def output_diff_line(diff) #:nodoc:
22
+ case diff.action
23
+ when "-"
24
+ say "- #{diff.old_element.chomp}", :red, true
25
+ when "+"
26
+ say "+ #{diff.new_element.chomp}", :green, true
27
+ when "!"
28
+ say "- #{diff.old_element.chomp}", :red, true
29
+ say "+ #{diff.new_element.chomp}", :green, true
30
+ else
31
+ say " #{diff.old_element.chomp}", nil, true
32
+ end
33
+ end
34
+
35
+ # Check if Diff::LCS is loaded. If it is, use it to create pretty output
36
+ # for diff.
37
+ def diff_lcs_loaded? #:nodoc:
38
+ return true if defined?(Diff::LCS)
39
+ return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
40
+
41
+ @diff_lcs_loaded = begin
42
+ require "diff/lcs"
43
+ true
44
+ rescue LoadError
45
+ false
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,118 @@
1
+ require_relative "column_printer"
2
+ require_relative "terminal"
3
+
4
+ class Thor
5
+ module Shell
6
+ class TablePrinter < ColumnPrinter
7
+ BORDER_SEPARATOR = :separator
8
+
9
+ def initialize(stdout, options = {})
10
+ super
11
+ @formats = []
12
+ @maximas = []
13
+ @colwidth = options[:colwidth]
14
+ @truncate = options[:truncate] == true ? Terminal.terminal_width : options[:truncate]
15
+ @padding = 1
16
+ end
17
+
18
+ def print(array)
19
+ return if array.empty?
20
+
21
+ prepare(array)
22
+
23
+ print_border_separator if options[:borders]
24
+
25
+ array.each do |row|
26
+ if options[:borders] && row == BORDER_SEPARATOR
27
+ print_border_separator
28
+ next
29
+ end
30
+
31
+ sentence = "".dup
32
+
33
+ row.each_with_index do |column, index|
34
+ sentence << format_cell(column, row.size, index)
35
+ end
36
+
37
+ sentence = truncate(sentence)
38
+ sentence << "|" if options[:borders]
39
+ stdout.puts indentation + sentence
40
+
41
+ end
42
+ print_border_separator if options[:borders]
43
+ end
44
+
45
+ private
46
+
47
+ def prepare(array)
48
+ array = array.reject{|row| row == BORDER_SEPARATOR }
49
+
50
+ @formats << "%-#{@colwidth + 2}s".dup if @colwidth
51
+ start = @colwidth ? 1 : 0
52
+
53
+ colcount = array.max { |a, b| a.size <=> b.size }.size
54
+
55
+ start.upto(colcount - 1) do |index|
56
+ maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
57
+
58
+ @maximas << maxima
59
+ @formats << if options[:borders]
60
+ "%-#{maxima}s".dup
61
+ elsif index == colcount - 1
62
+ # Don't output 2 trailing spaces when printing the last column
63
+ "%-s".dup
64
+ else
65
+ "%-#{maxima + 2}s".dup
66
+ end
67
+ end
68
+
69
+ @formats << "%s"
70
+ end
71
+
72
+ def format_cell(column, row_size, index)
73
+ maxima = @maximas[index]
74
+
75
+ f = if column.is_a?(Numeric)
76
+ if options[:borders]
77
+ # With borders we handle padding separately
78
+ "%#{maxima}s"
79
+ elsif index == row_size - 1
80
+ # Don't output 2 trailing spaces when printing the last column
81
+ "%#{maxima}s"
82
+ else
83
+ "%#{maxima}s "
84
+ end
85
+ else
86
+ @formats[index]
87
+ end
88
+
89
+ cell = "".dup
90
+ cell << "|" + " " * @padding if options[:borders]
91
+ cell << f % column.to_s
92
+ cell << " " * @padding if options[:borders]
93
+ cell
94
+ end
95
+
96
+ def print_border_separator
97
+ separator = @maximas.map do |maxima|
98
+ "+" + "-" * (maxima + 2 * @padding)
99
+ end
100
+ stdout.puts indentation + separator.join + "+"
101
+ end
102
+
103
+ def truncate(string)
104
+ return string unless @truncate
105
+ chars = string.chars.to_a
106
+ if chars.length <= @truncate
107
+ chars.join
108
+ else
109
+ chars[0, @truncate - 3 - @indent].join + "..."
110
+ end
111
+ end
112
+
113
+ def indentation
114
+ " " * @indent
115
+ end
116
+ end
117
+ end
118
+ end