thor 0.14.6 → 0.19.4

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