thor 0.14.6 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.autotest +8 -0
  2. data/.document +5 -0
  3. data/.gemtest +0 -0
  4. data/.gitignore +44 -0
  5. data/.rspec +2 -0
  6. data/.travis.yml +9 -0
  7. data/CHANGELOG.rdoc +4 -4
  8. data/Gemfile +19 -0
  9. data/{LICENSE → LICENSE.md} +2 -2
  10. data/README.md +21 -300
  11. data/Thorfile +21 -15
  12. data/lib/thor.rb +56 -11
  13. data/lib/thor/actions.rb +7 -3
  14. data/lib/thor/actions/create_link.rb +1 -1
  15. data/lib/thor/actions/directory.rb +7 -3
  16. data/lib/thor/actions/empty_directory.rb +24 -5
  17. data/lib/thor/actions/file_manipulation.rb +40 -2
  18. data/lib/thor/base.rb +66 -28
  19. data/lib/thor/error.rb +6 -1
  20. data/lib/thor/group.rb +20 -8
  21. data/lib/thor/invocation.rb +4 -2
  22. data/lib/thor/parser/arguments.rb +6 -2
  23. data/lib/thor/parser/option.rb +3 -2
  24. data/lib/thor/parser/options.rb +13 -8
  25. data/lib/thor/rake_compat.rb +13 -8
  26. data/lib/thor/runner.rb +16 -4
  27. data/lib/thor/shell.rb +2 -2
  28. data/lib/thor/shell/basic.rb +86 -29
  29. data/lib/thor/shell/color.rb +40 -4
  30. data/lib/thor/shell/html.rb +28 -26
  31. data/lib/thor/task.rb +26 -8
  32. data/lib/thor/util.rb +26 -7
  33. data/lib/thor/version.rb +1 -1
  34. data/spec/actions/create_link_spec.rb +81 -0
  35. data/spec/actions/empty_directory_spec.rb +32 -0
  36. data/spec/actions/file_manipulation_spec.rb +61 -1
  37. data/spec/actions_spec.rb +4 -0
  38. data/spec/base_spec.rb +10 -5
  39. data/spec/exit_condition_spec.rb +19 -0
  40. data/spec/fixtures/script.thor +8 -2
  41. data/spec/group_spec.rb +39 -1
  42. data/spec/parser/arguments_spec.rb +1 -0
  43. data/spec/parser/options_spec.rb +12 -2
  44. data/spec/rake_compat_spec.rb +11 -7
  45. data/spec/register_spec.rb +43 -0
  46. data/spec/runner_spec.rb +34 -3
  47. data/spec/shell/basic_spec.rb +50 -3
  48. data/spec/shell/color_spec.rb +46 -6
  49. data/spec/shell/html_spec.rb +10 -5
  50. data/spec/spec_helper.rb +4 -0
  51. data/spec/task_spec.rb +26 -16
  52. data/spec/thor_spec.rb +56 -3
  53. data/thor.gemspec +26 -0
  54. metadata +174 -117
data/Thorfile CHANGED
@@ -1,24 +1,30 @@
1
- # enconding: utf-8
1
+ # encoding: utf-8
2
+ $:.unshift File.expand_path("../lib", __FILE__)
3
+
4
+ require 'bundler'
2
5
  require 'thor/rake_compat'
3
6
 
4
7
  class Default < Thor
5
8
  include Thor::RakeCompat
9
+ Bundler::GemHelper.install_tasks
6
10
 
7
- require 'rspec/core/rake_task'
8
- RSpec::Core::RakeTask.new(:spec)
11
+ desc "build", "Build thor-#{Thor::VERSION}.gem into the pkg directory"
12
+ def build
13
+ Rake::Task["build"].execute
14
+ end
9
15
 
10
- require 'bundler'
11
- Bundler::GemHelper.install_tasks
16
+ desc "install", "Build and install thor-#{Thor::VERSION}.gem into system gems"
17
+ def install
18
+ Rake::Task["install"].execute
19
+ end
20
+
21
+ desc "release", "Create tag v#{Thor::VERSION} and build and push thor-#{Thor::VERSION}.gem to Rubygems"
22
+ def release
23
+ Rake::Task["release"].execute
24
+ end
12
25
 
13
- require 'rdoc/task'
14
- if defined?(RDoc)
15
- RDoc::Task.new do |rdoc|
16
- rdoc.main = 'README.md'
17
- rdoc.rdoc_dir = 'rdoc'
18
- rdoc.title = 'thor'
19
- rdoc.rdoc_files.include('README.md', 'LICENSE', 'CHANGELOG.rdoc', 'Thorfile')
20
- rdoc.rdoc_files.include('lib/**/*.rb')
21
- rdoc.options << '--line-numbers' << '--inline-source'
22
- end
26
+ desc "spec", "Run RSpec code examples"
27
+ def spec
28
+ exec "rspec --color --format=documentation spec"
23
29
  end
24
30
  end
@@ -5,7 +5,7 @@ class Thor
5
5
  # Sets the default task when thor is executed without an explicit task to be called.
6
6
  #
7
7
  # ==== Parameters
8
- # meth<Symbol>:: name of the defaut task
8
+ # meth<Symbol>:: name of the default task
9
9
  #
10
10
  def default_task(meth=nil)
11
11
  case meth
@@ -28,7 +28,7 @@ class Thor
28
28
  def register(klass, subcommand_name, usage, description, options={})
29
29
  if klass <= Thor::Group
30
30
  desc usage, description, options
31
- define_method(subcommand_name) { invoke klass }
31
+ define_method(subcommand_name) { |*args| invoke(klass, args) }
32
32
  else
33
33
  desc usage, description, options
34
34
  subcommand subcommand_name, klass
@@ -108,6 +108,8 @@ class Thor
108
108
  @method_options
109
109
  end
110
110
 
111
+ alias options method_options
112
+
111
113
  # Adds an option to the set of method options. If :for is given as option,
112
114
  # it allows you to change the options from a previous defined task.
113
115
  #
@@ -132,6 +134,7 @@ class Thor
132
134
  # :aliases - Aliases for this option.
133
135
  # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
134
136
  # :banner - String to show on usage notes.
137
+ # :hide - If you want to hide this option from the help.
135
138
  #
136
139
  def method_option(name, options={})
137
140
  scope = if options[:for]
@@ -143,6 +146,8 @@ class Thor
143
146
  build_option(name, options, scope)
144
147
  end
145
148
 
149
+ alias option method_option
150
+
146
151
  # Prints help information for the given task.
147
152
  #
148
153
  # ==== Parameters
@@ -160,7 +165,7 @@ class Thor
160
165
  class_options_help(shell, nil => task.options.map { |_, o| o })
161
166
  if task.long_description
162
167
  shell.say "Description:"
163
- shell.print_wrapped(task.long_description, :ident => 2)
168
+ shell.print_wrapped(task.long_description, :indent => 2)
164
169
  else
165
170
  shell.say task.description
166
171
  end
@@ -179,7 +184,7 @@ class Thor
179
184
  list.sort!{ |a,b| a[0] <=> b[0] }
180
185
 
181
186
  shell.say "Tasks:"
182
- shell.print_table(list, :ident => 2, :truncate => true)
187
+ shell.print_table(list, :indent => 2, :truncate => true)
183
188
  shell.say
184
189
  class_options_help(shell)
185
190
  end
@@ -202,7 +207,11 @@ class Thor
202
207
  def subcommand(subcommand, subcommand_class)
203
208
  self.subcommands << subcommand.to_s
204
209
  subcommand_class.subcommand_help subcommand
205
- define_method(subcommand) { |*args| invoke subcommand_class, args }
210
+
211
+ define_method(subcommand) do |*args|
212
+ args, opts = Thor::Arguments.split(args)
213
+ invoke subcommand_class, args, opts
214
+ end
206
215
  end
207
216
 
208
217
  # Extend check unknown options to accept a hash of conditions.
@@ -259,8 +268,11 @@ class Thor
259
268
  opts = given_opts || opts || []
260
269
  config.merge!(:current_task => task, :task_options => task.options)
261
270
 
271
+ instance = new(args, opts, config)
272
+ yield instance if block_given?
273
+ args = instance.args
262
274
  trailing = args[Range.new(arguments.size, -1)]
263
- new(args, opts, config).invoke_task(task, trailing || [])
275
+ instance.invoke_task(task, trailing || [])
264
276
  end
265
277
 
266
278
  # The banner for this class. You can customize it if you are invoking the
@@ -300,7 +312,6 @@ class Thor
300
312
  # Retrieve the task name from given args.
301
313
  def retrieve_task_name(args) #:nodoc:
302
314
  meth = args.first.to_s unless args.empty?
303
-
304
315
  if meth && (map[meth] || meth !~ /^\-/)
305
316
  args.shift
306
317
  else
@@ -308,11 +319,45 @@ class Thor
308
319
  end
309
320
  end
310
321
 
311
- # Receives a task name (can be nil), and try to get a map from it.
312
- # If a map can't be found use the sent name or the default task.
322
+ # receives a (possibly nil) task name and returns a name that is in
323
+ # the tasks hash. In addition to normalizing aliases, this logic
324
+ # will determine if a shortened command is an unambiguous prefix of
325
+ # a task or alias.
326
+ #
327
+ # +normalize_task_name+ also converts names like +animal-prison+
328
+ # into +animal_prison+.
313
329
  def normalize_task_name(meth) #:nodoc:
314
- meth = map[meth.to_s] || meth || default_task
315
- meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
330
+ return default_task.to_s.gsub('-', '_') unless meth
331
+
332
+ possibilities = find_task_possibilities(meth)
333
+ if possibilities.size > 1
334
+ raise ArgumentError, "Ambiguous task #{meth} matches [#{possibilities.join(', ')}]"
335
+ elsif possibilities.size < 1
336
+ meth = meth || default_task
337
+ elsif map[meth]
338
+ meth = map[meth]
339
+ else
340
+ meth = possibilities.first
341
+ end
342
+
343
+ meth.to_s.gsub('-','_') # treat foo-bar as foo_bar
344
+ end
345
+
346
+ # this is the logic that takes the task name passed in by the user
347
+ # and determines whether it is an unambiguous prefix of a task or
348
+ # alias name.
349
+ def find_task_possibilities(meth)
350
+ len = meth.to_s.length
351
+ possibilities = all_tasks.merge(map).keys.select { |n| meth == n[0, len] }.sort
352
+ unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
353
+
354
+ if possibilities.include?(meth)
355
+ [meth]
356
+ elsif unique_possibilities.size == 1
357
+ unique_possibilities
358
+ else
359
+ possibilities
360
+ end
316
361
  end
317
362
 
318
363
  def subcommand_help(cmd)
@@ -55,7 +55,7 @@ class Thor
55
55
  :desc => "Run but do not make any changes"
56
56
 
57
57
  class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
58
- :desc => "Supress status output"
58
+ :desc => "Suppress status output"
59
59
 
60
60
  class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
61
61
  :desc => "Skip files that already exist"
@@ -114,8 +114,12 @@ class Thor
114
114
  # the script started).
115
115
  #
116
116
  def relative_to_original_destination_root(path, remove_dot=true)
117
- path = path.gsub(@destination_stack[0], '.')
118
- remove_dot ? (path[2..-1] || '') : path
117
+ if path =~ /^#{@destination_stack[0]}/
118
+ path = path.gsub(@destination_stack[0], '.')
119
+ path = remove_dot ? (path[2..-1] || '') : path
120
+ end
121
+
122
+ path
119
123
  end
120
124
 
121
125
  # Holds source paths in instance so they can be manipulated.
@@ -41,7 +41,7 @@ class Thor
41
41
  invoke_with_conflict_check do
42
42
  FileUtils.mkdir_p(File.dirname(destination))
43
43
  # Create a symlink by default
44
- config[:symbolic] ||= true
44
+ config[:symbolic] = true if config[:symbolic].nil?
45
45
  File.unlink(destination) if exists?
46
46
  if config[:symbolic]
47
47
  File.symlink(render, destination)
@@ -2,13 +2,13 @@ require 'thor/actions/empty_directory'
2
2
 
3
3
  class Thor
4
4
  module Actions
5
-
6
5
  # Copies recursively the files from source directory to root directory.
7
6
  # If any of the files finishes with .tt, it's considered to be a template
8
7
  # and is placed in the destination without the extension .tt. If any
9
8
  # empty directory is found, it's copied and all .empty_directory files are
10
- # ignored. Remember that file paths can also be encoded, let's suppose a doc
11
- # directory with the following files:
9
+ # ignored. If any file name is wrapped within % signs, the text within
10
+ # the % signs will be executed as a method and replaced with the returned
11
+ # value. Let's suppose a doc directory with the following files:
12
12
  #
13
13
  # doc/
14
14
  # components/.empty_directory
@@ -29,6 +29,10 @@ class Thor
29
29
  # rdoc.rb
30
30
  # blog.rb
31
31
  #
32
+ # <b>Encoded path note:</b> Since Thor internals use Object#respond_to? to check if it can
33
+ # expand %something%, this `something` should be a public method in the class calling
34
+ # #directory. If a method is private, Thor stack raises PrivateMethodEncodedError.
35
+ #
32
36
  # ==== Parameters
33
37
  # source<String>:: the relative path to the source root.
34
38
  # destination<String>:: the relative path to the destination root.
@@ -90,16 +90,35 @@ class Thor
90
90
 
91
91
  # Filenames in the encoded form are converted. If you have a file:
92
92
  #
93
- # %class_name%.rb
93
+ # %file_name%.rb
94
94
  #
95
- # It gets the class name from the base and replace it:
95
+ # It calls #file_name from the base and replaces %-string with the
96
+ # return value (should be String) of #file_name:
96
97
  #
97
98
  # user.rb
98
99
  #
100
+ # The method referenced by %-string SHOULD be public. Otherwise you
101
+ # get the exception with the corresponding error message.
102
+ #
99
103
  def convert_encoded_instructions(filename)
100
- filename.gsub(/%(.*?)%/) do |string|
101
- instruction = $1.strip
102
- base.respond_to?(instruction) ? base.send(instruction) : string
104
+ filename.gsub(/%(.*?)%/) do |initial_string|
105
+ call_public_method($1.strip) or initial_string
106
+ end
107
+ end
108
+
109
+ # Calls `base`'s public method `sym`.
110
+ # Returns:: result of `base.sym` or `nil` if `sym` wasn't found in
111
+ # `base`
112
+ # Raises:: Thor::PrivateMethodEncodedError if `sym` references
113
+ # a private method.
114
+ def call_public_method(sym)
115
+ if base.respond_to?(sym)
116
+ base.send(sym)
117
+ elsif base.respond_to?(sym, true)
118
+ raise Thor::PrivateMethodEncodedError,
119
+ "Method #{base.class}##{sym} should be public, not private"
120
+ else
121
+ nil
103
122
  end
104
123
  end
105
124
 
@@ -102,7 +102,7 @@ class Thor
102
102
  #
103
103
  def template(source, *args, &block)
104
104
  config = args.last.is_a?(Hash) ? args.pop : {}
105
- destination = args.first || source
105
+ destination = args.first || source.sub(/\.tt$/, '')
106
106
 
107
107
  source = File.expand_path(find_in_source_paths(source.to_s))
108
108
  context = instance_eval('binding')
@@ -187,7 +187,7 @@ class Thor
187
187
  #
188
188
  # ==== Examples
189
189
  #
190
- # inject_into_class "app/controllers/application_controller.rb", " filter_parameter :password\n"
190
+ # inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n"
191
191
  #
192
192
  # inject_into_class "app/controllers/application_controller.rb", ApplicationController do
193
193
  # " filter_parameter :password\n"
@@ -229,6 +229,44 @@ class Thor
229
229
  end
230
230
  end
231
231
 
232
+ # Uncomment all lines matching a given regex. It will leave the space
233
+ # which existed before the comment hash in tact but will remove any spacing
234
+ # between the comment hash and the beginning of the line.
235
+ #
236
+ # ==== Parameters
237
+ # path<String>:: path of the file to be changed
238
+ # flag<Regexp|String>:: the regexp or string used to decide which lines to uncomment
239
+ # config<Hash>:: give :verbose => false to not log the status.
240
+ #
241
+ # ==== Example
242
+ #
243
+ # uncomment_lines 'config/initializers/session_store.rb', /active_record/
244
+ #
245
+ def uncomment_lines(path, flag, *args)
246
+ flag = flag.respond_to?(:source) ? flag.source : flag
247
+
248
+ gsub_file(path, /^(\s*)#\s*(.*#{flag})/, '\1\2', *args)
249
+ end
250
+
251
+ # Comment all lines matching a given regex. It will leave the space
252
+ # which existed before the beginning of the line in tact and will insert
253
+ # a single space after the comment hash.
254
+ #
255
+ # ==== Parameters
256
+ # path<String>:: path of the file to be changed
257
+ # flag<Regexp|String>:: the regexp or string used to decide which lines to comment
258
+ # config<Hash>:: give :verbose => false to not log the status.
259
+ #
260
+ # ==== Example
261
+ #
262
+ # comment_lines 'config/initializers/session_store.rb', /cookie_store/
263
+ #
264
+ def comment_lines(path, flag, *args)
265
+ flag = flag.respond_to?(:source) ? flag.source : flag
266
+
267
+ gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args)
268
+ end
269
+
232
270
  # Removes a file at the given location.
233
271
  #
234
272
  # ==== Parameters
@@ -19,7 +19,7 @@ class Thor
19
19
  action add_file create_file in_root inside run run_ruby_script)
20
20
 
21
21
  module Base
22
- attr_accessor :options
22
+ attr_accessor :options, :parent_options, :args
23
23
 
24
24
  # It receives arguments in an Array and two hashes, one for options and
25
25
  # other for configuration.
@@ -38,22 +38,43 @@ class Thor
38
38
  # config<Hash>:: Configuration for this Thor class.
39
39
  #
40
40
  def initialize(args=[], options={}, config={})
41
- args = Thor::Arguments.parse(self.class.arguments, args)
42
- args.each { |key, value| send("#{key}=", value) }
43
-
44
41
  parse_options = self.class.class_options
45
42
 
43
+ # The start method splits inbound arguments at the first argument
44
+ # that looks like an option (starts with - or --). It then calls
45
+ # new, passing in the two halves of the arguments Array as the
46
+ # first two parameters.
47
+
46
48
  if options.is_a?(Array)
47
49
  task_options = config.delete(:task_options) # hook for start
48
50
  parse_options = parse_options.merge(task_options) if task_options
49
51
  array_options, hash_options = options, {}
50
52
  else
53
+ # Handle the case where the class was explicitly instantiated
54
+ # with pre-parsed options.
51
55
  array_options, hash_options = [], options
52
56
  end
53
57
 
58
+ # Let Thor::Options parse the options first, so it can remove
59
+ # declared options from the array. This will leave us with
60
+ # a list of arguments that weren't declared.
54
61
  opts = Thor::Options.new(parse_options, hash_options)
55
62
  self.options = opts.parse(array_options)
63
+
64
+ # If unknown options are disallowed, make sure that none of the
65
+ # remaining arguments looks like an option.
56
66
  opts.check_unknown! if self.class.check_unknown_options?(config)
67
+
68
+ # Add the remaining arguments from the options parser to the
69
+ # arguments passed in to initialize. Then remove any positional
70
+ # arguments declared using #argument (this is primarily used
71
+ # by Thor::Group). Tis will leave us with the remaining
72
+ # positional arguments.
73
+ thor_args = Thor::Arguments.new(self.class.arguments)
74
+ thor_args.parse(args + opts.remaining).each { |k,v| send("#{k}=", v) }
75
+ args = thor_args.remaining
76
+
77
+ @args = args
57
78
  end
58
79
 
59
80
  class << self
@@ -94,8 +115,6 @@ class Thor
94
115
  end
95
116
 
96
117
  module ClassMethods
97
- attr_accessor :debugging
98
-
99
118
  def attr_reader(*) #:nodoc:
100
119
  no_tasks { super }
101
120
  end
@@ -212,13 +231,14 @@ class Thor
212
231
  # options<Hash>:: Described below.
213
232
  #
214
233
  # ==== Options
215
- # :desc - Description for the argument.
216
- # :required - If the argument is required or not.
217
- # :default - Default value for this argument.
218
- # :group - The group for this options. Use by class options to output options in different levels.
219
- # :aliases - Aliases for this option.
220
- # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
221
- # :banner - String to show on usage notes.
234
+ # :desc:: -- Description for the argument.
235
+ # :required:: -- If the argument is required or not.
236
+ # :default:: -- Default value for this argument.
237
+ # :group:: -- The group for this options. Use by class options to output options in different levels.
238
+ # :aliases:: -- Aliases for this option. <b>Note:</b> Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead.
239
+ # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
240
+ # :banner:: -- String to show on usage notes.
241
+ # :hide:: -- If you want to hide this option from the help.
222
242
  #
223
243
  def class_option(name, options={})
224
244
  build_option(name, options, class_options)
@@ -227,7 +247,7 @@ class Thor
227
247
  # Removes a previous defined argument. If :undefine is given, undefine
228
248
  # accessors as well.
229
249
  #
230
- # ==== Paremeters
250
+ # ==== Parameters
231
251
  # names<Array>:: Arguments to be removed
232
252
  #
233
253
  # ==== Examples
@@ -246,7 +266,7 @@ class Thor
246
266
 
247
267
  # Removes a previous defined class option.
248
268
  #
249
- # ==== Paremeters
269
+ # ==== Parameters
250
270
  # names<Array>:: Class options to be removed
251
271
  #
252
272
  # ==== Examples
@@ -384,17 +404,22 @@ class Thor
384
404
  # script.invoke(:task, first_arg, second_arg, third_arg)
385
405
  #
386
406
  def start(given_args=ARGV, config={})
387
- self.debugging = given_args.delete("--debug")
388
407
  config[:shell] ||= Thor::Base.shell.new
389
408
  dispatch(nil, given_args.dup, nil, config)
390
409
  rescue Thor::Error => e
391
- debugging ? (raise e) : config[:shell].error(e.message)
410
+ ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
392
411
  exit(1) if exit_on_failure?
412
+ rescue Errno::EPIPE
413
+ # This happens if a thor task is piped to something like `head`,
414
+ # which closes the pipe when it's done reading. This will also
415
+ # mean that if the pipe is closed, further unnecessary
416
+ # computation will not occur.
417
+ exit(0)
393
418
  end
394
419
 
395
420
  # Allows to use private methods from parent in child classes as tasks.
396
421
  #
397
- # ==== Paremeters
422
+ # ==== Parameters
398
423
  # names<Array>:: Method names to be used as tasks
399
424
  #
400
425
  # ==== Examples
@@ -408,16 +433,26 @@ class Thor
408
433
  end
409
434
  end
410
435
 
411
- def handle_no_task_error(task) #:nodoc:
412
- if $thor_runner
436
+ def handle_no_task_error(task, has_namespace = $thor_runner) #:nodoc:
437
+ if has_namespace
413
438
  raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
414
439
  else
415
440
  raise UndefinedTaskError, "Could not find task #{task.inspect}."
416
441
  end
417
442
  end
418
443
 
419
- def handle_argument_error(task, error) #:nodoc:
420
- raise InvocationError, "#{task.name.inspect} was called incorrectly. Call as #{self.banner(task).inspect}."
444
+ def handle_argument_error(task, error, arity=nil) #:nodoc:
445
+ msg = "#{basename} #{task.name}"
446
+ if arity
447
+ required = arity < 0 ? (-1 - arity) : arity
448
+ msg << " requires at least #{required} argument"
449
+ msg << "s" if required > 1
450
+ else
451
+ msg = "call #{msg} as"
452
+ end
453
+
454
+ msg << ": #{self.banner(task).inspect}."
455
+ raise InvocationError, msg
421
456
  end
422
457
 
423
458
  protected
@@ -450,15 +485,17 @@ class Thor
450
485
  padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
451
486
 
452
487
  options.each do |option|
453
- item = [ option.usage(padding) ]
454
- item.push(option.description ? "# #{option.description}" : "")
488
+ unless option.hide
489
+ item = [ option.usage(padding) ]
490
+ item.push(option.description ? "# #{option.description}" : "")
455
491
 
456
- list << item
457
- list << [ "", "# Default: #{option.default}" ] if option.show_default?
492
+ list << item
493
+ list << [ "", "# Default: #{option.default}" ] if option.show_default?
494
+ end
458
495
  end
459
496
 
460
497
  shell.say(group_name ? "#{group_name} options:" : "Options:")
461
- shell.print_table(list, :ident => 2)
498
+ shell.print_table(list, :indent => 2)
462
499
  shell.say ""
463
500
  end
464
501
 
@@ -476,7 +513,7 @@ class Thor
476
513
  def build_option(name, options, scope) #:nodoc:
477
514
  scope[name] = Thor::Option.new(name, options[:desc], options[:required],
478
515
  options[:type], options[:default], options[:banner],
479
- options[:lazy_default], options[:group], options[:aliases])
516
+ options[:lazy_default], options[:group], options[:aliases], options[:hide])
480
517
  end
481
518
 
482
519
  # Receives a hash of options, parse them and add to the scope. This is a
@@ -509,6 +546,7 @@ class Thor
509
546
  # and file into baseclass.
510
547
  def inherited(klass)
511
548
  Thor::Base.register_klass_file(klass)
549
+ klass.instance_variable_set(:@no_tasks, false)
512
550
  end
513
551
 
514
552
  # Fire this callback whenever a method is added. Added methods are