thor 0.12.0 → 0.12.2
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/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
|