thor 0.14.6 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +8 -0
- data/.document +5 -0
- data/.gemtest +0 -0
- data/.gitignore +44 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.rdoc +4 -4
- data/Gemfile +19 -0
- data/{LICENSE → LICENSE.md} +2 -2
- data/README.md +21 -300
- data/Thorfile +21 -15
- data/lib/thor.rb +56 -11
- data/lib/thor/actions.rb +7 -3
- data/lib/thor/actions/create_link.rb +1 -1
- data/lib/thor/actions/directory.rb +7 -3
- data/lib/thor/actions/empty_directory.rb +24 -5
- data/lib/thor/actions/file_manipulation.rb +40 -2
- data/lib/thor/base.rb +66 -28
- data/lib/thor/error.rb +6 -1
- data/lib/thor/group.rb +20 -8
- data/lib/thor/invocation.rb +4 -2
- data/lib/thor/parser/arguments.rb +6 -2
- data/lib/thor/parser/option.rb +3 -2
- data/lib/thor/parser/options.rb +13 -8
- data/lib/thor/rake_compat.rb +13 -8
- data/lib/thor/runner.rb +16 -4
- data/lib/thor/shell.rb +2 -2
- data/lib/thor/shell/basic.rb +86 -29
- data/lib/thor/shell/color.rb +40 -4
- data/lib/thor/shell/html.rb +28 -26
- data/lib/thor/task.rb +26 -8
- data/lib/thor/util.rb +26 -7
- data/lib/thor/version.rb +1 -1
- data/spec/actions/create_link_spec.rb +81 -0
- data/spec/actions/empty_directory_spec.rb +32 -0
- data/spec/actions/file_manipulation_spec.rb +61 -1
- data/spec/actions_spec.rb +4 -0
- data/spec/base_spec.rb +10 -5
- data/spec/exit_condition_spec.rb +19 -0
- data/spec/fixtures/script.thor +8 -2
- data/spec/group_spec.rb +39 -1
- data/spec/parser/arguments_spec.rb +1 -0
- data/spec/parser/options_spec.rb +12 -2
- data/spec/rake_compat_spec.rb +11 -7
- data/spec/register_spec.rb +43 -0
- data/spec/runner_spec.rb +34 -3
- data/spec/shell/basic_spec.rb +50 -3
- data/spec/shell/color_spec.rb +46 -6
- data/spec/shell/html_spec.rb +10 -5
- data/spec/spec_helper.rb +4 -0
- data/spec/task_spec.rb +26 -16
- data/spec/thor_spec.rb +56 -3
- data/thor.gemspec +26 -0
- metadata +174 -117
data/lib/thor/error.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class Thor
|
2
2
|
# Thor::Error is raised when it's caused by wrong usage of thor classes. Those
|
3
|
-
# errors have their backtrace
|
3
|
+
# errors have their backtrace suppressed and are nicely shown to the user.
|
4
4
|
#
|
5
5
|
# Errors that are caused by the developer, like declaring a method which
|
6
6
|
# overwrites a thor keyword, it SHOULD NOT raise a Thor::Error. This way, we
|
@@ -27,4 +27,9 @@ class Thor
|
|
27
27
|
|
28
28
|
class MalformattedArgumentError < InvocationError
|
29
29
|
end
|
30
|
+
|
31
|
+
# Raised when a user tries to call a private method encoded in templated filename.
|
32
|
+
#
|
33
|
+
class PrivateMethodEncodedError < Error
|
34
|
+
end
|
30
35
|
end
|
data/lib/thor/group.rb
CHANGED
@@ -104,7 +104,7 @@ class Thor::Group
|
|
104
104
|
#
|
105
105
|
# ==== Custom invocations
|
106
106
|
#
|
107
|
-
# You can also supply a block to customize how the option is
|
107
|
+
# You can also supply a block to customize how the option is going to be
|
108
108
|
# invoked. The block receives two parameters, an instance of the current
|
109
109
|
# class and the klass to be invoked.
|
110
110
|
#
|
@@ -187,9 +187,9 @@ class Thor::Group
|
|
187
187
|
human_name = value.respond_to?(:classify) ? value.classify : value
|
188
188
|
|
189
189
|
group_options[human_name] ||= []
|
190
|
-
group_options[human_name] += klass.class_options.values.select do |
|
191
|
-
base_options[
|
192
|
-
!group_options.values.flatten.any? { |i| i.name ==
|
190
|
+
group_options[human_name] += klass.class_options.values.select do |class_option|
|
191
|
+
base_options[class_option.name.to_sym].nil? && class_option.group.nil? &&
|
192
|
+
!group_options.values.flatten.any? { |i| i.name == class_option.name }
|
193
193
|
end
|
194
194
|
|
195
195
|
yield klass if block_given?
|
@@ -204,8 +204,16 @@ class Thor::Group
|
|
204
204
|
[item]
|
205
205
|
end
|
206
206
|
|
207
|
-
def handle_argument_error(task, error) #:nodoc:
|
208
|
-
|
207
|
+
def handle_argument_error(task, error, arity=nil) #:nodoc:
|
208
|
+
if arity > 0
|
209
|
+
msg = "#{basename} #{task.name} takes #{arity} argument"
|
210
|
+
msg << "s" if arity > 1
|
211
|
+
msg << ", but it should not."
|
212
|
+
else
|
213
|
+
msg = "You should not pass arguments to #{basename} #{task.name}."
|
214
|
+
end
|
215
|
+
|
216
|
+
raise error, msg
|
209
217
|
end
|
210
218
|
|
211
219
|
protected
|
@@ -220,10 +228,14 @@ class Thor::Group
|
|
220
228
|
args, opts = Thor::Options.split(given_args)
|
221
229
|
opts = given_opts || opts
|
222
230
|
|
231
|
+
instance = new(args, opts, config)
|
232
|
+
yield instance if block_given?
|
233
|
+
args = instance.args
|
234
|
+
|
223
235
|
if task
|
224
|
-
|
236
|
+
instance.invoke_task(all_tasks[task])
|
225
237
|
else
|
226
|
-
|
238
|
+
instance.invoke_all
|
227
239
|
end
|
228
240
|
end
|
229
241
|
|
data/lib/thor/invocation.rb
CHANGED
@@ -85,7 +85,7 @@ class Thor
|
|
85
85
|
# that it's going to use.
|
86
86
|
#
|
87
87
|
# If you want Rspec::RR to be initialized with its own set of options, you
|
88
|
-
# have to do that
|
88
|
+
# have to do that explicitly:
|
89
89
|
#
|
90
90
|
# invoke "rspec:rr", [], :style => :foo
|
91
91
|
#
|
@@ -106,7 +106,9 @@ class Thor
|
|
106
106
|
raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
|
107
107
|
|
108
108
|
args, opts, config = _parse_initialization_options(args, opts, config)
|
109
|
-
klass.send(:dispatch, task, args, opts, config)
|
109
|
+
klass.send(:dispatch, task, args, opts, config) do |instance|
|
110
|
+
instance.parent_options = options
|
111
|
+
end
|
110
112
|
end
|
111
113
|
|
112
114
|
# Invoke the given task if the given args.
|
@@ -28,7 +28,7 @@ class Thor
|
|
28
28
|
@switches = arguments
|
29
29
|
|
30
30
|
arguments.each do |argument|
|
31
|
-
if argument.default
|
31
|
+
if argument.default != nil
|
32
32
|
@assigns[argument.human_name] = argument.default
|
33
33
|
elsif argument.required?
|
34
34
|
@non_assigned_required << argument
|
@@ -49,6 +49,10 @@ class Thor
|
|
49
49
|
@assigns
|
50
50
|
end
|
51
51
|
|
52
|
+
def remaining
|
53
|
+
@pile
|
54
|
+
end
|
55
|
+
|
52
56
|
private
|
53
57
|
|
54
58
|
def no_or_skip?(arg)
|
@@ -94,7 +98,7 @@ class Thor
|
|
94
98
|
hash = {}
|
95
99
|
|
96
100
|
while current_is_value? && peek.include?(?:)
|
97
|
-
key, value = shift.split(':')
|
101
|
+
key, value = shift.split(':',2)
|
98
102
|
hash[key] = value
|
99
103
|
end
|
100
104
|
hash
|
data/lib/thor/parser/option.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
class Thor
|
2
2
|
class Option < Argument #:nodoc:
|
3
|
-
attr_reader :aliases, :group, :lazy_default
|
3
|
+
attr_reader :aliases, :group, :lazy_default, :hide
|
4
4
|
|
5
5
|
VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
|
6
6
|
|
7
|
-
def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, lazy_default=nil, group=nil, aliases=nil)
|
7
|
+
def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, lazy_default=nil, group=nil, aliases=nil, hide=nil)
|
8
8
|
super(name, description, required, type, default, banner)
|
9
9
|
@lazy_default = lazy_default
|
10
10
|
@group = group.to_s.capitalize if group
|
11
11
|
@aliases = [*aliases].compact
|
12
|
+
@hide = hide
|
12
13
|
end
|
13
14
|
|
14
15
|
# This parse quick options given as method_options. It makes several
|
data/lib/thor/parser/options.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
1
|
class Thor
|
2
|
-
# This is a modified version of Daniel Berger's Getopt::Long class, licensed
|
3
|
-
# under Ruby's license.
|
4
|
-
#
|
5
2
|
class Options < Arguments #:nodoc:
|
6
3
|
LONG_RE = /^(--\w+(?:-\w+)*)$/
|
7
4
|
SHORT_RE = /^(-[a-z])$/i
|
@@ -38,7 +35,7 @@ class Thor
|
|
38
35
|
@non_assigned_required.delete(hash_options[key])
|
39
36
|
end
|
40
37
|
|
41
|
-
@shorts, @switches, @
|
38
|
+
@shorts, @switches, @extra = {}, {}, []
|
42
39
|
|
43
40
|
options.each do |option|
|
44
41
|
@switches[option.switch_name] = option
|
@@ -49,14 +46,19 @@ class Thor
|
|
49
46
|
end
|
50
47
|
end
|
51
48
|
|
49
|
+
def remaining
|
50
|
+
@extra
|
51
|
+
end
|
52
|
+
|
52
53
|
def parse(args)
|
53
54
|
@pile = args.dup
|
54
55
|
|
55
56
|
while peek
|
56
57
|
match, is_switch = current_is_switch?
|
58
|
+
shifted = shift
|
57
59
|
|
58
60
|
if is_switch
|
59
|
-
case
|
61
|
+
case shifted
|
60
62
|
when SHORT_SQ_RE
|
61
63
|
unshift($1.split('').map { |f| "-#{f}" })
|
62
64
|
next
|
@@ -71,9 +73,10 @@ class Thor
|
|
71
73
|
option = switch_option(switch)
|
72
74
|
@assigns[option.human_name] = parse_peek(switch, option)
|
73
75
|
elsif match
|
74
|
-
@
|
76
|
+
@extra << shifted
|
77
|
+
@extra << shift while peek && peek !~ /^-/
|
75
78
|
else
|
76
|
-
|
79
|
+
@extra << shifted
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
@@ -85,7 +88,9 @@ class Thor
|
|
85
88
|
end
|
86
89
|
|
87
90
|
def check_unknown!
|
88
|
-
|
91
|
+
# an unknown option starts with - or -- and has no more --'s afterward.
|
92
|
+
unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ }
|
93
|
+
raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
|
89
94
|
end
|
90
95
|
|
91
96
|
protected
|
data/lib/thor/rake_compat.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rake'
|
2
|
+
require 'rake/dsl_definition'
|
2
3
|
|
3
4
|
class Thor
|
4
5
|
# Adds a compatibility layer to your Thor classes which allows you to use
|
@@ -16,6 +17,8 @@ class Thor
|
|
16
17
|
# end
|
17
18
|
#
|
18
19
|
module RakeCompat
|
20
|
+
include Rake::DSL if defined?(Rake::DSL)
|
21
|
+
|
19
22
|
def self.rake_classes
|
20
23
|
@rake_classes ||= []
|
21
24
|
end
|
@@ -29,12 +32,12 @@ class Thor
|
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
alias
|
35
|
+
# override task on (main), for compatibility with Rake 0.9
|
36
|
+
self.instance_eval do
|
37
|
+
alias rake_namespace namespace
|
35
38
|
|
36
|
-
def task(*
|
37
|
-
task =
|
39
|
+
def task(*)
|
40
|
+
task = super
|
38
41
|
|
39
42
|
if klass = Thor::RakeCompat.rake_classes.last
|
40
43
|
non_namespaced_name = task.name.split(':').last
|
@@ -43,7 +46,8 @@ class Object #:nodoc:
|
|
43
46
|
description << task.arg_names.map{ |n| n.to_s.upcase }.join(' ')
|
44
47
|
description.strip!
|
45
48
|
|
46
|
-
klass.desc description,
|
49
|
+
klass.desc description, Rake.application.last_description || non_namespaced_name
|
50
|
+
Rake.application.last_description = nil
|
47
51
|
klass.send :define_method, non_namespaced_name do |*args|
|
48
52
|
Rake::Task[task.name.to_sym].invoke(*args)
|
49
53
|
end
|
@@ -52,7 +56,7 @@ class Object #:nodoc:
|
|
52
56
|
task
|
53
57
|
end
|
54
58
|
|
55
|
-
def namespace(name
|
59
|
+
def namespace(name)
|
56
60
|
if klass = Thor::RakeCompat.rake_classes.last
|
57
61
|
const_name = Thor::Util.camel_case(name.to_s).to_sym
|
58
62
|
klass.const_set(const_name, Class.new(Thor))
|
@@ -60,7 +64,8 @@ class Object #:nodoc:
|
|
60
64
|
Thor::RakeCompat.rake_classes << new_klass
|
61
65
|
end
|
62
66
|
|
63
|
-
|
67
|
+
super
|
64
68
|
Thor::RakeCompat.rake_classes.pop
|
65
69
|
end
|
66
70
|
end
|
71
|
+
|
data/lib/thor/runner.rb
CHANGED
@@ -17,6 +17,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
17
17
|
if meth && !self.respond_to?(meth)
|
18
18
|
initialize_thorfiles(meth)
|
19
19
|
klass, task = Thor::Util.find_class_and_task_by_namespace(meth)
|
20
|
+
self.class.handle_no_task_error(task, false) if klass.nil?
|
20
21
|
klass.start(["-h", task].compact, :shell => self.shell)
|
21
22
|
else
|
22
23
|
super
|
@@ -30,6 +31,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
30
31
|
meth = meth.to_s
|
31
32
|
initialize_thorfiles(meth)
|
32
33
|
klass, task = Thor::Util.find_class_and_task_by_namespace(meth)
|
34
|
+
self.class.handle_no_task_error(task, false) if klass.nil?
|
33
35
|
args.unshift(task) if task
|
34
36
|
klass.start(args, :shell => self.shell)
|
35
37
|
end
|
@@ -73,7 +75,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
73
75
|
as = basename if as.empty?
|
74
76
|
end
|
75
77
|
|
76
|
-
location = if options[:relative] || name =~ /^
|
78
|
+
location = if options[:relative] || name =~ /^https?:\/\//
|
77
79
|
name
|
78
80
|
else
|
79
81
|
File.expand_path(name)
|
@@ -124,7 +126,17 @@ class Thor::Runner < Thor #:nodoc:
|
|
124
126
|
|
125
127
|
old_filename = thor_yaml[name][:filename]
|
126
128
|
self.options = self.options.merge("as" => name)
|
127
|
-
|
129
|
+
|
130
|
+
if File.directory? File.expand_path(name)
|
131
|
+
FileUtils.rm_rf(File.join(thor_root, old_filename))
|
132
|
+
|
133
|
+
thor_yaml.delete(old_filename)
|
134
|
+
save_yaml(thor_yaml)
|
135
|
+
|
136
|
+
filename = install(name)
|
137
|
+
else
|
138
|
+
filename = install(thor_yaml[name][:location])
|
139
|
+
end
|
128
140
|
|
129
141
|
unless filename == old_filename
|
130
142
|
File.delete(File.join(thor_root, old_filename))
|
@@ -190,7 +202,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
190
202
|
true
|
191
203
|
end
|
192
204
|
|
193
|
-
# Load the
|
205
|
+
# Load the Thorfiles. If relevant_to is supplied, looks for specific files
|
194
206
|
# in the thor_root instead of loading them all.
|
195
207
|
#
|
196
208
|
# By default, it also traverses the current path until find Thor files, as
|
@@ -244,7 +256,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
244
256
|
end
|
245
257
|
end
|
246
258
|
|
247
|
-
# Load
|
259
|
+
# Load Thorfiles relevant to the given method. If you provide "foo:bar" it
|
248
260
|
# will load all thor files in the thor.yaml that has "foo" e "foo:bar"
|
249
261
|
# namespaces registered.
|
250
262
|
#
|
data/lib/thor/shell.rb
CHANGED
@@ -8,7 +8,7 @@ class Thor
|
|
8
8
|
def self.shell
|
9
9
|
@shell ||= if ENV['THOR_SHELL'] && ENV['THOR_SHELL'].size > 0
|
10
10
|
Thor::Shell.const_get(ENV['THOR_SHELL'])
|
11
|
-
elsif RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
11
|
+
elsif ((RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) && !(ENV['ANSICON']))
|
12
12
|
Thor::Shell::Basic
|
13
13
|
else
|
14
14
|
Thor::Shell::Color
|
@@ -23,7 +23,7 @@ class Thor
|
|
23
23
|
end
|
24
24
|
|
25
25
|
module Shell
|
26
|
-
SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_table]
|
26
|
+
SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_table, :print_wrapped, :file_collision]
|
27
27
|
|
28
28
|
autoload :Basic, 'thor/shell/basic'
|
29
29
|
autoload :Color, 'thor/shell/color'
|
data/lib/thor/shell/basic.rb
CHANGED
@@ -5,10 +5,10 @@ class Thor
|
|
5
5
|
class Basic
|
6
6
|
attr_accessor :base, :padding
|
7
7
|
|
8
|
-
# Initialize base and padding to nil.
|
8
|
+
# Initialize base, mute and padding to nil.
|
9
9
|
#
|
10
10
|
def initialize #:nodoc:
|
11
|
-
@base, @padding = nil, 0
|
11
|
+
@base, @mute, @padding = nil, false, 0
|
12
12
|
end
|
13
13
|
|
14
14
|
# Mute everything that's inside given block
|
@@ -23,7 +23,7 @@ class Thor
|
|
23
23
|
# Check if base is muted
|
24
24
|
#
|
25
25
|
def mute?
|
26
|
-
@mute
|
26
|
+
@mute ||= false
|
27
27
|
end
|
28
28
|
|
29
29
|
# Sets the output padding, not allowing less than zero values.
|
@@ -32,14 +32,22 @@ class Thor
|
|
32
32
|
@padding = [0, value].max
|
33
33
|
end
|
34
34
|
|
35
|
-
#
|
35
|
+
# Asks something to the user and receives a response.
|
36
|
+
#
|
37
|
+
# If asked to limit the correct responses, you can pass in an
|
38
|
+
# array of acceptable answers. If one of those is not supplied,
|
39
|
+
# they will be shown a message stating that one of those answers
|
40
|
+
# must be given and re-asked the question.
|
36
41
|
#
|
37
42
|
# ==== Example
|
38
43
|
# ask("What is your name?")
|
39
44
|
#
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
# ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
|
46
|
+
#
|
47
|
+
def ask(statement, *args)
|
48
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
49
|
+
|
50
|
+
options[:limited_to] ? ask_filtered(statement, options[:limited_to], *args) : ask_simply(statement, *args)
|
43
51
|
end
|
44
52
|
|
45
53
|
# Say (print) something to the user. If the sentence ends with a whitespace
|
@@ -51,16 +59,17 @@ class Thor
|
|
51
59
|
#
|
52
60
|
def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/))
|
53
61
|
message = message.to_s
|
54
|
-
|
62
|
+
|
63
|
+
message = set_color(message, *color) if color
|
55
64
|
|
56
65
|
spaces = " " * padding
|
57
66
|
|
58
67
|
if force_new_line
|
59
|
-
|
68
|
+
stdout.puts(spaces + message)
|
60
69
|
else
|
61
|
-
|
70
|
+
stdout.print(spaces + message)
|
62
71
|
end
|
63
|
-
|
72
|
+
stdout.flush
|
64
73
|
end
|
65
74
|
|
66
75
|
# Say a status with the given color and appends the message. Since this
|
@@ -76,8 +85,8 @@ class Thor
|
|
76
85
|
status = status.to_s.rjust(12)
|
77
86
|
status = set_color status, color, true if color
|
78
87
|
|
79
|
-
|
80
|
-
|
88
|
+
stdout.puts "#{status}#{spaces}#{message}"
|
89
|
+
stdout.flush
|
81
90
|
end
|
82
91
|
|
83
92
|
# Make a question the to user and returns true if the user replies "y" or
|
@@ -100,35 +109,47 @@ class Thor
|
|
100
109
|
# Array[Array[String, String, ...]]
|
101
110
|
#
|
102
111
|
# ==== Options
|
103
|
-
#
|
112
|
+
# indent<Integer>:: Indent the first column by indent value.
|
104
113
|
# colwidth<Integer>:: Force the first column to colwidth spaces wide.
|
105
114
|
#
|
106
115
|
def print_table(table, options={})
|
107
116
|
return if table.empty?
|
108
117
|
|
109
|
-
formats,
|
118
|
+
formats, indent, colwidth = [], options[:indent].to_i, options[:colwidth]
|
110
119
|
options[:truncate] = terminal_width if options[:truncate] == true
|
111
120
|
|
112
121
|
formats << "%-#{colwidth + 2}s" if colwidth
|
113
122
|
start = colwidth ? 1 : 0
|
114
123
|
|
115
|
-
|
116
|
-
|
124
|
+
colcount = table.max{|a,b| a.size <=> b.size }.size
|
125
|
+
|
126
|
+
maximas = []
|
127
|
+
|
128
|
+
start.upto(colcount - 2) do |i|
|
129
|
+
maxima = table.map {|row| row[i] ? row[i].to_s.size : 0 }.max
|
130
|
+
maximas << maxima
|
117
131
|
formats << "%-#{maxima + 2}s"
|
118
132
|
end
|
119
133
|
|
120
|
-
formats[0] = formats[0].insert(0, " " *
|
134
|
+
formats[0] = formats[0].insert(0, " " * indent)
|
121
135
|
formats << "%s"
|
122
136
|
|
123
137
|
table.each do |row|
|
124
138
|
sentence = ""
|
125
139
|
|
126
140
|
row.each_with_index do |column, i|
|
127
|
-
|
141
|
+
maxima = maximas[i]
|
142
|
+
|
143
|
+
if column.is_a?(Numeric)
|
144
|
+
f = "%#{maxima}s "
|
145
|
+
else
|
146
|
+
f = formats[i]
|
147
|
+
end
|
148
|
+
sentence << f % column.to_s
|
128
149
|
end
|
129
150
|
|
130
151
|
sentence = truncate(sentence, options[:truncate]) if options[:truncate]
|
131
|
-
|
152
|
+
stdout.puts sentence
|
132
153
|
end
|
133
154
|
end
|
134
155
|
|
@@ -139,11 +160,11 @@ class Thor
|
|
139
160
|
# String
|
140
161
|
#
|
141
162
|
# ==== Options
|
142
|
-
#
|
163
|
+
# indent<Integer>:: Indent each line of the printed paragraph by indent value.
|
143
164
|
#
|
144
165
|
def print_wrapped(message, options={})
|
145
|
-
|
146
|
-
width = terminal_width -
|
166
|
+
indent = options[:indent] || 0
|
167
|
+
width = terminal_width - indent
|
147
168
|
paras = message.split("\n\n")
|
148
169
|
|
149
170
|
paras.map! do |unwrapped|
|
@@ -154,14 +175,14 @@ class Thor
|
|
154
175
|
|
155
176
|
paras.each do |para|
|
156
177
|
para.split("\n").each do |line|
|
157
|
-
|
178
|
+
stdout.puts line.insert(0, " " * indent)
|
158
179
|
end
|
159
|
-
|
180
|
+
stdout.puts unless para == paras.last
|
160
181
|
end
|
161
182
|
end
|
162
183
|
|
163
184
|
# Deals with file collision and returns true if the file should be
|
164
|
-
#
|
185
|
+
# overwritten and false otherwise. If a block is given, it uses the block
|
165
186
|
# response as the content for the diff.
|
166
187
|
#
|
167
188
|
# ==== Parameters
|
@@ -195,23 +216,40 @@ class Thor
|
|
195
216
|
end
|
196
217
|
|
197
218
|
# Called if something goes wrong during the execution. This is used by Thor
|
198
|
-
# internally and should not be used inside your scripts. If
|
219
|
+
# internally and should not be used inside your scripts. If something went
|
199
220
|
# wrong, you can always raise an exception. If you raise a Thor::Error, it
|
200
221
|
# will be rescued and wrapped in the method below.
|
201
222
|
#
|
202
223
|
def error(statement)
|
203
|
-
|
224
|
+
stderr.puts statement
|
204
225
|
end
|
205
226
|
|
206
227
|
# Apply color to the given string with optional bold. Disabled in the
|
207
228
|
# Thor::Shell::Basic class.
|
208
229
|
#
|
209
|
-
def set_color(string,
|
230
|
+
def set_color(string, *args) #:nodoc:
|
210
231
|
string
|
211
232
|
end
|
212
233
|
|
213
234
|
protected
|
214
235
|
|
236
|
+
def lookup_color(color)
|
237
|
+
return color unless color.is_a?(Symbol)
|
238
|
+
self.class.const_get(color.to_s.upcase)
|
239
|
+
end
|
240
|
+
|
241
|
+
def stdout
|
242
|
+
$stdout
|
243
|
+
end
|
244
|
+
|
245
|
+
def stdin
|
246
|
+
$stdin
|
247
|
+
end
|
248
|
+
|
249
|
+
def stderr
|
250
|
+
$stderr
|
251
|
+
end
|
252
|
+
|
215
253
|
def is?(value) #:nodoc:
|
216
254
|
value = value.to_s
|
217
255
|
|
@@ -285,6 +323,25 @@ HELP
|
|
285
323
|
end
|
286
324
|
end
|
287
325
|
|
326
|
+
def ask_simply(statement, color = nil)
|
327
|
+
say("#{statement} ", color)
|
328
|
+
stdin.gets.strip
|
329
|
+
end
|
330
|
+
|
331
|
+
def ask_filtered(statement, answer_set, *args)
|
332
|
+
correct_answer = nil
|
333
|
+
|
334
|
+
until correct_answer
|
335
|
+
answer = ask_simply("#{statement} #{answer_set.inspect}", *args)
|
336
|
+
|
337
|
+
correct_answer = answer_set.include?(answer) ? answer : nil
|
338
|
+
|
339
|
+
answers = answer_set.map(&:inspect).join(", ")
|
340
|
+
say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
|
341
|
+
end
|
342
|
+
|
343
|
+
correct_answer
|
344
|
+
end
|
288
345
|
end
|
289
346
|
end
|
290
347
|
end
|