wycats-thor 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/thor.rb +1 -4
- data/lib/thor/actions.rb +102 -80
- data/lib/thor/actions/copy_file.rb +5 -5
- data/lib/thor/actions/create_file.rb +5 -5
- data/lib/thor/actions/directory.rb +15 -19
- data/lib/thor/actions/empty_directory.rb +5 -5
- data/lib/thor/actions/get.rb +5 -5
- data/lib/thor/actions/inject_into_file.rb +13 -13
- data/lib/thor/actions/template.rb +5 -5
- data/lib/thor/actions/templater.rb +8 -9
- data/lib/thor/base.rb +38 -43
- data/lib/thor/group.rb +170 -2
- data/lib/thor/invocation.rb +50 -19
- data/lib/thor/parser/option.rb +10 -3
- data/lib/thor/parser/options.rb +10 -3
- metadata +2 -2
data/lib/thor.rb
CHANGED
@@ -4,9 +4,7 @@ require 'thor/group'
|
|
4
4
|
require 'thor/actions'
|
5
5
|
|
6
6
|
class Thor
|
7
|
-
|
8
7
|
class << self
|
9
|
-
|
10
8
|
# Sets the default task when thor is executed without an explicit task to be called.
|
11
9
|
#
|
12
10
|
# ==== Parameters
|
@@ -108,7 +106,7 @@ class Thor
|
|
108
106
|
# :group - The group for this options. Use by class options to output options in different levels.
|
109
107
|
# :banner - String to show on usage notes.
|
110
108
|
#
|
111
|
-
def method_option(name, options)
|
109
|
+
def method_option(name, options={})
|
112
110
|
scope = if options[:for]
|
113
111
|
find_and_refresh_task(options[:for]).options
|
114
112
|
else
|
@@ -220,7 +218,6 @@ class Thor
|
|
220
218
|
meth = mapping || meth || default_task
|
221
219
|
meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
|
222
220
|
end
|
223
|
-
|
224
221
|
end
|
225
222
|
|
226
223
|
include Thor::Base
|
data/lib/thor/actions.rb
CHANGED
@@ -5,17 +5,13 @@ Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action|
|
|
5
5
|
end
|
6
6
|
|
7
7
|
class Thor
|
8
|
-
# Some actions require that a class method called source root is defined in
|
9
|
-
# the class. Remember to always cache the source root value, because Ruby
|
10
|
-
# __FILE__ always return the relative path, which may lead to mistakes if you
|
11
|
-
# are calling an action inside the "inside(path)" method.
|
12
|
-
#
|
13
8
|
module Actions
|
14
9
|
attr_accessor :behavior
|
15
10
|
|
16
11
|
# On inclusion, add some options to base.
|
17
12
|
#
|
18
13
|
def self.included(base) #:nodoc:
|
14
|
+
base.extend ClassMethods
|
19
15
|
return unless base.respond_to?(:class_option)
|
20
16
|
|
21
17
|
base.class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
|
@@ -31,6 +27,32 @@ class Thor
|
|
31
27
|
:desc => "Supress status output"
|
32
28
|
end
|
33
29
|
|
30
|
+
module ClassMethods
|
31
|
+
# Hold source paths for one Thor instance. source_paths_for_search is the
|
32
|
+
# method responsible to gather source_paths from this current class,
|
33
|
+
# inherited paths and the source root.
|
34
|
+
#
|
35
|
+
def source_paths
|
36
|
+
@source_paths ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the source paths in the following order:
|
40
|
+
#
|
41
|
+
# 1) This class source paths
|
42
|
+
# 2) Source root
|
43
|
+
# 3) Parents source paths
|
44
|
+
#
|
45
|
+
def source_paths_for_search
|
46
|
+
@source_paths_for_search ||= begin
|
47
|
+
paths = []
|
48
|
+
paths += self.source_paths
|
49
|
+
paths << self.source_root if self.respond_to?(:source_root)
|
50
|
+
paths += from_superclass(:source_paths, [])
|
51
|
+
paths
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
34
56
|
# Extends initializer to add more configuration options.
|
35
57
|
#
|
36
58
|
# ==== Configuration
|
@@ -38,8 +60,8 @@ class Thor
|
|
38
60
|
# It also accepts :force, :skip and :pretend to set the behavior
|
39
61
|
# and the respective option.
|
40
62
|
#
|
41
|
-
#
|
42
|
-
#
|
63
|
+
# destination_root<String>:: The root directory needed for some actions. It's also known
|
64
|
+
# as destination root.
|
43
65
|
#
|
44
66
|
def initialize(args=[], options={}, config={})
|
45
67
|
self.behavior = case config[:behavior].to_s
|
@@ -53,7 +75,7 @@ class Thor
|
|
53
75
|
end
|
54
76
|
|
55
77
|
super
|
56
|
-
self.
|
78
|
+
self.destination_root = config[:destination_root]
|
57
79
|
end
|
58
80
|
|
59
81
|
# Wraps an action object and call it accordingly to the thor class behavior.
|
@@ -68,44 +90,43 @@ class Thor
|
|
68
90
|
|
69
91
|
# Returns the root for this thor class (also aliased as destination root).
|
70
92
|
#
|
71
|
-
def
|
72
|
-
@
|
93
|
+
def destination_root
|
94
|
+
@destination_stack.last
|
73
95
|
end
|
74
|
-
alias :destination_root :root
|
75
96
|
|
76
97
|
# Sets the root for this thor class. Relatives path are added to the
|
77
98
|
# directory where the script was invoked and expanded.
|
78
99
|
#
|
79
|
-
def
|
80
|
-
@
|
81
|
-
@
|
82
|
-
end
|
83
|
-
|
84
|
-
# Gets the current root relative to the absolute root.
|
85
|
-
#
|
86
|
-
# inside "foo" do
|
87
|
-
# relative_root #=> "foo"
|
88
|
-
# end
|
89
|
-
#
|
90
|
-
def relative_root(remove_dot=true)
|
91
|
-
relative_to_absolute_root(root, remove_dot)
|
100
|
+
def destination_root=(root)
|
101
|
+
@destination_stack ||= []
|
102
|
+
@destination_stack[0] = File.expand_path(root || '')
|
92
103
|
end
|
93
104
|
|
94
105
|
# Returns the given path relative to the absolute root (ie, root where
|
95
106
|
# the script started).
|
96
107
|
#
|
97
|
-
def
|
98
|
-
path = path.gsub(@
|
108
|
+
def relative_to_original_destination_root(path, remove_dot=true)
|
109
|
+
path = path.gsub(@destination_stack[0], '.')
|
99
110
|
remove_dot ? (path[2..-1] || '') : path
|
100
111
|
end
|
101
112
|
|
102
|
-
#
|
103
|
-
# not specified in the thor class.
|
113
|
+
# Receives a file or directory and search for it in the source paths.
|
104
114
|
#
|
105
|
-
def
|
106
|
-
|
107
|
-
|
108
|
-
|
115
|
+
def find_in_source_paths(file)
|
116
|
+
relative_root = relative_to_original_destination_root(destination_root, false)
|
117
|
+
paths = self.class.source_paths_for_search
|
118
|
+
|
119
|
+
paths.each do |source|
|
120
|
+
source_file = File.expand_path(file, File.join(source, relative_root))
|
121
|
+
return source_file if File.exists?(source_file)
|
122
|
+
end
|
123
|
+
|
124
|
+
if paths.empty?
|
125
|
+
raise Error, "You don't have any source path defined for class #{self.class.name}. To fix this, " <<
|
126
|
+
"you can define a source_root in your class."
|
127
|
+
else
|
128
|
+
raise Error, "Could not find #{file.inspect} in source paths."
|
129
|
+
end
|
109
130
|
end
|
110
131
|
|
111
132
|
# Do something in the root or on a provided subfolder. If a relative path
|
@@ -117,16 +138,25 @@ class Thor
|
|
117
138
|
# dir<String>:: the directory to move to.
|
118
139
|
#
|
119
140
|
def inside(dir='', &block)
|
120
|
-
@
|
121
|
-
FileUtils.mkdir_p(
|
122
|
-
FileUtils.cd(
|
123
|
-
@
|
141
|
+
@destination_stack.push File.expand_path(dir, destination_root)
|
142
|
+
FileUtils.mkdir_p(destination_root) unless File.exist?(destination_root)
|
143
|
+
FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
|
144
|
+
@destination_stack.pop
|
145
|
+
end
|
146
|
+
|
147
|
+
# Same as inside, but log status and use padding.
|
148
|
+
#
|
149
|
+
def inside_with_padding(dir='', config={}, &block)
|
150
|
+
say_status :inside, dir, config.fetch(:verbose, true)
|
151
|
+
shell.padding += 1
|
152
|
+
inside(dir, &block)
|
153
|
+
shell.padding -= 1
|
124
154
|
end
|
125
155
|
|
126
156
|
# Goes to the root and execute the given block.
|
127
157
|
#
|
128
158
|
def in_root
|
129
|
-
inside(@
|
159
|
+
inside(@destination_stack.first) { yield }
|
130
160
|
end
|
131
161
|
|
132
162
|
# Changes the mode of the given file or directory.
|
@@ -134,17 +164,16 @@ class Thor
|
|
134
164
|
# ==== Parameters
|
135
165
|
# mode<Integer>:: the file mode
|
136
166
|
# path<String>:: the name of the file to change mode
|
137
|
-
#
|
138
|
-
# If a symbol is given, uses it as the output color.
|
167
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
139
168
|
#
|
140
169
|
# ==== Example
|
141
170
|
#
|
142
171
|
# chmod "script/*", 0755
|
143
172
|
#
|
144
|
-
def chmod(path, mode,
|
173
|
+
def chmod(path, mode, config={})
|
145
174
|
return unless behavior == :invoke
|
146
|
-
path = File.expand_path(path,
|
147
|
-
say_status :chmod,
|
175
|
+
path = File.expand_path(path, destination_root)
|
176
|
+
say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
148
177
|
FileUtils.chmod_R(mode, path) unless options[:pretend]
|
149
178
|
end
|
150
179
|
|
@@ -152,8 +181,7 @@ class Thor
|
|
152
181
|
#
|
153
182
|
# ==== Parameters
|
154
183
|
# command<String>:: the command to be executed.
|
155
|
-
#
|
156
|
-
# If a symbol is given, uses it as the output color.
|
184
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
157
185
|
#
|
158
186
|
# ==== Example
|
159
187
|
#
|
@@ -161,9 +189,10 @@ class Thor
|
|
161
189
|
# run('ln -s ~/edge rails')
|
162
190
|
# end
|
163
191
|
#
|
164
|
-
def run(command,
|
192
|
+
def run(command, config={})
|
165
193
|
return unless behavior == :invoke
|
166
|
-
|
194
|
+
description = "#{command.inspect} from #{relative_to_original_destination_root(destination_root, false)}"
|
195
|
+
say_status :run, description, config.fetch(:verbose, true)
|
167
196
|
`#{command}` unless options[:pretend]
|
168
197
|
end
|
169
198
|
|
@@ -171,12 +200,11 @@ class Thor
|
|
171
200
|
#
|
172
201
|
# ==== Parameters
|
173
202
|
# command<String>:: the command to be executed.
|
174
|
-
#
|
175
|
-
# If a symbol is given, uses it as the output color.
|
203
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
176
204
|
#
|
177
|
-
def run_ruby_script(command,
|
205
|
+
def run_ruby_script(command, config={})
|
178
206
|
return unless behavior == :invoke
|
179
|
-
say_status File.basename(Thor::Util.ruby_command), command,
|
207
|
+
say_status File.basename(Thor::Util.ruby_command), command, config.fetch(:verbose, true)
|
180
208
|
`#{Thor::Util.ruby_command} #{command}` unless options[:pretend]
|
181
209
|
end
|
182
210
|
|
@@ -186,9 +214,8 @@ class Thor
|
|
186
214
|
# ==== Parameters
|
187
215
|
# task<String>:: the task to be invoked
|
188
216
|
# args<Array>:: arguments to the task
|
189
|
-
# options<Hash>::
|
190
|
-
#
|
191
|
-
# If a symbol is given, uses it as the output color.
|
217
|
+
# options<Hash>:: give :verbose => false to not log the status. Other options
|
218
|
+
# are given as parameter to Thor.
|
192
219
|
#
|
193
220
|
# ==== Examples
|
194
221
|
#
|
@@ -199,35 +226,33 @@ class Thor
|
|
199
226
|
# #=> thor list --all --substring=rails
|
200
227
|
#
|
201
228
|
def thor(task, *args)
|
202
|
-
|
203
|
-
|
229
|
+
config = args.last.is_a?(Hash) ? args.pop : {}
|
230
|
+
verbose = config.key?(:verbose) ? config.delete(:verbose) : true
|
204
231
|
|
205
232
|
args.unshift task
|
206
|
-
args.push Thor::Options.to_switches(
|
233
|
+
args.push Thor::Options.to_switches(config)
|
207
234
|
command = args.join(' ').strip
|
208
235
|
|
209
|
-
say_status :thor, command,
|
210
|
-
run "thor #{command}", false
|
236
|
+
say_status :thor, command, verbose
|
237
|
+
run "thor #{command}", :verbose => false
|
211
238
|
end
|
212
239
|
|
213
240
|
# Removes a file at the given location.
|
214
241
|
#
|
215
242
|
# ==== Parameters
|
216
243
|
# path<String>:: path of the file to be changed
|
217
|
-
#
|
218
|
-
# If a symbol is given, uses it as the output color.
|
244
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
219
245
|
#
|
220
246
|
# ==== Example
|
221
247
|
#
|
222
248
|
# remove_file 'README'
|
223
249
|
# remove_file 'app/controllers/application_controller.rb'
|
224
250
|
#
|
225
|
-
def remove_file(path,
|
251
|
+
def remove_file(path, config={})
|
226
252
|
return unless behavior == :invoke
|
227
|
-
path = File.expand_path(path,
|
228
|
-
color = log_status.is_a?(Symbol) ? log_status : :red
|
253
|
+
path = File.expand_path(path, destination_root)
|
229
254
|
|
230
|
-
say_status :remove,
|
255
|
+
say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
231
256
|
::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path)
|
232
257
|
end
|
233
258
|
|
@@ -237,8 +262,7 @@ class Thor
|
|
237
262
|
# path<String>:: path of the file to be changed
|
238
263
|
# flag<Regexp|String>:: the regexp or string to be replaced
|
239
264
|
# replacement<String>:: the replacement, can be also given as a block
|
240
|
-
#
|
241
|
-
# If a symbol is given, uses it as the output color.
|
265
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
242
266
|
#
|
243
267
|
# ==== Example
|
244
268
|
#
|
@@ -250,10 +274,10 @@ class Thor
|
|
250
274
|
#
|
251
275
|
def gsub_file(path, flag, *args, &block)
|
252
276
|
return unless behavior == :invoke
|
253
|
-
|
277
|
+
config = args.last.is_a?(Hash) ? args.pop : {}
|
254
278
|
|
255
|
-
path = File.expand_path(path,
|
256
|
-
say_status :gsub,
|
279
|
+
path = File.expand_path(path, destination_root)
|
280
|
+
say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
257
281
|
|
258
282
|
unless options[:pretend]
|
259
283
|
content = File.read(path)
|
@@ -267,17 +291,16 @@ class Thor
|
|
267
291
|
# ==== Parameters
|
268
292
|
# path<String>:: path of the file to be changed
|
269
293
|
# data<String>:: the data to append to the file, can be also given as a block.
|
270
|
-
#
|
271
|
-
# If a symbol is given, uses it as the output color.
|
294
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
272
295
|
#
|
273
296
|
# ==== Example
|
274
297
|
#
|
275
298
|
# append_file 'config/environments/test.rb', 'config.gem "rspec"'
|
276
299
|
#
|
277
|
-
def append_file(path, data=nil,
|
300
|
+
def append_file(path, data=nil, config={}, &block)
|
278
301
|
return unless behavior == :invoke
|
279
|
-
path = File.expand_path(path,
|
280
|
-
say_status :append,
|
302
|
+
path = File.expand_path(path, destination_root)
|
303
|
+
say_status :append, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
281
304
|
File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend]
|
282
305
|
end
|
283
306
|
|
@@ -286,17 +309,16 @@ class Thor
|
|
286
309
|
# ==== Parameters
|
287
310
|
# path<String>:: path of the file to be changed
|
288
311
|
# data<String>:: the data to prepend to the file, can be also given as a block.
|
289
|
-
#
|
290
|
-
# If a symbol is given, uses it as the output color.
|
312
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
291
313
|
#
|
292
314
|
# ==== Example
|
293
315
|
#
|
294
316
|
# prepend_file 'config/environments/test.rb', 'config.gem "rspec"'
|
295
317
|
#
|
296
|
-
def prepend_file(path, data=nil,
|
318
|
+
def prepend_file(path, data=nil, config={}, &block)
|
297
319
|
return unless behavior == :invoke
|
298
|
-
path = File.expand_path(path,
|
299
|
-
say_status :prepend,
|
320
|
+
path = File.expand_path(path, destination_root)
|
321
|
+
say_status :prepend, relative_to_original_destination_root(path), config.fetch(:verbose, true)
|
300
322
|
|
301
323
|
unless options[:pretend]
|
302
324
|
content = data || block.call
|
@@ -310,7 +332,7 @@ class Thor
|
|
310
332
|
# Allow current root to be shared between invocations.
|
311
333
|
#
|
312
334
|
def _shared_configuration #:nodoc:
|
313
|
-
super.merge!(:
|
335
|
+
super.merge!(:destination_root => self.destination_root)
|
314
336
|
end
|
315
337
|
|
316
338
|
def _cleanup_options_and_set(options, key) #:nodoc:
|
@@ -7,9 +7,9 @@ class Thor
|
|
7
7
|
# the destination is not given it's assumed to be equal to the source.
|
8
8
|
#
|
9
9
|
# ==== Parameters
|
10
|
-
# source<String>:: the relative path to the source root
|
11
|
-
# destination<String>:: the relative path to the destination root
|
12
|
-
#
|
10
|
+
# source<String>:: the relative path to the source root.
|
11
|
+
# destination<String>:: the relative path to the destination root.
|
12
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
13
13
|
#
|
14
14
|
# ==== Examples
|
15
15
|
#
|
@@ -17,8 +17,8 @@ class Thor
|
|
17
17
|
#
|
18
18
|
# copy_file "doc/README"
|
19
19
|
#
|
20
|
-
def copy_file(source, destination=nil,
|
21
|
-
action CopyFile.new(self, source, destination || source,
|
20
|
+
def copy_file(source, destination=nil, config={})
|
21
|
+
action CopyFile.new(self, source, destination || source, config)
|
22
22
|
end
|
23
23
|
|
24
24
|
class CopyFile < Templater #:nodoc:
|
@@ -9,7 +9,7 @@ class Thor
|
|
9
9
|
# ==== Parameters
|
10
10
|
# destination<String>:: the relative path to the destination root.
|
11
11
|
# data<String|NilClass>:: the data to append to the file.
|
12
|
-
#
|
12
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
13
13
|
#
|
14
14
|
# ==== Examples
|
15
15
|
#
|
@@ -20,8 +20,8 @@ class Thor
|
|
20
20
|
#
|
21
21
|
# create_file "config/apach.conf", "your apache config"
|
22
22
|
#
|
23
|
-
def create_file(destination, data=nil,
|
24
|
-
action CreateFile.new(self, destination, block || data.to_s,
|
23
|
+
def create_file(destination, data=nil, config={}, &block)
|
24
|
+
action CreateFile.new(self, destination, block || data.to_s, config)
|
25
25
|
end
|
26
26
|
alias :add_file :create_file
|
27
27
|
|
@@ -31,8 +31,8 @@ class Thor
|
|
31
31
|
class CreateFile < Templater #:nodoc:
|
32
32
|
attr_reader :data
|
33
33
|
|
34
|
-
def initialize(base, destination, data,
|
35
|
-
super(base, nil, destination,
|
34
|
+
def initialize(base, destination, data, config={})
|
35
|
+
super(base, nil, destination, config)
|
36
36
|
@data = data
|
37
37
|
end
|
38
38
|
|
@@ -30,31 +30,27 @@ class Thor
|
|
30
30
|
# blog.rb
|
31
31
|
#
|
32
32
|
# ==== Parameters
|
33
|
-
# source<String>:: the relative path to the source root
|
34
|
-
# destination<String>:: the relative path to the destination root
|
35
|
-
#
|
36
|
-
#
|
33
|
+
# source<String>:: the relative path to the source root.
|
34
|
+
# destination<String>:: the relative path to the destination root.
|
35
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
36
|
+
# If :recursive => false, does not look for paths recursively.
|
37
37
|
#
|
38
38
|
# ==== Examples
|
39
39
|
#
|
40
40
|
# directory "doc"
|
41
|
-
# directory "doc", "docs", false
|
41
|
+
# directory "doc", "docs", :recursive => false
|
42
42
|
#
|
43
|
-
def directory(source, destination=nil,
|
44
|
-
action Directory.new(self, source, destination || source,
|
43
|
+
def directory(source, destination=nil, config={})
|
44
|
+
action Directory.new(self, source, destination || source, config)
|
45
45
|
end
|
46
46
|
|
47
47
|
class Directory < Templater #:nodoc:
|
48
|
-
|
49
|
-
|
50
|
-
def initialize(base, source, destination=nil, recursive=true, log_status=true)
|
51
|
-
@recursive = recursive
|
52
|
-
super(base, source, destination, log_status)
|
48
|
+
def initialize(base, source, destination=nil, config={})
|
49
|
+
super(base, source, destination, { :recursive => true }.merge(config))
|
53
50
|
end
|
54
51
|
|
55
52
|
def invoke!
|
56
|
-
|
57
|
-
base.empty_directory given_destination, @log_status
|
53
|
+
base.empty_directory given_destination, config
|
58
54
|
execute!
|
59
55
|
end
|
60
56
|
|
@@ -65,20 +61,20 @@ class Thor
|
|
65
61
|
protected
|
66
62
|
|
67
63
|
def execute!
|
68
|
-
lookup = recursive ? File.join(source, '**') : source
|
64
|
+
lookup = config[:recursive] ? File.join(source, '**') : source
|
69
65
|
lookup = File.join(lookup, '{*,.[a-z]*}')
|
70
66
|
|
71
67
|
Dir[lookup].each do |file_source|
|
72
|
-
file_destination = File.join(given_destination, file_source.gsub(source, '.'))
|
73
68
|
next if File.directory?(file_source)
|
69
|
+
file_destination = File.join(given_destination, file_source.gsub(source, '.'))
|
74
70
|
|
75
71
|
case file_source
|
76
72
|
when /\.empty_directory$/
|
77
|
-
base.empty_directory(File.dirname(file_destination),
|
73
|
+
base.empty_directory(File.dirname(file_destination), config)
|
78
74
|
when /\.tt$/
|
79
|
-
base.template(file_source, file_destination[0..-4],
|
75
|
+
base.template(file_source, file_destination[0..-4], config)
|
80
76
|
else
|
81
|
-
base.copy_file(file_source, file_destination,
|
77
|
+
base.copy_file(file_source, file_destination, config)
|
82
78
|
end
|
83
79
|
end
|
84
80
|
end
|
@@ -6,21 +6,21 @@ class Thor
|
|
6
6
|
# Creates an empty directory.
|
7
7
|
#
|
8
8
|
# ==== Parameters
|
9
|
-
# destination<String>:: the relative path to the destination root
|
10
|
-
#
|
9
|
+
# destination<String>:: the relative path to the destination root.
|
10
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
11
11
|
#
|
12
12
|
# ==== Examples
|
13
13
|
#
|
14
14
|
# empty_directory "doc"
|
15
15
|
#
|
16
|
-
def empty_directory(destination,
|
17
|
-
action EmptyDirectory.new(self, nil, destination,
|
16
|
+
def empty_directory(destination, config={})
|
17
|
+
action EmptyDirectory.new(self, nil, destination, config)
|
18
18
|
end
|
19
19
|
|
20
20
|
class EmptyDirectory < Templater #:nodoc:
|
21
21
|
|
22
22
|
def invoke!
|
23
|
-
invoke_with_options!(base.options) do
|
23
|
+
invoke_with_options!(base.options.merge(config)) do
|
24
24
|
::FileUtils.mkdir_p(destination)
|
25
25
|
end
|
26
26
|
end
|
data/lib/thor/actions/get.rb
CHANGED
@@ -9,9 +9,9 @@ class Thor
|
|
9
9
|
# the url is yielded and used as location.
|
10
10
|
#
|
11
11
|
# ==== Parameters
|
12
|
-
# source<String>:: the address of the given content
|
13
|
-
# destination<String>:: the relative path to the destination root
|
14
|
-
#
|
12
|
+
# source<String>:: the address of the given content.
|
13
|
+
# destination<String>:: the relative path to the destination root.
|
14
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
15
15
|
#
|
16
16
|
# ==== Examples
|
17
17
|
#
|
@@ -21,8 +21,8 @@ class Thor
|
|
21
21
|
# content.split("\n").first
|
22
22
|
# end
|
23
23
|
#
|
24
|
-
def get(source, destination=nil,
|
25
|
-
action Get.new(self, source, block || destination,
|
24
|
+
def get(source, destination=nil, config={}, &block)
|
25
|
+
action Get.new(self, source, block || destination, config)
|
26
26
|
end
|
27
27
|
|
28
28
|
class Get < Templater #:nodoc:
|
@@ -9,9 +9,8 @@ class Thor
|
|
9
9
|
# ==== Parameters
|
10
10
|
# destination<String>:: Relative path to the destination root
|
11
11
|
# data<String>:: Data to add to the file. Can be given as a block.
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# If a symbol is given, uses it as the output color.
|
12
|
+
# config<Hash>:: give :verbose => false to not log the status and the flag
|
13
|
+
# for injection (:after or :before).
|
15
14
|
#
|
16
15
|
# ==== Examples
|
17
16
|
#
|
@@ -24,28 +23,29 @@ class Thor
|
|
24
23
|
#
|
25
24
|
def inject_into_file(destination, *args, &block)
|
26
25
|
if block_given?
|
27
|
-
data,
|
26
|
+
data, config = block, args.shift
|
28
27
|
else
|
29
|
-
data,
|
28
|
+
data, config = args.shift, args.shift
|
30
29
|
end
|
31
30
|
|
32
31
|
log_status = args.empty? || args.pop
|
33
|
-
action InjectIntoFile.new(self, destination, data,
|
32
|
+
action InjectIntoFile.new(self, destination, data, config)
|
34
33
|
end
|
35
34
|
|
36
35
|
class InjectIntoFile #:nodoc:
|
37
|
-
attr_reader :base, :destination, :relative_destination, :flag, :replacement
|
36
|
+
attr_reader :base, :destination, :relative_destination, :flag, :replacement, :config
|
38
37
|
|
39
|
-
def initialize(base, destination, data,
|
40
|
-
@base, @
|
41
|
-
behavior, @flag = flag.keys.first, flag.values.first
|
38
|
+
def initialize(base, destination, data, config)
|
39
|
+
@base, @config = base, { :verbose => true }.merge(config)
|
42
40
|
|
43
41
|
self.destination = destination
|
44
42
|
data = data.call if data.is_a?(Proc)
|
45
43
|
|
46
|
-
@replacement = if
|
44
|
+
@replacement = if @config.key?(:after)
|
45
|
+
@flag = @config.delete(:after)
|
47
46
|
@flag + data
|
48
47
|
else
|
48
|
+
@flag = @config.delete(:before)
|
49
49
|
data + @flag
|
50
50
|
end
|
51
51
|
end
|
@@ -68,14 +68,14 @@ class Thor
|
|
68
68
|
def destination=(destination)
|
69
69
|
if destination
|
70
70
|
@destination = ::File.expand_path(destination.to_s, base.destination_root)
|
71
|
-
@relative_destination = base.
|
71
|
+
@relative_destination = base.relative_to_original_destination_root(@destination)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
75
|
# Shortcut to say_status shell method.
|
76
76
|
#
|
77
77
|
def say_status(status)
|
78
|
-
base.shell.say_status status, relative_destination,
|
78
|
+
base.shell.say_status status, relative_destination, config[:verbose]
|
79
79
|
end
|
80
80
|
|
81
81
|
# Adds the content to the file.
|
@@ -9,9 +9,9 @@ class Thor
|
|
9
9
|
# to be equal to the source removing .tt from the filename.
|
10
10
|
#
|
11
11
|
# ==== Parameters
|
12
|
-
# source<String>:: the relative path to the source root
|
13
|
-
# destination<String>:: the relative path to the destination root
|
14
|
-
#
|
12
|
+
# source<String>:: the relative path to the source root.
|
13
|
+
# destination<String>:: the relative path to the destination root.
|
14
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
15
15
|
#
|
16
16
|
# ==== Examples
|
17
17
|
#
|
@@ -19,9 +19,9 @@ class Thor
|
|
19
19
|
#
|
20
20
|
# template "doc/README"
|
21
21
|
#
|
22
|
-
def template(source, destination=nil,
|
22
|
+
def template(source, destination=nil, config={})
|
23
23
|
destination ||= source.gsub(/.tt$/, '')
|
24
|
-
action Template.new(self, source, destination,
|
24
|
+
action Template.new(self, source, destination, config)
|
25
25
|
end
|
26
26
|
|
27
27
|
class Template < Templater #:nodoc:
|
@@ -8,7 +8,7 @@ class Thor
|
|
8
8
|
# by Jonas Nicklas and Michael S. Klishin under MIT LICENSE.
|
9
9
|
#
|
10
10
|
class Templater #:nodoc:
|
11
|
-
attr_reader :base, :source, :destination, :given_destination, :relative_destination
|
11
|
+
attr_reader :base, :source, :destination, :given_destination, :relative_destination, :config
|
12
12
|
|
13
13
|
# Initializes given the source and destination.
|
14
14
|
#
|
@@ -16,11 +16,10 @@ class Thor
|
|
16
16
|
# base<Thor::Base>:: A Thor::Base instance
|
17
17
|
# source<String>:: Relative path to the source of this file
|
18
18
|
# destination<String>:: Relative path to the destination of this file
|
19
|
-
#
|
20
|
-
# Templater log status does not accept color.
|
19
|
+
# config<Hash>:: give :verbose => false to not log the status.
|
21
20
|
#
|
22
|
-
def initialize(base, source, destination,
|
23
|
-
@base, @
|
21
|
+
def initialize(base, source, destination, config={})
|
22
|
+
@base, @config = base, { :verbose => true }.merge(config)
|
24
23
|
self.source = source
|
25
24
|
self.destination = destination
|
26
25
|
end
|
@@ -56,7 +55,7 @@ class Thor
|
|
56
55
|
# but you can modify in the subclass.
|
57
56
|
#
|
58
57
|
def invoke!
|
59
|
-
invoke_with_options!(base.options) do
|
58
|
+
invoke_with_options!(base.options.merge(config)) do
|
60
59
|
::FileUtils.mkdir_p(::File.dirname(destination))
|
61
60
|
::File.open(destination, 'w'){ |f| f.write render }
|
62
61
|
end
|
@@ -101,7 +100,7 @@ class Thor
|
|
101
100
|
#
|
102
101
|
def source=(source)
|
103
102
|
if source
|
104
|
-
@source = ::File.expand_path(source.to_s
|
103
|
+
@source = ::File.expand_path(base.find_in_source_paths(source.to_s))
|
105
104
|
end
|
106
105
|
end
|
107
106
|
|
@@ -123,7 +122,7 @@ class Thor
|
|
123
122
|
if destination
|
124
123
|
@given_destination = convert_encoded_instructions(destination.to_s)
|
125
124
|
@destination = ::File.expand_path(@given_destination, base.destination_root)
|
126
|
-
@relative_destination = base.
|
125
|
+
@relative_destination = base.relative_to_original_destination_root(@destination)
|
127
126
|
end
|
128
127
|
end
|
129
128
|
|
@@ -187,7 +186,7 @@ class Thor
|
|
187
186
|
# Shortcut to say_status shell method.
|
188
187
|
#
|
189
188
|
def say_status(status, color)
|
190
|
-
base.shell.say_status status, relative_destination, color if
|
189
|
+
base.shell.say_status status, relative_destination, color if config[:verbose]
|
191
190
|
end
|
192
191
|
|
193
192
|
end
|
data/lib/thor/base.rb
CHANGED
@@ -9,7 +9,7 @@ require 'thor/util'
|
|
9
9
|
|
10
10
|
class Thor
|
11
11
|
HELP_MAPPINGS = %w(-h -? --help -D)
|
12
|
-
THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
|
12
|
+
THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root)
|
13
13
|
|
14
14
|
module Base
|
15
15
|
attr_accessor :options
|
@@ -37,15 +37,17 @@ class Thor
|
|
37
37
|
|
38
38
|
parse_options = self.class.class_options
|
39
39
|
|
40
|
-
|
40
|
+
if options.is_a?(Array)
|
41
41
|
task_options = config.delete(:task_options) # hook for start
|
42
42
|
parse_options = parse_options.merge(task_options) if task_options
|
43
|
-
|
43
|
+
array_options, hash_options = options, {}
|
44
44
|
else
|
45
|
-
|
45
|
+
array_options, hash_options = [], options
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
options = Thor::Options.parse(parse_options, array_options)
|
49
|
+
self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).merge!(hash_options)
|
50
|
+
self.options.freeze
|
49
51
|
end
|
50
52
|
|
51
53
|
class << self
|
@@ -75,14 +77,10 @@ class Thor
|
|
75
77
|
|
76
78
|
# Whenever a class inherits from Thor or Thor::Group, we should track the
|
77
79
|
# class and the file on Thor::Base. This is the method responsable for it.
|
78
|
-
# Also
|
79
|
-
# to ensure that the source_root does not change after FileUtils#cd is
|
80
|
-
# called.
|
80
|
+
# Also adds the source root to the source paths if the klass respond to it.
|
81
81
|
#
|
82
82
|
def register_klass_file(klass) #:nodoc:
|
83
83
|
file = caller[1].match(/(.*):\d+/)[1]
|
84
|
-
|
85
|
-
klass.source_root if klass.respond_to?(:source_root)
|
86
84
|
Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass)
|
87
85
|
|
88
86
|
file_subclasses = Thor::Base.subclass_files[File.expand_path(file)]
|
@@ -189,7 +187,7 @@ class Thor
|
|
189
187
|
# :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
|
190
188
|
# :banner - String to show on usage notes.
|
191
189
|
#
|
192
|
-
def class_option(name, options)
|
190
|
+
def class_option(name, options={})
|
193
191
|
build_option(name, options, class_options)
|
194
192
|
end
|
195
193
|
|
@@ -359,51 +357,48 @@ class Thor
|
|
359
357
|
# Prints the class options per group. If an option does not belong to
|
360
358
|
# any group, it uses the ungrouped name value. This method provide to
|
361
359
|
# hooks to add extra options, one of them if the third argument called
|
362
|
-
# extra_group that should be a
|
363
|
-
# :group => Array[Options].
|
360
|
+
# extra_group that should be a hash in the format :group => Array[Options].
|
364
361
|
#
|
365
362
|
# The second is by returning a lamda used to print values. The lambda
|
366
363
|
# requires two options: the group name and the array of options.
|
367
364
|
#
|
368
365
|
def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc:
|
369
|
-
|
370
|
-
groups = {}
|
371
|
-
|
372
|
-
class_options.each do |_, value|
|
373
|
-
groups[value.group] ||= []
|
374
|
-
groups[value.group] << value
|
375
|
-
end
|
366
|
+
groups = {}
|
376
367
|
|
377
|
-
|
378
|
-
|
379
|
-
|
368
|
+
class_options.each do |_, value|
|
369
|
+
groups[value.group] ||= []
|
370
|
+
groups[value.group] << value
|
371
|
+
end
|
380
372
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
end
|
373
|
+
printer = proc do |group_name, options|
|
374
|
+
list = []
|
375
|
+
padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
|
385
376
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
shell.say "Options:"
|
391
|
-
end
|
377
|
+
options.each do |option|
|
378
|
+
list << [ option.usage(padding), option.description || "" ]
|
379
|
+
list << [ "", "Default: #{option.default}" ] if option.show_default?
|
380
|
+
end
|
392
381
|
|
393
|
-
|
394
|
-
|
382
|
+
unless list.empty?
|
383
|
+
if group_name
|
384
|
+
shell.say "#{group_name} options:"
|
385
|
+
else
|
386
|
+
shell.say "Options:"
|
395
387
|
end
|
388
|
+
|
389
|
+
shell.print_table(list, :emphasize_last => true, :ident => 2)
|
390
|
+
shell.say ""
|
396
391
|
end
|
392
|
+
end
|
397
393
|
|
398
|
-
|
399
|
-
|
400
|
-
|
394
|
+
# Deal with default group
|
395
|
+
global_options = groups.delete(nil) || []
|
396
|
+
printer.call(ungrouped_name, global_options) if global_options
|
401
397
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
end
|
398
|
+
# Print all others
|
399
|
+
groups = extra_group.merge(groups) if extra_group
|
400
|
+
groups.each(&printer)
|
401
|
+
printer
|
407
402
|
end
|
408
403
|
|
409
404
|
# Raises an error if the word given is a Thor reserved word.
|
data/lib/thor/group.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# Thor has a special class called Thor::Group. The main difference to Thor class
|
2
|
+
# is that it invokes all tasks at once. It also include some methods that allows
|
3
|
+
# invocations to be done at the class method, which are not available to Thor
|
4
|
+
# tasks.
|
5
|
+
#
|
1
6
|
class Thor::Group
|
2
|
-
|
3
7
|
class << self
|
4
|
-
|
5
8
|
# The descrition for this Thor::Group. If none is provided, but a source root
|
6
9
|
# exists, tries to find the USAGE one folder above it, otherwise searches
|
7
10
|
# in the superclass.
|
@@ -50,6 +53,171 @@ class Thor::Group
|
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
56
|
+
# Stores invocations for this class merging with superclass values.
|
57
|
+
#
|
58
|
+
def invocations #:nodoc:
|
59
|
+
@invocations ||= from_superclass(:invocations, {})
|
60
|
+
end
|
61
|
+
|
62
|
+
# Stores invocation blocks used on invoke_from_option.
|
63
|
+
#
|
64
|
+
def invocation_blocks #:nodoc:
|
65
|
+
@invocation_blocks ||= from_superclass(:invocation_blocks, {})
|
66
|
+
end
|
67
|
+
|
68
|
+
# Invoke the given namespace or class given. It adds an instance
|
69
|
+
# method that will invoke the klass and task. You can give a block to
|
70
|
+
# configure how it will be invoked.
|
71
|
+
#
|
72
|
+
# The namespace/class given will have its options showed on the help
|
73
|
+
# usage. Check invoke_from_option for more information.
|
74
|
+
#
|
75
|
+
def invoke(*names, &block)
|
76
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
77
|
+
verbose = options.fetch(:verbose, :white)
|
78
|
+
|
79
|
+
names.each do |name|
|
80
|
+
invocations[name] = false
|
81
|
+
invocation_blocks[name] = block if block_given?
|
82
|
+
|
83
|
+
class_eval <<-METHOD, __FILE__, __LINE__
|
84
|
+
def _invoke_#{name.to_s.gsub(/\W/, '_')}
|
85
|
+
klass, task = self.class.prepare_for_invocation(nil, #{name.inspect})
|
86
|
+
|
87
|
+
if klass
|
88
|
+
say_status :invoke, #{name.inspect}, #{verbose.inspect}
|
89
|
+
block = self.class.invocation_blocks[#{name.inspect}]
|
90
|
+
invoke_with_padding klass, task, &block
|
91
|
+
else
|
92
|
+
say_status :error, %(#{name.inspect} [not found]), :red
|
93
|
+
end
|
94
|
+
end
|
95
|
+
METHOD
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Invoke a thor class based on the value supplied by the user to the
|
100
|
+
# given option named "name". A class option must be created before this
|
101
|
+
# method is invoked for each name given.
|
102
|
+
#
|
103
|
+
# ==== Examples
|
104
|
+
#
|
105
|
+
# class GemGenerator < Thor::Group
|
106
|
+
# class_option :test_framework, :type => :string
|
107
|
+
# invoke_from_option :test_framework
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# ==== Boolean options
|
111
|
+
#
|
112
|
+
# In some cases, you want to invoke a thor class if some option is true or
|
113
|
+
# false. This is automatically handled by invoke_from_option. Then the
|
114
|
+
# option name is used to invoke the generator.
|
115
|
+
#
|
116
|
+
# ==== Preparing for invocation
|
117
|
+
#
|
118
|
+
# In some cases you want to customize how a specified hook is going to be
|
119
|
+
# invoked. You can do that by overwriting the class method
|
120
|
+
# prepare_for_invocation. The class method must necessarily return a klass
|
121
|
+
# and an optional task.
|
122
|
+
#
|
123
|
+
# ==== Custom invocations
|
124
|
+
#
|
125
|
+
# You can also supply a block to customize how the option is giong to be
|
126
|
+
# invoked. The block receives two parameters, an instance of the current
|
127
|
+
# class and the klass to be invoked.
|
128
|
+
#
|
129
|
+
def invoke_from_option(*names, &block)
|
130
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
131
|
+
verbose = options.fetch(:verbose, :white)
|
132
|
+
|
133
|
+
names.each do |name|
|
134
|
+
unless class_options.key?(name)
|
135
|
+
raise ArgumentError, "You have to define the option #{name.inspect} " <<
|
136
|
+
"before setting invoke_from_option."
|
137
|
+
end
|
138
|
+
|
139
|
+
invocations[name] = true
|
140
|
+
invocation_blocks[name] = block if block_given?
|
141
|
+
|
142
|
+
class_eval <<-METHOD, __FILE__, __LINE__
|
143
|
+
def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')}
|
144
|
+
return unless options[#{name.inspect}]
|
145
|
+
|
146
|
+
value = options[#{name.inspect}]
|
147
|
+
value = #{name.inspect} if TrueClass === value
|
148
|
+
klass, task = self.class.prepare_for_invocation(#{name.inspect}, value)
|
149
|
+
|
150
|
+
if klass
|
151
|
+
say_status :invoke, value, #{verbose.inspect}
|
152
|
+
block = self.class.invocation_blocks[#{name.inspect}]
|
153
|
+
invoke_with_padding klass, task, &block
|
154
|
+
else
|
155
|
+
say_status :error, %(\#{value} [not found]), :red
|
156
|
+
end
|
157
|
+
end
|
158
|
+
METHOD
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Remove a previously added invocation.
|
163
|
+
#
|
164
|
+
# ==== Examples
|
165
|
+
#
|
166
|
+
# remove_invocation :test_framework
|
167
|
+
#
|
168
|
+
def remove_invocation(*names)
|
169
|
+
names.each do |name|
|
170
|
+
remove_task(name)
|
171
|
+
remove_class_option(name)
|
172
|
+
invocations.delete(name)
|
173
|
+
invocation_blocks.delete(name)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Overwrite class options help to allow invoked generators options to be
|
178
|
+
# shown recursively when invoking a generator.
|
179
|
+
#
|
180
|
+
def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc:
|
181
|
+
group_options = {}
|
182
|
+
|
183
|
+
get_options_from_invocations(group_options, class_options) do |klass|
|
184
|
+
klass.send(:get_options_from_invocations, group_options, class_options)
|
185
|
+
end
|
186
|
+
|
187
|
+
group_options.merge!(extra_group) if extra_group
|
188
|
+
super(shell, ungrouped_name, group_options)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Get invocations array and merge options from invocations. Those
|
192
|
+
# options are added to group_options hash. Options that already exists
|
193
|
+
# in base_options are not added twice.
|
194
|
+
#
|
195
|
+
def get_options_from_invocations(group_options, base_options) #:nodoc:
|
196
|
+
invocations.each do |name, from_option|
|
197
|
+
value = if from_option
|
198
|
+
option = class_options[name]
|
199
|
+
option.type == :boolean ? name : option.default
|
200
|
+
else
|
201
|
+
name
|
202
|
+
end
|
203
|
+
next unless value
|
204
|
+
|
205
|
+
klass, task = prepare_for_invocation(name, value)
|
206
|
+
next unless klass && klass.respond_to?(:class_options)
|
207
|
+
|
208
|
+
value = value.to_s
|
209
|
+
human_name = value.respond_to?(:classify) ? value.classify : value
|
210
|
+
|
211
|
+
group_options[human_name] ||= []
|
212
|
+
group_options[human_name] += klass.class_options.values.select do |option|
|
213
|
+
base_options[option.name.to_sym].nil? && option.group.nil? &&
|
214
|
+
!group_options.values.flatten.any? { |i| i.name == option.name }
|
215
|
+
end
|
216
|
+
|
217
|
+
yield klass if block_given?
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
53
221
|
protected
|
54
222
|
|
55
223
|
# The banner for this class. You can customize it if you are invoking the
|
data/lib/thor/invocation.rb
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
class Thor
|
2
2
|
module Invocation
|
3
|
+
def self.included(base) #:nodoc:
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Prepare for class methods invocations. This method must return a klass to
|
9
|
+
# have the invoked class options showed in help messages in generators.
|
10
|
+
#
|
11
|
+
def prepare_for_invocation(key, name) #:nodoc:
|
12
|
+
case name
|
13
|
+
when Symbol, String
|
14
|
+
Thor::Util.namespace_to_thor_class(name.to_s, false)
|
15
|
+
else
|
16
|
+
name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
3
20
|
|
4
21
|
# Make initializer aware of invocations and the initializer proc.
|
5
22
|
#
|
@@ -79,7 +96,7 @@ class Thor
|
|
79
96
|
task, args, opts, config = nil, task, args, opts if task.nil? || task.is_a?(Array)
|
80
97
|
args, opts, config = nil, args, opts if args.is_a?(Hash)
|
81
98
|
|
82
|
-
object, task =
|
99
|
+
object, task = _prepare_for_invocation(name, task)
|
83
100
|
if object.is_a?(Class)
|
84
101
|
klass = object
|
85
102
|
|
@@ -113,6 +130,26 @@ class Thor
|
|
113
130
|
end
|
114
131
|
end
|
115
132
|
|
133
|
+
# Shortcut for invoke with padding and status handling. Used internally by
|
134
|
+
# class options invoke and invoke_from_option.
|
135
|
+
#
|
136
|
+
def invoke_with_padding(klass, task=nil, *args, &block)
|
137
|
+
shell.padding += 1
|
138
|
+
|
139
|
+
result = if block_given?
|
140
|
+
if block.arity == 2
|
141
|
+
block.call(self, klass)
|
142
|
+
else
|
143
|
+
block.call(self, klass, task)
|
144
|
+
end
|
145
|
+
else
|
146
|
+
invoke klass, task, *args
|
147
|
+
end
|
148
|
+
|
149
|
+
shell.padding -= 1
|
150
|
+
result
|
151
|
+
end
|
152
|
+
|
116
153
|
protected
|
117
154
|
|
118
155
|
# Configuration values that are shared between invocations.
|
@@ -121,23 +158,18 @@ class Thor
|
|
121
158
|
{ :invocations => @_invocations }
|
122
159
|
end
|
123
160
|
|
124
|
-
#
|
125
|
-
#
|
161
|
+
# Prepare for invocation in the instance level. In this case, we have to
|
162
|
+
# take into account that a just a task name from the current class was
|
163
|
+
# given or even a Thor::Task object.
|
126
164
|
#
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
unless task = self.class.all_tasks[name]
|
136
|
-
object, task = Thor::Util.namespace_to_thor_class(name, false)
|
137
|
-
task ||= sent_task
|
138
|
-
end
|
139
|
-
else
|
140
|
-
object, task = name, sent_task
|
165
|
+
def _prepare_for_invocation(name, sent_task=nil) #:nodoc:
|
166
|
+
if name.is_a?(Thor::Task)
|
167
|
+
task = name
|
168
|
+
elsif task = self.class.all_tasks[name.to_s]
|
169
|
+
object = self
|
170
|
+
else
|
171
|
+
object, task = self.class.prepare_for_invocation(nil, name)
|
172
|
+
task ||= sent_task
|
141
173
|
end
|
142
174
|
|
143
175
|
# If the object was not set, use self and use the name as task.
|
@@ -148,7 +180,7 @@ class Thor
|
|
148
180
|
# Check if the object given is a Thor class object and get a task object
|
149
181
|
# for it.
|
150
182
|
#
|
151
|
-
def _validate_klass_and_task(object, task)
|
183
|
+
def _validate_klass_and_task(object, task) #:nodoc:
|
152
184
|
klass = object.is_a?(Class) ? object : object.class
|
153
185
|
raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
|
154
186
|
|
@@ -156,6 +188,5 @@ class Thor
|
|
156
188
|
task = klass.all_tasks[task.to_s] || Task.dynamic(task) if task && !task.is_a?(Thor::Task)
|
157
189
|
task
|
158
190
|
end
|
159
|
-
|
160
191
|
end
|
161
192
|
end
|
data/lib/thor/parser/option.rb
CHANGED
@@ -95,14 +95,21 @@ class Thor
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
|
99
|
-
|
98
|
+
# Allow some type predicates as: boolean?, string? and etc.
|
99
|
+
#
|
100
|
+
def method_missing(method, *args, &block)
|
101
|
+
given = method.to_s.sub(/\?$/, '').to_sym
|
102
|
+
if valid_type?(given)
|
103
|
+
self.type == given
|
104
|
+
else
|
105
|
+
super
|
106
|
+
end
|
100
107
|
end
|
101
108
|
|
102
109
|
protected
|
103
110
|
|
104
111
|
def validate!
|
105
|
-
raise ArgumentError, "An option cannot be boolean and required." if
|
112
|
+
raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
|
106
113
|
end
|
107
114
|
|
108
115
|
def valid_type?(type)
|
data/lib/thor/parser/options.rb
CHANGED
@@ -122,9 +122,16 @@ class Thor
|
|
122
122
|
# Parse the value at the peek analyzing if it requires an input or not.
|
123
123
|
#
|
124
124
|
def parse_peek(switch, option)
|
125
|
-
|
126
|
-
|
127
|
-
|
125
|
+
unless current_is_value?
|
126
|
+
if option.boolean?
|
127
|
+
# No problem for boolean types
|
128
|
+
elsif no_or_skip?(switch)
|
129
|
+
return nil # User set value to nil
|
130
|
+
elsif option.string? && !option.required?
|
131
|
+
return option.human_name # Return the option name
|
132
|
+
else
|
133
|
+
raise MalformattedArgumentError, "no value provided for option '#{switch}'"
|
134
|
+
end
|
128
135
|
end
|
129
136
|
|
130
137
|
@non_assigned_required.delete(option)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wycats-thor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yehuda Katz
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-07-
|
12
|
+
date: 2009-07-16 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|