thor 0.14.6 → 0.19.4
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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/{CHANGELOG.rdoc → CHANGELOG.md} +91 -31
- data/CONTRIBUTING.md +15 -0
- data/{LICENSE → LICENSE.md} +2 -2
- data/README.md +36 -296
- data/bin/thor +1 -1
- data/lib/thor/actions/create_file.rb +33 -35
- data/lib/thor/actions/create_link.rb +7 -5
- data/lib/thor/actions/directory.rb +49 -24
- data/lib/thor/actions/empty_directory.rb +68 -67
- data/lib/thor/actions/file_manipulation.rb +85 -28
- data/lib/thor/actions/inject_into_file.rb +26 -32
- data/lib/thor/actions.rb +70 -66
- data/lib/thor/base.rb +314 -237
- data/lib/thor/command.rb +133 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +34 -24
- data/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/thor/core_ext/ordered_hash.rb +94 -65
- data/lib/thor/error.rb +10 -8
- data/lib/thor/group.rb +74 -66
- data/lib/thor/invocation.rb +65 -56
- data/lib/thor/line_editor/basic.rb +35 -0
- data/lib/thor/line_editor/readline.rb +88 -0
- data/lib/thor/line_editor.rb +17 -0
- data/lib/thor/parser/argument.rb +32 -29
- data/lib/thor/parser/arguments.rb +107 -93
- data/lib/thor/parser/option.rb +39 -13
- data/lib/thor/parser/options.rb +144 -97
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +21 -16
- data/lib/thor/runner.rb +166 -153
- data/lib/thor/shell/basic.rb +268 -122
- data/lib/thor/shell/color.rb +84 -43
- data/lib/thor/shell/html.rb +71 -66
- data/lib/thor/shell.rb +30 -37
- data/lib/thor/util.rb +227 -188
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +289 -131
- data/thor.gemspec +21 -0
- metadata +50 -189
- data/Thorfile +0 -24
- data/bin/rake2thor +0 -86
- data/lib/thor/core_ext/file_binary_read.rb +0 -9
- data/lib/thor/task.rb +0 -114
- data/spec/actions/create_file_spec.rb +0 -170
- data/spec/actions/directory_spec.rb +0 -136
- data/spec/actions/empty_directory_spec.rb +0 -98
- data/spec/actions/file_manipulation_spec.rb +0 -310
- data/spec/actions/inject_into_file_spec.rb +0 -135
- data/spec/actions_spec.rb +0 -322
- data/spec/base_spec.rb +0 -269
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
- data/spec/core_ext/ordered_hash_spec.rb +0 -115
- data/spec/fixtures/application.rb +0 -2
- data/spec/fixtures/bundle/execute.rb +0 -6
- data/spec/fixtures/bundle/main.thor +0 -1
- data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
- data/spec/fixtures/doc/README +0 -3
- data/spec/fixtures/doc/block_helper.rb +0 -3
- data/spec/fixtures/doc/components/.empty_directory +0 -0
- data/spec/fixtures/doc/config.rb +0 -1
- data/spec/fixtures/group.thor +0 -114
- data/spec/fixtures/invoke.thor +0 -112
- data/spec/fixtures/path with spaces +0 -0
- data/spec/fixtures/script.thor +0 -184
- data/spec/fixtures/task.thor +0 -10
- data/spec/group_spec.rb +0 -178
- data/spec/invocation_spec.rb +0 -100
- data/spec/parser/argument_spec.rb +0 -47
- data/spec/parser/arguments_spec.rb +0 -64
- data/spec/parser/option_spec.rb +0 -202
- data/spec/parser/options_spec.rb +0 -319
- data/spec/rake_compat_spec.rb +0 -68
- data/spec/register_spec.rb +0 -92
- data/spec/runner_spec.rb +0 -210
- data/spec/shell/basic_spec.rb +0 -223
- data/spec/shell/color_spec.rb +0 -41
- data/spec/shell/html_spec.rb +0 -27
- data/spec/shell_spec.rb +0 -47
- data/spec/spec_helper.rb +0 -54
- data/spec/task_spec.rb +0 -69
- data/spec/thor_spec.rb +0 -334
- data/spec/util_spec.rb +0 -163
data/lib/thor.rb
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "set"
|
|
2
|
+
require "thor/base"
|
|
2
3
|
|
|
3
4
|
class Thor
|
|
4
5
|
class << self
|
|
5
|
-
#
|
|
6
|
+
# Allows for custom "Command" package naming.
|
|
7
|
+
#
|
|
8
|
+
# === Parameters
|
|
9
|
+
# name<String>
|
|
10
|
+
# options<Hash>
|
|
11
|
+
#
|
|
12
|
+
def package_name(name, _ = {})
|
|
13
|
+
@package_name = name.nil? || name == "" ? nil : name
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Sets the default command when thor is executed without an explicit command to be called.
|
|
6
17
|
#
|
|
7
18
|
# ==== Parameters
|
|
8
|
-
# meth<Symbol>:: name of the
|
|
9
|
-
#
|
|
10
|
-
def
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@default_task ||= from_superclass(:default_task, 'help')
|
|
16
|
-
else
|
|
17
|
-
@default_task = meth.to_s
|
|
19
|
+
# meth<Symbol>:: name of the default command
|
|
20
|
+
#
|
|
21
|
+
def default_command(meth = nil)
|
|
22
|
+
if meth
|
|
23
|
+
@default_command = meth == :none ? "help" : meth.to_s
|
|
24
|
+
else
|
|
25
|
+
@default_command ||= from_superclass(:default_command, "help")
|
|
18
26
|
end
|
|
19
27
|
end
|
|
28
|
+
alias_method :default_task, :default_command
|
|
20
29
|
|
|
21
30
|
# Registers another Thor subclass as a command.
|
|
22
31
|
#
|
|
@@ -25,48 +34,50 @@ class Thor
|
|
|
25
34
|
# command<String>:: Subcommand name to use
|
|
26
35
|
# usage<String>:: Short usage for the subcommand
|
|
27
36
|
# description<String>:: Description for the subcommand
|
|
28
|
-
def register(klass, subcommand_name, usage, description, options={})
|
|
37
|
+
def register(klass, subcommand_name, usage, description, options = {})
|
|
29
38
|
if klass <= Thor::Group
|
|
30
39
|
desc usage, description, options
|
|
31
|
-
define_method(subcommand_name) { invoke
|
|
40
|
+
define_method(subcommand_name) { |*args| invoke(klass, args) }
|
|
32
41
|
else
|
|
33
42
|
desc usage, description, options
|
|
34
43
|
subcommand subcommand_name, klass
|
|
35
44
|
end
|
|
36
45
|
end
|
|
37
46
|
|
|
38
|
-
# Defines the usage and the description of the next
|
|
47
|
+
# Defines the usage and the description of the next command.
|
|
39
48
|
#
|
|
40
49
|
# ==== Parameters
|
|
41
50
|
# usage<String>
|
|
42
51
|
# description<String>
|
|
43
52
|
# options<String>
|
|
44
53
|
#
|
|
45
|
-
def desc(usage, description, options={})
|
|
54
|
+
def desc(usage, description, options = {})
|
|
46
55
|
if options[:for]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
command = find_and_refresh_command(options[:for])
|
|
57
|
+
command.usage = usage if usage
|
|
58
|
+
command.description = description if description
|
|
50
59
|
else
|
|
51
|
-
@usage
|
|
60
|
+
@usage = usage
|
|
61
|
+
@desc = description
|
|
62
|
+
@hide = options[:hide] || false
|
|
52
63
|
end
|
|
53
64
|
end
|
|
54
65
|
|
|
55
|
-
# Defines the long description of the next
|
|
66
|
+
# Defines the long description of the next command.
|
|
56
67
|
#
|
|
57
68
|
# ==== Parameters
|
|
58
69
|
# long description<String>
|
|
59
70
|
#
|
|
60
|
-
def long_desc(long_description, options={})
|
|
71
|
+
def long_desc(long_description, options = {})
|
|
61
72
|
if options[:for]
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
command = find_and_refresh_command(options[:for])
|
|
74
|
+
command.long_description = long_description if long_description
|
|
64
75
|
else
|
|
65
76
|
@long_desc = long_description
|
|
66
77
|
end
|
|
67
78
|
end
|
|
68
79
|
|
|
69
|
-
# Maps an input to a
|
|
80
|
+
# Maps an input to a command. If you define:
|
|
70
81
|
#
|
|
71
82
|
# map "-T" => "list"
|
|
72
83
|
#
|
|
@@ -74,18 +85,18 @@ class Thor
|
|
|
74
85
|
#
|
|
75
86
|
# thor -T
|
|
76
87
|
#
|
|
77
|
-
# Will invoke the list
|
|
88
|
+
# Will invoke the list command.
|
|
78
89
|
#
|
|
79
90
|
# ==== Parameters
|
|
80
|
-
# Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given
|
|
91
|
+
# Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
|
|
81
92
|
#
|
|
82
|
-
def map(mappings=nil)
|
|
93
|
+
def map(mappings = nil)
|
|
83
94
|
@map ||= from_superclass(:map, {})
|
|
84
95
|
|
|
85
96
|
if mappings
|
|
86
97
|
mappings.each do |key, value|
|
|
87
98
|
if key.respond_to?(:each)
|
|
88
|
-
key.each {|subkey| @map[subkey] = value}
|
|
99
|
+
key.each { |subkey| @map[subkey] = value }
|
|
89
100
|
else
|
|
90
101
|
@map[key] = value
|
|
91
102
|
end
|
|
@@ -95,29 +106,31 @@ class Thor
|
|
|
95
106
|
@map
|
|
96
107
|
end
|
|
97
108
|
|
|
98
|
-
# Declares the options for the next
|
|
109
|
+
# Declares the options for the next command to be declared.
|
|
99
110
|
#
|
|
100
111
|
# ==== Parameters
|
|
101
112
|
# Hash[Symbol => Object]:: The hash key is the name of the option and the value
|
|
102
113
|
# is the type of the option. Can be :string, :array, :hash, :boolean, :numeric
|
|
103
114
|
# or :required (string). If you give a value, the type of the value is used.
|
|
104
115
|
#
|
|
105
|
-
def method_options(options=nil)
|
|
116
|
+
def method_options(options = nil)
|
|
106
117
|
@method_options ||= {}
|
|
107
118
|
build_options(options, @method_options) if options
|
|
108
119
|
@method_options
|
|
109
120
|
end
|
|
110
121
|
|
|
122
|
+
alias_method :options, :method_options
|
|
123
|
+
|
|
111
124
|
# Adds an option to the set of method options. If :for is given as option,
|
|
112
|
-
# it allows you to change the options from a previous defined
|
|
125
|
+
# it allows you to change the options from a previous defined command.
|
|
113
126
|
#
|
|
114
|
-
# def
|
|
127
|
+
# def previous_command
|
|
115
128
|
# # magic
|
|
116
129
|
# end
|
|
117
130
|
#
|
|
118
|
-
# method_option :foo => :bar, :for => :
|
|
131
|
+
# method_option :foo => :bar, :for => :previous_command
|
|
119
132
|
#
|
|
120
|
-
# def
|
|
133
|
+
# def next_command
|
|
121
134
|
# # magic
|
|
122
135
|
# end
|
|
123
136
|
#
|
|
@@ -132,39 +145,46 @@ class Thor
|
|
|
132
145
|
# :aliases - Aliases for this option.
|
|
133
146
|
# :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
|
|
134
147
|
# :banner - String to show on usage notes.
|
|
148
|
+
# :hide - If you want to hide this option from the help.
|
|
135
149
|
#
|
|
136
|
-
def method_option(name, options={})
|
|
150
|
+
def method_option(name, options = {})
|
|
137
151
|
scope = if options[:for]
|
|
138
|
-
|
|
152
|
+
find_and_refresh_command(options[:for]).options
|
|
139
153
|
else
|
|
140
154
|
method_options
|
|
141
155
|
end
|
|
142
156
|
|
|
143
157
|
build_option(name, options, scope)
|
|
144
158
|
end
|
|
159
|
+
alias_method :option, :method_option
|
|
145
160
|
|
|
146
|
-
|
|
161
|
+
def disable_class_options
|
|
162
|
+
@disable_class_options = true
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Prints help information for the given command.
|
|
147
166
|
#
|
|
148
167
|
# ==== Parameters
|
|
149
168
|
# shell<Thor::Shell>
|
|
150
|
-
#
|
|
169
|
+
# command_name<String>
|
|
151
170
|
#
|
|
152
|
-
def
|
|
153
|
-
meth =
|
|
154
|
-
|
|
155
|
-
|
|
171
|
+
def command_help(shell, command_name)
|
|
172
|
+
meth = normalize_command_name(command_name)
|
|
173
|
+
command = all_commands[meth]
|
|
174
|
+
handle_no_command_error(meth) unless command
|
|
156
175
|
|
|
157
176
|
shell.say "Usage:"
|
|
158
|
-
shell.say " #{banner(
|
|
177
|
+
shell.say " #{banner(command)}"
|
|
159
178
|
shell.say
|
|
160
|
-
class_options_help(shell, nil =>
|
|
161
|
-
if
|
|
179
|
+
class_options_help(shell, nil => command.options.values)
|
|
180
|
+
if command.long_description
|
|
162
181
|
shell.say "Description:"
|
|
163
|
-
shell.print_wrapped(
|
|
182
|
+
shell.print_wrapped(command.long_description, :indent => 2)
|
|
164
183
|
else
|
|
165
|
-
shell.say
|
|
184
|
+
shell.say command.description
|
|
166
185
|
end
|
|
167
186
|
end
|
|
187
|
+
alias_method :task_help, :command_help
|
|
168
188
|
|
|
169
189
|
# Prints help information for this class.
|
|
170
190
|
#
|
|
@@ -172,45 +192,64 @@ class Thor
|
|
|
172
192
|
# shell<Thor::Shell>
|
|
173
193
|
#
|
|
174
194
|
def help(shell, subcommand = false)
|
|
175
|
-
list =
|
|
195
|
+
list = printable_commands(true, subcommand)
|
|
176
196
|
Thor::Util.thor_classes_in(self).each do |klass|
|
|
177
|
-
list += klass.
|
|
197
|
+
list += klass.printable_commands(false)
|
|
198
|
+
end
|
|
199
|
+
list.sort! { |a, b| a[0] <=> b[0] }
|
|
200
|
+
|
|
201
|
+
if defined?(@package_name) && @package_name
|
|
202
|
+
shell.say "#{@package_name} commands:"
|
|
203
|
+
else
|
|
204
|
+
shell.say "Commands:"
|
|
178
205
|
end
|
|
179
|
-
list.sort!{ |a,b| a[0] <=> b[0] }
|
|
180
206
|
|
|
181
|
-
shell.
|
|
182
|
-
shell.print_table(list, :ident => 2, :truncate => true)
|
|
207
|
+
shell.print_table(list, :indent => 2, :truncate => true)
|
|
183
208
|
shell.say
|
|
184
209
|
class_options_help(shell)
|
|
185
210
|
end
|
|
186
211
|
|
|
187
|
-
# Returns
|
|
188
|
-
def
|
|
189
|
-
(all ?
|
|
190
|
-
next if
|
|
212
|
+
# Returns commands ready to be printed.
|
|
213
|
+
def printable_commands(all = true, subcommand = false)
|
|
214
|
+
(all ? all_commands : commands).map do |_, command|
|
|
215
|
+
next if command.hidden?
|
|
191
216
|
item = []
|
|
192
|
-
item << banner(
|
|
193
|
-
item << (
|
|
217
|
+
item << banner(command, false, subcommand)
|
|
218
|
+
item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "")
|
|
194
219
|
item
|
|
195
220
|
end.compact
|
|
196
221
|
end
|
|
222
|
+
alias_method :printable_tasks, :printable_commands
|
|
197
223
|
|
|
198
224
|
def subcommands
|
|
199
225
|
@subcommands ||= from_superclass(:subcommands, [])
|
|
200
226
|
end
|
|
227
|
+
alias_method :subtasks, :subcommands
|
|
228
|
+
|
|
229
|
+
def subcommand_classes
|
|
230
|
+
@subcommand_classes ||= {}
|
|
231
|
+
end
|
|
201
232
|
|
|
202
233
|
def subcommand(subcommand, subcommand_class)
|
|
203
|
-
|
|
234
|
+
subcommands << subcommand.to_s
|
|
204
235
|
subcommand_class.subcommand_help subcommand
|
|
205
|
-
|
|
236
|
+
subcommand_classes[subcommand.to_s] = subcommand_class
|
|
237
|
+
|
|
238
|
+
define_method(subcommand) do |*args|
|
|
239
|
+
args, opts = Thor::Arguments.split(args)
|
|
240
|
+
invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}]
|
|
241
|
+
invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h")
|
|
242
|
+
invoke subcommand_class, *invoke_args
|
|
243
|
+
end
|
|
206
244
|
end
|
|
245
|
+
alias_method :subtask, :subcommand
|
|
207
246
|
|
|
208
247
|
# Extend check unknown options to accept a hash of conditions.
|
|
209
248
|
#
|
|
210
249
|
# === Parameters
|
|
211
250
|
# options<Hash>: A hash containing :only and/or :except keys
|
|
212
|
-
def check_unknown_options!(options={})
|
|
213
|
-
@check_unknown_options ||=
|
|
251
|
+
def check_unknown_options!(options = {})
|
|
252
|
+
@check_unknown_options ||= {}
|
|
214
253
|
options.each do |key, value|
|
|
215
254
|
if value
|
|
216
255
|
@check_unknown_options[key] = Array(value)
|
|
@@ -226,10 +265,10 @@ class Thor
|
|
|
226
265
|
options = check_unknown_options
|
|
227
266
|
return false unless options
|
|
228
267
|
|
|
229
|
-
|
|
230
|
-
return true unless
|
|
268
|
+
command = config[:current_command]
|
|
269
|
+
return true unless command
|
|
231
270
|
|
|
232
|
-
name =
|
|
271
|
+
name = command.name
|
|
233
272
|
|
|
234
273
|
if subcommands.include?(name)
|
|
235
274
|
false
|
|
@@ -242,93 +281,212 @@ class Thor
|
|
|
242
281
|
end
|
|
243
282
|
end
|
|
244
283
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
284
|
+
# Stop parsing of options as soon as an unknown option or a regular
|
|
285
|
+
# argument is encountered. All remaining arguments are passed to the command.
|
|
286
|
+
# This is useful if you have a command that can receive arbitrary additional
|
|
287
|
+
# options, and where those additional options should not be handled by
|
|
288
|
+
# Thor.
|
|
289
|
+
#
|
|
290
|
+
# ==== Example
|
|
291
|
+
#
|
|
292
|
+
# To better understand how this is useful, let's consider a command that calls
|
|
293
|
+
# an external command. A user may want to pass arbitrary options and
|
|
294
|
+
# arguments to that command. The command itself also accepts some options,
|
|
295
|
+
# which should be handled by Thor.
|
|
296
|
+
#
|
|
297
|
+
# class_option "verbose", :type => :boolean
|
|
298
|
+
# stop_on_unknown_option! :exec
|
|
299
|
+
# check_unknown_options! :except => :exec
|
|
300
|
+
#
|
|
301
|
+
# desc "exec", "Run a shell command"
|
|
302
|
+
# def exec(*args)
|
|
303
|
+
# puts "diagnostic output" if options[:verbose]
|
|
304
|
+
# Kernel.exec(*args)
|
|
305
|
+
# end
|
|
306
|
+
#
|
|
307
|
+
# Here +exec+ can be called with +--verbose+ to get diagnostic output,
|
|
308
|
+
# e.g.:
|
|
309
|
+
#
|
|
310
|
+
# $ thor exec --verbose echo foo
|
|
311
|
+
# diagnostic output
|
|
312
|
+
# foo
|
|
313
|
+
#
|
|
314
|
+
# But if +--verbose+ is given after +echo+, it is passed to +echo+ instead:
|
|
315
|
+
#
|
|
316
|
+
# $ thor exec echo --verbose foo
|
|
317
|
+
# --verbose foo
|
|
318
|
+
#
|
|
319
|
+
# ==== Parameters
|
|
320
|
+
# Symbol ...:: A list of commands that should be affected.
|
|
321
|
+
def stop_on_unknown_option!(*command_names)
|
|
322
|
+
stop_on_unknown_option.merge(command_names)
|
|
323
|
+
end
|
|
251
324
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
args, opts = given_args, nil
|
|
256
|
-
task = Thor::DynamicTask.new(meth)
|
|
257
|
-
end
|
|
325
|
+
def stop_on_unknown_option?(command) #:nodoc:
|
|
326
|
+
command && stop_on_unknown_option.include?(command.name.to_sym)
|
|
327
|
+
end
|
|
258
328
|
|
|
259
|
-
|
|
260
|
-
config.merge!(:current_task => task, :task_options => task.options)
|
|
329
|
+
protected
|
|
261
330
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
331
|
+
def stop_on_unknown_option #:nodoc:
|
|
332
|
+
@stop_on_unknown_option ||= Set.new
|
|
333
|
+
end
|
|
265
334
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
#
|
|
271
|
-
def banner(task, namespace = nil, subcommand = false)
|
|
272
|
-
"#{basename} #{task.formatted_usage(self, $thor_runner, subcommand)}"
|
|
273
|
-
end
|
|
335
|
+
# The method responsible for dispatching given the args.
|
|
336
|
+
def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
|
|
337
|
+
meth ||= retrieve_command_name(given_args)
|
|
338
|
+
command = all_commands[normalize_command_name(meth)]
|
|
274
339
|
|
|
275
|
-
|
|
276
|
-
|
|
340
|
+
if !command && config[:invoked_via_subcommand]
|
|
341
|
+
# We're a subcommand and our first argument didn't match any of our
|
|
342
|
+
# commands. So we put it back and call our default command.
|
|
343
|
+
given_args.unshift(meth)
|
|
344
|
+
command = all_commands[normalize_command_name(default_command)]
|
|
277
345
|
end
|
|
278
346
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
true
|
|
287
|
-
else
|
|
288
|
-
puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " <<
|
|
289
|
-
"Call desc if you want this method to be available as task or declare it inside a " <<
|
|
290
|
-
"no_tasks{} block. Invoked from #{caller[1].inspect}."
|
|
291
|
-
false
|
|
347
|
+
if command
|
|
348
|
+
args, opts = Thor::Options.split(given_args)
|
|
349
|
+
if stop_on_unknown_option?(command) && !args.empty?
|
|
350
|
+
# given_args starts with a non-option, so we treat everything as
|
|
351
|
+
# ordinary arguments
|
|
352
|
+
args.concat opts
|
|
353
|
+
opts.clear
|
|
292
354
|
end
|
|
355
|
+
else
|
|
356
|
+
args = given_args
|
|
357
|
+
opts = nil
|
|
358
|
+
command = dynamic_command_class.new(meth)
|
|
293
359
|
end
|
|
294
360
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
end
|
|
361
|
+
opts = given_opts || opts || []
|
|
362
|
+
config[:current_command] = command
|
|
363
|
+
config[:command_options] = command.options
|
|
299
364
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
365
|
+
instance = new(args, opts, config)
|
|
366
|
+
yield instance if block_given?
|
|
367
|
+
args = instance.args
|
|
368
|
+
trailing = args[Range.new(arguments.size, -1)]
|
|
369
|
+
instance.invoke_command(command, trailing || [])
|
|
370
|
+
end
|
|
303
371
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
372
|
+
# The banner for this class. You can customize it if you are invoking the
|
|
373
|
+
# thor class by another ways which is not the Thor::Runner. It receives
|
|
374
|
+
# the command that is going to be invoked and a boolean which indicates if
|
|
375
|
+
# the namespace should be displayed as arguments.
|
|
376
|
+
#
|
|
377
|
+
def banner(command, namespace = nil, subcommand = false)
|
|
378
|
+
"#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def baseclass #:nodoc:
|
|
382
|
+
Thor
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def dynamic_command_class #:nodoc:
|
|
386
|
+
Thor::DynamicCommand
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def create_command(meth) #:nodoc:
|
|
390
|
+
@usage ||= nil
|
|
391
|
+
@desc ||= nil
|
|
392
|
+
@long_desc ||= nil
|
|
393
|
+
@disable_class_options ||= nil
|
|
394
|
+
|
|
395
|
+
if @usage && @desc
|
|
396
|
+
base_class = @hide ? Thor::HiddenCommand : Thor::Command
|
|
397
|
+
commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options, @disable_class_options)
|
|
398
|
+
@usage, @desc, @long_desc, @method_options, @hide, @disable_class_options = nil
|
|
399
|
+
true
|
|
400
|
+
elsif all_commands[meth] || meth == "method_missing"
|
|
401
|
+
true
|
|
402
|
+
else
|
|
403
|
+
puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \
|
|
404
|
+
"Call desc if you want this method to be available as command or declare it inside a " \
|
|
405
|
+
"no_commands{} block. Invoked from #{caller[1].inspect}."
|
|
406
|
+
false
|
|
309
407
|
end
|
|
408
|
+
end
|
|
409
|
+
alias_method :create_task, :create_command
|
|
410
|
+
|
|
411
|
+
def initialize_added #:nodoc:
|
|
412
|
+
class_options.merge!(method_options)
|
|
413
|
+
@method_options = nil
|
|
414
|
+
end
|
|
310
415
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
416
|
+
# Retrieve the command name from given args.
|
|
417
|
+
def retrieve_command_name(args) #:nodoc:
|
|
418
|
+
meth = args.first.to_s unless args.empty?
|
|
419
|
+
args.shift if meth && (map[meth] || meth !~ /^\-/)
|
|
420
|
+
end
|
|
421
|
+
alias_method :retrieve_task_name, :retrieve_command_name
|
|
422
|
+
|
|
423
|
+
# receives a (possibly nil) command name and returns a name that is in
|
|
424
|
+
# the commands hash. In addition to normalizing aliases, this logic
|
|
425
|
+
# will determine if a shortened command is an unambiguous substring of
|
|
426
|
+
# a command or alias.
|
|
427
|
+
#
|
|
428
|
+
# +normalize_command_name+ also converts names like +animal-prison+
|
|
429
|
+
# into +animal_prison+.
|
|
430
|
+
def normalize_command_name(meth) #:nodoc:
|
|
431
|
+
return default_command.to_s.tr("-", "_") unless meth
|
|
432
|
+
|
|
433
|
+
possibilities = find_command_possibilities(meth)
|
|
434
|
+
raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" if possibilities.size > 1
|
|
435
|
+
|
|
436
|
+
if possibilities.empty?
|
|
437
|
+
meth ||= default_command
|
|
438
|
+
elsif map[meth]
|
|
439
|
+
meth = map[meth]
|
|
440
|
+
else
|
|
441
|
+
meth = possibilities.first
|
|
316
442
|
end
|
|
317
443
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
444
|
+
meth.to_s.tr("-", "_") # treat foo-bar as foo_bar
|
|
445
|
+
end
|
|
446
|
+
alias_method :normalize_task_name, :normalize_command_name
|
|
447
|
+
|
|
448
|
+
# this is the logic that takes the command name passed in by the user
|
|
449
|
+
# and determines whether it is an unambiguous substrings of a command or
|
|
450
|
+
# alias name.
|
|
451
|
+
def find_command_possibilities(meth)
|
|
452
|
+
len = meth.to_s.length
|
|
453
|
+
possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
|
|
454
|
+
unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
|
|
455
|
+
|
|
456
|
+
if possibilities.include?(meth)
|
|
457
|
+
[meth]
|
|
458
|
+
elsif unique_possibilities.size == 1
|
|
459
|
+
unique_possibilities
|
|
460
|
+
else
|
|
461
|
+
possibilities
|
|
323
462
|
end
|
|
463
|
+
end
|
|
464
|
+
alias_method :find_task_possibilities, :find_command_possibilities
|
|
465
|
+
|
|
466
|
+
def subcommand_help(cmd)
|
|
467
|
+
desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
|
|
468
|
+
class_eval "
|
|
469
|
+
def help(command = nil, subcommand = true); super; end
|
|
470
|
+
"
|
|
471
|
+
end
|
|
472
|
+
alias_method :subtask_help, :subcommand_help
|
|
324
473
|
end
|
|
325
474
|
|
|
326
475
|
include Thor::Base
|
|
327
476
|
|
|
328
477
|
map HELP_MAPPINGS => :help
|
|
329
478
|
|
|
330
|
-
desc "help [
|
|
331
|
-
|
|
332
|
-
|
|
479
|
+
desc "help [COMMAND]", "Describe available commands or one specific command"
|
|
480
|
+
disable_class_options
|
|
481
|
+
def help(command = nil, subcommand = false)
|
|
482
|
+
if command
|
|
483
|
+
if self.class.subcommands.include? command
|
|
484
|
+
self.class.subcommand_classes[command].help(shell, true)
|
|
485
|
+
else
|
|
486
|
+
self.class.command_help(shell, command)
|
|
487
|
+
end
|
|
488
|
+
else
|
|
489
|
+
self.class.help(shell, subcommand)
|
|
490
|
+
end
|
|
333
491
|
end
|
|
334
492
|
end
|
data/thor.gemspec
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path("../lib/", __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require "thor/version"
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.add_development_dependency "bundler", "~> 1.0"
|
|
8
|
+
spec.authors = ["Yehuda Katz", "José Valim"]
|
|
9
|
+
spec.description = "Thor is a toolkit for building powerful command-line interfaces."
|
|
10
|
+
spec.email = "ruby-thor@googlegroups.com"
|
|
11
|
+
spec.executables = %w(thor)
|
|
12
|
+
spec.files = %w(.document thor.gemspec) + Dir["*.md", "bin/*", "lib/**/*.rb"]
|
|
13
|
+
spec.homepage = "http://whatisthor.com/"
|
|
14
|
+
spec.licenses = %w(MIT)
|
|
15
|
+
spec.name = "thor"
|
|
16
|
+
spec.require_paths = %w(lib)
|
|
17
|
+
spec.required_ruby_version = ">= 1.8.7"
|
|
18
|
+
spec.required_rubygems_version = ">= 1.3.5"
|
|
19
|
+
spec.summary = spec.description
|
|
20
|
+
spec.version = Thor::VERSION
|
|
21
|
+
end
|