atli 0.1.2

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 (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