thor 0.16.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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