thor 0.14.6 → 0.19.4

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.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/{CHANGELOG.rdoc → CHANGELOG.md} +91 -31
  4. data/CONTRIBUTING.md +15 -0
  5. data/{LICENSE → LICENSE.md} +2 -2
  6. data/README.md +36 -296
  7. data/bin/thor +1 -1
  8. data/lib/thor/actions/create_file.rb +33 -35
  9. data/lib/thor/actions/create_link.rb +7 -5
  10. data/lib/thor/actions/directory.rb +49 -24
  11. data/lib/thor/actions/empty_directory.rb +68 -67
  12. data/lib/thor/actions/file_manipulation.rb +85 -28
  13. data/lib/thor/actions/inject_into_file.rb +26 -32
  14. data/lib/thor/actions.rb +70 -66
  15. data/lib/thor/base.rb +314 -237
  16. data/lib/thor/command.rb +133 -0
  17. data/lib/thor/core_ext/hash_with_indifferent_access.rb +34 -24
  18. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  19. data/lib/thor/core_ext/ordered_hash.rb +94 -65
  20. data/lib/thor/error.rb +10 -8
  21. data/lib/thor/group.rb +74 -66
  22. data/lib/thor/invocation.rb +65 -56
  23. data/lib/thor/line_editor/basic.rb +35 -0
  24. data/lib/thor/line_editor/readline.rb +88 -0
  25. data/lib/thor/line_editor.rb +17 -0
  26. data/lib/thor/parser/argument.rb +32 -29
  27. data/lib/thor/parser/arguments.rb +107 -93
  28. data/lib/thor/parser/option.rb +39 -13
  29. data/lib/thor/parser/options.rb +144 -97
  30. data/lib/thor/parser.rb +4 -4
  31. data/lib/thor/rake_compat.rb +21 -16
  32. data/lib/thor/runner.rb +166 -153
  33. data/lib/thor/shell/basic.rb +268 -122
  34. data/lib/thor/shell/color.rb +84 -43
  35. data/lib/thor/shell/html.rb +71 -66
  36. data/lib/thor/shell.rb +30 -37
  37. data/lib/thor/util.rb +227 -188
  38. data/lib/thor/version.rb +1 -1
  39. data/lib/thor.rb +289 -131
  40. data/thor.gemspec +21 -0
  41. metadata +50 -189
  42. data/Thorfile +0 -24
  43. data/bin/rake2thor +0 -86
  44. data/lib/thor/core_ext/file_binary_read.rb +0 -9
  45. data/lib/thor/task.rb +0 -114
  46. data/spec/actions/create_file_spec.rb +0 -170
  47. data/spec/actions/directory_spec.rb +0 -136
  48. data/spec/actions/empty_directory_spec.rb +0 -98
  49. data/spec/actions/file_manipulation_spec.rb +0 -310
  50. data/spec/actions/inject_into_file_spec.rb +0 -135
  51. data/spec/actions_spec.rb +0 -322
  52. data/spec/base_spec.rb +0 -269
  53. data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
  54. data/spec/core_ext/ordered_hash_spec.rb +0 -115
  55. data/spec/fixtures/application.rb +0 -2
  56. data/spec/fixtures/bundle/execute.rb +0 -6
  57. data/spec/fixtures/bundle/main.thor +0 -1
  58. data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
  59. data/spec/fixtures/doc/README +0 -3
  60. data/spec/fixtures/doc/block_helper.rb +0 -3
  61. data/spec/fixtures/doc/components/.empty_directory +0 -0
  62. data/spec/fixtures/doc/config.rb +0 -1
  63. data/spec/fixtures/group.thor +0 -114
  64. data/spec/fixtures/invoke.thor +0 -112
  65. data/spec/fixtures/path with spaces +0 -0
  66. data/spec/fixtures/script.thor +0 -184
  67. data/spec/fixtures/task.thor +0 -10
  68. data/spec/group_spec.rb +0 -178
  69. data/spec/invocation_spec.rb +0 -100
  70. data/spec/parser/argument_spec.rb +0 -47
  71. data/spec/parser/arguments_spec.rb +0 -64
  72. data/spec/parser/option_spec.rb +0 -202
  73. data/spec/parser/options_spec.rb +0 -319
  74. data/spec/rake_compat_spec.rb +0 -68
  75. data/spec/register_spec.rb +0 -92
  76. data/spec/runner_spec.rb +0 -210
  77. data/spec/shell/basic_spec.rb +0 -223
  78. data/spec/shell/color_spec.rb +0 -41
  79. data/spec/shell/html_spec.rb +0 -27
  80. data/spec/shell_spec.rb +0 -47
  81. data/spec/spec_helper.rb +0 -54
  82. data/spec/task_spec.rb +0 -69
  83. data/spec/thor_spec.rb +0 -334
  84. data/spec/util_spec.rb +0 -163
@@ -1,14 +1,19 @@
1
- require 'tempfile'
1
+ require "tempfile"
2
+ require "io/console" if RUBY_VERSION > "1.9.2"
2
3
 
3
4
  class Thor
4
5
  module Shell
5
6
  class Basic
6
- attr_accessor :base, :padding
7
+ attr_accessor :base
8
+ attr_reader :padding
7
9
 
8
- # Initialize base and padding to nil.
10
+ # Initialize base, mute and padding to nil.
9
11
  #
10
12
  def initialize #:nodoc:
11
- @base, @padding = nil, 0
13
+ @base = nil
14
+ @mute = false
15
+ @padding = 0
16
+ @always_force = false
12
17
  end
13
18
 
14
19
  # Mute everything that's inside given block
@@ -32,14 +37,48 @@ class Thor
32
37
  @padding = [0, value].max
33
38
  end
34
39
 
35
- # Ask something to the user and receives a response.
40
+ # Sets the output padding while executing a block and resets it.
41
+ #
42
+ def indent(count = 1)
43
+ orig_padding = padding
44
+ self.padding = padding + count
45
+ yield
46
+ self.padding = orig_padding
47
+ end
48
+
49
+ # Asks something to the user and receives a response.
50
+ #
51
+ # If asked to limit the correct responses, you can pass in an
52
+ # array of acceptable answers. If one of those is not supplied,
53
+ # they will be shown a message stating that one of those answers
54
+ # must be given and re-asked the question.
55
+ #
56
+ # If asking for sensitive information, the :echo option can be set
57
+ # to false to mask user input from $stdin.
58
+ #
59
+ # If the required input is a path, then set the path option to
60
+ # true. This will enable tab completion for file paths relative
61
+ # to the current working directory on systems that support
62
+ # Readline.
36
63
  #
37
64
  # ==== Example
38
65
  # ask("What is your name?")
39
66
  #
40
- def ask(statement, color=nil)
41
- say("#{statement} ", color)
42
- $stdin.gets.strip
67
+ # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
68
+ #
69
+ # ask("What is your password?", :echo => false)
70
+ #
71
+ # ask("Where should the file be saved?", :path => true)
72
+ #
73
+ def ask(statement, *args)
74
+ options = args.last.is_a?(Hash) ? args.pop : {}
75
+ color = args.first
76
+
77
+ if options[:limited_to]
78
+ ask_filtered(statement, color, options)
79
+ else
80
+ ask_simply(statement, color, options)
81
+ end
43
82
  end
44
83
 
45
84
  # Say (print) something to the user. If the sentence ends with a whitespace
@@ -49,18 +88,12 @@ class Thor
49
88
  # ==== Example
50
89
  # say("I know you knew that.")
51
90
  #
52
- def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/))
53
- message = message.to_s
54
- message = set_color(message, color) if color
55
-
56
- spaces = " " * padding
91
+ def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
92
+ buffer = prepare_message(message, *color)
93
+ buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
57
94
 
58
- if force_new_line
59
- $stdout.puts(spaces + message)
60
- else
61
- $stdout.print(spaces + message)
62
- end
63
- $stdout.flush
95
+ stdout.print(buffer)
96
+ stdout.flush
64
97
  end
65
98
 
66
99
  # Say a status with the given color and appends the message. Since this
@@ -68,7 +101,7 @@ class Thor
68
101
  # in log_status, avoiding the message from being shown. If a Symbol is
69
102
  # given in log_status, it's used as the color.
70
103
  #
71
- def say_status(status, message, log_status=true)
104
+ def say_status(status, message, log_status = true)
72
105
  return if quiet? || log_status == false
73
106
  spaces = " " * (padding + 1)
74
107
  color = log_status.is_a?(Symbol) ? log_status : :green
@@ -76,22 +109,43 @@ class Thor
76
109
  status = status.to_s.rjust(12)
77
110
  status = set_color status, color, true if color
78
111
 
79
- $stdout.puts "#{status}#{spaces}#{message}"
80
- $stdout.flush
112
+ buffer = "#{status}#{spaces}#{message}"
113
+ buffer << "\n" unless buffer.end_with?("\n")
114
+
115
+ stdout.print(buffer)
116
+ stdout.flush
81
117
  end
82
118
 
83
119
  # Make a question the to user and returns true if the user replies "y" or
84
120
  # "yes".
85
121
  #
86
- def yes?(statement, color=nil)
87
- !!(ask(statement, color) =~ is?(:yes))
122
+ def yes?(statement, color = nil)
123
+ !!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
88
124
  end
89
125
 
90
126
  # Make a question the to user and returns true if the user replies "n" or
91
127
  # "no".
92
128
  #
93
- def no?(statement, color=nil)
94
- !yes?(statement, color)
129
+ def no?(statement, color = nil)
130
+ !!(ask(statement, color, :add_to_history => false) =~ is?(:no))
131
+ end
132
+
133
+ # Prints values in columns
134
+ #
135
+ # ==== Parameters
136
+ # Array[String, String, ...]
137
+ #
138
+ def print_in_columns(array)
139
+ return if array.empty?
140
+ colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
141
+ array.each_with_index do |value, index|
142
+ # Don't output trailing spaces when printing the last column
143
+ if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
144
+ stdout.puts value
145
+ else
146
+ stdout.printf("%-#{colwidth}s", value)
147
+ end
148
+ end
95
149
  end
96
150
 
97
151
  # Prints a table.
@@ -100,35 +154,59 @@ class Thor
100
154
  # Array[Array[String, String, ...]]
101
155
  #
102
156
  # ==== Options
103
- # ident<Integer>:: Indent the first column by ident value.
157
+ # indent<Integer>:: Indent the first column by indent value.
104
158
  # colwidth<Integer>:: Force the first column to colwidth spaces wide.
105
159
  #
106
- def print_table(table, options={})
107
- return if table.empty?
160
+ def print_table(array, options = {}) # rubocop:disable MethodLength
161
+ return if array.empty?
108
162
 
109
- formats, ident, colwidth = [], options[:ident].to_i, options[:colwidth]
163
+ formats = []
164
+ indent = options[:indent].to_i
165
+ colwidth = options[:colwidth]
110
166
  options[:truncate] = terminal_width if options[:truncate] == true
111
167
 
112
168
  formats << "%-#{colwidth + 2}s" if colwidth
113
169
  start = colwidth ? 1 : 0
114
170
 
115
- start.upto(table.first.length - 2) do |i|
116
- maxima ||= table.max{|a,b| a[i].size <=> b[i].size }[i].size
117
- formats << "%-#{maxima + 2}s"
171
+ colcount = array.max { |a, b| a.size <=> b.size }.size
172
+
173
+ maximas = []
174
+
175
+ start.upto(colcount - 1) do |index|
176
+ maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
177
+ maximas << maxima
178
+ formats << if index == colcount - 1
179
+ # Don't output 2 trailing spaces when printing the last column
180
+ "%-s"
181
+ else
182
+ "%-#{maxima + 2}s"
183
+ end
118
184
  end
119
185
 
120
- formats[0] = formats[0].insert(0, " " * ident)
186
+ formats[0] = formats[0].insert(0, " " * indent)
121
187
  formats << "%s"
122
188
 
123
- table.each do |row|
189
+ array.each do |row|
124
190
  sentence = ""
125
191
 
126
- row.each_with_index do |column, i|
127
- sentence << formats[i] % column.to_s
192
+ row.each_with_index do |column, index|
193
+ maxima = maximas[index]
194
+
195
+ f = if column.is_a?(Numeric)
196
+ if index == row.size - 1
197
+ # Don't output 2 trailing spaces when printing the last column
198
+ "%#{maxima}s"
199
+ else
200
+ "%#{maxima}s "
201
+ end
202
+ else
203
+ formats[index]
204
+ end
205
+ sentence << f % column.to_s
128
206
  end
129
207
 
130
208
  sentence = truncate(sentence, options[:truncate]) if options[:truncate]
131
- $stdout.puts sentence
209
+ stdout.puts sentence
132
210
  end
133
211
  end
134
212
 
@@ -139,29 +217,27 @@ class Thor
139
217
  # String
140
218
  #
141
219
  # ==== Options
142
- # ident<Integer>:: Indent each line of the printed paragraph by ident value.
220
+ # indent<Integer>:: Indent each line of the printed paragraph by indent value.
143
221
  #
144
- def print_wrapped(message, options={})
145
- ident = options[:ident] || 0
146
- width = terminal_width - ident
222
+ def print_wrapped(message, options = {})
223
+ indent = options[:indent] || 0
224
+ width = terminal_width - indent
147
225
  paras = message.split("\n\n")
148
226
 
149
227
  paras.map! do |unwrapped|
150
- unwrapped.strip.gsub(/\n/, " ").squeeze(" ").
151
- gsub(/.{1,#{width}}(?:\s|\Z)/){($& + 5.chr).
152
- gsub(/\n\005/,"\n").gsub(/\005/,"\n")}
228
+ unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
153
229
  end
154
230
 
155
231
  paras.each do |para|
156
232
  para.split("\n").each do |line|
157
- $stdout.puts line.insert(0, " " * ident)
233
+ stdout.puts line.insert(0, " " * indent)
158
234
  end
159
- $stdout.puts unless para == paras.last
235
+ stdout.puts unless para == paras.last
160
236
  end
161
237
  end
162
238
 
163
239
  # Deals with file collision and returns true if the file should be
164
- # overwriten and false otherwise. If a block is given, it uses the block
240
+ # overwritten and false otherwise. If a block is given, it uses the block
165
241
  # response as the content for the diff.
166
242
  #
167
243
  # ==== Parameters
@@ -172,119 +248,189 @@ class Thor
172
248
  return true if @always_force
173
249
  options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
174
250
 
175
- while true
176
- answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
251
+ loop do
252
+ answer = ask(
253
+ %[Overwrite #{destination}? (enter "h" for help) #{options}],
254
+ :add_to_history => false
255
+ )
177
256
 
178
257
  case answer
179
- when is?(:yes), is?(:force), ""
180
- return true
181
- when is?(:no), is?(:skip)
182
- return false
183
- when is?(:always)
184
- return @always_force = true
185
- when is?(:quit)
186
- say 'Aborting...'
187
- raise SystemExit
188
- when is?(:diff)
189
- show_diff(destination, yield) if block_given?
190
- say 'Retrying...'
191
- else
192
- say file_collision_help
258
+ when is?(:yes), is?(:force), ""
259
+ return true
260
+ when is?(:no), is?(:skip)
261
+ return false
262
+ when is?(:always)
263
+ return @always_force = true
264
+ when is?(:quit)
265
+ say "Aborting..."
266
+ raise SystemExit
267
+ when is?(:diff)
268
+ show_diff(destination, yield) if block_given?
269
+ say "Retrying..."
270
+ else
271
+ say file_collision_help
193
272
  end
194
273
  end
195
274
  end
196
275
 
276
+ # This code was copied from Rake, available under MIT-LICENSE
277
+ # Copyright (c) 2003, 2004 Jim Weirich
278
+ def terminal_width
279
+ result = if ENV["THOR_COLUMNS"]
280
+ ENV["THOR_COLUMNS"].to_i
281
+ else
282
+ unix? ? dynamic_width : 80
283
+ end
284
+ result < 10 ? 80 : result
285
+ rescue
286
+ 80
287
+ end
288
+
197
289
  # Called if something goes wrong during the execution. This is used by Thor
198
- # internally and should not be used inside your scripts. If someone went
290
+ # internally and should not be used inside your scripts. If something went
199
291
  # wrong, you can always raise an exception. If you raise a Thor::Error, it
200
292
  # will be rescued and wrapped in the method below.
201
293
  #
202
294
  def error(statement)
203
- $stderr.puts statement
295
+ stderr.puts statement
204
296
  end
205
297
 
206
298
  # Apply color to the given string with optional bold. Disabled in the
207
299
  # Thor::Shell::Basic class.
208
300
  #
209
- def set_color(string, color, bold=false) #:nodoc:
301
+ def set_color(string, *) #:nodoc:
210
302
  string
211
303
  end
212
304
 
213
- protected
305
+ protected
306
+
307
+ def prepare_message(message, *color)
308
+ spaces = " " * padding
309
+ spaces + set_color(message.to_s, *color)
310
+ end
311
+
312
+ def can_display_colors?
313
+ false
314
+ end
214
315
 
215
- def is?(value) #:nodoc:
216
- value = value.to_s
316
+ def lookup_color(color)
317
+ return color unless color.is_a?(Symbol)
318
+ self.class.const_get(color.to_s.upcase)
319
+ end
217
320
 
218
- if value.size == 1
219
- /\A#{value}\z/i
220
- else
221
- /\A(#{value}|#{value[0,1]})\z/i
222
- end
223
- end
321
+ def stdout
322
+ $stdout
323
+ end
224
324
 
225
- def file_collision_help #:nodoc:
226
- <<HELP
227
- Y - yes, overwrite
228
- n - no, do not overwrite
229
- a - all, overwrite this and all others
230
- q - quit, abort
231
- d - diff, show the differences between the old and the new
232
- h - help, show this help
233
- HELP
234
- end
325
+ def stderr
326
+ $stderr
327
+ end
235
328
 
236
- def show_diff(destination, content) #:nodoc:
237
- diff_cmd = ENV['THOR_DIFF'] || ENV['RAILS_DIFF'] || 'diff -u'
329
+ def is?(value) #:nodoc:
330
+ value = value.to_s
238
331
 
239
- Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
240
- temp.write content
241
- temp.rewind
242
- system %(#{diff_cmd} "#{destination}" "#{temp.path}")
243
- end
332
+ if value.size == 1
333
+ /\A#{value}\z/i
334
+ else
335
+ /\A(#{value}|#{value[0, 1]})\z/i
244
336
  end
337
+ end
338
+
339
+ def file_collision_help #:nodoc:
340
+ <<-HELP
341
+ Y - yes, overwrite
342
+ n - no, do not overwrite
343
+ a - all, overwrite this and all others
344
+ q - quit, abort
345
+ d - diff, show the differences between the old and the new
346
+ h - help, show this help
347
+ HELP
348
+ end
245
349
 
246
- def quiet? #:nodoc:
247
- mute? || (base && base.options[:quiet])
350
+ def show_diff(destination, content) #:nodoc:
351
+ diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
352
+
353
+ Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
354
+ temp.write content
355
+ temp.rewind
356
+ system %(#{diff_cmd} "#{destination}" "#{temp.path}")
248
357
  end
358
+ end
249
359
 
250
- # This code was copied from Rake, available under MIT-LICENSE
251
- # Copyright (c) 2003, 2004 Jim Weirich
252
- def terminal_width
253
- if ENV['THOR_COLUMNS']
254
- result = ENV['THOR_COLUMNS'].to_i
360
+ def quiet? #:nodoc:
361
+ mute? || (base && base.options[:quiet])
362
+ end
363
+
364
+ # Calculate the dynamic width of the terminal
365
+ def dynamic_width
366
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
367
+ end
368
+
369
+ def dynamic_width_stty
370
+ `stty size 2>/dev/null`.split[1].to_i
371
+ end
372
+
373
+ def dynamic_width_tput
374
+ `tput cols 2>/dev/null`.to_i
375
+ end
376
+
377
+ def unix?
378
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
379
+ end
380
+
381
+ def truncate(string, width)
382
+ as_unicode do
383
+ chars = string.chars.to_a
384
+ if chars.length <= width
385
+ chars.join
255
386
  else
256
- result = unix? ? dynamic_width : 80
387
+ chars[0, width - 3].join + "..."
257
388
  end
258
- (result < 10) ? 80 : result
259
- rescue
260
- 80
261
389
  end
390
+ end
262
391
 
263
- # Calculate the dynamic width of the terminal
264
- def dynamic_width
265
- @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
392
+ if "".respond_to?(:encode)
393
+ def as_unicode
394
+ yield
266
395
  end
267
-
268
- def dynamic_width_stty
269
- %x{stty size 2>/dev/null}.split[1].to_i
396
+ else
397
+ def as_unicode
398
+ old = $KCODE
399
+ $KCODE = "U"
400
+ yield
401
+ ensure
402
+ $KCODE = old
270
403
  end
404
+ end
271
405
 
272
- def dynamic_width_tput
273
- %x{tput cols 2>/dev/null}.to_i
274
- end
406
+ def ask_simply(statement, color, options)
407
+ default = options[:default]
408
+ message = [statement, ("(#{default})" if default), nil].uniq.join(" ")
409
+ message = prepare_message(message, *color)
410
+ result = Thor::LineEditor.readline(message, options)
275
411
 
276
- def unix?
277
- RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
278
- end
412
+ return unless result
279
413
 
280
- def truncate(string, width)
281
- if string.length <= width
282
- string
283
- else
284
- ( string[0, width-3] || "" ) + "..."
285
- end
414
+ result.strip!
415
+
416
+ if default && result == ""
417
+ default
418
+ else
419
+ result
286
420
  end
421
+ end
287
422
 
423
+ def ask_filtered(statement, color, options)
424
+ answer_set = options[:limited_to]
425
+ correct_answer = nil
426
+ until correct_answer
427
+ answers = answer_set.join(", ")
428
+ answer = ask_simply("#{statement} [#{answers}]", color, options)
429
+ correct_answer = answer_set.include?(answer) ? answer : nil
430
+ say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
431
+ end
432
+ correct_answer
433
+ end
288
434
  end
289
435
  end
290
436
  end
@@ -1,4 +1,4 @@
1
- require 'thor/shell/basic'
1
+ require "thor/shell/basic"
2
2
 
3
3
  class Thor
4
4
  module Shell
@@ -50,59 +50,100 @@ class Thor
50
50
  # on Highline implementation and it automatically appends CLEAR to the end
51
51
  # of the returned String.
52
52
  #
53
- def set_color(string, color, bold=false)
54
- color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
55
- bold = bold ? BOLD : ""
56
- "#{bold}#{color}#{string}#{CLEAR}"
53
+ # Pass foreground, background and bold options to this method as
54
+ # symbols.
55
+ #
56
+ # Example:
57
+ #
58
+ # set_color "Hi!", :red, :on_white, :bold
59
+ #
60
+ # The available colors are:
61
+ #
62
+ # :bold
63
+ # :black
64
+ # :red
65
+ # :green
66
+ # :yellow
67
+ # :blue
68
+ # :magenta
69
+ # :cyan
70
+ # :white
71
+ # :on_black
72
+ # :on_red
73
+ # :on_green
74
+ # :on_yellow
75
+ # :on_blue
76
+ # :on_magenta
77
+ # :on_cyan
78
+ # :on_white
79
+ def set_color(string, *colors)
80
+ if colors.compact.empty? || !can_display_colors?
81
+ string
82
+ elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
83
+ ansi_colors = colors.map { |color| lookup_color(color) }
84
+ "#{ansi_colors.join}#{string}#{CLEAR}"
85
+ else
86
+ # The old API was `set_color(color, bold=boolean)`. We
87
+ # continue to support the old API because you should never
88
+ # break old APIs unnecessarily :P
89
+ foreground, bold = colors
90
+ foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol)
91
+
92
+ bold = bold ? BOLD : ""
93
+ "#{bold}#{foreground}#{string}#{CLEAR}"
94
+ end
57
95
  end
58
96
 
59
- protected
97
+ protected
98
+
99
+ def can_display_colors?
100
+ stdout.tty?
101
+ end
60
102
 
61
- # Overwrite show_diff to show diff with colors if Diff::LCS is
62
- # available.
63
- #
64
- def show_diff(destination, content) #:nodoc:
65
- if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil?
66
- actual = File.binread(destination).to_s.split("\n")
67
- content = content.to_s.split("\n")
103
+ # Overwrite show_diff to show diff with colors if Diff::LCS is
104
+ # available.
105
+ #
106
+ def show_diff(destination, content) #:nodoc:
107
+ if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
108
+ actual = File.binread(destination).to_s.split("\n")
109
+ content = content.to_s.split("\n")
68
110
 
69
- Diff::LCS.sdiff(actual, content).each do |diff|
70
- output_diff_line(diff)
71
- end
72
- else
73
- super
111
+ Diff::LCS.sdiff(actual, content).each do |diff|
112
+ output_diff_line(diff)
74
113
  end
114
+ else
115
+ super
75
116
  end
117
+ end
76
118
 
77
- def output_diff_line(diff) #:nodoc:
78
- case diff.action
79
- when '-'
80
- say "- #{diff.old_element.chomp}", :red, true
81
- when '+'
82
- say "+ #{diff.new_element.chomp}", :green, true
83
- when '!'
84
- say "- #{diff.old_element.chomp}", :red, true
85
- say "+ #{diff.new_element.chomp}", :green, true
86
- else
87
- say " #{diff.old_element.chomp}", nil, true
88
- end
119
+ def output_diff_line(diff) #:nodoc:
120
+ case diff.action
121
+ when "-"
122
+ say "- #{diff.old_element.chomp}", :red, true
123
+ when "+"
124
+ say "+ #{diff.new_element.chomp}", :green, true
125
+ when "!"
126
+ say "- #{diff.old_element.chomp}", :red, true
127
+ say "+ #{diff.new_element.chomp}", :green, true
128
+ else
129
+ say " #{diff.old_element.chomp}", nil, true
89
130
  end
131
+ end
90
132
 
91
- # Check if Diff::LCS is loaded. If it is, use it to create pretty output
92
- # for diff.
93
- #
94
- def diff_lcs_loaded? #:nodoc:
95
- return true if defined?(Diff::LCS)
96
- return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
133
+ # Check if Diff::LCS is loaded. If it is, use it to create pretty output
134
+ # for diff.
135
+ #
136
+ def diff_lcs_loaded? #:nodoc:
137
+ return true if defined?(Diff::LCS)
138
+ return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
97
139
 
98
- @diff_lcs_loaded = begin
99
- require 'diff/lcs'
100
- true
101
- rescue LoadError
102
- false
103
- end
140
+ @diff_lcs_loaded = begin
141
+ require "diff/lcs"
142
+ true
143
+ rescue LoadError
144
+ false
104
145
  end
105
-
146
+ end
106
147
  end
107
148
  end
108
149
  end