thor_dleavitt 0.18.1

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