bundler 1.2.0.pre.1 → 1.2.0.rc

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (50) hide show
  1. data/CHANGELOG.md +27 -1
  2. data/ISSUES.md +3 -3
  3. data/lib/bundler.rb +7 -5
  4. data/lib/bundler/cli.rb +17 -20
  5. data/lib/bundler/definition.rb +5 -0
  6. data/lib/bundler/dsl.rb +1 -1
  7. data/lib/bundler/fetcher.rb +5 -6
  8. data/lib/bundler/gem_path_manipulation.rb +8 -0
  9. data/lib/bundler/ruby_version.rb +4 -2
  10. data/lib/bundler/runtime.rb +46 -19
  11. data/lib/bundler/source.rb +25 -10
  12. data/lib/bundler/templates/newgem/{LICENSE.tt → LICENSE.txt.tt} +0 -0
  13. data/lib/bundler/templates/newgem/Rakefile.tt +0 -1
  14. data/lib/bundler/templates/newgem/newgem.gemspec.tt +6 -4
  15. data/lib/bundler/vendor/thor.rb +49 -28
  16. data/lib/bundler/vendor/thor/actions.rb +7 -3
  17. data/lib/bundler/vendor/thor/actions/create_link.rb +1 -1
  18. data/lib/bundler/vendor/thor/actions/directory.rb +9 -4
  19. data/lib/bundler/vendor/thor/actions/empty_directory.rb +24 -5
  20. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +39 -1
  21. data/lib/bundler/vendor/thor/base.rb +65 -24
  22. data/lib/bundler/vendor/thor/core_ext/dir_escape.rb +0 -0
  23. data/lib/bundler/vendor/thor/error.rb +6 -1
  24. data/lib/bundler/vendor/thor/group.rb +21 -9
  25. data/lib/bundler/vendor/thor/invocation.rb +4 -2
  26. data/lib/bundler/vendor/thor/parser/arguments.rb +4 -0
  27. data/lib/bundler/vendor/thor/parser/option.rb +3 -2
  28. data/lib/bundler/vendor/thor/parser/options.rb +13 -7
  29. data/lib/bundler/vendor/thor/rake_compat.rb +13 -8
  30. data/lib/bundler/vendor/thor/runner.rb +15 -3
  31. data/lib/bundler/vendor/thor/shell.rb +4 -4
  32. data/lib/bundler/vendor/thor/shell/basic.rb +169 -82
  33. data/lib/bundler/vendor/thor/shell/color.rb +40 -4
  34. data/lib/bundler/vendor/thor/shell/html.rb +28 -26
  35. data/lib/bundler/vendor/thor/task.rb +24 -5
  36. data/lib/bundler/vendor/thor/util.rb +43 -6
  37. data/lib/bundler/vendor/thor/version.rb +1 -1
  38. data/lib/bundler/version.rb +1 -1
  39. data/man/bundle-config.ronn +2 -2
  40. data/spec/bundler/definition_spec.rb +25 -0
  41. data/spec/cache/git_spec.rb +47 -0
  42. data/spec/cache/path_spec.rb +18 -0
  43. data/spec/install/git_spec.rb +21 -6
  44. data/spec/lock/lockfile_spec.rb +1 -1
  45. data/spec/other/check_spec.rb +14 -1
  46. data/spec/other/newgem_spec.rb +1 -0
  47. data/spec/other/platform_spec.rb +164 -0
  48. data/spec/runtime/setup_spec.rb +87 -0
  49. data/spec/runtime/with_clean_env_spec.rb +14 -0
  50. metadata +78 -133
@@ -1,2 +1 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
@@ -1,17 +1,19 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/<%=config[:name]%>/version', __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require '<%=config[:name]%>/version'
3
5
 
4
6
  Gem::Specification.new do |gem|
7
+ gem.name = <%=config[:name].inspect%>
8
+ gem.version = <%=config[:constant_name]%>::VERSION
5
9
  gem.authors = [<%=config[:author].inspect%>]
6
10
  gem.email = [<%=config[:email].inspect%>]
7
11
  gem.description = %q{TODO: Write a gem description}
8
12
  gem.summary = %q{TODO: Write a gem summary}
9
13
  gem.homepage = ""
10
14
 
11
- gem.files = `git ls-files`.split($\)
15
+ gem.files = `git ls-files`.split($/)
12
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = <%=config[:name].inspect%>
15
18
  gem.require_paths = ["lib"]
16
- gem.version = <%=config[:constant_name]%>::VERSION
17
19
  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,35 +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] || find_subcommand_and_update_argv(meth) || meth || default_task
315
- meth.to_s.gsub('-','_') # treat foo-bar > foo_bar
316
- end
317
-
318
- # terrible hack that overwrites ARGV
319
- def find_subcommand_and_update_argv(subcmd_name) #:nodoc:
320
- return unless subcmd_name
321
- cmd = find_subcommand(subcmd_name)
322
- ARGV[0] = cmd if cmd
323
- cmd
324
- end
330
+ return default_task.to_s.gsub('-', '_') unless meth
325
331
 
326
- def find_subcommand(subcmd_name)
327
- possibilities = find_subcommand_possibilities subcmd_name
332
+ possibilities = find_task_possibilities(meth)
328
333
  if possibilities.size > 1
329
- raise "Ambiguous subcommand #{subcmd_name} matches [#{possibilities.join(', ')}]"
334
+ raise ArgumentError, "Ambiguous task #{meth} matches [#{possibilities.join(', ')}]"
330
335
  elsif possibilities.size < 1
331
- return nil
336
+ meth = meth || default_task
337
+ elsif map[meth]
338
+ meth = map[meth]
339
+ else
340
+ meth = possibilities.first
332
341
  end
333
342
 
334
- possibilities.first
343
+ meth.to_s.gsub('-','_') # treat foo-bar as foo_bar
335
344
  end
336
345
 
337
- def find_subcommand_possibilities(subcmd_name)
338
- len = subcmd_name.length
339
- all_tasks.map {|t| t.first}.select { |n| subcmd_name == n[0, len] }
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
340
361
  end
341
362
 
342
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.
@@ -67,7 +71,8 @@ class Thor
67
71
  protected
68
72
 
69
73
  def execute!
70
- lookup = config[:recursive] ? File.join(source, '**') : source
74
+ lookup = Util.escape_globs(source)
75
+ lookup = config[:recursive] ? File.join(lookup, '**') : lookup
71
76
  lookup = File.join(lookup, '{*,.[a-z]*}')
72
77
 
73
78
  Dir[lookup].sort.each do |file_source|
@@ -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
 
@@ -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
@@ -210,13 +231,14 @@ class Thor
210
231
  # options<Hash>:: Described below.
211
232
  #
212
233
  # ==== Options
213
- # :desc - Description for the argument.
214
- # :required - If the argument is required or not.
215
- # :default - Default value for this argument.
216
- # :group - The group for this options. Use by class options to output options in different levels.
217
- # :aliases - Aliases for this option.
218
- # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
219
- # :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.
220
242
  #
221
243
  def class_option(name, options={})
222
244
  build_option(name, options, class_options)
@@ -225,7 +247,7 @@ class Thor
225
247
  # Removes a previous defined argument. If :undefine is given, undefine
226
248
  # accessors as well.
227
249
  #
228
- # ==== Paremeters
250
+ # ==== Parameters
229
251
  # names<Array>:: Arguments to be removed
230
252
  #
231
253
  # ==== Examples
@@ -244,7 +266,7 @@ class Thor
244
266
 
245
267
  # Removes a previous defined class option.
246
268
  #
247
- # ==== Paremeters
269
+ # ==== Parameters
248
270
  # names<Array>:: Class options to be removed
249
271
  #
250
272
  # ==== Examples
@@ -387,11 +409,17 @@ class Thor
387
409
  rescue Thor::Error => e
388
410
  ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
389
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)
390
418
  end
391
419
 
392
420
  # Allows to use private methods from parent in child classes as tasks.
393
421
  #
394
- # ==== Paremeters
422
+ # ==== Parameters
395
423
  # names<Array>:: Method names to be used as tasks
396
424
  #
397
425
  # ==== Examples
@@ -405,16 +433,26 @@ class Thor
405
433
  end
406
434
  end
407
435
 
408
- def handle_no_task_error(task) #:nodoc:
409
- if $thor_runner
436
+ def handle_no_task_error(task, has_namespace = $thor_runner) #:nodoc:
437
+ if has_namespace
410
438
  raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
411
439
  else
412
440
  raise UndefinedTaskError, "Could not find task #{task.inspect}."
413
441
  end
414
442
  end
415
443
 
416
- def handle_argument_error(task, error) #:nodoc:
417
- 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
418
456
  end
419
457
 
420
458
  protected
@@ -447,15 +485,17 @@ class Thor
447
485
  padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
448
486
 
449
487
  options.each do |option|
450
- item = [ option.usage(padding) ]
451
- item.push(option.description ? "# #{option.description}" : "")
488
+ unless option.hide
489
+ item = [ option.usage(padding) ]
490
+ item.push(option.description ? "# #{option.description}" : "")
452
491
 
453
- list << item
454
- list << [ "", "# Default: #{option.default}" ] if option.show_default?
492
+ list << item
493
+ list << [ "", "# Default: #{option.default}" ] if option.show_default?
494
+ end
455
495
  end
456
496
 
457
497
  shell.say(group_name ? "#{group_name} options:" : "Options:")
458
- shell.print_table(list, :ident => 2)
498
+ shell.print_table(list, :indent => 2)
459
499
  shell.say ""
460
500
  end
461
501
 
@@ -473,7 +513,7 @@ class Thor
473
513
  def build_option(name, options, scope) #:nodoc:
474
514
  scope[name] = Thor::Option.new(name, options[:desc], options[:required],
475
515
  options[:type], options[:default], options[:banner],
476
- options[:lazy_default], options[:group], options[:aliases])
516
+ options[:lazy_default], options[:group], options[:aliases], options[:hide])
477
517
  end
478
518
 
479
519
  # Receives a hash of options, parse them and add to the scope. This is a
@@ -506,6 +546,7 @@ class Thor
506
546
  # and file into baseclass.
507
547
  def inherited(klass)
508
548
  Thor::Base.register_klass_file(klass)
549
+ klass.instance_variable_set(:@no_tasks, false)
509
550
  end
510
551
 
511
552
  # Fire this callback whenever a method is added. Added methods are