thor 0.16.0 → 1.2.1

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +15 -0
  3. data/README.md +23 -6
  4. data/bin/thor +1 -1
  5. data/lib/thor/actions/create_file.rb +34 -35
  6. data/lib/thor/actions/create_link.rb +9 -5
  7. data/lib/thor/actions/directory.rb +33 -23
  8. data/lib/thor/actions/empty_directory.rb +75 -85
  9. data/lib/thor/actions/file_manipulation.rb +103 -36
  10. data/lib/thor/actions/inject_into_file.rb +46 -36
  11. data/lib/thor/actions.rb +90 -68
  12. data/lib/thor/base.rb +302 -244
  13. data/lib/thor/command.rb +142 -0
  14. data/lib/thor/core_ext/hash_with_indifferent_access.rb +52 -24
  15. data/lib/thor/error.rb +90 -10
  16. data/lib/thor/group.rb +70 -74
  17. data/lib/thor/invocation.rb +63 -55
  18. data/lib/thor/line_editor/basic.rb +37 -0
  19. data/lib/thor/line_editor/readline.rb +88 -0
  20. data/lib/thor/line_editor.rb +17 -0
  21. data/lib/thor/nested_context.rb +29 -0
  22. data/lib/thor/parser/argument.rb +24 -28
  23. data/lib/thor/parser/arguments.rb +110 -102
  24. data/lib/thor/parser/option.rb +53 -15
  25. data/lib/thor/parser/options.rb +174 -97
  26. data/lib/thor/parser.rb +4 -4
  27. data/lib/thor/rake_compat.rb +12 -11
  28. data/lib/thor/runner.rb +159 -155
  29. data/lib/thor/shell/basic.rb +216 -93
  30. data/lib/thor/shell/color.rb +53 -40
  31. data/lib/thor/shell/html.rb +61 -58
  32. data/lib/thor/shell.rb +29 -36
  33. data/lib/thor/util.rb +231 -213
  34. data/lib/thor/version.rb +1 -1
  35. data/lib/thor.rb +303 -166
  36. data/thor.gemspec +27 -24
  37. metadata +36 -226
  38. data/.gitignore +0 -44
  39. data/.rspec +0 -2
  40. data/.travis.yml +0 -7
  41. data/CHANGELOG.rdoc +0 -134
  42. data/Gemfile +0 -15
  43. data/Thorfile +0 -30
  44. data/bin/rake2thor +0 -86
  45. data/lib/thor/core_ext/dir_escape.rb +0 -0
  46. data/lib/thor/core_ext/file_binary_read.rb +0 -9
  47. data/lib/thor/core_ext/ordered_hash.rb +0 -100
  48. data/lib/thor/task.rb +0 -132
  49. data/spec/actions/create_file_spec.rb +0 -170
  50. data/spec/actions/create_link_spec.rb +0 -81
  51. data/spec/actions/directory_spec.rb +0 -149
  52. data/spec/actions/empty_directory_spec.rb +0 -130
  53. data/spec/actions/file_manipulation_spec.rb +0 -370
  54. data/spec/actions/inject_into_file_spec.rb +0 -135
  55. data/spec/actions_spec.rb +0 -331
  56. data/spec/base_spec.rb +0 -279
  57. data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
  58. data/spec/core_ext/ordered_hash_spec.rb +0 -115
  59. data/spec/exit_condition_spec.rb +0 -19
  60. data/spec/fixtures/application.rb +0 -2
  61. data/spec/fixtures/app{1}/README +0 -3
  62. data/spec/fixtures/bundle/execute.rb +0 -6
  63. data/spec/fixtures/bundle/main.thor +0 -1
  64. data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
  65. data/spec/fixtures/doc/COMMENTER +0 -10
  66. data/spec/fixtures/doc/README +0 -3
  67. data/spec/fixtures/doc/block_helper.rb +0 -3
  68. data/spec/fixtures/doc/components/.empty_directory +0 -0
  69. data/spec/fixtures/doc/config.rb +0 -1
  70. data/spec/fixtures/doc/config.yaml.tt +0 -1
  71. data/spec/fixtures/enum.thor +0 -10
  72. data/spec/fixtures/group.thor +0 -114
  73. data/spec/fixtures/invoke.thor +0 -112
  74. data/spec/fixtures/path with spaces +0 -0
  75. data/spec/fixtures/script.thor +0 -190
  76. data/spec/fixtures/task.thor +0 -10
  77. data/spec/group_spec.rb +0 -216
  78. data/spec/invocation_spec.rb +0 -100
  79. data/spec/parser/argument_spec.rb +0 -53
  80. data/spec/parser/arguments_spec.rb +0 -66
  81. data/spec/parser/option_spec.rb +0 -202
  82. data/spec/parser/options_spec.rb +0 -330
  83. data/spec/rake_compat_spec.rb +0 -72
  84. data/spec/register_spec.rb +0 -135
  85. data/spec/runner_spec.rb +0 -241
  86. data/spec/shell/basic_spec.rb +0 -300
  87. data/spec/shell/color_spec.rb +0 -81
  88. data/spec/shell/html_spec.rb +0 -32
  89. data/spec/shell_spec.rb +0 -47
  90. data/spec/spec_helper.rb +0 -59
  91. data/spec/task_spec.rb +0 -80
  92. data/spec/thor_spec.rb +0 -418
  93. data/spec/util_spec.rb +0 -196
data/lib/thor/runner.rb CHANGED
@@ -1,55 +1,64 @@
1
- require 'thor'
2
- require 'thor/group'
3
- require 'thor/core_ext/file_binary_read'
1
+ require_relative "../thor"
2
+ require_relative "group"
4
3
 
5
- require 'fileutils'
6
- require 'open-uri'
7
- require 'yaml'
8
- require 'digest/md5'
9
- require 'pathname'
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 && !self.respond_to?(meth)
24
+ if meth && !respond_to?(meth)
18
25
  initialize_thorfiles(meth)
19
- klass, task = Thor::Util.find_class_and_task_by_namespace(meth)
20
- self.class.handle_no_task_error(task, false) if klass.nil?
21
- klass.start(["-h", task].compact, :shell => self.shell)
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 task is not found on Thor::Runner, method missing is invoked and
28
- # Thor::Runner is then responsable for finding the task in all classes.
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, task = Thor::Util.find_class_and_task_by_namespace(meth)
34
- self.class.handle_no_task_error(task, false) if klass.nil?
35
- args.unshift(task) if task
36
- klass.start(args, :shell => self.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 tasks"
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
- # task in said directory.
52
+ # command in said directory.
46
53
  begin
47
54
  if File.directory?(File.expand_path(name))
48
- base, package = File.join(name, "main.thor"), :directory
49
- contents = open(base) {|input| input.read }
55
+ base = File.join(name, "main.thor")
56
+ package = :directory
57
+ contents = open(base, &:read)
50
58
  else
51
- base, package = name, :file
52
- contents = open(name) {|input| input.read }
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 =~ /^https?:\/\//
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
- require 'thor/version'
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
- FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}"))
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 = self.options.merge("as" => name)
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 tasks"
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 tasks (--substring means .*SEARCH)"
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
- private
179
+ private
170
180
 
171
- def self.banner(task, all = false, subcommand = false)
172
- "thor " + task.formatted_usage(self, all, subcommand)
173
- end
181
+ def thor_root
182
+ Thor::Util.thor_root
183
+ end
174
184
 
175
- def thor_root
176
- Thor::Util.thor_root
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
- def thor_yaml
180
- @thor_yaml ||= begin
181
- yaml_file = File.join(thor_root, "thor.yml")
182
- yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
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
- # Save the yaml file. If none exists in thor root, creates one.
188
- #
189
- def save_yaml(yaml)
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
- unless File.exists?(yaml_file)
193
- FileUtils.mkdir_p(thor_root)
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
- File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml }
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
- def self.exit_on_failure?
202
- true
203
- end
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
- # 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 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
- # 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 = []
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
- files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob)
251
- files += thorfiles
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
- # 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(":") ]
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
- files.map { |k, v| File.join(thor_root, "#{v[:filename]}") }
269
+ files = thor_yaml.select do |_, v|
270
+ v[:namespaces] && !(v[:namespaces] & lookup).empty?
271
271
  end
272
272
 
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
273
+ files.map { |_, v| File.join(thor_root, (v[:filename]).to_s) }
274
+ end
278
275
 
279
- raise Error, "No Thor tasks available" if klasses.empty?
280
- show_modules if with_modules && !thor_yaml.empty?
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
- list = Hash.new { |h,k| h[k] = [] }
283
- groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
282
+ raise Error, "No Thor commands available" if klasses.empty?
283
+ show_modules if with_modules && !thor_yaml.empty?
284
284
 
285
- # Get classes which inherit from Thor
286
- (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_tasks(false) }
285
+ list = Hash.new { |h, k| h[k] = [] }
286
+ groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
287
287
 
288
- # Get classes which inherit from Thor::Base
289
- groups.map! { |k| k.printable_tasks(false).first }
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
- # Order namespaces with default coming first
293
- list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
294
- list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? }
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
- def display_tasks(namespace, list) #:nodoc:
298
- list.sort!{ |a,b| a[0] <=> b[0] }
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
- say shell.set_color(namespace, :blue, true)
301
- say "-" * namespace.size
300
+ def display_commands(namespace, list) #:nodoc:
301
+ list.sort! { |a, b| a[0] <=> b[0] }
302
302
 
303
- print_table(list, :truncate => true)
304
- say
305
- end
303
+ say shell.set_color(namespace, :blue, true)
304
+ say "-" * namespace.size
306
305
 
307
- def show_modules #:nodoc:
308
- info = []
309
- labels = ["Modules", "Namespaces"]
306
+ print_table(list, :truncate => true)
307
+ say
308
+ end
309
+ alias_method :display_tasks, :display_commands
310
310
 
311
- info << labels
312
- info << [ "-" * labels[0].size, "-" * labels[1].size ]
311
+ def show_modules #:nodoc:
312
+ info = []
313
+ labels = %w(Modules Namespaces)
313
314
 
314
- thor_yaml.each do |name, hash|
315
- info << [ name, hash[:namespaces].join(", ") ]
316
- end
315
+ info << labels
316
+ info << ["-" * labels[0].size, "-" * labels[1].size]
317
317
 
318
- print_table info
319
- say ""
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