josevalim-thor 0.10.0 → 0.10.1
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.
- data/lib/thor/actions/directory.rb +29 -4
- data/lib/thor/actions/template.rb +3 -2
- data/lib/thor/actions/templater.rb +18 -11
- data/lib/thor/actions.rb +152 -12
- data/lib/thor/base.rb +59 -10
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +8 -2
- data/lib/thor/core_ext/ordered_hash.rb +21 -11
- data/lib/thor/group.rb +3 -14
- data/lib/thor/option.rb +4 -3
- data/lib/thor/shell/basic.rb +3 -3
- data/lib/thor/task.rb +18 -6
- data/lib/thor/tasks.rb +1 -0
- data/lib/thor.rb +18 -27
- metadata +2 -4
- data/lib/thor/actions/commands.rb +0 -61
- data/lib/thor/actions/gsub_file.rb +0 -77
@@ -4,7 +4,27 @@ class Thor
|
|
4
4
|
module Actions
|
5
5
|
|
6
6
|
# Copies interactively the files from source directory to root directory.
|
7
|
-
#
|
7
|
+
# If any of the files finishes with .tt, it's considered to be a template
|
8
|
+
# and is placed in the destination without the extension .tt. Remember that
|
9
|
+
# file paths can also be encoded, let's suppose a doc directory with the
|
10
|
+
# following files:
|
11
|
+
#
|
12
|
+
# doc/
|
13
|
+
# README
|
14
|
+
# rdoc.rb.tt
|
15
|
+
# %app_name%.rb
|
16
|
+
#
|
17
|
+
# When invoked as:
|
18
|
+
#
|
19
|
+
# directory "doc"
|
20
|
+
#
|
21
|
+
# It will create a doc directory in the destination with the following
|
22
|
+
# files (assuming that the app_name is "blog"):
|
23
|
+
#
|
24
|
+
# doc/
|
25
|
+
# README
|
26
|
+
# rdoc.rb
|
27
|
+
# blog.rb
|
8
28
|
#
|
9
29
|
# ==== Parameters
|
10
30
|
# source<String>:: the relative path to the source root
|
@@ -22,12 +42,17 @@ class Thor
|
|
22
42
|
class Directory < Templater #:nodoc:
|
23
43
|
|
24
44
|
def invoke!
|
25
|
-
|
45
|
+
Dir[File.join(source, '**', '*')].each do |file_source|
|
46
|
+
next if File.directory?(file_source)
|
26
47
|
|
27
|
-
files.each do |file_source|
|
28
48
|
file_destination = File.join(relative_destination, file_source.gsub(source, ''))
|
29
49
|
file_source.gsub!(base.source_root, '.')
|
30
|
-
|
50
|
+
|
51
|
+
if file_source =~ /.tt$/
|
52
|
+
base.template(file_source, file_destination[0..-4], @log_status)
|
53
|
+
else
|
54
|
+
base.copy_file(file_source, file_destination, @log_status)
|
55
|
+
end
|
31
56
|
end
|
32
57
|
end
|
33
58
|
|
@@ -6,7 +6,7 @@ class Thor
|
|
6
6
|
|
7
7
|
# Gets an ERB template at the relative source, executes it and makes a copy
|
8
8
|
# at the relative destination. If the destination is not given it's assumed
|
9
|
-
# to be equal to the source.
|
9
|
+
# to be equal to the source removing .tt from the filename.
|
10
10
|
#
|
11
11
|
# ==== Parameters
|
12
12
|
# source<String>:: the relative path to the source root
|
@@ -20,7 +20,8 @@ class Thor
|
|
20
20
|
# template "doc/README"
|
21
21
|
#
|
22
22
|
def template(source, destination=nil, log_status=true)
|
23
|
-
|
23
|
+
destination ||= source.gsub('.tt$', '')
|
24
|
+
action Template.new(self, source, destination, log_status)
|
24
25
|
end
|
25
26
|
|
26
27
|
class Template < Templater #:nodoc:
|
@@ -97,11 +97,26 @@ class Thor
|
|
97
97
|
#
|
98
98
|
def destination=(destination)
|
99
99
|
if destination
|
100
|
-
@destination = ::File.expand_path(destination.to_s, base.destination_root)
|
100
|
+
@destination = ::File.expand_path(convert_encoded_instructions(destination.to_s), base.destination_root)
|
101
101
|
@relative_destination = base.relative_to_absolute_root(@destination)
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
# Filenames in the encoded form are converted. If you have a file:
|
106
|
+
#
|
107
|
+
# %class_name%.rb
|
108
|
+
#
|
109
|
+
# It gets the class name from the base and replace it:
|
110
|
+
#
|
111
|
+
# user.rb
|
112
|
+
#
|
113
|
+
def convert_encoded_instructions(filename)
|
114
|
+
filename.gsub(/%(.*?)%/) do |string|
|
115
|
+
instruction = $1.strip
|
116
|
+
base.respond_to?(instruction) ? base.send(instruction) : string
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
105
120
|
# Receives a hash of options and just execute the block if some
|
106
121
|
# conditions are met.
|
107
122
|
#
|
@@ -118,6 +133,8 @@ class Thor
|
|
118
133
|
say_status :create, :green
|
119
134
|
block.call unless pretend?
|
120
135
|
end
|
136
|
+
|
137
|
+
destination
|
121
138
|
end
|
122
139
|
|
123
140
|
# If force is true, run the action, otherwise check if it's not being
|
@@ -148,16 +165,6 @@ class Thor
|
|
148
165
|
base.shell.say_status status, relative_destination, color if @log_status
|
149
166
|
end
|
150
167
|
|
151
|
-
# TODO Add this behavior to all actions.
|
152
|
-
#
|
153
|
-
def after_invoke
|
154
|
-
# Optionally change permissions.
|
155
|
-
FileUtils.chmod(base.options[:chmod], destination) if base.options[:chmod]
|
156
|
-
|
157
|
-
# Optionally add file to subversion or git
|
158
|
-
system("git add -v #{relative_destination}") if options[:git]
|
159
|
-
end
|
160
|
-
|
161
168
|
end
|
162
169
|
end
|
163
170
|
end
|
data/lib/thor/actions.rb
CHANGED
@@ -13,25 +13,17 @@ class Thor
|
|
13
13
|
def self.included(base) #:nodoc:
|
14
14
|
return unless base.respond_to?(:class_option)
|
15
15
|
|
16
|
-
base.class_option :pretend, :type => :boolean, :aliases => "-p",
|
16
|
+
base.class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
|
17
17
|
:desc => "Run but do not make any changes"
|
18
18
|
|
19
|
-
base.class_option :force, :type => :boolean, :aliases => "-f",
|
19
|
+
base.class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
|
20
20
|
:desc => "Overwrite files that already exist"
|
21
21
|
|
22
|
-
base.class_option :skip, :type => :boolean, :aliases => "-s",
|
22
|
+
base.class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
|
23
23
|
:desc => "Skip files that already exist"
|
24
24
|
|
25
|
-
base.class_option :quiet, :type => :boolean, :aliases => "-q",
|
25
|
+
base.class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
|
26
26
|
:desc => "Supress status output"
|
27
|
-
|
28
|
-
# TODO Add git support
|
29
|
-
base.class_option :git, :type => :boolean, :aliases => "-g",
|
30
|
-
:desc => "Modify files with git (note: git must be on the path)"
|
31
|
-
|
32
|
-
# TODO Add chmod support
|
33
|
-
base.class_option :chmod, :type => :string, :aliases => "-c",
|
34
|
-
:desc => "Set the mode of added/copied files"
|
35
27
|
end
|
36
28
|
|
37
29
|
# Extends initializer to add more configuration options.
|
@@ -136,6 +128,154 @@ class Thor
|
|
136
128
|
inside(@root_stack.first) { yield }
|
137
129
|
end
|
138
130
|
|
131
|
+
# Changes the mode of the given file or directory.
|
132
|
+
#
|
133
|
+
# ==== Parameters
|
134
|
+
# mode<Integer>:: the file mode
|
135
|
+
# path<String>:: the name of the file to change mode
|
136
|
+
# log_status<Boolean>:: if false, does not log the status. True by default.
|
137
|
+
# If a symbol is given, uses it as the output color.
|
138
|
+
#
|
139
|
+
# ==== Example
|
140
|
+
#
|
141
|
+
# chmod "script/*", 0755
|
142
|
+
#
|
143
|
+
def chmod(mode, path, log_status=true)
|
144
|
+
path = File.expand_path(path, root)
|
145
|
+
say_status_if_log :chmod, relative_to_absolute_root(path), log_status
|
146
|
+
FileUtils.chmod_R(mode, path) unless options[:pretend]
|
147
|
+
end
|
148
|
+
|
149
|
+
# Executes a command.
|
150
|
+
#
|
151
|
+
# ==== Parameters
|
152
|
+
# command<String>:: the command to be executed.
|
153
|
+
# log_status<Boolean>:: if false, does not log the status. True by default.
|
154
|
+
# If a symbol is given, uses it as the output color.
|
155
|
+
#
|
156
|
+
# ==== Example
|
157
|
+
#
|
158
|
+
# inside('vendor') do
|
159
|
+
# run('ln -s ~/edge rails')
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
def run(command, log_status=true)
|
163
|
+
say_status_if_log :run, "#{command} from #{relative_to_absolute_root(root, false)}", log_status
|
164
|
+
`#{command}` unless options[:pretend]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Executes a ruby script (taking into account WIN32 platform quirks).
|
168
|
+
#
|
169
|
+
# ==== Parameters
|
170
|
+
# command<String>:: the command to be executed.
|
171
|
+
# log_status<Boolean>:: if false, does not log the status. True by default.
|
172
|
+
# If a symbol is given, uses it as the output color.
|
173
|
+
#
|
174
|
+
def run_ruby_script(command, log_status=true)
|
175
|
+
run("ruby #{command}", log_status)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Run a thor command. A hash of options can be given and it's converted to
|
179
|
+
# switches.
|
180
|
+
#
|
181
|
+
# ==== Parameters
|
182
|
+
# task<String>:: the task to be invoked
|
183
|
+
# args<Array>:: arguments to the task
|
184
|
+
# options<Hash>:: a hash with options used on invocation
|
185
|
+
# log_status<Boolean>:: if false, does not log the status. True by default.
|
186
|
+
# If a symbol is given, uses it as the output color.
|
187
|
+
#
|
188
|
+
# ==== Examples
|
189
|
+
#
|
190
|
+
# thor :install, "http://gist.github.com/103208"
|
191
|
+
# #=> thor install http://gist.github.com/103208
|
192
|
+
#
|
193
|
+
# thor :list, :all => true, :substring => 'rails'
|
194
|
+
# #=> thor list --all --substring=rails
|
195
|
+
#
|
196
|
+
def thor(task, *args)
|
197
|
+
log_status = args.last.is_a?(Symbol) || [true, false].include?(args.last) ? args.pop : true
|
198
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
199
|
+
|
200
|
+
in_root do
|
201
|
+
args.unshift "thor #{task}"
|
202
|
+
args.push Thor::Options.to_switches(options)
|
203
|
+
run args.join(' ').strip, log_status
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Run a regular expression replacement on a file.
|
208
|
+
#
|
209
|
+
# ==== Parameters
|
210
|
+
# path<String>:: path of the file to be changed
|
211
|
+
# flag<Regexp|String>:: the regexp or string to be replaced
|
212
|
+
# replacement<String>:: the replacement, can be also given as a block
|
213
|
+
# log_status<Boolean>:: if false, does not log the status. True by default.
|
214
|
+
# If a symbol is given, uses it as the output color.
|
215
|
+
#
|
216
|
+
# ==== Example
|
217
|
+
#
|
218
|
+
# gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
|
219
|
+
#
|
220
|
+
# gsub_file 'README', /rake/, :green do |match|
|
221
|
+
# match << " no more. Use thor!"
|
222
|
+
# end
|
223
|
+
#
|
224
|
+
def gsub_file(path, flag, *args, &block)
|
225
|
+
log_status = args.last.is_a?(Symbol) || [ true, false ].include?(args.last) ? args.pop : true
|
226
|
+
|
227
|
+
path = File.expand_path(path, root)
|
228
|
+
say_status_if_log :gsub, relative_to_absolute_root(path), log_status
|
229
|
+
|
230
|
+
unless options[:pretend]
|
231
|
+
content = File.read(path)
|
232
|
+
content.gsub!(flag, *args, &block)
|
233
|
+
File.open(path, 'wb') { |file| file.write(content) }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Append text to a file.
|
238
|
+
#
|
239
|
+
# ==== Parameters
|
240
|
+
# path<String>:: path of the file to be changed
|
241
|
+
# data<String>:: the data to append to the file, can be also given as a block.
|
242
|
+
# log_status<Boolean>:: if false, does not log the status. True by default.
|
243
|
+
# If a symbol is given, uses it as the output color.
|
244
|
+
#
|
245
|
+
# ==== Example
|
246
|
+
#
|
247
|
+
# append_file 'config/environments/test.rb', 'config.gem "rspec"'
|
248
|
+
#
|
249
|
+
def append_file(path, data=nil, log_status=true, &block)
|
250
|
+
path = File.expand_path(path, root)
|
251
|
+
say_status_if_log :append, relative_to_absolute_root(path), log_status
|
252
|
+
|
253
|
+
File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend]
|
254
|
+
end
|
255
|
+
|
256
|
+
# Prepend text to a file.
|
257
|
+
#
|
258
|
+
# ==== Parameters
|
259
|
+
# path<String>:: path of the file to be changed
|
260
|
+
# data<String>:: the data to prepend to the file, can be also given as a block.
|
261
|
+
# log_status<Boolean>:: if false, does not log the status. True by default.
|
262
|
+
# If a symbol is given, uses it as the output color.
|
263
|
+
#
|
264
|
+
# ==== Example
|
265
|
+
#
|
266
|
+
# prepend_file 'config/environments/test.rb', 'config.gem "rspec"'
|
267
|
+
#
|
268
|
+
def prepend_file(path, data=nil, log_status=true, &block)
|
269
|
+
path = File.expand_path(path, root)
|
270
|
+
say_status_if_log :prepend, relative_to_absolute_root(path), log_status
|
271
|
+
|
272
|
+
unless options[:pretend]
|
273
|
+
content = data || block.call
|
274
|
+
content << File.read(path)
|
275
|
+
File.open(path, 'wb') { |file| file.write(content) }
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
139
279
|
protected
|
140
280
|
|
141
281
|
def say_status_if_log(status, message, log_status)
|
data/lib/thor/base.rb
CHANGED
@@ -83,8 +83,14 @@ class Thor
|
|
83
83
|
#
|
84
84
|
# ==== Parameters
|
85
85
|
# name<Symbol>:: The name of the argument.
|
86
|
-
# options<Hash>::
|
87
|
-
#
|
86
|
+
# options<Hash>:: Described below.
|
87
|
+
#
|
88
|
+
# ==== Options
|
89
|
+
# :desc - Description for the argument.
|
90
|
+
# :required - If the argument is required or not.
|
91
|
+
# :optional - If the argument is optional or not.
|
92
|
+
# :type - The type of the argument, can be :string, :hash, :array, :numeric.
|
93
|
+
# :default - Default value for this argument. It cannot be required and have default values.
|
88
94
|
#
|
89
95
|
# ==== Errors
|
90
96
|
# ArgumentError:: Raised if you supply a required argument after a non required one.
|
@@ -138,9 +144,16 @@ class Thor
|
|
138
144
|
#
|
139
145
|
# ==== Parameters
|
140
146
|
# name<Symbol>:: The name of the argument.
|
141
|
-
# options<Hash>::
|
142
|
-
#
|
143
|
-
#
|
147
|
+
# options<Hash>:: Described below.
|
148
|
+
#
|
149
|
+
# ==== Options
|
150
|
+
# :desc - Description for the argument.
|
151
|
+
# :required - If the argument is required or not.
|
152
|
+
# :default - Default value for this argument. It cannot be required and have default values.
|
153
|
+
# :group - The group for this options. Use by class options to output options in different levels.
|
154
|
+
# :aliases - Aliases for this option.
|
155
|
+
# :type - The type of the argument, can be :string, :hash, :array, :numeric, :boolean or :default.
|
156
|
+
# Default accepts arguments as booleans (--switch) or as strings (--switch=VALUE).
|
144
157
|
#
|
145
158
|
def class_option(name, options)
|
146
159
|
build_option(name, options, class_options)
|
@@ -277,17 +290,53 @@ class Thor
|
|
277
290
|
|
278
291
|
protected
|
279
292
|
|
293
|
+
# Prints the class optins per group. If a class options does not belong
|
294
|
+
# to any group, it's grouped as "Class options" in Thor classes and as
|
295
|
+
# "Options" in Thor::Group (since Thor::Group does not have method
|
296
|
+
# options, there is not need to add "Class" frist).
|
297
|
+
#
|
298
|
+
def class_options_help(shell, ungrouped_name=nil) #:nodoc:
|
299
|
+
unless self.class_options.empty?
|
300
|
+
groups = self.class_options.group_values_by { |o| o.group }
|
301
|
+
|
302
|
+
printer = lambda do |group_name, options|
|
303
|
+
unless options.empty?
|
304
|
+
options.map! do |option|
|
305
|
+
next if option.argument?
|
306
|
+
[ option.usage, option.description || '' ]
|
307
|
+
end
|
308
|
+
|
309
|
+
options.compact!
|
310
|
+
|
311
|
+
if group_name
|
312
|
+
shell.say "#{group_name} options:"
|
313
|
+
else
|
314
|
+
shell.say "Options:"
|
315
|
+
end
|
316
|
+
|
317
|
+
shell.print_table(options, :emphasize_last => true, :ident => 2)
|
318
|
+
shell.say ""
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Deal with default group
|
323
|
+
global_options = groups.delete(nil)
|
324
|
+
printer.call(ungrouped_name, global_options) if global_options
|
325
|
+
|
326
|
+
# Print all others
|
327
|
+
groups.each(&printer)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
280
331
|
# Build an option and adds it to the given scope.
|
281
332
|
#
|
282
333
|
# ==== Parameters
|
283
334
|
# name<Symbol>:: The name of the argument.
|
284
|
-
# options<Hash>::
|
285
|
-
# The type can be :string, :boolean, :numeric, :hash or :array. If none is given
|
286
|
-
# a default type which accepts both (--name and --name=NAME) entries is assumed.
|
335
|
+
# options<Hash>:: Described in both class_option and method_option.
|
287
336
|
#
|
288
337
|
def build_option(name, options, scope)
|
289
|
-
scope[name] = Thor::Option.new(name, options[:desc], options[:required],
|
290
|
-
|
338
|
+
scope[name] = Thor::Option.new(name, options[:desc], options[:required], options[:type],
|
339
|
+
options[:default], options[:aliases], options[:group])
|
291
340
|
end
|
292
341
|
|
293
342
|
# Receives a hash of options, parse them and add to the scope. This is a
|
@@ -43,12 +43,18 @@ class Thor
|
|
43
43
|
|
44
44
|
# Magic predicates. For instance:
|
45
45
|
#
|
46
|
-
# options.force?
|
46
|
+
# options.force? # => !!options['force']
|
47
|
+
# options.shebang # => "/usr/lib/local/ruby"
|
48
|
+
# options.test_framework?(:rspec) # => options[:test_framework] == :rspec
|
47
49
|
#
|
48
50
|
def method_missing(method, *args, &block)
|
49
51
|
method = method.to_s
|
50
52
|
if method =~ /^(\w+)\?$/
|
51
|
-
|
53
|
+
if args.empty?
|
54
|
+
!!self[$1]
|
55
|
+
else
|
56
|
+
self[$1] == args.first
|
57
|
+
end
|
52
58
|
else
|
53
59
|
self[method]
|
54
60
|
end
|
@@ -7,9 +7,10 @@ class Thor #:nodoc:
|
|
7
7
|
# while also keeping track of the order in which elements were set.
|
8
8
|
#
|
9
9
|
class OrderedHash #:nodoc:
|
10
|
-
Node = Struct.new(:key, :value, :next, :prev)
|
11
10
|
include Enumerable
|
12
11
|
|
12
|
+
Node = Struct.new(:key, :value, :next, :prev)
|
13
|
+
|
13
14
|
def initialize
|
14
15
|
@hash = {}
|
15
16
|
end
|
@@ -82,6 +83,14 @@ class Thor #:nodoc:
|
|
82
83
|
value
|
83
84
|
end
|
84
85
|
|
86
|
+
def keys
|
87
|
+
self.map { |k, v| k }
|
88
|
+
end
|
89
|
+
|
90
|
+
def values
|
91
|
+
self.map { |k, v| v }
|
92
|
+
end
|
93
|
+
|
85
94
|
def each
|
86
95
|
return unless @first
|
87
96
|
yield [@first.key, @first.value]
|
@@ -90,12 +99,14 @@ class Thor #:nodoc:
|
|
90
99
|
self
|
91
100
|
end
|
92
101
|
|
93
|
-
def
|
94
|
-
self.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
102
|
+
def group_values_by
|
103
|
+
assoc = self.class.new
|
104
|
+
each do |_, element|
|
105
|
+
key = yield(element)
|
106
|
+
assoc[key] ||= []
|
107
|
+
assoc[key] << element
|
108
|
+
end
|
109
|
+
assoc
|
99
110
|
end
|
100
111
|
|
101
112
|
def merge(other)
|
@@ -118,10 +129,9 @@ class Thor #:nodoc:
|
|
118
129
|
end
|
119
130
|
|
120
131
|
def to_a
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
132
|
+
array = []
|
133
|
+
each { |k, v| array << [k, v] }
|
134
|
+
array
|
125
135
|
end
|
126
136
|
|
127
137
|
def to_s
|
data/lib/thor/group.rb
CHANGED
@@ -44,23 +44,12 @@ class Thor::Group
|
|
44
44
|
#
|
45
45
|
def help(shell, options={})
|
46
46
|
if options[:short]
|
47
|
-
shell.say "#{self.namespace} #{self.
|
47
|
+
shell.say "#{self.namespace} #{self.arguments.map {|o| o.usage }.join(' ')}"
|
48
48
|
else
|
49
49
|
shell.say "Usage:"
|
50
|
-
shell.say " #{self.namespace} #{self.arguments.map{|o| o.usage}.join(' ')}"
|
50
|
+
shell.say " #{self.namespace} #{self.arguments.map {|o| o.usage }.join(' ')}"
|
51
51
|
shell.say
|
52
|
-
|
53
|
-
list = self.class_options.map do |_, option|
|
54
|
-
next if option.argument?
|
55
|
-
[ option.usage, option.description || '' ]
|
56
|
-
end.compact
|
57
|
-
|
58
|
-
unless list.empty?
|
59
|
-
shell.say "Global options:"
|
60
|
-
shell.print_table(list, :emphasize_last => true, :ident => 2)
|
61
|
-
shell.say
|
62
|
-
end
|
63
|
-
|
52
|
+
class_options_help(shell)
|
64
53
|
shell.say self.desc if self.desc
|
65
54
|
end
|
66
55
|
end
|
data/lib/thor/option.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
class Thor
|
2
2
|
class Option
|
3
|
-
attr_reader :name, :description, :required, :type, :default, :aliases
|
3
|
+
attr_reader :name, :description, :required, :type, :default, :aliases, :group
|
4
4
|
|
5
5
|
VALID_TYPES = [:boolean, :numeric, :hash, :array, :string, :default]
|
6
6
|
|
7
|
-
def initialize(name, description=nil, required=nil, type=nil, default=nil, aliases=nil)
|
7
|
+
def initialize(name, description=nil, required=nil, type=nil, default=nil, aliases=nil, group=nil)
|
8
8
|
raise ArgumentError, "Option name can't be nil." if name.nil?
|
9
9
|
raise ArgumentError, "Option cannot be required and have default values." if required && !default.nil?
|
10
10
|
raise ArgumentError, "Type :#{type} is not valid for options." if type && !VALID_TYPES.include?(type.to_sym)
|
@@ -15,6 +15,7 @@ class Thor
|
|
15
15
|
@type = (type || :default).to_sym
|
16
16
|
@default = default
|
17
17
|
@aliases = [*aliases].compact
|
18
|
+
@group = group.to_s.capitalize if group
|
18
19
|
end
|
19
20
|
|
20
21
|
# This parse quick options given as method_options. It makes several
|
@@ -196,7 +197,7 @@ class Thor
|
|
196
197
|
raise ArgumentError, "Argument name can't be nil." if name.nil?
|
197
198
|
raise ArgumentError, "Type :#{type} is not valid for arguments." if type && !VALID_TYPES.include?(type.to_sym)
|
198
199
|
|
199
|
-
super(name, description, required, type || :string, default
|
200
|
+
super(name, description, required, type || :string, default)
|
200
201
|
end
|
201
202
|
|
202
203
|
def argument?
|
data/lib/thor/shell/basic.rb
CHANGED
@@ -22,10 +22,10 @@ class Thor
|
|
22
22
|
# ==== Example
|
23
23
|
# say("I know you knew that.")
|
24
24
|
#
|
25
|
-
def say(statement="", color=nil)
|
25
|
+
def say(statement="", color=nil, force_new_line=false)
|
26
26
|
statement = statement.to_s
|
27
27
|
|
28
|
-
if statement[-1, 1] == " " || statement[-1, 1] == "\t"
|
28
|
+
if !force_new_line && (statement[-1, 1] == " " || statement[-1, 1] == "\t")
|
29
29
|
$stdout.print(statement)
|
30
30
|
$stdout.flush
|
31
31
|
else
|
@@ -40,7 +40,7 @@ class Thor
|
|
40
40
|
return if base && base.options[:quiet]
|
41
41
|
|
42
42
|
status_flag = "[#{status.to_s.upcase}]".rjust(12)
|
43
|
-
say "#{status_flag} #{message}"
|
43
|
+
say "#{status_flag} #{message}", color, true
|
44
44
|
end
|
45
45
|
|
46
46
|
# Make a question the to user and returns true if the user replies "y" or
|
data/lib/thor/task.rb
CHANGED
@@ -29,7 +29,7 @@ class Thor
|
|
29
29
|
backtrace = sans_backtrace(e.backtrace, caller)
|
30
30
|
|
31
31
|
if instance.is_a?(Thor) && backtrace.empty?
|
32
|
-
raise InvocationError, "'#{name}' was called incorrectly. Call as '#{formatted_usage(instance.class)}'"
|
32
|
+
raise InvocationError, "'#{name}' was called incorrectly. Call as '#{formatted_usage(instance.class, true)}'"
|
33
33
|
else
|
34
34
|
raise e
|
35
35
|
end
|
@@ -47,18 +47,30 @@ class Thor
|
|
47
47
|
description.split("\n").first if description
|
48
48
|
end
|
49
49
|
|
50
|
-
# Returns the formatted usage. If a klass is given, the klass
|
51
|
-
#
|
50
|
+
# Returns the formatted usage. If a klass is given, the klass arguments are
|
51
|
+
# injected in the usage.
|
52
52
|
#
|
53
|
-
def formatted_usage(klass=nil)
|
53
|
+
def formatted_usage(klass=nil, namespace=false)
|
54
54
|
formatted = ''
|
55
|
-
formatted << "#{klass.namespace.gsub(/^default/,'')}:" if klass
|
56
|
-
formatted <<
|
55
|
+
formatted << "#{klass.namespace.gsub(/^default/,'')}:" if klass && namespace
|
56
|
+
formatted << formatted_arguments(klass)
|
57
57
|
formatted << " #{formatted_options}"
|
58
58
|
formatted.strip!
|
59
59
|
formatted
|
60
60
|
end
|
61
61
|
|
62
|
+
# Injects the klass arguments into the defined usage.
|
63
|
+
#
|
64
|
+
def formatted_arguments(klass)
|
65
|
+
if klass && !klass.arguments.empty?
|
66
|
+
usage.to_s.gsub(/^#{name}/) do |match|
|
67
|
+
match << " " << klass.arguments.map{ |a| a.usage }.join(' ')
|
68
|
+
end
|
69
|
+
else
|
70
|
+
usage.to_s
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
62
74
|
# Returns the options usage for this task.
|
63
75
|
#
|
64
76
|
def formatted_options
|
data/lib/thor/tasks.rb
CHANGED
data/lib/thor.rb
CHANGED
@@ -95,9 +95,15 @@ class Thor
|
|
95
95
|
#
|
96
96
|
# ==== Parameters
|
97
97
|
# name<Symbol>:: The name of the argument.
|
98
|
-
# options<Hash>::
|
99
|
-
#
|
100
|
-
#
|
98
|
+
# options<Hash>:: Described below.
|
99
|
+
#
|
100
|
+
# ==== Options
|
101
|
+
# :desc - Description for the argument.
|
102
|
+
# :required - If the argument is required or not.
|
103
|
+
# :default - Default value for this argument. It cannot be required and have default values.
|
104
|
+
# :aliases - Aliases for this option.
|
105
|
+
# :type - The type of the argument, can be :string, :hash, :array, :numeric, :boolean or :default.
|
106
|
+
# Default accepts arguments as booleans (--switch) or as strings (--switch=VALUE).
|
101
107
|
#
|
102
108
|
def method_option(name, options)
|
103
109
|
scope = if options[:for]
|
@@ -145,33 +151,29 @@ class Thor
|
|
145
151
|
#
|
146
152
|
def help(shell, meth=nil, options={})
|
147
153
|
meth, options = nil, meth if meth.is_a?(Hash)
|
148
|
-
namespace = options[:namespace] ? self : nil
|
149
154
|
|
150
155
|
if meth
|
151
156
|
task = self.all_tasks[meth]
|
152
157
|
raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task
|
153
158
|
|
154
159
|
shell.say "Usage:"
|
155
|
-
shell.say " #{task.formatted_usage(namespace)}"
|
160
|
+
shell.say " #{task.formatted_usage(self, options[:namespace])}"
|
156
161
|
shell.say
|
157
|
-
|
162
|
+
class_options_help(shell, "Class")
|
158
163
|
shell.say task.description
|
159
164
|
else
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
end
|
165
|
+
list = (options[:short] ? tasks : all_tasks).map do |_, task|
|
166
|
+
[ task.formatted_usage(self, options[:namespace]), task.short_description || '' ]
|
167
|
+
end
|
164
168
|
|
169
|
+
if options[:short]
|
165
170
|
shell.print_table(list, :emphasize_last => true)
|
166
171
|
else
|
167
|
-
options_help(shell)
|
168
|
-
|
169
|
-
list = self.all_tasks.map do |_, task|
|
170
|
-
[ task.formatted_usage(namespace), task.short_description || '' ]
|
171
|
-
end
|
172
|
-
|
173
172
|
shell.say "Tasks:"
|
174
173
|
shell.print_table(list, :ident => 2, :emphasize_last => true)
|
174
|
+
shell.say
|
175
|
+
|
176
|
+
class_options_help(shell, "Class")
|
175
177
|
end
|
176
178
|
end
|
177
179
|
end
|
@@ -205,17 +207,6 @@ class Thor
|
|
205
207
|
meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
|
206
208
|
end
|
207
209
|
|
208
|
-
def options_help(shell) #:nodoc:
|
209
|
-
unless self.class_options.empty?
|
210
|
-
list = self.class_options.map do |_, option|
|
211
|
-
[ option.usage, option.description || '' ]
|
212
|
-
end
|
213
|
-
|
214
|
-
shell.say "Global arguments:"
|
215
|
-
shell.print_table(list, :emphasize_last => true, :ident => 2)
|
216
|
-
shell.say ""
|
217
|
-
end
|
218
|
-
end
|
219
210
|
end
|
220
211
|
|
221
212
|
include Thor::Base
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: josevalim-thor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.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-06-
|
12
|
+
date: 2009-06-15 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -42,10 +42,8 @@ files:
|
|
42
42
|
- lib/thor/actions/inject_into_file.rb
|
43
43
|
- lib/thor/actions/directory.rb
|
44
44
|
- lib/thor/actions/template.rb
|
45
|
-
- lib/thor/actions/gsub_file.rb
|
46
45
|
- lib/thor/actions/get.rb
|
47
46
|
- lib/thor/actions/create_file.rb
|
48
|
-
- lib/thor/actions/commands.rb
|
49
47
|
- lib/thor/actions/empty_directory.rb
|
50
48
|
- lib/thor/util.rb
|
51
49
|
- lib/thor/runner.rb
|
@@ -1,61 +0,0 @@
|
|
1
|
-
class Thor
|
2
|
-
module Actions
|
3
|
-
# Executes a command.
|
4
|
-
#
|
5
|
-
# ==== Parameters
|
6
|
-
# command<String>:: the command to be executed.
|
7
|
-
# log_status<Boolean>:: if false, does not log the status. True by default.
|
8
|
-
# If a symbol is given, uses it as the output color.
|
9
|
-
#
|
10
|
-
# ==== Example
|
11
|
-
#
|
12
|
-
# inside('vendor') do
|
13
|
-
# run('ln -s ~/edge rails')
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
def run(command, log_status=true)
|
17
|
-
say_status_if_log :run, "#{command} from #{relative_to_absolute_root(root, false)}", log_status
|
18
|
-
`#{command}` unless options[:pretend]
|
19
|
-
end
|
20
|
-
|
21
|
-
# Executes a ruby script (taking into account WIN32 platform quirks).
|
22
|
-
#
|
23
|
-
# ==== Parameters
|
24
|
-
# command<String>:: the command to be executed.
|
25
|
-
# log_status<Boolean>:: if false, does not log the status. True by default.
|
26
|
-
# If a symbol is given, uses it as the output color.
|
27
|
-
#
|
28
|
-
def run_ruby_script(command, log_status=true)
|
29
|
-
run("ruby #{command}", log_status)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Run a thor command. A hash of options can be given and it's converted to
|
33
|
-
# switches.
|
34
|
-
#
|
35
|
-
# ==== Parameters
|
36
|
-
# task<String>:: the task to be invoked
|
37
|
-
# args<Array>:: arguments to the task
|
38
|
-
# options<Hash>:: a hash with options used on invocation
|
39
|
-
# log_status<Boolean>:: if false, does not log the status. True by default.
|
40
|
-
# If a symbol is given, uses it as the output color.
|
41
|
-
#
|
42
|
-
# ==== Examples
|
43
|
-
#
|
44
|
-
# thor :install, "http://gist.github.com/103208"
|
45
|
-
# #=> thor install http://gist.github.com/103208
|
46
|
-
#
|
47
|
-
# thor :list, :all => true, :substring => 'rails'
|
48
|
-
# #=> thor list --all --substring=rails
|
49
|
-
#
|
50
|
-
def thor(task, *args)
|
51
|
-
log_status = args.last.is_a?(Symbol) || [true, false].include?(args.last) ? args.pop : true
|
52
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
53
|
-
|
54
|
-
in_root do
|
55
|
-
args.unshift "thor #{task}"
|
56
|
-
args.push Thor::Options.to_switches(options)
|
57
|
-
run args.join(' ').strip, log_status
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
class Thor
|
2
|
-
module Actions
|
3
|
-
|
4
|
-
# Run a regular expression replacement on a file.
|
5
|
-
#
|
6
|
-
# ==== Parameters
|
7
|
-
# path<String>:: path of the file to be changed
|
8
|
-
# flag<Regexp|String>:: the regexp or string to be replaced
|
9
|
-
# replacement<String>:: the replacement, can be also given as a block
|
10
|
-
# log_status<Boolean>:: if false, does not log the status. True by default.
|
11
|
-
# If a symbol is given, uses it as the output color.
|
12
|
-
#
|
13
|
-
# ==== Example
|
14
|
-
#
|
15
|
-
# gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
|
16
|
-
#
|
17
|
-
# gsub_file 'README', /rake/, :green do |match|
|
18
|
-
# match << " no more. Use thor!"
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
def gsub_file(path, flag, *args, &block)
|
22
|
-
log_status = args.last.is_a?(Symbol) || [ true, false ].include?(args.last) ? args.pop : true
|
23
|
-
|
24
|
-
path = File.expand_path(path, root)
|
25
|
-
say_status_if_log :gsub, relative_to_absolute_root(path), log_status
|
26
|
-
|
27
|
-
unless options[:pretend]
|
28
|
-
content = File.read(path)
|
29
|
-
content.gsub!(flag, *args, &block)
|
30
|
-
File.open(path, 'wb') { |file| file.write(content) }
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# Append text to a file.
|
35
|
-
#
|
36
|
-
# ==== Parameters
|
37
|
-
# path<String>:: path of the file to be changed
|
38
|
-
# data<String>:: the data to append to the file, can be also given as a block.
|
39
|
-
# log_status<Boolean>:: if false, does not log the status. True by default.
|
40
|
-
# If a symbol is given, uses it as the output color.
|
41
|
-
#
|
42
|
-
# ==== Example
|
43
|
-
#
|
44
|
-
# append_file 'config/environments/test.rb', 'config.gem "rspec"'
|
45
|
-
#
|
46
|
-
def append_file(path, data=nil, log_status=true, &block)
|
47
|
-
path = File.expand_path(path, root)
|
48
|
-
say_status_if_log :append, relative_to_absolute_root(path), log_status
|
49
|
-
|
50
|
-
File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend]
|
51
|
-
end
|
52
|
-
|
53
|
-
# Prepend text to a file.
|
54
|
-
#
|
55
|
-
# ==== Parameters
|
56
|
-
# path<String>:: path of the file to be changed
|
57
|
-
# data<String>:: the data to prepend to the file, can be also given as a block.
|
58
|
-
# log_status<Boolean>:: if false, does not log the status. True by default.
|
59
|
-
# If a symbol is given, uses it as the output color.
|
60
|
-
#
|
61
|
-
# ==== Example
|
62
|
-
#
|
63
|
-
# prepend_file 'config/environments/test.rb', 'config.gem "rspec"'
|
64
|
-
#
|
65
|
-
def prepend_file(path, data=nil, log_status=true, &block)
|
66
|
-
path = File.expand_path(path, root)
|
67
|
-
say_status_if_log :prepend, relative_to_absolute_root(path), log_status
|
68
|
-
|
69
|
-
unless options[:pretend]
|
70
|
-
content = data || block.call
|
71
|
-
content << File.read(path)
|
72
|
-
File.open(path, 'wb') { |file| file.write(content) }
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
end
|