thor 0.12.0 → 0.13.0
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/CHANGELOG.rdoc +5 -5
- data/README.rdoc +65 -2
- data/Thorfile +15 -9
- data/bin/thor +1 -0
- data/lib/thor/actions/create_file.rb +2 -2
- data/lib/thor/actions/directory.rb +2 -4
- data/lib/thor/actions/file_manipulation.rb +10 -6
- data/lib/thor/actions/inject_into_file.rb +10 -7
- data/lib/thor/actions.rb +6 -5
- data/lib/thor/base.rb +45 -32
- data/lib/thor/core_ext/file_binary_read.rb +9 -0
- data/lib/thor/group.rb +46 -37
- data/lib/thor/runner.rb +49 -42
- data/lib/thor/shell/basic.rb +49 -29
- data/lib/thor/shell/color.rb +1 -1
- data/lib/thor/shell.rb +1 -1
- data/lib/thor/task.rb +27 -38
- data/lib/thor/util.rb +4 -22
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +43 -45
- data/spec/actions/create_file_spec.rb +7 -7
- data/spec/actions/directory_spec.rb +5 -4
- data/spec/actions/file_manipulation_spec.rb +29 -16
- data/spec/actions/inject_into_file_spec.rb +29 -0
- data/spec/actions_spec.rb +14 -13
- data/spec/base_spec.rb +16 -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 +134 -0
- data/spec/fixtures/task.thor +10 -0
- data/spec/group_spec.rb +1 -7
- data/spec/runner_spec.rb +35 -39
- data/spec/shell/basic_spec.rb +56 -62
- data/spec/shell/color_spec.rb +6 -6
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +5 -4
- data/spec/task_spec.rb +14 -32
- data/spec/thor_spec.rb +21 -22
- data/spec/util_spec.rb +7 -31
- metadata +28 -19
data/lib/thor/runner.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'fileutils'
|
|
2
|
+
require 'thor/core_ext/file_binary_read'
|
|
2
3
|
require 'open-uri'
|
|
3
4
|
require 'yaml'
|
|
4
5
|
require 'digest/md5'
|
|
@@ -32,7 +33,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
desc "install NAME", "Install an optionally named Thor file into your system tasks"
|
|
35
|
-
method_options :as => :string, :relative => :boolean
|
|
36
|
+
method_options :as => :string, :relative => :boolean, :force => :boolean
|
|
36
37
|
def install(name)
|
|
37
38
|
initialize_thorfiles
|
|
38
39
|
|
|
@@ -55,7 +56,9 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
55
56
|
say "Your Thorfile contains:"
|
|
56
57
|
say contents
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
unless options["force"]
|
|
60
|
+
return false if no?("Do you wish to continue [y/N]?")
|
|
61
|
+
end
|
|
59
62
|
|
|
60
63
|
as = options["as"] || begin
|
|
61
64
|
first_line = contents.split("\n")[0]
|
|
@@ -124,11 +127,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
124
127
|
method_options :internal => :boolean
|
|
125
128
|
def installed
|
|
126
129
|
initialize_thorfiles(nil, true)
|
|
127
|
-
|
|
128
|
-
klasses = Thor::Base.subclasses
|
|
129
|
-
klasses -= [Thor, Thor::Runner] unless options["internal"]
|
|
130
|
-
|
|
131
|
-
display_klasses(true, klasses)
|
|
130
|
+
display_klasses(true, options["internal"])
|
|
132
131
|
end
|
|
133
132
|
|
|
134
133
|
desc "list [SEARCH]", "List the available thor tasks (--substring means .*SEARCH)"
|
|
@@ -144,11 +143,15 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
144
143
|
(options[:all] || k.group == group) && k.namespace =~ search
|
|
145
144
|
end
|
|
146
145
|
|
|
147
|
-
display_klasses(false, klasses)
|
|
146
|
+
display_klasses(false, false, klasses)
|
|
148
147
|
end
|
|
149
148
|
|
|
150
149
|
private
|
|
151
150
|
|
|
151
|
+
def self.banner(task)
|
|
152
|
+
"thor " + task.formatted_usage(self, false)
|
|
153
|
+
end
|
|
154
|
+
|
|
152
155
|
def thor_root
|
|
153
156
|
Thor::Util.thor_root
|
|
154
157
|
end
|
|
@@ -156,7 +159,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
156
159
|
def thor_yaml
|
|
157
160
|
@thor_yaml ||= begin
|
|
158
161
|
yaml_file = File.join(thor_root, "thor.yml")
|
|
159
|
-
yaml
|
|
162
|
+
yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
|
|
160
163
|
yaml || {}
|
|
161
164
|
end
|
|
162
165
|
end
|
|
@@ -215,9 +218,6 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
215
218
|
# 5. c:\ <-- no Thorfiles found!
|
|
216
219
|
#
|
|
217
220
|
def thorfiles(relevant_to=nil, skip_lookup=false)
|
|
218
|
-
# TODO Remove this dealing with deprecated thor when :namespaces: is available as constants
|
|
219
|
-
save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml)
|
|
220
|
-
|
|
221
221
|
thorfiles = []
|
|
222
222
|
|
|
223
223
|
unless skip_lookup
|
|
@@ -253,47 +253,54 @@ class Thor::Runner < Thor #:nodoc:
|
|
|
253
253
|
# Display information about the given klasses. If with_module is given,
|
|
254
254
|
# it shows a table with information extracted from the yaml file.
|
|
255
255
|
#
|
|
256
|
-
def display_klasses(with_modules=false, klasses=Thor.subclasses)
|
|
257
|
-
klasses -= [Thor, Thor::Runner] unless
|
|
256
|
+
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
|
|
257
|
+
klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
|
|
258
|
+
|
|
258
259
|
raise Error, "No Thor tasks available" if klasses.empty?
|
|
260
|
+
show_modules if with_modules && !thor_yaml.empty?
|
|
259
261
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
262
|
+
# Remove subclasses
|
|
263
|
+
klasses.dup.each do |klass|
|
|
264
|
+
klasses -= Thor::Util.thor_classes_in(klass)
|
|
265
|
+
end
|
|
263
266
|
|
|
264
|
-
|
|
265
|
-
|
|
267
|
+
list = Hash.new { |h,k| h[k] = [] }
|
|
268
|
+
groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
|
|
266
269
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
end
|
|
270
|
+
# Get classes which inherit from Thor
|
|
271
|
+
(klasses - groups).each { |k| list[k.namespace] += k.printable_tasks(false) }
|
|
270
272
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
# Get classes which inherit from Thor::Base
|
|
274
|
+
groups.map! { |k| k.printable_tasks(false).first }
|
|
275
|
+
list["root"] = groups
|
|
274
276
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
# Order namespaces with default coming first
|
|
278
|
+
list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
|
|
279
|
+
list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? }
|
|
280
|
+
end
|
|
279
281
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
def display_tasks(namespace, list) #:nodoc:
|
|
283
|
+
list.sort!{ |a,b| a[0] <=> b[0] }
|
|
284
|
+
|
|
285
|
+
say shell.set_color(namespace, :blue, true)
|
|
286
|
+
say "-" * namespace.size
|
|
287
|
+
|
|
288
|
+
print_table(list, :truncate => true)
|
|
289
|
+
say
|
|
284
290
|
end
|
|
285
291
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
unless klass.tasks.empty?
|
|
290
|
-
base = klass.namespace
|
|
292
|
+
def show_modules #:nodoc:
|
|
293
|
+
info = []
|
|
294
|
+
labels = ["Modules", "Namespaces"]
|
|
291
295
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
say "-" * base.length
|
|
296
|
+
info << labels
|
|
297
|
+
info << [ "-" * labels[0].size, "-" * labels[1].size ]
|
|
295
298
|
|
|
296
|
-
|
|
299
|
+
thor_yaml.each do |name, hash|
|
|
300
|
+
info << [ name, hash[:namespaces].join(", ") ]
|
|
297
301
|
end
|
|
302
|
+
|
|
303
|
+
print_table info
|
|
304
|
+
say ""
|
|
298
305
|
end
|
|
299
306
|
end
|
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
|
data/lib/thor/shell/color.rb
CHANGED
|
@@ -63,7 +63,7 @@ class Thor
|
|
|
63
63
|
#
|
|
64
64
|
def show_diff(destination, content) #:nodoc:
|
|
65
65
|
if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil?
|
|
66
|
-
actual = File.
|
|
66
|
+
actual = File.binread(destination).to_s.split("\n")
|
|
67
67
|
content = content.to_s.split("\n")
|
|
68
68
|
|
|
69
69
|
Diff::LCS.sdiff(actual, content).each do |diff|
|
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/task.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
class Thor
|
|
2
2
|
class Task < Struct.new(:name, :description, :usage, :options)
|
|
3
|
+
FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
|
|
3
4
|
|
|
4
5
|
# A dynamic task that handles method missing scenarios.
|
|
5
|
-
#
|
|
6
6
|
class Dynamic < Task
|
|
7
|
-
def initialize(name)
|
|
8
|
-
super(name.to_s, "A dynamically-generated task", name.to_s)
|
|
7
|
+
def initialize(name, options=nil)
|
|
8
|
+
super(name.to_s, "A dynamically-generated task", name.to_s, options)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def run(instance, args=[])
|
|
@@ -25,72 +25,61 @@ class Thor
|
|
|
25
25
|
self.options = other.options.dup if other.options
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def short_description
|
|
29
|
-
description.split("\n").first if description
|
|
30
|
-
end
|
|
31
|
-
|
|
32
28
|
# By default, a task invokes a method in the thor class. You can change this
|
|
33
29
|
# implementation to create custom tasks.
|
|
34
|
-
#
|
|
35
30
|
def run(instance, args=[])
|
|
36
31
|
raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
|
|
37
32
|
instance.send(name, *args)
|
|
38
33
|
rescue ArgumentError => e
|
|
34
|
+
raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
|
|
39
35
|
parse_argument_error(instance, e, caller)
|
|
40
36
|
rescue NoMethodError => e
|
|
37
|
+
raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
|
|
41
38
|
parse_no_method_error(instance, e)
|
|
42
39
|
end
|
|
43
40
|
|
|
44
|
-
# Returns the formatted usage
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"#{
|
|
41
|
+
# Returns the formatted usage by injecting given required arguments
|
|
42
|
+
# and required options into the given usage.
|
|
43
|
+
def formatted_usage(klass, namespace=true)
|
|
44
|
+
namespace = klass.namespace unless namespace == false
|
|
45
|
+
|
|
46
|
+
# Add namespace
|
|
47
|
+
formatted = if namespace
|
|
48
|
+
"#{namespace.gsub(/^(default|thor:runner:)/,'')}:"
|
|
52
49
|
else
|
|
53
50
|
""
|
|
54
51
|
end
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
formatted <<
|
|
58
|
-
formatted.strip!
|
|
59
|
-
formatted
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Injects the class arguments into the task usage.
|
|
63
|
-
#
|
|
64
|
-
def formatted_arguments(klass)
|
|
65
|
-
if klass && !klass.arguments.empty?
|
|
53
|
+
# Add usage with required arguments
|
|
54
|
+
formatted << if klass && !klass.arguments.empty?
|
|
66
55
|
usage.to_s.gsub(/^#{name}/) do |match|
|
|
67
|
-
match << " " << klass.arguments.map{ |a| a.usage }.join(' ')
|
|
56
|
+
match << " " << klass.arguments.map{ |a| a.usage }.compact.join(' ')
|
|
68
57
|
end
|
|
69
58
|
else
|
|
70
59
|
usage.to_s
|
|
71
60
|
end
|
|
72
|
-
end
|
|
73
61
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
62
|
+
# Add required options
|
|
63
|
+
formatted << " #{required_options}"
|
|
64
|
+
|
|
65
|
+
# Strip and go!
|
|
66
|
+
formatted.strip
|
|
78
67
|
end
|
|
79
68
|
|
|
80
69
|
protected
|
|
81
70
|
|
|
71
|
+
def required_options
|
|
72
|
+
@required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
|
|
73
|
+
end
|
|
74
|
+
|
|
82
75
|
# Given a target, checks if this class name is not a private/protected method.
|
|
83
|
-
#
|
|
84
76
|
def public_method?(instance) #:nodoc:
|
|
85
77
|
collection = instance.private_methods + instance.protected_methods
|
|
86
78
|
(collection & [name.to_s, name.to_sym]).empty?
|
|
87
79
|
end
|
|
88
80
|
|
|
89
|
-
# Clean everything that comes from the Thor gempath and remove the caller.
|
|
90
|
-
#
|
|
91
81
|
def sans_backtrace(backtrace, caller) #:nodoc:
|
|
92
|
-
|
|
93
|
-
saned = backtrace.reject { |frame| frame =~ dirname }
|
|
82
|
+
saned = backtrace.reject { |frame| frame =~ FILE_REGEXP }
|
|
94
83
|
saned -= caller
|
|
95
84
|
end
|
|
96
85
|
|
|
@@ -102,7 +91,7 @@ class Thor
|
|
|
102
91
|
raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
|
|
103
92
|
else
|
|
104
93
|
raise InvocationError, "'#{name}' was called incorrectly. Call as " <<
|
|
105
|
-
"'#{formatted_usage(instance.class
|
|
94
|
+
"'#{formatted_usage(instance.class)}'"
|
|
106
95
|
end
|
|
107
96
|
else
|
|
108
97
|
raise e
|
data/lib/thor/util.rb
CHANGED
|
@@ -76,8 +76,10 @@ class Thor
|
|
|
76
76
|
# Returns the thor classes declared inside the given class.
|
|
77
77
|
#
|
|
78
78
|
def self.thor_classes_in(klass)
|
|
79
|
+
stringfied_constants = klass.constants.map { |c| c.to_s }
|
|
79
80
|
Thor::Base.subclasses.select do |subclass|
|
|
80
|
-
|
|
81
|
+
next unless subclass.name
|
|
82
|
+
stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", ''))
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
|
|
@@ -155,7 +157,7 @@ class Thor
|
|
|
155
157
|
# inside the sandbox to avoid namespacing conflicts.
|
|
156
158
|
#
|
|
157
159
|
def self.load_thorfile(path, content=nil)
|
|
158
|
-
content ||= File.
|
|
160
|
+
content ||= File.binread(path)
|
|
159
161
|
|
|
160
162
|
begin
|
|
161
163
|
Thor::Sandbox.class_eval(content, path)
|
|
@@ -164,26 +166,6 @@ class Thor
|
|
|
164
166
|
end
|
|
165
167
|
end
|
|
166
168
|
|
|
167
|
-
# Receives a yaml (hash) and updates all constants entries to namespace.
|
|
168
|
-
# This was added to deal with deprecated versions of Thor.
|
|
169
|
-
#
|
|
170
|
-
# TODO Deprecate this method in the future.
|
|
171
|
-
#
|
|
172
|
-
# ==== Returns
|
|
173
|
-
# TrueClass|FalseClass:: Returns true if any change to the yaml file was made.
|
|
174
|
-
#
|
|
175
|
-
def self.convert_constants_to_namespaces(yaml)
|
|
176
|
-
yaml_changed = false
|
|
177
|
-
|
|
178
|
-
yaml.each do |k, v|
|
|
179
|
-
next unless v[:constants] && v[:namespaces].nil?
|
|
180
|
-
yaml_changed = true
|
|
181
|
-
yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.namespace_from_thor_class(c)}
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
yaml_changed
|
|
185
|
-
end
|
|
186
|
-
|
|
187
169
|
def self.user_home
|
|
188
170
|
@@user_home ||= if ENV["HOME"]
|
|
189
171
|
ENV["HOME"]
|
data/lib/thor/version.rb
CHANGED
data/lib/thor.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'thor/base'
|
|
2
|
-
require 'thor/group'
|
|
3
|
-
require 'thor/actions'
|
|
4
2
|
|
|
3
|
+
# TODO: Update thor to allow for git-style CLI (git bisect run)
|
|
5
4
|
class Thor
|
|
6
5
|
class << self
|
|
7
6
|
# Sets the default task when thor is executed without an explicit task to be called.
|
|
@@ -78,14 +77,14 @@ class Thor
|
|
|
78
77
|
@method_options
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
# Adds an option to the set of
|
|
80
|
+
# Adds an option to the set of method options. If :for is given as option,
|
|
82
81
|
# it allows you to change the options from a previous defined task.
|
|
83
82
|
#
|
|
84
83
|
# def previous_task
|
|
85
84
|
# # magic
|
|
86
85
|
# end
|
|
87
86
|
#
|
|
88
|
-
#
|
|
87
|
+
# method_option :foo => :bar, :for => :previous_task
|
|
89
88
|
#
|
|
90
89
|
# def next_task
|
|
91
90
|
# # magic
|
|
@@ -101,7 +100,6 @@ class Thor
|
|
|
101
100
|
# :default - Default value for this argument. It cannot be required and have default values.
|
|
102
101
|
# :aliases - Aliases for this option.
|
|
103
102
|
# :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
103
|
# :banner - String to show on usage notes.
|
|
106
104
|
#
|
|
107
105
|
def method_option(name, options={})
|
|
@@ -140,49 +138,48 @@ class Thor
|
|
|
140
138
|
end
|
|
141
139
|
end
|
|
142
140
|
|
|
143
|
-
# Prints help information
|
|
144
|
-
# only about the specific task.
|
|
141
|
+
# Prints help information for the given task.
|
|
145
142
|
#
|
|
146
143
|
# ==== Parameters
|
|
147
|
-
#
|
|
144
|
+
# shell<Thor::Shell>
|
|
145
|
+
# task_name<String>
|
|
146
|
+
#
|
|
147
|
+
def task_help(shell, task_name)
|
|
148
|
+
task = all_tasks[task_name]
|
|
149
|
+
raise UndefinedTaskError, "task '#{task_name}' could not be found in namespace '#{self.namespace}'" unless task
|
|
150
|
+
|
|
151
|
+
shell.say "Usage:"
|
|
152
|
+
shell.say " #{banner(task)}"
|
|
153
|
+
shell.say
|
|
154
|
+
class_options_help(shell, nil => task.options.map { |_, o| o })
|
|
155
|
+
shell.say task.description
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Prints help information for this class.
|
|
148
159
|
#
|
|
149
|
-
# ====
|
|
150
|
-
#
|
|
151
|
-
# skip_inherited:: When true, does not show tasks from superclass.
|
|
160
|
+
# ==== Parameters
|
|
161
|
+
# shell<Thor::Shell>
|
|
152
162
|
#
|
|
153
|
-
def help(shell
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
shell.say "Usage:"
|
|
161
|
-
shell.say " #{banner(task, options[:namespace], false)}"
|
|
162
|
-
shell.say
|
|
163
|
-
class_options_help(shell, "Class", :Method => task.options.map { |_, o| o })
|
|
164
|
-
shell.say task.description
|
|
165
|
-
else
|
|
166
|
-
list = (options[:short] ? tasks : all_tasks).map do |_, task|
|
|
167
|
-
item = [ banner(task, options[:namespace]) ]
|
|
168
|
-
item << "# #{task.short_description}" if task.short_description
|
|
169
|
-
item << " "
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
options[:ident] ||= 2
|
|
173
|
-
if options[:short]
|
|
174
|
-
shell.print_list(list, :ident => options[:ident])
|
|
175
|
-
else
|
|
176
|
-
shell.say "Tasks:"
|
|
177
|
-
shell.print_list(list, :ident => options[:ident])
|
|
178
|
-
end
|
|
163
|
+
def help(shell)
|
|
164
|
+
list = printable_tasks
|
|
165
|
+
Thor::Util.thor_classes_in(self).each do |klass|
|
|
166
|
+
list += klass.printable_tasks(false)
|
|
167
|
+
end
|
|
168
|
+
list.sort!{ |a,b| a[0] <=> b[0] }
|
|
179
169
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
170
|
+
shell.say "Tasks:"
|
|
171
|
+
shell.print_table(list, :ident => 2, :truncate => true)
|
|
172
|
+
shell.say
|
|
173
|
+
class_options_help(shell)
|
|
174
|
+
end
|
|
184
175
|
|
|
185
|
-
|
|
176
|
+
# Returns tasks ready to be printed.
|
|
177
|
+
def printable_tasks(all=true)
|
|
178
|
+
(all ? all_tasks : tasks).map do |_, task|
|
|
179
|
+
item = []
|
|
180
|
+
item << banner(task)
|
|
181
|
+
item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
|
|
182
|
+
item
|
|
186
183
|
end
|
|
187
184
|
end
|
|
188
185
|
|
|
@@ -193,8 +190,9 @@ class Thor
|
|
|
193
190
|
# the task that is going to be invoked and a boolean which indicates if
|
|
194
191
|
# the namespace should be displayed as arguments.
|
|
195
192
|
#
|
|
196
|
-
def banner(task
|
|
197
|
-
|
|
193
|
+
def banner(task)
|
|
194
|
+
base = $thor_runner ? "thor" : File.basename($0.split(" ").first)
|
|
195
|
+
"#{base} #{task.formatted_usage(self, base == "thor")}"
|
|
198
196
|
end
|
|
199
197
|
|
|
200
198
|
def baseclass #:nodoc:
|
|
@@ -237,6 +235,6 @@ class Thor
|
|
|
237
235
|
|
|
238
236
|
desc "help [TASK]", "Describe available tasks or one specific task"
|
|
239
237
|
def help(task=nil)
|
|
240
|
-
self.class.
|
|
238
|
+
task ? self.class.task_help(shell, task) : self.class.help(shell)
|
|
241
239
|
end
|
|
242
240
|
end
|
|
@@ -8,7 +8,7 @@ describe Thor::Actions::CreateFile do
|
|
|
8
8
|
|
|
9
9
|
def create_file(destination=nil, config={}, options={})
|
|
10
10
|
@base = MyCounter.new([1,2], options, { :destination_root => destination_root })
|
|
11
|
-
stub(
|
|
11
|
+
@base.stub!(:file_name).and_return('rdoc')
|
|
12
12
|
|
|
13
13
|
@action = Thor::Actions::CreateFile.new(@base, destination, "CONFIGURATION",
|
|
14
14
|
{ :verbose => !@silence }.merge(config))
|
|
@@ -103,7 +103,7 @@ describe Thor::Actions::CreateFile do
|
|
|
103
103
|
|
|
104
104
|
it "shows conflict status to ther user" do
|
|
105
105
|
create_file("doc/config.rb").must_not be_identical
|
|
106
|
-
|
|
106
|
+
$stdin.should_receive(:gets).and_return('s')
|
|
107
107
|
file = File.join(destination_root, 'doc/config.rb')
|
|
108
108
|
|
|
109
109
|
content = invoke!
|
|
@@ -114,21 +114,21 @@ describe Thor::Actions::CreateFile do
|
|
|
114
114
|
|
|
115
115
|
it "creates the file if the file collision menu returns true" do
|
|
116
116
|
create_file("doc/config.rb")
|
|
117
|
-
|
|
117
|
+
$stdin.should_receive(:gets).and_return('y')
|
|
118
118
|
invoke!.must =~ /force doc\/config\.rb/
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
it "skips the file if the file collision menu returns false" do
|
|
122
122
|
create_file("doc/config.rb")
|
|
123
|
-
|
|
123
|
+
$stdin.should_receive(:gets).and_return('n')
|
|
124
124
|
invoke!.must =~ /skip doc\/config\.rb/
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
it "executes the block given to show file content" do
|
|
128
128
|
create_file("doc/config.rb")
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
$stdin.should_receive(:gets).and_return('d')
|
|
130
|
+
$stdin.should_receive(:gets).and_return('n')
|
|
131
|
+
@base.shell.should_receive(:system).with(/diff -u/)
|
|
132
132
|
invoke!
|
|
133
133
|
end
|
|
134
134
|
end
|
|
@@ -4,7 +4,7 @@ require 'thor/actions'
|
|
|
4
4
|
describe Thor::Actions::Directory do
|
|
5
5
|
before(:each) do
|
|
6
6
|
::FileUtils.rm_rf(destination_root)
|
|
7
|
-
stub(
|
|
7
|
+
invoker.stub!(:file_name).and_return("rdoc")
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def invoker
|
|
@@ -110,10 +110,11 @@ describe Thor::Actions::Directory do
|
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
it "yields a block" do
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
checked = false
|
|
114
|
+
invoke!("doc") do |content|
|
|
115
|
+
checked ||= !!(content =~ /FOO/)
|
|
116
116
|
end
|
|
117
|
+
checked.must be_true
|
|
117
118
|
end
|
|
118
119
|
end
|
|
119
120
|
|