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/group.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "thor/base"
|
|
2
2
|
|
|
3
3
|
# Thor has a special class called Thor::Group. The main difference to Thor class
|
|
4
|
-
# is that it invokes all
|
|
4
|
+
# is that it invokes all commands at once. It also include some methods that allows
|
|
5
5
|
# invocations to be done at the class method, which are not available to Thor
|
|
6
|
-
#
|
|
6
|
+
# commands.
|
|
7
7
|
class Thor::Group
|
|
8
8
|
class << self
|
|
9
9
|
# The description for this Thor::Group. If none is provided, but a source root
|
|
@@ -13,12 +13,11 @@ class Thor::Group
|
|
|
13
13
|
# ==== Parameters
|
|
14
14
|
# description<String>:: The description for this Thor::Group.
|
|
15
15
|
#
|
|
16
|
-
def desc(description=nil)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@desc = description
|
|
16
|
+
def desc(description = nil)
|
|
17
|
+
if description
|
|
18
|
+
@desc = description
|
|
19
|
+
else
|
|
20
|
+
@desc ||= from_superclass(:desc, nil)
|
|
22
21
|
end
|
|
23
22
|
end
|
|
24
23
|
|
|
@@ -32,7 +31,7 @@ class Thor::Group
|
|
|
32
31
|
shell.say " #{banner}\n"
|
|
33
32
|
shell.say
|
|
34
33
|
class_options_help(shell)
|
|
35
|
-
shell.say
|
|
34
|
+
shell.say desc if desc
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
# Stores invocations for this class merging with superclass values.
|
|
@@ -48,7 +47,7 @@ class Thor::Group
|
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
# Invoke the given namespace or class given. It adds an instance
|
|
51
|
-
# method that will invoke the klass and
|
|
50
|
+
# method that will invoke the klass and command. You can give a block to
|
|
52
51
|
# configure how it will be invoked.
|
|
53
52
|
#
|
|
54
53
|
# The namespace/class given will have its options showed on the help
|
|
@@ -64,12 +63,12 @@ class Thor::Group
|
|
|
64
63
|
|
|
65
64
|
class_eval <<-METHOD, __FILE__, __LINE__
|
|
66
65
|
def _invoke_#{name.to_s.gsub(/\W/, '_')}
|
|
67
|
-
klass,
|
|
66
|
+
klass, command = self.class.prepare_for_invocation(nil, #{name.inspect})
|
|
68
67
|
|
|
69
68
|
if klass
|
|
70
69
|
say_status :invoke, #{name.inspect}, #{verbose.inspect}
|
|
71
70
|
block = self.class.invocation_blocks[#{name.inspect}]
|
|
72
|
-
_invoke_for_class_method klass,
|
|
71
|
+
_invoke_for_class_method klass, command, &block
|
|
73
72
|
else
|
|
74
73
|
say_status :error, %(#{name.inspect} [not found]), :red
|
|
75
74
|
end
|
|
@@ -100,11 +99,11 @@ class Thor::Group
|
|
|
100
99
|
# In some cases you want to customize how a specified hook is going to be
|
|
101
100
|
# invoked. You can do that by overwriting the class method
|
|
102
101
|
# prepare_for_invocation. The class method must necessarily return a klass
|
|
103
|
-
# and an optional
|
|
102
|
+
# and an optional command.
|
|
104
103
|
#
|
|
105
104
|
# ==== Custom invocations
|
|
106
105
|
#
|
|
107
|
-
# You can also supply a block to customize how the option is
|
|
106
|
+
# You can also supply a block to customize how the option is going to be
|
|
108
107
|
# invoked. The block receives two parameters, an instance of the current
|
|
109
108
|
# class and the klass to be invoked.
|
|
110
109
|
#
|
|
@@ -114,8 +113,8 @@ class Thor::Group
|
|
|
114
113
|
|
|
115
114
|
names.each do |name|
|
|
116
115
|
unless class_options.key?(name)
|
|
117
|
-
raise ArgumentError, "You have to define the option #{name.inspect} "
|
|
118
|
-
|
|
116
|
+
raise ArgumentError, "You have to define the option #{name.inspect} " \
|
|
117
|
+
"before setting invoke_from_option."
|
|
119
118
|
end
|
|
120
119
|
|
|
121
120
|
invocations[name] = true
|
|
@@ -127,12 +126,12 @@ class Thor::Group
|
|
|
127
126
|
|
|
128
127
|
value = options[#{name.inspect}]
|
|
129
128
|
value = #{name.inspect} if TrueClass === value
|
|
130
|
-
klass,
|
|
129
|
+
klass, command = self.class.prepare_for_invocation(#{name.inspect}, value)
|
|
131
130
|
|
|
132
131
|
if klass
|
|
133
132
|
say_status :invoke, value, #{verbose.inspect}
|
|
134
133
|
block = self.class.invocation_blocks[#{name.inspect}]
|
|
135
|
-
_invoke_for_class_method klass,
|
|
134
|
+
_invoke_for_class_method klass, command, &block
|
|
136
135
|
else
|
|
137
136
|
say_status :error, %(\#{value} [not found]), :red
|
|
138
137
|
end
|
|
@@ -149,7 +148,7 @@ class Thor::Group
|
|
|
149
148
|
#
|
|
150
149
|
def remove_invocation(*names)
|
|
151
150
|
names.each do |name|
|
|
152
|
-
|
|
151
|
+
remove_command(name)
|
|
153
152
|
remove_class_option(name)
|
|
154
153
|
invocations.delete(name)
|
|
155
154
|
invocation_blocks.delete(name)
|
|
@@ -159,7 +158,7 @@ class Thor::Group
|
|
|
159
158
|
# Overwrite class options help to allow invoked generators options to be
|
|
160
159
|
# shown recursively when invoking a generator.
|
|
161
160
|
#
|
|
162
|
-
def class_options_help(shell, groups={}) #:nodoc:
|
|
161
|
+
def class_options_help(shell, groups = {}) #:nodoc:
|
|
163
162
|
get_options_from_invocations(groups, class_options) do |klass|
|
|
164
163
|
klass.send(:get_options_from_invocations, groups, class_options)
|
|
165
164
|
end
|
|
@@ -170,7 +169,7 @@ class Thor::Group
|
|
|
170
169
|
# options are added to group_options hash. Options that already exists
|
|
171
170
|
# in base_options are not added twice.
|
|
172
171
|
#
|
|
173
|
-
def get_options_from_invocations(group_options, base_options) #:nodoc:
|
|
172
|
+
def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength
|
|
174
173
|
invocations.each do |name, from_option|
|
|
175
174
|
value = if from_option
|
|
176
175
|
option = class_options[name]
|
|
@@ -180,93 +179,102 @@ class Thor::Group
|
|
|
180
179
|
end
|
|
181
180
|
next unless value
|
|
182
181
|
|
|
183
|
-
klass,
|
|
182
|
+
klass, _ = prepare_for_invocation(name, value)
|
|
184
183
|
next unless klass && klass.respond_to?(:class_options)
|
|
185
184
|
|
|
186
185
|
value = value.to_s
|
|
187
186
|
human_name = value.respond_to?(:classify) ? value.classify : value
|
|
188
187
|
|
|
189
188
|
group_options[human_name] ||= []
|
|
190
|
-
group_options[human_name] += klass.class_options.values.select do |
|
|
191
|
-
base_options[
|
|
192
|
-
|
|
189
|
+
group_options[human_name] += klass.class_options.values.select do |class_option|
|
|
190
|
+
base_options[class_option.name.to_sym].nil? && class_option.group.nil? &&
|
|
191
|
+
!group_options.values.flatten.any? { |i| i.name == class_option.name }
|
|
193
192
|
end
|
|
194
193
|
|
|
195
194
|
yield klass if block_given?
|
|
196
195
|
end
|
|
197
196
|
end
|
|
198
197
|
|
|
199
|
-
# Returns
|
|
200
|
-
def
|
|
198
|
+
# Returns commands ready to be printed.
|
|
199
|
+
def printable_commands(*)
|
|
201
200
|
item = []
|
|
202
201
|
item << banner
|
|
203
|
-
item << (desc ? "# #{desc.gsub(/\s+/m,' ')}" : "")
|
|
202
|
+
item << (desc ? "# #{desc.gsub(/\s+/m, ' ')}" : "")
|
|
204
203
|
[item]
|
|
205
204
|
end
|
|
205
|
+
alias_method :printable_tasks, :printable_commands
|
|
206
206
|
|
|
207
|
-
def handle_argument_error(
|
|
208
|
-
|
|
207
|
+
def handle_argument_error(command, error, _args, arity) #:nodoc:
|
|
208
|
+
msg = "#{basename} #{command.name} takes #{arity} argument"
|
|
209
|
+
msg << "s" if arity > 1
|
|
210
|
+
msg << ", but it should not."
|
|
211
|
+
raise error, msg
|
|
209
212
|
end
|
|
210
213
|
|
|
211
|
-
|
|
214
|
+
protected
|
|
212
215
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
# The method responsible for dispatching given the args.
|
|
217
|
+
def dispatch(command, given_args, given_opts, config) #:nodoc:
|
|
218
|
+
if Thor::HELP_MAPPINGS.include?(given_args.first)
|
|
219
|
+
help(config[:shell])
|
|
220
|
+
return
|
|
221
|
+
end
|
|
219
222
|
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
args, opts = Thor::Options.split(given_args)
|
|
224
|
+
opts = given_opts || opts
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
else
|
|
226
|
-
new(args, opts, config).invoke_all
|
|
227
|
-
end
|
|
228
|
-
end
|
|
226
|
+
instance = new(args, opts, config)
|
|
227
|
+
yield instance if block_given?
|
|
229
228
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
if command
|
|
230
|
+
instance.invoke_command(all_commands[command])
|
|
231
|
+
else
|
|
232
|
+
instance.invoke_all
|
|
234
233
|
end
|
|
234
|
+
end
|
|
235
235
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
236
|
+
# The banner for this class. You can customize it if you are invoking the
|
|
237
|
+
# thor class by another ways which is not the Thor::Runner.
|
|
238
|
+
def banner
|
|
239
|
+
"#{basename} #{self_command.formatted_usage(self, false)}"
|
|
240
|
+
end
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
# Represents the whole class as a command.
|
|
243
|
+
def self_command #:nodoc:
|
|
244
|
+
Thor::DynamicCommand.new(namespace, class_options)
|
|
245
|
+
end
|
|
246
|
+
alias_method :self_task, :self_command
|
|
244
247
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
def baseclass #:nodoc:
|
|
249
|
+
Thor::Group
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def create_command(meth) #:nodoc:
|
|
253
|
+
commands[meth.to_s] = Thor::Command.new(meth, nil, nil, nil, nil)
|
|
254
|
+
true
|
|
255
|
+
end
|
|
256
|
+
alias_method :create_task, :create_command
|
|
249
257
|
end
|
|
250
258
|
|
|
251
259
|
include Thor::Base
|
|
252
260
|
|
|
253
|
-
|
|
261
|
+
protected
|
|
254
262
|
|
|
255
263
|
# Shortcut to invoke with padding and block handling. Use internally by
|
|
256
264
|
# invoke and invoke_from_option class methods.
|
|
257
|
-
def _invoke_for_class_method(klass,
|
|
265
|
+
def _invoke_for_class_method(klass, command = nil, *args, &block) #:nodoc:
|
|
258
266
|
with_padding do
|
|
259
267
|
if block
|
|
260
268
|
case block.arity
|
|
261
269
|
when 3
|
|
262
|
-
|
|
270
|
+
yield(self, klass, command)
|
|
263
271
|
when 2
|
|
264
|
-
|
|
272
|
+
yield(self, klass)
|
|
265
273
|
when 1
|
|
266
274
|
instance_exec(klass, &block)
|
|
267
275
|
end
|
|
268
276
|
else
|
|
269
|
-
invoke klass,
|
|
277
|
+
invoke klass, command, *args
|
|
270
278
|
end
|
|
271
279
|
end
|
|
272
280
|
end
|
data/lib/thor/invocation.rb
CHANGED
|
@@ -6,12 +6,12 @@ class Thor
|
|
|
6
6
|
|
|
7
7
|
module ClassMethods
|
|
8
8
|
# This method is responsible for receiving a name and find the proper
|
|
9
|
-
# class and
|
|
9
|
+
# class and command for it. The key is an optional parameter which is
|
|
10
10
|
# available only in class methods invocations (i.e. in Thor::Group).
|
|
11
11
|
def prepare_for_invocation(key, name) #:nodoc:
|
|
12
12
|
case name
|
|
13
13
|
when Symbol, String
|
|
14
|
-
Thor::Util.
|
|
14
|
+
Thor::Util.find_class_and_command_by_namespace(name.to_s, !key)
|
|
15
15
|
else
|
|
16
16
|
name
|
|
17
17
|
end
|
|
@@ -19,32 +19,37 @@ class Thor
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# Make initializer aware of invocations and the initialization args.
|
|
22
|
-
def initialize(args=[], options={}, config={}, &block) #:nodoc:
|
|
23
|
-
@_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
|
|
24
|
-
@_initializer = [
|
|
22
|
+
def initialize(args = [], options = {}, config = {}, &block) #:nodoc:
|
|
23
|
+
@_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] }
|
|
24
|
+
@_initializer = [args, options, config]
|
|
25
25
|
super
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
# Make the current command chain accessible with in a Thor-(sub)command
|
|
29
|
+
def current_command_chain
|
|
30
|
+
@_invocations.values.flatten.map(&:to_sym)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Receives a name and invokes it. The name can be a string (either "command" or
|
|
34
|
+
# "namespace:command"), a Thor::Command, a Class or a Thor instance. If the
|
|
35
|
+
# command cannot be guessed by name, it can also be supplied as second argument.
|
|
31
36
|
#
|
|
32
37
|
# You can also supply the arguments, options and configuration values for
|
|
33
|
-
# the
|
|
38
|
+
# the command to be invoked, if none is given, the same values used to
|
|
34
39
|
# initialize the invoker are used to initialize the invoked.
|
|
35
40
|
#
|
|
36
|
-
# When no name is given, it will invoke the default
|
|
41
|
+
# When no name is given, it will invoke the default command of the current class.
|
|
37
42
|
#
|
|
38
43
|
# ==== Examples
|
|
39
44
|
#
|
|
40
45
|
# class A < Thor
|
|
41
46
|
# def foo
|
|
42
47
|
# invoke :bar
|
|
43
|
-
# invoke "b:hello", ["
|
|
48
|
+
# invoke "b:hello", ["Erik"]
|
|
44
49
|
# end
|
|
45
50
|
#
|
|
46
51
|
# def bar
|
|
47
|
-
# invoke "b:hello", ["
|
|
52
|
+
# invoke "b:hello", ["Erik"]
|
|
48
53
|
# end
|
|
49
54
|
# end
|
|
50
55
|
#
|
|
@@ -54,16 +59,16 @@ class Thor
|
|
|
54
59
|
# end
|
|
55
60
|
# end
|
|
56
61
|
#
|
|
57
|
-
# You can notice that the method "foo" above invokes two
|
|
62
|
+
# You can notice that the method "foo" above invokes two commands: "bar",
|
|
58
63
|
# which belongs to the same class and "hello" which belongs to the class B.
|
|
59
64
|
#
|
|
60
|
-
# By using an invocation system you ensure that a
|
|
65
|
+
# By using an invocation system you ensure that a command is invoked only once.
|
|
61
66
|
# In the example above, invoking "foo" will invoke "b:hello" just once, even
|
|
62
67
|
# if it's invoked later by "bar" method.
|
|
63
68
|
#
|
|
64
69
|
# When class A invokes class B, all arguments used on A initialization are
|
|
65
70
|
# supplied to B. This allows lazy parse of options. Let's suppose you have
|
|
66
|
-
# some rspec
|
|
71
|
+
# some rspec commands:
|
|
67
72
|
#
|
|
68
73
|
# class Rspec < Thor::Group
|
|
69
74
|
# class_option :mock_framework, :type => :string, :default => :rr
|
|
@@ -85,7 +90,7 @@ class Thor
|
|
|
85
90
|
# that it's going to use.
|
|
86
91
|
#
|
|
87
92
|
# If you want Rspec::RR to be initialized with its own set of options, you
|
|
88
|
-
# have to do that
|
|
93
|
+
# have to do that explicitly:
|
|
89
94
|
#
|
|
90
95
|
# invoke "rspec:rr", [], :style => :foo
|
|
91
96
|
#
|
|
@@ -93,35 +98,39 @@ class Thor
|
|
|
93
98
|
#
|
|
94
99
|
# invoke Rspec::RR, [], :style => :foo
|
|
95
100
|
#
|
|
96
|
-
def invoke(name=nil, *args)
|
|
101
|
+
def invoke(name = nil, *args)
|
|
97
102
|
if name.nil?
|
|
98
103
|
warn "[Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}"
|
|
99
104
|
return invoke_all
|
|
100
105
|
end
|
|
101
106
|
|
|
102
|
-
args.unshift(nil) if
|
|
103
|
-
|
|
107
|
+
args.unshift(nil) if args.first.is_a?(Array) || args.first.nil?
|
|
108
|
+
command, args, opts, config = args
|
|
104
109
|
|
|
105
|
-
klass,
|
|
110
|
+
klass, command = _retrieve_class_and_command(name, command)
|
|
111
|
+
raise "Missing Thor class for invoke #{name}" unless klass
|
|
106
112
|
raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
|
|
107
113
|
|
|
108
114
|
args, opts, config = _parse_initialization_options(args, opts, config)
|
|
109
|
-
klass.send(:dispatch,
|
|
115
|
+
klass.send(:dispatch, command, args, opts, config) do |instance|
|
|
116
|
+
instance.parent_options = options
|
|
117
|
+
end
|
|
110
118
|
end
|
|
111
119
|
|
|
112
|
-
# Invoke the given
|
|
113
|
-
def
|
|
120
|
+
# Invoke the given command if the given args.
|
|
121
|
+
def invoke_command(command, *args) #:nodoc:
|
|
114
122
|
current = @_invocations[self.class]
|
|
115
123
|
|
|
116
|
-
unless current.include?(
|
|
117
|
-
current <<
|
|
118
|
-
|
|
124
|
+
unless current.include?(command.name)
|
|
125
|
+
current << command.name
|
|
126
|
+
command.run(self, *args)
|
|
119
127
|
end
|
|
120
128
|
end
|
|
129
|
+
alias_method :invoke_task, :invoke_command
|
|
121
130
|
|
|
122
|
-
# Invoke all
|
|
131
|
+
# Invoke all commands for the current instance.
|
|
123
132
|
def invoke_all #:nodoc:
|
|
124
|
-
self.class.
|
|
133
|
+
self.class.all_commands.map { |_, command| invoke_command(command) }
|
|
125
134
|
end
|
|
126
135
|
|
|
127
136
|
# Invokes using shell padding.
|
|
@@ -129,40 +138,40 @@ class Thor
|
|
|
129
138
|
with_padding { invoke(*args) }
|
|
130
139
|
end
|
|
131
140
|
|
|
132
|
-
|
|
141
|
+
protected
|
|
133
142
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
143
|
+
# Configuration values that are shared between invocations.
|
|
144
|
+
def _shared_configuration #:nodoc:
|
|
145
|
+
{:invocations => @_invocations}
|
|
146
|
+
end
|
|
138
147
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
[klass, task || sent_task]
|
|
152
|
-
end
|
|
148
|
+
# This method simply retrieves the class and command to be invoked.
|
|
149
|
+
# If the name is nil or the given name is a command in the current class,
|
|
150
|
+
# use the given name and return self as class. Otherwise, call
|
|
151
|
+
# prepare_for_invocation in the current class.
|
|
152
|
+
def _retrieve_class_and_command(name, sent_command = nil) #:nodoc:
|
|
153
|
+
if name.nil?
|
|
154
|
+
[self.class, nil]
|
|
155
|
+
elsif self.class.all_commands[name.to_s]
|
|
156
|
+
[self.class, name.to_s]
|
|
157
|
+
else
|
|
158
|
+
klass, command = self.class.prepare_for_invocation(nil, name)
|
|
159
|
+
[klass, command || sent_command]
|
|
153
160
|
end
|
|
161
|
+
end
|
|
162
|
+
alias_method :_retrieve_class_and_task, :_retrieve_class_and_command
|
|
154
163
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
164
|
+
# Initialize klass using values stored in the @_initializer.
|
|
165
|
+
def _parse_initialization_options(args, opts, config) #:nodoc:
|
|
166
|
+
stored_args, stored_opts, stored_config = @_initializer
|
|
158
167
|
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
args ||= stored_args.dup
|
|
169
|
+
opts ||= stored_opts.dup
|
|
161
170
|
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
config ||= {}
|
|
172
|
+
config = stored_config.merge(_shared_configuration).merge!(config)
|
|
164
173
|
|
|
165
|
-
|
|
166
|
-
|
|
174
|
+
[args, opts, config]
|
|
175
|
+
end
|
|
167
176
|
end
|
|
168
177
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class Thor
|
|
2
|
+
module LineEditor
|
|
3
|
+
class Basic
|
|
4
|
+
attr_reader :prompt, :options
|
|
5
|
+
|
|
6
|
+
def self.available?
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(prompt, options)
|
|
11
|
+
@prompt = prompt
|
|
12
|
+
@options = options
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def readline
|
|
16
|
+
$stdout.print(prompt)
|
|
17
|
+
get_input
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def get_input
|
|
23
|
+
if echo?
|
|
24
|
+
$stdin.gets
|
|
25
|
+
else
|
|
26
|
+
$stdin.noecho(&:gets)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def echo?
|
|
31
|
+
options.fetch(:echo, true)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require "readline"
|
|
3
|
+
rescue LoadError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class Thor
|
|
7
|
+
module LineEditor
|
|
8
|
+
class Readline < Basic
|
|
9
|
+
def self.available?
|
|
10
|
+
Object.const_defined?(:Readline)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def readline
|
|
14
|
+
if echo?
|
|
15
|
+
::Readline.completion_append_character = nil
|
|
16
|
+
# Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil.
|
|
17
|
+
if complete = completion_proc
|
|
18
|
+
::Readline.completion_proc = complete
|
|
19
|
+
end
|
|
20
|
+
::Readline.readline(prompt, add_to_history?)
|
|
21
|
+
else
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def add_to_history?
|
|
29
|
+
options.fetch(:add_to_history, true)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def completion_proc
|
|
33
|
+
if use_path_completion?
|
|
34
|
+
proc { |text| PathCompletion.new(text).matches }
|
|
35
|
+
elsif completion_options.any?
|
|
36
|
+
proc do |text|
|
|
37
|
+
completion_options.select { |option| option.start_with?(text) }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def completion_options
|
|
43
|
+
options.fetch(:limited_to, [])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def use_path_completion?
|
|
47
|
+
options.fetch(:path, false)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class PathCompletion
|
|
51
|
+
attr_reader :text
|
|
52
|
+
private :text
|
|
53
|
+
|
|
54
|
+
def initialize(text)
|
|
55
|
+
@text = text
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def matches
|
|
59
|
+
relative_matches
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def relative_matches
|
|
65
|
+
absolute_matches.map { |path| path.sub(base_path, "") }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def absolute_matches
|
|
69
|
+
Dir[glob_pattern].map do |path|
|
|
70
|
+
if File.directory?(path)
|
|
71
|
+
"#{path}/"
|
|
72
|
+
else
|
|
73
|
+
path
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def glob_pattern
|
|
79
|
+
"#{base_path}#{text}*"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def base_path
|
|
83
|
+
"#{Dir.pwd}/"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "thor/line_editor/basic"
|
|
2
|
+
require "thor/line_editor/readline"
|
|
3
|
+
|
|
4
|
+
class Thor
|
|
5
|
+
module LineEditor
|
|
6
|
+
def self.readline(prompt, options = {})
|
|
7
|
+
best_available.new(prompt, options).readline
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.best_available
|
|
11
|
+
[
|
|
12
|
+
Thor::LineEditor::Readline,
|
|
13
|
+
Thor::LineEditor::Basic
|
|
14
|
+
].detect(&:available?)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|