atli 0.1.2

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