thor 0.16.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +15 -0
- data/README.md +23 -6
- data/bin/thor +1 -1
- data/lib/thor/actions/create_file.rb +34 -35
- data/lib/thor/actions/create_link.rb +9 -5
- data/lib/thor/actions/directory.rb +33 -23
- data/lib/thor/actions/empty_directory.rb +75 -85
- data/lib/thor/actions/file_manipulation.rb +103 -36
- data/lib/thor/actions/inject_into_file.rb +46 -36
- data/lib/thor/actions.rb +90 -68
- data/lib/thor/base.rb +302 -244
- data/lib/thor/command.rb +142 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +52 -24
- data/lib/thor/error.rb +90 -10
- data/lib/thor/group.rb +70 -74
- data/lib/thor/invocation.rb +63 -55
- data/lib/thor/line_editor/basic.rb +37 -0
- data/lib/thor/line_editor/readline.rb +88 -0
- data/lib/thor/line_editor.rb +17 -0
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser/argument.rb +24 -28
- data/lib/thor/parser/arguments.rb +110 -102
- data/lib/thor/parser/option.rb +53 -15
- data/lib/thor/parser/options.rb +174 -97
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +12 -11
- data/lib/thor/runner.rb +159 -155
- data/lib/thor/shell/basic.rb +216 -93
- data/lib/thor/shell/color.rb +53 -40
- data/lib/thor/shell/html.rb +61 -58
- data/lib/thor/shell.rb +29 -36
- data/lib/thor/util.rb +231 -213
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +303 -166
- data/thor.gemspec +27 -24
- metadata +36 -226
- data/.gitignore +0 -44
- data/.rspec +0 -2
- data/.travis.yml +0 -7
- data/CHANGELOG.rdoc +0 -134
- data/Gemfile +0 -15
- data/Thorfile +0 -30
- data/bin/rake2thor +0 -86
- data/lib/thor/core_ext/dir_escape.rb +0 -0
- data/lib/thor/core_ext/file_binary_read.rb +0 -9
- data/lib/thor/core_ext/ordered_hash.rb +0 -100
- data/lib/thor/task.rb +0 -132
- data/spec/actions/create_file_spec.rb +0 -170
- data/spec/actions/create_link_spec.rb +0 -81
- data/spec/actions/directory_spec.rb +0 -149
- data/spec/actions/empty_directory_spec.rb +0 -130
- data/spec/actions/file_manipulation_spec.rb +0 -370
- data/spec/actions/inject_into_file_spec.rb +0 -135
- data/spec/actions_spec.rb +0 -331
- data/spec/base_spec.rb +0 -279
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
- data/spec/core_ext/ordered_hash_spec.rb +0 -115
- data/spec/exit_condition_spec.rb +0 -19
- data/spec/fixtures/application.rb +0 -2
- data/spec/fixtures/app{1}/README +0 -3
- data/spec/fixtures/bundle/execute.rb +0 -6
- data/spec/fixtures/bundle/main.thor +0 -1
- data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
- data/spec/fixtures/doc/COMMENTER +0 -10
- data/spec/fixtures/doc/README +0 -3
- data/spec/fixtures/doc/block_helper.rb +0 -3
- data/spec/fixtures/doc/components/.empty_directory +0 -0
- data/spec/fixtures/doc/config.rb +0 -1
- data/spec/fixtures/doc/config.yaml.tt +0 -1
- data/spec/fixtures/enum.thor +0 -10
- data/spec/fixtures/group.thor +0 -114
- data/spec/fixtures/invoke.thor +0 -112
- data/spec/fixtures/path with spaces +0 -0
- data/spec/fixtures/script.thor +0 -190
- data/spec/fixtures/task.thor +0 -10
- data/spec/group_spec.rb +0 -216
- data/spec/invocation_spec.rb +0 -100
- data/spec/parser/argument_spec.rb +0 -53
- data/spec/parser/arguments_spec.rb +0 -66
- data/spec/parser/option_spec.rb +0 -202
- data/spec/parser/options_spec.rb +0 -330
- data/spec/rake_compat_spec.rb +0 -72
- data/spec/register_spec.rb +0 -135
- data/spec/runner_spec.rb +0 -241
- data/spec/shell/basic_spec.rb +0 -300
- data/spec/shell/color_spec.rb +0 -81
- data/spec/shell/html_spec.rb +0 -32
- data/spec/shell_spec.rb +0 -47
- data/spec/spec_helper.rb +0 -59
- data/spec/task_spec.rb +0 -80
- data/spec/thor_spec.rb +0 -418
- data/spec/util_spec.rb +0 -196
data/lib/thor/runner.rb
CHANGED
@@ -1,55 +1,64 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'thor/core_ext/file_binary_read'
|
1
|
+
require_relative "../thor"
|
2
|
+
require_relative "group"
|
4
3
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
|
9
|
-
|
4
|
+
require "yaml"
|
5
|
+
require "digest/md5"
|
6
|
+
require "pathname"
|
7
|
+
|
8
|
+
class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength
|
9
|
+
autoload :OpenURI, "open-uri"
|
10
10
|
|
11
|
-
class Thor::Runner < Thor #:nodoc:
|
12
11
|
map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version
|
13
12
|
|
13
|
+
def self.banner(command, all = false, subcommand = false)
|
14
|
+
"thor " + command.formatted_usage(self, all, subcommand)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.exit_on_failure?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
14
21
|
# Override Thor#help so it can give information about any class and any method.
|
15
22
|
#
|
16
23
|
def help(meth = nil)
|
17
|
-
if meth && !
|
24
|
+
if meth && !respond_to?(meth)
|
18
25
|
initialize_thorfiles(meth)
|
19
|
-
klass,
|
20
|
-
self.class.
|
21
|
-
klass.start(["-h",
|
26
|
+
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
27
|
+
self.class.handle_no_command_error(command, false) if klass.nil?
|
28
|
+
klass.start(["-h", command].compact, :shell => shell)
|
22
29
|
else
|
23
30
|
super
|
24
31
|
end
|
25
32
|
end
|
26
33
|
|
27
|
-
# If a
|
28
|
-
# Thor::Runner is then
|
34
|
+
# If a command is not found on Thor::Runner, method missing is invoked and
|
35
|
+
# Thor::Runner is then responsible for finding the command in all classes.
|
29
36
|
#
|
30
37
|
def method_missing(meth, *args)
|
31
38
|
meth = meth.to_s
|
32
39
|
initialize_thorfiles(meth)
|
33
|
-
klass,
|
34
|
-
self.class.
|
35
|
-
args.unshift(
|
36
|
-
klass.start(args, :shell =>
|
40
|
+
klass, command = Thor::Util.find_class_and_command_by_namespace(meth)
|
41
|
+
self.class.handle_no_command_error(command, false) if klass.nil?
|
42
|
+
args.unshift(command) if command
|
43
|
+
klass.start(args, :shell => shell)
|
37
44
|
end
|
38
45
|
|
39
|
-
desc "install NAME", "Install an optionally named Thor file into your system
|
46
|
+
desc "install NAME", "Install an optionally named Thor file into your system commands"
|
40
47
|
method_options :as => :string, :relative => :boolean, :force => :boolean
|
41
|
-
def install(name)
|
48
|
+
def install(name) # rubocop:disable MethodLength
|
42
49
|
initialize_thorfiles
|
43
50
|
|
44
51
|
# If a directory name is provided as the argument, look for a 'main.thor'
|
45
|
-
#
|
52
|
+
# command in said directory.
|
46
53
|
begin
|
47
54
|
if File.directory?(File.expand_path(name))
|
48
|
-
base
|
49
|
-
|
55
|
+
base = File.join(name, "main.thor")
|
56
|
+
package = :directory
|
57
|
+
contents = open(base, &:read)
|
50
58
|
else
|
51
|
-
base
|
52
|
-
|
59
|
+
base = name
|
60
|
+
package = :file
|
61
|
+
contents = open(name, &:read)
|
53
62
|
end
|
54
63
|
rescue OpenURI::HTTPError
|
55
64
|
raise Error, "Error opening URI '#{name}'"
|
@@ -75,7 +84,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
75
84
|
as = basename if as.empty?
|
76
85
|
end
|
77
86
|
|
78
|
-
location = if options[:relative] || name =~
|
87
|
+
location = if options[:relative] || name =~ %r{^https?://}
|
79
88
|
name
|
80
89
|
else
|
81
90
|
File.expand_path(name)
|
@@ -94,6 +103,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
94
103
|
if package == :file
|
95
104
|
File.open(destination, "w") { |f| f.puts contents }
|
96
105
|
else
|
106
|
+
require "fileutils"
|
97
107
|
FileUtils.cp_r(name, destination)
|
98
108
|
end
|
99
109
|
|
@@ -102,7 +112,7 @@ class Thor::Runner < Thor #:nodoc:
|
|
102
112
|
|
103
113
|
desc "version", "Show Thor version"
|
104
114
|
def version
|
105
|
-
|
115
|
+
require_relative "version"
|
106
116
|
say "Thor #{Thor::VERSION}"
|
107
117
|
end
|
108
118
|
|
@@ -110,7 +120,8 @@ class Thor::Runner < Thor #:nodoc:
|
|
110
120
|
def uninstall(name)
|
111
121
|
raise Error, "Can't find module '#{name}'" unless thor_yaml[name]
|
112
122
|
say "Uninstalling #{name}."
|
113
|
-
|
123
|
+
require "fileutils"
|
124
|
+
FileUtils.rm_rf(File.join(thor_root, (thor_yaml[name][:filename]).to_s))
|
114
125
|
|
115
126
|
thor_yaml.delete(name)
|
116
127
|
save_yaml(thor_yaml)
|
@@ -125,9 +136,10 @@ class Thor::Runner < Thor #:nodoc:
|
|
125
136
|
say "Updating '#{name}' from #{thor_yaml[name][:location]}"
|
126
137
|
|
127
138
|
old_filename = thor_yaml[name][:filename]
|
128
|
-
self.options =
|
139
|
+
self.options = options.merge("as" => name)
|
129
140
|
|
130
141
|
if File.directory? File.expand_path(name)
|
142
|
+
require "fileutils"
|
131
143
|
FileUtils.rm_rf(File.join(thor_root, old_filename))
|
132
144
|
|
133
145
|
thor_yaml.delete(old_filename)
|
@@ -138,21 +150,19 @@ class Thor::Runner < Thor #:nodoc:
|
|
138
150
|
filename = install(thor_yaml[name][:location])
|
139
151
|
end
|
140
152
|
|
141
|
-
unless filename == old_filename
|
142
|
-
File.delete(File.join(thor_root, old_filename))
|
143
|
-
end
|
153
|
+
File.delete(File.join(thor_root, old_filename)) unless filename == old_filename
|
144
154
|
end
|
145
155
|
|
146
|
-
desc "installed", "List the installed Thor modules and
|
156
|
+
desc "installed", "List the installed Thor modules and commands"
|
147
157
|
method_options :internal => :boolean
|
148
158
|
def installed
|
149
159
|
initialize_thorfiles(nil, true)
|
150
160
|
display_klasses(true, options["internal"])
|
151
161
|
end
|
152
162
|
|
153
|
-
desc "list [SEARCH]", "List the available thor
|
163
|
+
desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)"
|
154
164
|
method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean
|
155
|
-
def list(search="")
|
165
|
+
def list(search = "")
|
156
166
|
initialize_thorfiles
|
157
167
|
|
158
168
|
search = ".*#{search}" if options["substring"]
|
@@ -166,156 +176,150 @@ class Thor::Runner < Thor #:nodoc:
|
|
166
176
|
display_klasses(false, false, klasses)
|
167
177
|
end
|
168
178
|
|
169
|
-
|
179
|
+
private
|
170
180
|
|
171
|
-
|
172
|
-
|
173
|
-
|
181
|
+
def thor_root
|
182
|
+
Thor::Util.thor_root
|
183
|
+
end
|
174
184
|
|
175
|
-
|
176
|
-
|
185
|
+
def thor_yaml
|
186
|
+
@thor_yaml ||= begin
|
187
|
+
yaml_file = File.join(thor_root, "thor.yml")
|
188
|
+
yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file)
|
189
|
+
yaml || {}
|
177
190
|
end
|
191
|
+
end
|
178
192
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
yaml || {}
|
184
|
-
end
|
185
|
-
end
|
193
|
+
# Save the yaml file. If none exists in thor root, creates one.
|
194
|
+
#
|
195
|
+
def save_yaml(yaml)
|
196
|
+
yaml_file = File.join(thor_root, "thor.yml")
|
186
197
|
|
187
|
-
|
188
|
-
|
189
|
-
|
198
|
+
unless File.exist?(yaml_file)
|
199
|
+
require "fileutils"
|
200
|
+
FileUtils.mkdir_p(thor_root)
|
190
201
|
yaml_file = File.join(thor_root, "thor.yml")
|
202
|
+
FileUtils.touch(yaml_file)
|
203
|
+
end
|
191
204
|
|
192
|
-
|
193
|
-
|
194
|
-
yaml_file = File.join(thor_root, "thor.yml")
|
195
|
-
FileUtils.touch(yaml_file)
|
196
|
-
end
|
205
|
+
File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
|
206
|
+
end
|
197
207
|
|
198
|
-
|
208
|
+
# Load the Thorfiles. If relevant_to is supplied, looks for specific files
|
209
|
+
# in the thor_root instead of loading them all.
|
210
|
+
#
|
211
|
+
# By default, it also traverses the current path until find Thor files, as
|
212
|
+
# described in thorfiles. This look up can be skipped by supplying
|
213
|
+
# skip_lookup true.
|
214
|
+
#
|
215
|
+
def initialize_thorfiles(relevant_to = nil, skip_lookup = false)
|
216
|
+
thorfiles(relevant_to, skip_lookup).each do |f|
|
217
|
+
Thor::Util.load_thorfile(f, nil, options[:debug]) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f))
|
199
218
|
end
|
219
|
+
end
|
200
220
|
|
201
|
-
|
202
|
-
|
203
|
-
|
221
|
+
# Finds Thorfiles by traversing from your current directory down to the root
|
222
|
+
# directory of your system. If at any time we find a Thor file, we stop.
|
223
|
+
#
|
224
|
+
# We also ensure that system-wide Thorfiles are loaded first, so local
|
225
|
+
# Thorfiles can override them.
|
226
|
+
#
|
227
|
+
# ==== Example
|
228
|
+
#
|
229
|
+
# If we start at /Users/wycats/dev/thor ...
|
230
|
+
#
|
231
|
+
# 1. /Users/wycats/dev/thor
|
232
|
+
# 2. /Users/wycats/dev
|
233
|
+
# 3. /Users/wycats <-- we find a Thorfile here, so we stop
|
234
|
+
#
|
235
|
+
# Suppose we start at c:\Documents and Settings\james\dev\thor ...
|
236
|
+
#
|
237
|
+
# 1. c:\Documents and Settings\james\dev\thor
|
238
|
+
# 2. c:\Documents and Settings\james\dev
|
239
|
+
# 3. c:\Documents and Settings\james
|
240
|
+
# 4. c:\Documents and Settings
|
241
|
+
# 5. c:\ <-- no Thorfiles found!
|
242
|
+
#
|
243
|
+
def thorfiles(relevant_to = nil, skip_lookup = false)
|
244
|
+
thorfiles = []
|
204
245
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
# described in thorfiles. This look up can be skipped by suppliying
|
210
|
-
# skip_lookup true.
|
211
|
-
#
|
212
|
-
def initialize_thorfiles(relevant_to=nil, skip_lookup=false)
|
213
|
-
thorfiles(relevant_to, skip_lookup).each do |f|
|
214
|
-
Thor::Util.load_thorfile(f, nil, options[:debug]) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f))
|
246
|
+
unless skip_lookup
|
247
|
+
Pathname.pwd.ascend do |path|
|
248
|
+
thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
|
249
|
+
break unless thorfiles.empty?
|
215
250
|
end
|
216
251
|
end
|
217
252
|
|
218
|
-
|
219
|
-
|
220
|
-
#
|
221
|
-
# We also ensure that system-wide Thorfiles are loaded first, so local
|
222
|
-
# Thorfiles can override them.
|
223
|
-
#
|
224
|
-
# ==== Example
|
225
|
-
#
|
226
|
-
# If we start at /Users/wycats/dev/thor ...
|
227
|
-
#
|
228
|
-
# 1. /Users/wycats/dev/thor
|
229
|
-
# 2. /Users/wycats/dev
|
230
|
-
# 3. /Users/wycats <-- we find a Thorfile here, so we stop
|
231
|
-
#
|
232
|
-
# Suppose we start at c:\Documents and Settings\james\dev\thor ...
|
233
|
-
#
|
234
|
-
# 1. c:\Documents and Settings\james\dev\thor
|
235
|
-
# 2. c:\Documents and Settings\james\dev
|
236
|
-
# 3. c:\Documents and Settings\james
|
237
|
-
# 4. c:\Documents and Settings
|
238
|
-
# 5. c:\ <-- no Thorfiles found!
|
239
|
-
#
|
240
|
-
def thorfiles(relevant_to=nil, skip_lookup=false)
|
241
|
-
thorfiles = []
|
242
|
-
|
243
|
-
unless skip_lookup
|
244
|
-
Pathname.pwd.ascend do |path|
|
245
|
-
thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten
|
246
|
-
break unless thorfiles.empty?
|
247
|
-
end
|
248
|
-
end
|
253
|
+
files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob)
|
254
|
+
files += thorfiles
|
255
|
+
files -= ["#{thor_root}/thor.yml"]
|
249
256
|
|
250
|
-
|
251
|
-
|
252
|
-
files -= ["#{thor_root}/thor.yml"]
|
253
|
-
|
254
|
-
files.map! do |file|
|
255
|
-
File.directory?(file) ? File.join(file, "main.thor") : file
|
256
|
-
end
|
257
|
+
files.map! do |file|
|
258
|
+
File.directory?(file) ? File.join(file, "main.thor") : file
|
257
259
|
end
|
260
|
+
end
|
258
261
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
files = thor_yaml.select do |k, v|
|
267
|
-
v[:namespaces] && !(v[:namespaces] & lookup).empty?
|
268
|
-
end
|
262
|
+
# Load Thorfiles relevant to the given method. If you provide "foo:bar" it
|
263
|
+
# will load all thor files in the thor.yaml that has "foo" e "foo:bar"
|
264
|
+
# namespaces registered.
|
265
|
+
#
|
266
|
+
def thorfiles_relevant_to(meth)
|
267
|
+
lookup = [meth, meth.split(":")[0...-1].join(":")]
|
269
268
|
|
270
|
-
|
269
|
+
files = thor_yaml.select do |_, v|
|
270
|
+
v[:namespaces] && !(v[:namespaces] & lookup).empty?
|
271
271
|
end
|
272
272
|
|
273
|
-
|
274
|
-
|
275
|
-
#
|
276
|
-
def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
|
277
|
-
klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
|
273
|
+
files.map { |_, v| File.join(thor_root, (v[:filename]).to_s) }
|
274
|
+
end
|
278
275
|
|
279
|
-
|
280
|
-
|
276
|
+
# Display information about the given klasses. If with_module is given,
|
277
|
+
# it shows a table with information extracted from the yaml file.
|
278
|
+
#
|
279
|
+
def display_klasses(with_modules = false, show_internal = false, klasses = Thor::Base.subclasses)
|
280
|
+
klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
|
281
281
|
|
282
|
-
|
283
|
-
|
282
|
+
raise Error, "No Thor commands available" if klasses.empty?
|
283
|
+
show_modules if with_modules && !thor_yaml.empty?
|
284
284
|
|
285
|
-
|
286
|
-
|
285
|
+
list = Hash.new { |h, k| h[k] = [] }
|
286
|
+
groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
|
287
287
|
|
288
|
-
|
289
|
-
|
290
|
-
list["root"] = groups
|
288
|
+
# Get classes which inherit from Thor
|
289
|
+
(klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) }
|
291
290
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
end
|
291
|
+
# Get classes which inherit from Thor::Base
|
292
|
+
groups.map! { |k| k.printable_commands(false).first }
|
293
|
+
list["root"] = groups
|
296
294
|
|
297
|
-
|
298
|
-
|
295
|
+
# Order namespaces with default coming first
|
296
|
+
list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") }
|
297
|
+
list.each { |n, commands| display_commands(n, commands) unless commands.empty? }
|
298
|
+
end
|
299
299
|
|
300
|
-
|
301
|
-
|
300
|
+
def display_commands(namespace, list) #:nodoc:
|
301
|
+
list.sort! { |a, b| a[0] <=> b[0] }
|
302
302
|
|
303
|
-
|
304
|
-
|
305
|
-
end
|
303
|
+
say shell.set_color(namespace, :blue, true)
|
304
|
+
say "-" * namespace.size
|
306
305
|
|
307
|
-
|
308
|
-
|
309
|
-
|
306
|
+
print_table(list, :truncate => true)
|
307
|
+
say
|
308
|
+
end
|
309
|
+
alias_method :display_tasks, :display_commands
|
310
310
|
|
311
|
-
|
312
|
-
|
311
|
+
def show_modules #:nodoc:
|
312
|
+
info = []
|
313
|
+
labels = %w(Modules Namespaces)
|
313
314
|
|
314
|
-
|
315
|
-
|
316
|
-
end
|
315
|
+
info << labels
|
316
|
+
info << ["-" * labels[0].size, "-" * labels[1].size]
|
317
317
|
|
318
|
-
|
319
|
-
|
318
|
+
thor_yaml.each do |name, hash|
|
319
|
+
info << [name, hash[:namespaces].join(", ")]
|
320
320
|
end
|
321
|
+
|
322
|
+
print_table info
|
323
|
+
say ""
|
324
|
+
end
|
321
325
|
end
|