thor 0.9.5 → 0.9.9
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 +17 -0
- data/lib/thor/options.rb +5 -1
- data/lib/thor/runner.rb +71 -26
- data/lib/thor/task.rb +16 -5
- data/lib/thor/tasks.rb +1 -1
- data/lib/thor/util.rb +41 -9
- data/lib/thor.rb +9 -1
- metadata +4 -4
data/CHANGELOG.rdoc
CHANGED
|
@@ -5,6 +5,23 @@
|
|
|
5
5
|
* Improve help output to list shorthand switches, too
|
|
6
6
|
* Investigate and fix deep namespacing ("foo:bar:baz") issues
|
|
7
7
|
|
|
8
|
+
== 0.9.8, released 2008-10-20
|
|
9
|
+
|
|
10
|
+
* Fixed some tiny issues that were introduced lately.
|
|
11
|
+
|
|
12
|
+
== 0.9.7, released 2008-10-13
|
|
13
|
+
|
|
14
|
+
* Setting global method options on the initialize method works as expected:
|
|
15
|
+
All other tasks will accept these global options in addition to their own.
|
|
16
|
+
* Added 'group' notion to Thor task sets (class Thor); by default all tasks
|
|
17
|
+
are in the 'standard' group. Running 'thor -T' will only show the standard
|
|
18
|
+
tasks - adding --all will show all tasks. You can also filter on a specific
|
|
19
|
+
group using the --group option: thor -T --group advanced
|
|
20
|
+
|
|
21
|
+
== 0.9.6, released 2008-09-13
|
|
22
|
+
|
|
23
|
+
* Generic improvements
|
|
24
|
+
|
|
8
25
|
== 0.9.5, released 2008-08-27
|
|
9
26
|
|
|
10
27
|
* Improve Windows compatibility
|
data/lib/thor/options.rb
CHANGED
|
@@ -65,6 +65,8 @@ class Thor
|
|
|
65
65
|
def initialize(switches)
|
|
66
66
|
@defaults = {}
|
|
67
67
|
@shorts = {}
|
|
68
|
+
|
|
69
|
+
@leading_non_opts, @trailing_non_opts = [], []
|
|
68
70
|
|
|
69
71
|
@switches = switches.inject({}) do |mem, (name, type)|
|
|
70
72
|
if name.is_a?(Array)
|
|
@@ -167,13 +169,15 @@ class Thor
|
|
|
167
169
|
else
|
|
168
170
|
sample = @defaults[undasherize(opt)]
|
|
169
171
|
sample ||= case type
|
|
170
|
-
when :optional then opt.gsub(/\-/, "").upcase
|
|
172
|
+
when :optional then undasherize(opt).gsub(/\-/, "_").upcase
|
|
171
173
|
when :numeric then "N"
|
|
172
174
|
end
|
|
173
175
|
"[" + opt + "=" + sample.to_s + "]"
|
|
174
176
|
end
|
|
175
177
|
end.join(" ")
|
|
176
178
|
end
|
|
179
|
+
|
|
180
|
+
alias :to_s :formatted_usage
|
|
177
181
|
|
|
178
182
|
private
|
|
179
183
|
|
data/lib/thor/runner.rb
CHANGED
|
@@ -18,8 +18,17 @@ class Thor::Runner < Thor
|
|
|
18
18
|
method_options :as => :optional, :relative => :boolean
|
|
19
19
|
def install(name)
|
|
20
20
|
initialize_thorfiles
|
|
21
|
+
|
|
22
|
+
base = name
|
|
23
|
+
package = :file
|
|
24
|
+
|
|
21
25
|
begin
|
|
22
|
-
|
|
26
|
+
if File.directory?(File.expand_path(name))
|
|
27
|
+
base, package = File.join(name, "main.thor"), :directory
|
|
28
|
+
contents = open(base).read
|
|
29
|
+
else
|
|
30
|
+
contents = open(name).read
|
|
31
|
+
end
|
|
23
32
|
rescue OpenURI::HTTPError
|
|
24
33
|
raise Error, "Error opening URI `#{name}'"
|
|
25
34
|
rescue Errno::ENOENT
|
|
@@ -35,9 +44,7 @@ class Thor::Runner < Thor
|
|
|
35
44
|
|
|
36
45
|
return false unless response =~ /^\s*y/i
|
|
37
46
|
|
|
38
|
-
constants = Thor::Util.constants_in_contents(contents)
|
|
39
|
-
|
|
40
|
-
# name = name =~ /\.thor$/ || is_uri ? name : "#{name}.thor"
|
|
47
|
+
constants = Thor::Util.constants_in_contents(contents, base)
|
|
41
48
|
|
|
42
49
|
as = options["as"] || begin
|
|
43
50
|
first_line = contents.split("\n")[0]
|
|
@@ -63,8 +70,12 @@ class Thor::Runner < Thor
|
|
|
63
70
|
|
|
64
71
|
puts "Storing thor file in your system repository"
|
|
65
72
|
|
|
66
|
-
File.
|
|
67
|
-
|
|
73
|
+
destination = File.join(thor_root, yaml[as][:filename])
|
|
74
|
+
|
|
75
|
+
if package == :file
|
|
76
|
+
File.open(destination, "w") {|f| f.puts contents }
|
|
77
|
+
else
|
|
78
|
+
FileUtils.cp_r(name, destination)
|
|
68
79
|
end
|
|
69
80
|
|
|
70
81
|
yaml[as][:filename] # Indicate sucess
|
|
@@ -78,7 +89,7 @@ class Thor::Runner < Thor
|
|
|
78
89
|
puts "Uninstalling #{name}."
|
|
79
90
|
|
|
80
91
|
file = File.join(thor_root, "#{yaml[name][:filename]}")
|
|
81
|
-
|
|
92
|
+
FileUtils.rm_rf(file)
|
|
82
93
|
yaml.delete(name)
|
|
83
94
|
save_yaml(yaml)
|
|
84
95
|
|
|
@@ -92,7 +103,7 @@ class Thor::Runner < Thor
|
|
|
92
103
|
|
|
93
104
|
puts "Updating `#{name}' from #{yaml[name][:location]}"
|
|
94
105
|
old_filename = yaml[name][:filename]
|
|
95
|
-
options
|
|
106
|
+
self.options = self.options.merge("as" => name)
|
|
96
107
|
filename = install(yaml[name][:location])
|
|
97
108
|
unless filename == old_filename
|
|
98
109
|
File.delete(File.join(thor_root, old_filename))
|
|
@@ -113,14 +124,20 @@ class Thor::Runner < Thor
|
|
|
113
124
|
end
|
|
114
125
|
|
|
115
126
|
desc "list [SEARCH]", "list the available thor tasks (--substring means SEARCH can be anywhere in the module)"
|
|
116
|
-
method_options :substring => :boolean
|
|
127
|
+
method_options :substring => :boolean,
|
|
128
|
+
:group => :optional,
|
|
129
|
+
:all => :boolean
|
|
117
130
|
def list(search = "")
|
|
118
131
|
initialize_thorfiles
|
|
119
132
|
search = ".*#{search}" if options["substring"]
|
|
120
133
|
search = /^#{search}.*/i
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
group = options[:group] || 'standard'
|
|
135
|
+
|
|
136
|
+
classes = Thor.subclasses.select do |k|
|
|
137
|
+
(options[:all] || k.group_name == group) &&
|
|
138
|
+
Thor::Util.constant_to_thor_path(k.name) =~ search
|
|
139
|
+
end
|
|
140
|
+
display_klasses(false, classes)
|
|
124
141
|
end
|
|
125
142
|
|
|
126
143
|
# Override Thor#help so we can give info about not-yet-loaded tasks
|
|
@@ -148,7 +165,10 @@ class Thor::Runner < Thor
|
|
|
148
165
|
# C:\Documents and Settings\james\.thor
|
|
149
166
|
#
|
|
150
167
|
# If we don't #gsub the \ character, Dir.glob will fail.
|
|
151
|
-
Dir["#{thor_root.gsub(/\\/, '/')}
|
|
168
|
+
files = Dir["#{thor_root.gsub(/\\/, '/')}/*"]
|
|
169
|
+
files.map! do |file|
|
|
170
|
+
File.directory?(file) ? File.join(file, "main.thor") : file
|
|
171
|
+
end
|
|
152
172
|
end
|
|
153
173
|
|
|
154
174
|
private
|
|
@@ -176,7 +196,7 @@ class Thor::Runner < Thor
|
|
|
176
196
|
raise Error, "No Thor tasks available" if klasses.empty?
|
|
177
197
|
|
|
178
198
|
if with_modules && !(yaml = thor_yaml).empty?
|
|
179
|
-
max_name = yaml.max {|(xk,xv),(yk,yv)| xk.size <=> yk.size }.first.size
|
|
199
|
+
max_name = yaml.max {|(xk,xv),(yk,yv)| xk.to_s.size <=> yk.to_s.size }.first.size
|
|
180
200
|
modules_label = "Modules"
|
|
181
201
|
namespaces_label = "Namespaces"
|
|
182
202
|
column_width = [max_name + 4, modules_label.size + 1].max
|
|
@@ -194,9 +214,6 @@ class Thor::Runner < Thor
|
|
|
194
214
|
puts
|
|
195
215
|
end
|
|
196
216
|
|
|
197
|
-
puts "Tasks"
|
|
198
|
-
puts "-----"
|
|
199
|
-
|
|
200
217
|
# Calculate the largest base class name
|
|
201
218
|
max_base = klasses.max do |x,y|
|
|
202
219
|
Thor::Util.constant_to_thor_path(x.name).size <=> Thor::Util.constant_to_thor_path(y.name).size
|
|
@@ -208,16 +225,39 @@ class Thor::Runner < Thor
|
|
|
208
225
|
end
|
|
209
226
|
|
|
210
227
|
max_left = max_left_item.maxima.usage + max_left_item.maxima.opt
|
|
211
|
-
|
|
212
|
-
klasses.
|
|
228
|
+
|
|
229
|
+
unless klasses.empty?
|
|
230
|
+
puts # add some spacing
|
|
231
|
+
klasses.each { |k| display_tasks(k, max_base, max_left); }
|
|
232
|
+
else
|
|
233
|
+
puts "\033[1;34mNo Thor tasks available\033[0m"
|
|
234
|
+
end
|
|
213
235
|
end
|
|
214
236
|
|
|
215
237
|
def display_tasks(klass, max_base, max_left)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
238
|
+
if klass.tasks.values.length > 1
|
|
239
|
+
|
|
240
|
+
base = Thor::Util.constant_to_thor_path(klass.name)
|
|
241
|
+
|
|
242
|
+
if base.to_a.empty?
|
|
243
|
+
base = 'default'
|
|
244
|
+
puts "\033[1;35m#{base}\033[0m"
|
|
245
|
+
else
|
|
246
|
+
puts "\033[1;34m#{base}\033[0m"
|
|
247
|
+
end
|
|
248
|
+
puts "-" * base.length
|
|
249
|
+
|
|
250
|
+
klass.tasks.each true do |name, task|
|
|
251
|
+
format_string = "%-#{max_left + max_base + 5}s"
|
|
252
|
+
print format_string % task.formatted_usage(true)
|
|
253
|
+
puts task.description
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
unless klass.opts.empty?
|
|
257
|
+
puts "\nglobal options: #{Options.new(klass.opts)}"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
puts # add some spacing
|
|
221
261
|
end
|
|
222
262
|
end
|
|
223
263
|
|
|
@@ -226,8 +266,9 @@ class Thor::Runner < Thor
|
|
|
226
266
|
end
|
|
227
267
|
|
|
228
268
|
def load_thorfile(path)
|
|
269
|
+
txt = File.read(path)
|
|
229
270
|
begin
|
|
230
|
-
|
|
271
|
+
Thor::Tasks.class_eval txt, path
|
|
231
272
|
rescue Object => e
|
|
232
273
|
$stderr.puts "WARNING: unable to load thorfile #{path.inspect}: #{e.message}"
|
|
233
274
|
end
|
|
@@ -246,8 +287,12 @@ class Thor::Runner < Thor
|
|
|
246
287
|
|
|
247
288
|
# We want to load system-wide Thorfiles first
|
|
248
289
|
# so the local Thorfiles will override them.
|
|
249
|
-
(relevant_to ? thorfiles_relevant_to(relevant_to) :
|
|
290
|
+
files = (relevant_to ? thorfiles_relevant_to(relevant_to) :
|
|
250
291
|
thor_root_glob) + thorfiles - ["#{thor_root}/thor.yml"]
|
|
292
|
+
|
|
293
|
+
files.map! do |file|
|
|
294
|
+
File.directory?(file) ? File.join(file, "main.thor") : file
|
|
295
|
+
end
|
|
251
296
|
end
|
|
252
297
|
|
|
253
298
|
def thorfiles_relevant_to(meth)
|
data/lib/thor/task.rb
CHANGED
|
@@ -3,9 +3,16 @@ require 'thor/util'
|
|
|
3
3
|
|
|
4
4
|
class Thor
|
|
5
5
|
class Task < Struct.new(:meth, :description, :usage, :opts, :klass)
|
|
6
|
+
|
|
6
7
|
def self.dynamic(meth, klass)
|
|
7
8
|
new(meth, "A dynamically-generated task", meth.to_s, nil, klass)
|
|
8
9
|
end
|
|
10
|
+
|
|
11
|
+
def initialize(*args)
|
|
12
|
+
# keep the original opts - we need them later on
|
|
13
|
+
@options = args[3] || {}
|
|
14
|
+
super
|
|
15
|
+
end
|
|
9
16
|
|
|
10
17
|
def parse(obj, args)
|
|
11
18
|
list, hash = parse_args(args)
|
|
@@ -48,9 +55,13 @@ class Thor
|
|
|
48
55
|
|
|
49
56
|
def opts
|
|
50
57
|
return super unless super.kind_of? Hash
|
|
51
|
-
|
|
58
|
+
@_opts ||= Options.new(super)
|
|
52
59
|
end
|
|
53
|
-
|
|
60
|
+
|
|
61
|
+
def full_opts
|
|
62
|
+
@_full_opts ||= Options.new((klass.opts || {}).merge(@options))
|
|
63
|
+
end
|
|
64
|
+
|
|
54
65
|
def formatted_usage(namespace = false)
|
|
55
66
|
(namespace ? self.namespace + ':' : '') + usage +
|
|
56
67
|
(opts ? " " + opts.formatted_usage : "")
|
|
@@ -59,9 +70,9 @@ class Thor
|
|
|
59
70
|
protected
|
|
60
71
|
|
|
61
72
|
def parse_args(args)
|
|
62
|
-
return [
|
|
63
|
-
hash =
|
|
64
|
-
list =
|
|
73
|
+
return [[], {}] if args.nil?
|
|
74
|
+
hash = full_opts.parse(args)
|
|
75
|
+
list = full_opts.non_opts
|
|
65
76
|
[list, hash]
|
|
66
77
|
end
|
|
67
78
|
end
|
data/lib/thor/tasks.rb
CHANGED
|
@@ -22,7 +22,7 @@ class Thor
|
|
|
22
22
|
old_stderr, $stderr = $stderr.dup, File.open(null, "w")
|
|
23
23
|
package
|
|
24
24
|
$stderr = old_stderr
|
|
25
|
-
system %{#{sudo} #{gem} install pkg/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources}
|
|
25
|
+
system %{#{sudo} #{Gem.ruby} -S #{gem} install pkg/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources}
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
data/lib/thor/util.rb
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
require 'thor/error'
|
|
2
2
|
|
|
3
|
+
module ObjectSpace
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
|
|
7
|
+
# @return <Array[Class]> All the classes in the object space.
|
|
8
|
+
def classes
|
|
9
|
+
klasses = []
|
|
10
|
+
ObjectSpace.each_object(Class) {|o| klasses << o}
|
|
11
|
+
klasses
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
3
17
|
class Thor
|
|
18
|
+
module Tasks; end
|
|
19
|
+
|
|
4
20
|
module Util
|
|
5
21
|
|
|
22
|
+
def self.full_const_get(obj, name)
|
|
23
|
+
list = name.split("::")
|
|
24
|
+
list.shift if list.first.empty?
|
|
25
|
+
list.each do |x|
|
|
26
|
+
# This is required because const_get tries to look for constants in the
|
|
27
|
+
# ancestor chain, but we only want constants that are HERE
|
|
28
|
+
obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
|
|
29
|
+
end
|
|
30
|
+
obj
|
|
31
|
+
end
|
|
32
|
+
|
|
6
33
|
def self.constant_to_thor_path(str, remove_default = true)
|
|
7
|
-
str =
|
|
34
|
+
str = str.to_s.gsub(/^Thor::Tasks::/, "")
|
|
35
|
+
str = snake_case(str).squeeze(":")
|
|
8
36
|
str.gsub!(/^default/, '') if remove_default
|
|
9
37
|
str
|
|
10
38
|
end
|
|
@@ -21,16 +49,20 @@ class Thor
|
|
|
21
49
|
str.gsub(/:(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
|
22
50
|
end
|
|
23
51
|
|
|
24
|
-
def self.constants_in_contents(str)
|
|
25
|
-
klasses =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
52
|
+
def self.constants_in_contents(str, file = __FILE__)
|
|
53
|
+
klasses = ObjectSpace.classes.dup
|
|
54
|
+
Module.new.class_eval(str, file)
|
|
55
|
+
klasses = ObjectSpace.classes - klasses
|
|
56
|
+
klasses = klasses.select {|k| k < Thor }
|
|
57
|
+
klasses.map! {|k| k.to_s.gsub(/#<Module:\w+>::/, '')}
|
|
30
58
|
end
|
|
31
59
|
|
|
32
|
-
def self.make_constant(str)
|
|
33
|
-
|
|
60
|
+
def self.make_constant(str, base = [Thor::Tasks, Object])
|
|
61
|
+
which = base.find do |obj|
|
|
62
|
+
full_const_get(obj, str) rescue nil
|
|
63
|
+
end
|
|
64
|
+
return full_const_get(which, str) if which
|
|
65
|
+
raise NameError, "uninitialized constant #{str}"
|
|
34
66
|
end
|
|
35
67
|
|
|
36
68
|
def self.snake_case(str)
|
data/lib/thor.rb
CHANGED
|
@@ -22,6 +22,14 @@ class Thor
|
|
|
22
22
|
@usage, @desc = usage, description
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def self.group(name)
|
|
26
|
+
@group_name = name.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.group_name
|
|
30
|
+
@group_name || 'standard'
|
|
31
|
+
end
|
|
32
|
+
|
|
25
33
|
def self.method_options(opts)
|
|
26
34
|
@method_options = opts
|
|
27
35
|
end
|
|
@@ -80,7 +88,7 @@ class Thor
|
|
|
80
88
|
|
|
81
89
|
def method_added(meth)
|
|
82
90
|
meth = meth.to_s
|
|
83
|
-
|
|
91
|
+
|
|
84
92
|
if meth == "initialize"
|
|
85
93
|
@opts = @method_options
|
|
86
94
|
@method_options = nil
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.9
|
|
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: 2008-
|
|
12
|
+
date: 2008-12-15 00:00:00 -08:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|
|
@@ -22,8 +22,8 @@ extensions: []
|
|
|
22
22
|
|
|
23
23
|
extra_rdoc_files:
|
|
24
24
|
- README.markdown
|
|
25
|
-
- CHANGELOG.rdoc
|
|
26
25
|
- LICENSE
|
|
26
|
+
- CHANGELOG.rdoc
|
|
27
27
|
files:
|
|
28
28
|
- README.markdown
|
|
29
29
|
- LICENSE
|
|
@@ -65,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
65
65
|
requirements: []
|
|
66
66
|
|
|
67
67
|
rubyforge_project: thor
|
|
68
|
-
rubygems_version: 1.
|
|
68
|
+
rubygems_version: 1.3.1
|
|
69
69
|
signing_key:
|
|
70
70
|
specification_version: 2
|
|
71
71
|
summary: A gem that maps options to a class
|