foreman 0.85.0 → 0.86.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/foreman/cli.rb +3 -3
  4. data/lib/foreman/vendor/thor/lib/thor.rb +492 -0
  5. data/lib/foreman/vendor/thor/lib/thor/actions.rb +318 -0
  6. data/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  7. data/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  8. data/lib/foreman/vendor/thor/lib/thor/actions/directory.rb +118 -0
  9. data/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  10. data/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb +327 -0
  11. data/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb +103 -0
  12. data/lib/foreman/vendor/thor/lib/thor/base.rb +656 -0
  13. data/lib/foreman/vendor/thor/lib/thor/command.rb +133 -0
  14. data/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +85 -0
  15. data/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
  16. data/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb +129 -0
  17. data/lib/foreman/vendor/thor/lib/thor/error.rb +32 -0
  18. data/lib/foreman/vendor/thor/lib/thor/group.rb +281 -0
  19. data/lib/foreman/vendor/thor/lib/thor/invocation.rb +177 -0
  20. data/lib/foreman/vendor/thor/lib/thor/line_editor.rb +17 -0
  21. data/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  22. data/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  23. data/lib/foreman/vendor/thor/lib/thor/parser.rb +4 -0
  24. data/lib/foreman/vendor/thor/lib/thor/parser/argument.rb +70 -0
  25. data/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  26. data/lib/foreman/vendor/thor/lib/thor/parser/option.rb +146 -0
  27. data/lib/foreman/vendor/thor/lib/thor/parser/options.rb +220 -0
  28. data/lib/foreman/vendor/thor/lib/thor/rake_compat.rb +71 -0
  29. data/lib/foreman/vendor/thor/lib/thor/runner.rb +322 -0
  30. data/lib/foreman/vendor/thor/lib/thor/shell.rb +81 -0
  31. data/lib/foreman/vendor/thor/lib/thor/shell/basic.rb +436 -0
  32. data/lib/foreman/vendor/thor/lib/thor/shell/color.rb +149 -0
  33. data/lib/foreman/vendor/thor/lib/thor/shell/html.rb +126 -0
  34. data/lib/foreman/vendor/thor/lib/thor/util.rb +268 -0
  35. data/lib/foreman/vendor/thor/lib/thor/version.rb +3 -0
  36. data/lib/foreman/version.rb +1 -1
  37. data/man/foreman.1 +1 -1
  38. metadata +36 -19
@@ -0,0 +1,81 @@
1
+ require "rbconfig"
2
+
3
+ class Foreman::Thor
4
+ module Base
5
+ class << self
6
+ attr_writer :shell
7
+
8
+ # Returns the shell used in all Foreman::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
+ Foreman::Thor::Shell.const_get(ENV["THOR_SHELL"])
14
+ elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"]
15
+ Foreman::Thor::Shell::Basic
16
+ else
17
+ Foreman::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, "foreman/vendor/thor/lib/thor/shell/basic"
28
+ autoload :Color, "foreman/vendor/thor/lib/thor/shell/color"
29
+ autoload :HTML, "foreman/vendor/thor/lib/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 < Foreman::Thor
39
+ # argument :first, :type => :numeric
40
+ # end
41
+ #
42
+ # MyScript.new [1.0], { :foo => :bar }, :shell => Foreman::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 Foreman::Thor instance. If no shell is given,
51
+ # it gets a default shell from Foreman::Thor::Base.shell.
52
+ def shell
53
+ @shell ||= Foreman::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,436 @@
1
+ require "tempfile"
2
+ require "io/console" if RUBY_VERSION > "1.9.2"
3
+
4
+ class Foreman::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 = nil
14
+ @mute = false
15
+ @padding = 0
16
+ @always_force = false
17
+ end
18
+
19
+ # Mute everything that's inside given block
20
+ #
21
+ def mute
22
+ @mute = true
23
+ yield
24
+ ensure
25
+ @mute = false
26
+ end
27
+
28
+ # Check if base is muted
29
+ #
30
+ def mute?
31
+ @mute
32
+ end
33
+
34
+ # Sets the output padding, not allowing less than zero values.
35
+ #
36
+ def padding=(value)
37
+ @padding = [0, value].max
38
+ end
39
+
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.
63
+ #
64
+ # ==== Example
65
+ # ask("What is your name?")
66
+ #
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
82
+ end
83
+
84
+ # Say (print) something to the user. If the sentence ends with a whitespace
85
+ # or tab character, a new line is not appended (print + flush). Otherwise
86
+ # are passed straight to puts (behavior got from Highline).
87
+ #
88
+ # ==== Example
89
+ # say("I know you knew that.")
90
+ #
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")
94
+
95
+ stdout.print(buffer)
96
+ stdout.flush
97
+ end
98
+
99
+ # Say a status with the given color and appends the message. Since this
100
+ # method is used frequently by actions, it allows nil or false to be given
101
+ # in log_status, avoiding the message from being shown. If a Symbol is
102
+ # given in log_status, it's used as the color.
103
+ #
104
+ def say_status(status, message, log_status = true)
105
+ return if quiet? || log_status == false
106
+ spaces = " " * (padding + 1)
107
+ color = log_status.is_a?(Symbol) ? log_status : :green
108
+
109
+ status = status.to_s.rjust(12)
110
+ status = set_color status, color, true if color
111
+
112
+ buffer = "#{status}#{spaces}#{message}"
113
+ buffer << "\n" unless buffer.end_with?("\n")
114
+
115
+ stdout.print(buffer)
116
+ stdout.flush
117
+ end
118
+
119
+ # Make a question the to user and returns true if the user replies "y" or
120
+ # "yes".
121
+ #
122
+ def yes?(statement, color = nil)
123
+ !!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
124
+ end
125
+
126
+ # Make a question the to user and returns true if the user replies "n" or
127
+ # "no".
128
+ #
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
149
+ end
150
+
151
+ # Prints a table.
152
+ #
153
+ # ==== Parameters
154
+ # Array[Array[String, String, ...]]
155
+ #
156
+ # ==== Options
157
+ # indent<Integer>:: Indent the first column by indent value.
158
+ # colwidth<Integer>:: Force the first column to colwidth spaces wide.
159
+ #
160
+ def print_table(array, options = {}) # rubocop:disable MethodLength
161
+ return if array.empty?
162
+
163
+ formats = []
164
+ indent = options[:indent].to_i
165
+ colwidth = options[:colwidth]
166
+ options[:truncate] = terminal_width if options[:truncate] == true
167
+
168
+ formats << "%-#{colwidth + 2}s" if colwidth
169
+ start = colwidth ? 1 : 0
170
+
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
184
+ end
185
+
186
+ formats[0] = formats[0].insert(0, " " * indent)
187
+ formats << "%s"
188
+
189
+ array.each do |row|
190
+ sentence = ""
191
+
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
206
+ end
207
+
208
+ sentence = truncate(sentence, options[:truncate]) if options[:truncate]
209
+ stdout.puts sentence
210
+ end
211
+ end
212
+
213
+ # Prints a long string, word-wrapping the text to the current width of the
214
+ # terminal display. Ideal for printing heredocs.
215
+ #
216
+ # ==== Parameters
217
+ # String
218
+ #
219
+ # ==== Options
220
+ # indent<Integer>:: Indent each line of the printed paragraph by indent value.
221
+ #
222
+ def print_wrapped(message, options = {})
223
+ indent = options[:indent] || 0
224
+ width = terminal_width - indent
225
+ paras = message.split("\n\n")
226
+
227
+ paras.map! do |unwrapped|
228
+ unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") }
229
+ end
230
+
231
+ paras.each do |para|
232
+ para.split("\n").each do |line|
233
+ stdout.puts line.insert(0, " " * indent)
234
+ end
235
+ stdout.puts unless para == paras.last
236
+ end
237
+ end
238
+
239
+ # Deals with file collision and returns true if the file should be
240
+ # overwritten and false otherwise. If a block is given, it uses the block
241
+ # response as the content for the diff.
242
+ #
243
+ # ==== Parameters
244
+ # destination<String>:: the destination file to solve conflicts
245
+ # block<Proc>:: an optional block that returns the value to be used in diff
246
+ #
247
+ def file_collision(destination)
248
+ return true if @always_force
249
+ options = block_given? ? "[Ynaqdh]" : "[Ynaqh]"
250
+
251
+ loop do
252
+ answer = ask(
253
+ %[Overwrite #{destination}? (enter "h" for help) #{options}],
254
+ :add_to_history => false
255
+ )
256
+
257
+ case answer
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
272
+ end
273
+ end
274
+ end
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
+
289
+ # Called if something goes wrong during the execution. This is used by Foreman::Thor
290
+ # internally and should not be used inside your scripts. If something went
291
+ # wrong, you can always raise an exception. If you raise a Foreman::Thor::Error, it
292
+ # will be rescued and wrapped in the method below.
293
+ #
294
+ def error(statement)
295
+ stderr.puts statement
296
+ end
297
+
298
+ # Apply color to the given string with optional bold. Disabled in the
299
+ # Foreman::Thor::Shell::Basic class.
300
+ #
301
+ def set_color(string, *) #:nodoc:
302
+ string
303
+ end
304
+
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
315
+
316
+ def lookup_color(color)
317
+ return color unless color.is_a?(Symbol)
318
+ self.class.const_get(color.to_s.upcase)
319
+ end
320
+
321
+ def stdout
322
+ $stdout
323
+ end
324
+
325
+ def stderr
326
+ $stderr
327
+ end
328
+
329
+ def is?(value) #:nodoc:
330
+ value = value.to_s
331
+
332
+ if value.size == 1
333
+ /\A#{value}\z/i
334
+ else
335
+ /\A(#{value}|#{value[0, 1]})\z/i
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
349
+
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}")
357
+ end
358
+ end
359
+
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
386
+ else
387
+ chars[0, width - 3].join + "..."
388
+ end
389
+ end
390
+ end
391
+
392
+ if "".respond_to?(:encode)
393
+ def as_unicode
394
+ yield
395
+ end
396
+ else
397
+ def as_unicode
398
+ old = $KCODE
399
+ $KCODE = "U"
400
+ yield
401
+ ensure
402
+ $KCODE = old
403
+ end
404
+ end
405
+
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 = Foreman::Thor::LineEditor.readline(message, options)
411
+
412
+ return unless result
413
+
414
+ result.strip!
415
+
416
+ if default && result == ""
417
+ default
418
+ else
419
+ result
420
+ end
421
+ end
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
434
+ end
435
+ end
436
+ end