thor 0.12.0 → 0.12.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Thorfile +2 -1
- data/lib/thor.rb +29 -27
- data/lib/thor/actions.rb +5 -5
- data/lib/thor/actions/directory.rb +2 -4
- data/lib/thor/actions/file_manipulation.rb +8 -4
- data/lib/thor/base.rb +30 -32
- data/lib/thor/group.rb +7 -11
- data/lib/thor/runner.rb +7 -3
- data/lib/thor/shell.rb +1 -1
- data/lib/thor/shell/basic.rb +49 -29
- data/lib/thor/task.rb +29 -39
- data/lib/thor/version.rb +1 -1
- data/spec/actions/directory_spec.rb +4 -3
- data/spec/actions/file_manipulation_spec.rb +14 -1
- data/spec/base_spec.rb +1 -1
- data/spec/fixtures/bundle/main.thor +1 -0
- data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
- data/spec/fixtures/doc/README +3 -0
- data/spec/fixtures/group.thor +83 -0
- data/spec/fixtures/invoke.thor +112 -0
- data/spec/fixtures/script.thor +130 -0
- data/spec/fixtures/task.thor +10 -0
- data/spec/runner_spec.rb +5 -5
- data/spec/shell/basic_spec.rb +16 -22
- data/spec/spec.opts +1 -0
- data/spec/task_spec.rb +3 -23
- data/spec/thor_spec.rb +19 -17
- metadata +51 -16
data/Thorfile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# enconding: utf-8
|
2
2
|
|
3
3
|
require File.join(File.dirname(__FILE__), "lib", "thor", "version")
|
4
|
+
require 'rubygems'
|
4
5
|
require 'thor/rake_compat'
|
5
6
|
require 'spec/rake/spectask'
|
6
7
|
require 'rdoc/task'
|
@@ -52,7 +53,7 @@ class Default < Thor
|
|
52
53
|
s.bindir = "bin"
|
53
54
|
s.executables = %w( thor rake2thor )
|
54
55
|
s.files = s.extra_rdoc_files + Dir.glob("{bin,lib}/**/*")
|
55
|
-
s.
|
56
|
+
s.test_files.include 'spec/**/*'
|
56
57
|
s.test_files.exclude 'spec/sandbox/**/*'
|
57
58
|
end
|
58
59
|
|
data/lib/thor.rb
CHANGED
@@ -78,14 +78,14 @@ class Thor
|
|
78
78
|
@method_options
|
79
79
|
end
|
80
80
|
|
81
|
-
# Adds an option to the set of
|
81
|
+
# Adds an option to the set of method options. If :for is given as option,
|
82
82
|
# it allows you to change the options from a previous defined task.
|
83
83
|
#
|
84
84
|
# def previous_task
|
85
85
|
# # magic
|
86
86
|
# end
|
87
87
|
#
|
88
|
-
#
|
88
|
+
# method_option :foo => :bar, :for => :previous_task
|
89
89
|
#
|
90
90
|
# def next_task
|
91
91
|
# # magic
|
@@ -101,7 +101,6 @@ class Thor
|
|
101
101
|
# :default - Default value for this argument. It cannot be required and have default values.
|
102
102
|
# :aliases - Aliases for this option.
|
103
103
|
# :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
|
104
|
-
# :group - The group for this options. Use by class options to output options in different levels.
|
105
104
|
# :banner - String to show on usage notes.
|
106
105
|
#
|
107
106
|
def method_option(name, options={})
|
@@ -150,39 +149,42 @@ class Thor
|
|
150
149
|
# namespace:: When true, shows the namespace in the output before the usage.
|
151
150
|
# skip_inherited:: When true, does not show tasks from superclass.
|
152
151
|
#
|
153
|
-
def help(shell,
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
task = all_tasks[meth]
|
158
|
-
raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task
|
152
|
+
def help(shell, options={})
|
153
|
+
if options[:task]
|
154
|
+
task = all_tasks[options[:task]]
|
155
|
+
raise UndefinedTaskError, "task '#{options[:task]}' could not be found in namespace '#{self.namespace}'" unless task
|
159
156
|
|
160
157
|
shell.say "Usage:"
|
161
|
-
shell.say " #{banner(task
|
158
|
+
shell.say " #{banner(task)}"
|
162
159
|
shell.say
|
163
|
-
class_options_help(shell,
|
160
|
+
class_options_help(shell, nil => task.options.map { |_, o| o })
|
164
161
|
shell.say task.description
|
165
162
|
else
|
166
|
-
list = (options[:short]
|
167
|
-
|
168
|
-
|
169
|
-
|
163
|
+
list = printable_tasks(!options[:short])
|
164
|
+
|
165
|
+
Thor::Util.thor_classes_in(self).each do |klass|
|
166
|
+
list += klass.printable_tasks(false)
|
170
167
|
end
|
171
168
|
|
172
|
-
|
169
|
+
list.sort!{ |a,b| a[0] <=> b[0] }
|
170
|
+
|
173
171
|
if options[:short]
|
174
|
-
shell.
|
172
|
+
shell.print_table(list, :truncate => true)
|
175
173
|
else
|
176
174
|
shell.say "Tasks:"
|
177
|
-
shell.
|
178
|
-
|
179
|
-
|
180
|
-
Thor::Util.thor_classes_in(self).each do |subclass|
|
181
|
-
namespace = options[:namespace] == true || subclass.namespace.gsub(/^#{self.namespace}:/, '')
|
182
|
-
subclass.help(shell, options.merge(:short => true, :namespace => namespace))
|
175
|
+
shell.print_table(list, :ident => 2, :truncate => true)
|
176
|
+
shell.say
|
177
|
+
class_options_help(shell)
|
183
178
|
end
|
179
|
+
end
|
180
|
+
end
|
184
181
|
|
185
|
-
|
182
|
+
def printable_tasks(all=true)
|
183
|
+
(all ? all_tasks : tasks).map do |_, task|
|
184
|
+
item = []
|
185
|
+
item << banner(task)
|
186
|
+
item << "# #{task.description}" if task.description
|
187
|
+
item
|
186
188
|
end
|
187
189
|
end
|
188
190
|
|
@@ -193,8 +195,8 @@ class Thor
|
|
193
195
|
# the task that is going to be invoked and a boolean which indicates if
|
194
196
|
# the namespace should be displayed as arguments.
|
195
197
|
#
|
196
|
-
def banner(task
|
197
|
-
task.formatted_usage(self
|
198
|
+
def banner(task)
|
199
|
+
"thor " + task.formatted_usage(self)
|
198
200
|
end
|
199
201
|
|
200
202
|
def baseclass #:nodoc:
|
@@ -237,6 +239,6 @@ class Thor
|
|
237
239
|
|
238
240
|
desc "help [TASK]", "Describe available tasks or one specific task"
|
239
241
|
def help(task=nil)
|
240
|
-
self.class.help(shell, task
|
242
|
+
self.class.help(shell, :task => task)
|
241
243
|
end
|
242
244
|
end
|
data/lib/thor/actions.rb
CHANGED
@@ -38,17 +38,17 @@ class Thor
|
|
38
38
|
# Add runtime options that help actions execution.
|
39
39
|
#
|
40
40
|
def add_runtime_options!
|
41
|
-
class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
|
42
|
-
:desc => "Run but do not make any changes"
|
43
|
-
|
44
41
|
class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
|
45
42
|
:desc => "Overwrite files that already exist"
|
46
43
|
|
47
|
-
class_option :
|
48
|
-
|
44
|
+
class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
|
45
|
+
:desc => "Run but do not make any changes"
|
49
46
|
|
50
47
|
class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
|
51
48
|
:desc => "Supress status output"
|
49
|
+
|
50
|
+
class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
|
51
|
+
:desc => "Skip files that already exist"
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -79,11 +79,9 @@ class Thor
|
|
79
79
|
next if dirname == given_destination
|
80
80
|
base.empty_directory(dirname, config)
|
81
81
|
when /\.tt$/
|
82
|
-
destination = base.template(file_source, file_destination[0..-4], config)
|
83
|
-
@block.call(destination) if @block
|
82
|
+
destination = base.template(file_source, file_destination[0..-4], config, &@block)
|
84
83
|
else
|
85
|
-
destination = base.copy_file(file_source, file_destination, config)
|
86
|
-
@block.call(destination) if @block
|
84
|
+
destination = base.copy_file(file_source, file_destination, config, &@block)
|
87
85
|
end
|
88
86
|
end
|
89
87
|
end
|
@@ -18,12 +18,14 @@ class Thor
|
|
18
18
|
#
|
19
19
|
# copy_file "doc/README"
|
20
20
|
#
|
21
|
-
def copy_file(source, destination=nil, config={})
|
21
|
+
def copy_file(source, destination=nil, config={}, &block)
|
22
22
|
destination ||= source
|
23
23
|
source = File.expand_path(find_in_source_paths(source.to_s))
|
24
24
|
|
25
25
|
create_file destination, nil, config do
|
26
|
-
File.read(source)
|
26
|
+
content = File.read(source)
|
27
|
+
content = block.call(content) if block
|
28
|
+
content
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -72,13 +74,15 @@ class Thor
|
|
72
74
|
#
|
73
75
|
# template "doc/README"
|
74
76
|
#
|
75
|
-
def template(source, destination=nil, config={})
|
77
|
+
def template(source, destination=nil, config={}, &block)
|
76
78
|
destination ||= source
|
77
79
|
source = File.expand_path(find_in_source_paths(source.to_s))
|
78
80
|
context = instance_eval('binding')
|
79
81
|
|
80
82
|
create_file destination, nil, config do
|
81
|
-
ERB.new(::File.read(source), nil, '-').result(context)
|
83
|
+
content = ERB.new(::File.read(source), nil, '-').result(context)
|
84
|
+
content = block.call(content) if block
|
85
|
+
content
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
data/lib/thor/base.rb
CHANGED
@@ -92,6 +92,8 @@ class Thor
|
|
92
92
|
end
|
93
93
|
|
94
94
|
module ClassMethods
|
95
|
+
attr_accessor :debugging
|
96
|
+
|
95
97
|
# Adds an argument to the class and creates an attr_accessor for it.
|
96
98
|
#
|
97
99
|
# Arguments are different from options in several aspects. The first one
|
@@ -347,10 +349,11 @@ class Thor
|
|
347
349
|
# Default way to start generators from the command line.
|
348
350
|
#
|
349
351
|
def start(given_args=ARGV, config={})
|
352
|
+
self.debugging = given_args.include?("--debug")
|
350
353
|
config[:shell] ||= Thor::Base.shell.new
|
351
354
|
yield
|
352
355
|
rescue Thor::Error => e
|
353
|
-
if
|
356
|
+
if debugging
|
354
357
|
raise e
|
355
358
|
else
|
356
359
|
config[:shell].error e.message
|
@@ -361,48 +364,43 @@ class Thor
|
|
361
364
|
protected
|
362
365
|
|
363
366
|
# Prints the class options per group. If an option does not belong to
|
364
|
-
# any group, it
|
365
|
-
# hooks to add extra options, one of them if the third argument called
|
366
|
-
# extra_group that should be a hash in the format :group => Array[Options].
|
367
|
-
#
|
368
|
-
# The second is by returning a lambda used to print values. The lambda
|
369
|
-
# requires two options: the group name and the array of options.
|
367
|
+
# any group, it's printed as Class option.
|
370
368
|
#
|
371
|
-
def class_options_help(shell,
|
372
|
-
|
373
|
-
|
369
|
+
def class_options_help(shell, groups={}) #:nodoc:
|
370
|
+
# Group options by group
|
374
371
|
class_options.each do |_, value|
|
375
372
|
groups[value.group] ||= []
|
376
373
|
groups[value.group] << value
|
377
374
|
end
|
378
375
|
|
379
|
-
|
380
|
-
|
381
|
-
|
376
|
+
# Deal with default group
|
377
|
+
global_options = groups.delete(nil) || []
|
378
|
+
print_options(shell, global_options)
|
379
|
+
|
380
|
+
# Print all others
|
381
|
+
groups.each do |group_name, options|
|
382
|
+
print_options(shell, options, group_name)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# Receives a set of options and print them.
|
387
|
+
def print_options(shell, options, group_name=nil)
|
388
|
+
return if options.empty?
|
382
389
|
|
383
|
-
|
384
|
-
|
385
|
-
item.push(option.description ? "# #{option.description}" : "")
|
390
|
+
list = []
|
391
|
+
padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
|
386
392
|
|
387
|
-
|
388
|
-
|
389
|
-
|
393
|
+
options.each do |option|
|
394
|
+
item = [ option.usage(padding) ]
|
395
|
+
item.push(option.description ? "# #{option.description}" : "")
|
390
396
|
|
391
|
-
|
392
|
-
|
393
|
-
shell.print_table(list, :ident => 2)
|
394
|
-
shell.say ""
|
395
|
-
end
|
397
|
+
list << item
|
398
|
+
list << [ "", "# Default: #{option.default}" ] if option.show_default?
|
396
399
|
end
|
397
400
|
|
398
|
-
#
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
# Print all others
|
403
|
-
groups = extra_group.merge(groups) if extra_group
|
404
|
-
groups.each(&printer)
|
405
|
-
printer
|
401
|
+
shell.say(group_name ? "#{group_name} options:" : "Options:")
|
402
|
+
shell.print_table(list, :ident => 2)
|
403
|
+
shell.say ""
|
406
404
|
end
|
407
405
|
|
408
406
|
# Raises an error if the word given is a Thor reserved word.
|
data/lib/thor/group.rb
CHANGED
@@ -46,8 +46,8 @@ class Thor::Group
|
|
46
46
|
shell.say banner
|
47
47
|
else
|
48
48
|
shell.say "Usage:"
|
49
|
-
shell.say " #{banner}"
|
50
|
-
shell.say
|
49
|
+
shell.say " #{banner}\n"
|
50
|
+
shell.say ""
|
51
51
|
class_options_help(shell)
|
52
52
|
shell.say self.desc if self.desc
|
53
53
|
end
|
@@ -177,15 +177,11 @@ class Thor::Group
|
|
177
177
|
# Overwrite class options help to allow invoked generators options to be
|
178
178
|
# shown recursively when invoking a generator.
|
179
179
|
#
|
180
|
-
def class_options_help(shell,
|
181
|
-
|
182
|
-
|
183
|
-
get_options_from_invocations(group_options, class_options) do |klass|
|
184
|
-
klass.send(:get_options_from_invocations, group_options, class_options)
|
180
|
+
def class_options_help(shell, groups={}) #:nodoc:
|
181
|
+
get_options_from_invocations(groups, class_options) do |klass|
|
182
|
+
klass.send(:get_options_from_invocations, groups, class_options)
|
185
183
|
end
|
186
|
-
|
187
|
-
group_options.merge!(extra_group) if extra_group
|
188
|
-
super(shell, ungrouped_name, group_options)
|
184
|
+
super(shell, groups)
|
189
185
|
end
|
190
186
|
|
191
187
|
# Get invocations array and merge options from invocations. Those
|
@@ -224,7 +220,7 @@ class Thor::Group
|
|
224
220
|
# thor class by another ways which is not the Thor::Runner.
|
225
221
|
#
|
226
222
|
def banner
|
227
|
-
"#{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}"
|
223
|
+
"thor #{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}"
|
228
224
|
end
|
229
225
|
|
230
226
|
def baseclass #:nodoc:
|
data/lib/thor/runner.rb
CHANGED
@@ -149,6 +149,10 @@ class Thor::Runner < Thor #:nodoc:
|
|
149
149
|
|
150
150
|
private
|
151
151
|
|
152
|
+
def self.banner(task)
|
153
|
+
"thor " + task.formatted_usage(self, false)
|
154
|
+
end
|
155
|
+
|
152
156
|
def thor_root
|
153
157
|
Thor::Util.thor_root
|
154
158
|
end
|
@@ -289,11 +293,11 @@ class Thor::Runner < Thor #:nodoc:
|
|
289
293
|
unless klass.tasks.empty?
|
290
294
|
base = klass.namespace
|
291
295
|
|
292
|
-
|
293
|
-
say shell.set_color(base, color, true)
|
296
|
+
say shell.set_color(base, :blue, true)
|
294
297
|
say "-" * base.length
|
295
298
|
|
296
|
-
klass.help(shell, :short => true
|
299
|
+
klass.help(shell, :short => true)
|
300
|
+
say
|
297
301
|
end
|
298
302
|
end
|
299
303
|
end
|
data/lib/thor/shell.rb
CHANGED
@@ -22,7 +22,7 @@ class Thor
|
|
22
22
|
end
|
23
23
|
|
24
24
|
module Shell
|
25
|
-
SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :
|
25
|
+
SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_table]
|
26
26
|
|
27
27
|
# Add shell to initialize config values.
|
28
28
|
#
|
data/lib/thor/shell/basic.rb
CHANGED
@@ -75,30 +75,6 @@ class Thor
|
|
75
75
|
!yes?(statement, color)
|
76
76
|
end
|
77
77
|
|
78
|
-
# Prints a list of items.
|
79
|
-
#
|
80
|
-
# ==== Parameters
|
81
|
-
# list<Array[String, String, ...]>
|
82
|
-
#
|
83
|
-
# ==== Options
|
84
|
-
# mode:: Can be :rows or :inline. Defaults to :rows.
|
85
|
-
# ident:: Ident each item with the value given.
|
86
|
-
#
|
87
|
-
def print_list(list, options={})
|
88
|
-
return if list.empty?
|
89
|
-
|
90
|
-
ident = " " * (options[:ident] || 0)
|
91
|
-
content = case options[:mode]
|
92
|
-
when :inline
|
93
|
-
last = list.pop
|
94
|
-
"#{list.join(", ")}, and #{last}"
|
95
|
-
else # rows
|
96
|
-
ident + list.join("\n#{ident}")
|
97
|
-
end
|
98
|
-
|
99
|
-
$stdout.puts content
|
100
|
-
end
|
101
|
-
|
102
78
|
# Prints a table.
|
103
79
|
#
|
104
80
|
# ==== Parameters
|
@@ -110,20 +86,26 @@ class Thor
|
|
110
86
|
def print_table(table, options={})
|
111
87
|
return if table.empty?
|
112
88
|
|
113
|
-
formats = []
|
89
|
+
formats, ident = [], options[:ident].to_i
|
90
|
+
options[:truncate] = terminal_width if options[:truncate] == true
|
91
|
+
|
114
92
|
0.upto(table.first.length - 2) do |i|
|
115
93
|
maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size
|
116
94
|
formats << "%-#{maxima + 2}s"
|
117
95
|
end
|
118
96
|
|
119
|
-
formats[0] = formats[0].insert(0, " " *
|
97
|
+
formats[0] = formats[0].insert(0, " " * ident)
|
120
98
|
formats << "%s"
|
121
99
|
|
122
100
|
table.each do |row|
|
101
|
+
sentence = ""
|
102
|
+
|
123
103
|
row.each_with_index do |column, i|
|
124
|
-
|
104
|
+
sentence << formats[i] % column.to_s
|
125
105
|
end
|
126
|
-
|
106
|
+
|
107
|
+
sentence = truncate(sentence, options[:truncate]) if options[:truncate]
|
108
|
+
$stdout.puts sentence
|
127
109
|
end
|
128
110
|
end
|
129
111
|
|
@@ -143,7 +125,7 @@ class Thor
|
|
143
125
|
answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
|
144
126
|
|
145
127
|
case answer
|
146
|
-
when is?(:yes), is?(:force)
|
128
|
+
when is?(:yes), is?(:force), ""
|
147
129
|
return true
|
148
130
|
when is?(:no), is?(:skip)
|
149
131
|
return false
|
@@ -214,6 +196,44 @@ HELP
|
|
214
196
|
base && base.options[:quiet]
|
215
197
|
end
|
216
198
|
|
199
|
+
# This code was copied from Rake, available under MIT-LICENSE
|
200
|
+
# Copyright (c) 2003, 2004 Jim Weirich
|
201
|
+
def terminal_width
|
202
|
+
if ENV['THOR_COLUMNS']
|
203
|
+
result = ENV['THOR_COLUMNS'].to_i
|
204
|
+
else
|
205
|
+
result = unix? ? dynamic_width : 80
|
206
|
+
end
|
207
|
+
(result < 10) ? 80 : result
|
208
|
+
rescue
|
209
|
+
80
|
210
|
+
end
|
211
|
+
|
212
|
+
# Calculate the dynamic width of the terminal
|
213
|
+
def dynamic_width
|
214
|
+
@dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
|
215
|
+
end
|
216
|
+
|
217
|
+
def dynamic_width_stty
|
218
|
+
%x{stty size 2>/dev/null}.split[1].to_i
|
219
|
+
end
|
220
|
+
|
221
|
+
def dynamic_width_tput
|
222
|
+
%x{tput cols 2>/dev/null}.to_i
|
223
|
+
end
|
224
|
+
|
225
|
+
def unix?
|
226
|
+
RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
|
227
|
+
end
|
228
|
+
|
229
|
+
def truncate(string, width)
|
230
|
+
if string.length <= width
|
231
|
+
string
|
232
|
+
else
|
233
|
+
( string[0, width-3] || "" ) + "..."
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
217
237
|
end
|
218
238
|
end
|
219
239
|
end
|