bundler 1.3.6 → 1.4.0.pre.1

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/CHANGELOG.md +27 -14
  4. data/CONTRIBUTING.md +2 -2
  5. data/{CONTRIBUTE.md → DEVELOPMENT.md} +31 -12
  6. data/ISSUES.md +1 -1
  7. data/README.md +6 -4
  8. data/Rakefile +1 -15
  9. data/bin/bundle +5 -8
  10. data/bundler.gemspec +1 -1
  11. data/lib/bundler.rb +37 -21
  12. data/lib/bundler/cli.rb +33 -21
  13. data/lib/bundler/constants.rb +5 -0
  14. data/lib/bundler/current_ruby.rb +88 -0
  15. data/lib/bundler/definition.rb +35 -11
  16. data/lib/bundler/dependency.rb +7 -78
  17. data/lib/bundler/dsl.rb +1 -1
  18. data/lib/bundler/fetcher.rb +37 -24
  19. data/lib/bundler/gem_helper.rb +2 -2
  20. data/lib/bundler/gem_installer.rb +9 -0
  21. data/lib/bundler/installer.rb +76 -7
  22. data/lib/bundler/parallel_workers.rb +18 -0
  23. data/lib/bundler/parallel_workers/thread_worker.rb +27 -0
  24. data/lib/bundler/parallel_workers/unix_worker.rb +88 -0
  25. data/lib/bundler/parallel_workers/worker.rb +68 -0
  26. data/lib/bundler/resolver.rb +17 -11
  27. data/lib/bundler/rubygems_ext.rb +2 -2
  28. data/lib/bundler/rubygems_integration.rb +37 -25
  29. data/lib/bundler/runtime.rb +8 -1
  30. data/lib/bundler/safe_catch.rb +101 -0
  31. data/lib/bundler/shared_helpers.rb +27 -1
  32. data/lib/bundler/source/git.rb +2 -1
  33. data/lib/bundler/source/git/git_proxy.rb +3 -3
  34. data/lib/bundler/source/path.rb +3 -2
  35. data/lib/bundler/source/rubygems.rb +5 -17
  36. data/lib/bundler/spec_set.rb +16 -1
  37. data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  38. data/lib/bundler/vendor/net/http/persistent.rb +136 -38
  39. data/lib/bundler/vendor/thor.rb +211 -188
  40. data/lib/bundler/vendor/thor/actions.rb +19 -19
  41. data/lib/bundler/vendor/thor/actions/create_link.rb +3 -0
  42. data/lib/bundler/vendor/thor/actions/directory.rb +30 -10
  43. data/lib/bundler/vendor/thor/actions/empty_directory.rb +3 -19
  44. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +6 -3
  45. data/lib/bundler/vendor/thor/base.rb +101 -97
  46. data/lib/bundler/vendor/thor/{task.rb → command.rb} +17 -13
  47. data/lib/bundler/vendor/thor/core_ext/io_binary_read.rb +12 -0
  48. data/lib/bundler/vendor/thor/error.rb +8 -11
  49. data/lib/bundler/vendor/thor/group.rb +35 -38
  50. data/lib/bundler/vendor/thor/invocation.rb +28 -26
  51. data/lib/bundler/vendor/thor/parser/options.rb +21 -19
  52. data/lib/bundler/vendor/thor/rake_compat.rb +3 -2
  53. data/lib/bundler/vendor/thor/runner.rb +22 -21
  54. data/lib/bundler/vendor/thor/shell/basic.rb +44 -22
  55. data/lib/bundler/vendor/thor/shell/color.rb +13 -9
  56. data/lib/bundler/vendor/thor/shell/html.rb +13 -9
  57. data/lib/bundler/vendor/thor/util.rb +214 -210
  58. data/lib/bundler/vendor/thor/version.rb +1 -1
  59. data/lib/bundler/version.rb +1 -1
  60. data/man/bundle-install.ronn +5 -1
  61. data/man/gemfile.5.ronn +10 -2
  62. data/spec/bundler/dsl_spec.rb +3 -3
  63. data/spec/bundler/gem_helper_spec.rb +14 -17
  64. data/spec/bundler/safe_catch_spec.rb +37 -0
  65. data/spec/install/gems/dependency_api_spec.rb +1 -36
  66. data/spec/install/gems/packed_spec.rb +4 -2
  67. data/spec/install/gems/resolving_spec.rb +37 -0
  68. data/spec/install/gems/simple_case_spec.rb +18 -16
  69. data/spec/install/git_spec.rb +1 -1
  70. data/spec/other/binstubs_spec.rb +24 -13
  71. data/spec/other/exec_spec.rb +24 -2
  72. data/spec/other/help_spec.rb +6 -6
  73. data/spec/other/outdated_spec.rb +3 -3
  74. data/spec/quality_spec.rb +3 -2
  75. data/spec/realworld/dependency_api_spec.rb +1 -1
  76. data/spec/realworld/edgecases_spec.rb +3 -3
  77. data/spec/realworld/parallel_install_spec.rb +19 -0
  78. data/spec/resolver/basic_spec.rb +11 -0
  79. data/spec/runtime/require_spec.rb +9 -0
  80. data/spec/runtime/setup_spec.rb +2 -3
  81. data/spec/spec_helper.rb +0 -1
  82. data/spec/support/builders.rb +2 -4
  83. data/spec/support/helpers.rb +4 -8
  84. data/spec/support/indexes.rb +18 -0
  85. data/spec/support/streams.rb +13 -0
  86. metadata +19 -11
  87. data/lib/bundler/vendor/thor/core_ext/dir_escape.rb +0 -0
  88. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +0 -9
  89. data/spec/support/artifice/endpoint_host_redirect.rb +0 -15
  90. data/spec/support/permissions.rb +0 -11
@@ -3,21 +3,32 @@ require 'thor/base'
3
3
 
4
4
  class Thor
5
5
  class << self
6
- # Sets the default task when thor is executed without an explicit task to be called.
6
+ # Allows for custom "Command" package naming.
7
+ #
8
+ # === Parameters
9
+ # name<String>
10
+ # options<Hash>
11
+ #
12
+ def package_name(name, options={})
13
+ @package_name = name.nil? || name == '' ? nil : name
14
+ end
15
+
16
+ # Sets the default command when thor is executed without an explicit command to be called.
7
17
  #
8
18
  # ==== Parameters
9
- # meth<Symbol>:: name of the default task
10
- #
11
- def default_task(meth=nil)
12
- case meth
13
- when :none
14
- @default_task = 'help'
15
- when nil
16
- @default_task ||= from_superclass(:default_task, 'help')
17
- else
18
- @default_task = meth.to_s
19
+ # meth<Symbol>:: name of the default command
20
+ #
21
+ def default_command(meth=nil)
22
+ @default_command = case meth
23
+ when :none
24
+ 'help'
25
+ when nil
26
+ @default_command || from_superclass(:default_command, 'help')
27
+ else
28
+ meth.to_s
19
29
  end
20
30
  end
31
+ alias default_task default_command
21
32
 
22
33
  # Registers another Thor subclass as a command.
23
34
  #
@@ -36,7 +47,7 @@ class Thor
36
47
  end
37
48
  end
38
49
 
39
- # Defines the usage and the description of the next task.
50
+ # Defines the usage and the description of the next command.
40
51
  #
41
52
  # ==== Parameters
42
53
  # usage<String>
@@ -45,29 +56,29 @@ class Thor
45
56
  #
46
57
  def desc(usage, description, options={})
47
58
  if options[:for]
48
- task = find_and_refresh_task(options[:for])
49
- task.usage = usage if usage
50
- task.description = description if description
59
+ command = find_and_refresh_command(options[:for])
60
+ command.usage = usage if usage
61
+ command.description = description if description
51
62
  else
52
63
  @usage, @desc, @hide = usage, description, options[:hide] || false
53
64
  end
54
65
  end
55
66
 
56
- # Defines the long description of the next task.
67
+ # Defines the long description of the next command.
57
68
  #
58
69
  # ==== Parameters
59
70
  # long description<String>
60
71
  #
61
72
  def long_desc(long_description, options={})
62
73
  if options[:for]
63
- task = find_and_refresh_task(options[:for])
64
- task.long_description = long_description if long_description
74
+ command = find_and_refresh_command(options[:for])
75
+ command.long_description = long_description if long_description
65
76
  else
66
77
  @long_desc = long_description
67
78
  end
68
79
  end
69
80
 
70
- # Maps an input to a task. If you define:
81
+ # Maps an input to a command. If you define:
71
82
  #
72
83
  # map "-T" => "list"
73
84
  #
@@ -75,10 +86,10 @@ class Thor
75
86
  #
76
87
  # thor -T
77
88
  #
78
- # Will invoke the list task.
89
+ # Will invoke the list command.
79
90
  #
80
91
  # ==== Parameters
81
- # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task.
92
+ # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
82
93
  #
83
94
  def map(mappings=nil)
84
95
  @map ||= from_superclass(:map, {})
@@ -96,7 +107,7 @@ class Thor
96
107
  @map
97
108
  end
98
109
 
99
- # Declares the options for the next task to be declared.
110
+ # Declares the options for the next command to be declared.
100
111
  #
101
112
  # ==== Parameters
102
113
  # Hash[Symbol => Object]:: The hash key is the name of the option and the value
@@ -112,15 +123,15 @@ class Thor
112
123
  alias options method_options
113
124
 
114
125
  # Adds an option to the set of method options. If :for is given as option,
115
- # it allows you to change the options from a previous defined task.
126
+ # it allows you to change the options from a previous defined command.
116
127
  #
117
- # def previous_task
128
+ # def previous_command
118
129
  # # magic
119
130
  # end
120
131
  #
121
- # method_option :foo => :bar, :for => :previous_task
132
+ # method_option :foo => :bar, :for => :previous_command
122
133
  #
123
- # def next_task
134
+ # def next_command
124
135
  # # magic
125
136
  # end
126
137
  #
@@ -139,38 +150,38 @@ class Thor
139
150
  #
140
151
  def method_option(name, options={})
141
152
  scope = if options[:for]
142
- find_and_refresh_task(options[:for]).options
153
+ find_and_refresh_command(options[:for]).options
143
154
  else
144
155
  method_options
145
156
  end
146
157
 
147
158
  build_option(name, options, scope)
148
159
  end
149
-
150
160
  alias option method_option
151
161
 
152
- # Prints help information for the given task.
162
+ # Prints help information for the given command.
153
163
  #
154
164
  # ==== Parameters
155
165
  # shell<Thor::Shell>
156
- # task_name<String>
166
+ # command_name<String>
157
167
  #
158
- def task_help(shell, task_name)
159
- meth = normalize_task_name(task_name)
160
- task = all_tasks[meth]
161
- handle_no_task_error(meth) unless task
168
+ def command_help(shell, command_name)
169
+ meth = normalize_command_name(command_name)
170
+ command = all_commands[meth]
171
+ handle_no_command_error(meth) unless command
162
172
 
163
173
  shell.say "Usage:"
164
- shell.say " #{banner(task)}"
174
+ shell.say " #{banner(command)}"
165
175
  shell.say
166
- class_options_help(shell, nil => task.options.map { |_, o| o })
167
- if task.long_description
176
+ class_options_help(shell, nil => command.options.map { |_, o| o })
177
+ if command.long_description
168
178
  shell.say "Description:"
169
- shell.print_wrapped(task.long_description, :indent => 2)
179
+ shell.print_wrapped(command.long_description, :indent => 2)
170
180
  else
171
- shell.say task.description
181
+ shell.say command.description
172
182
  end
173
183
  end
184
+ alias task_help command_help
174
185
 
175
186
  # Prints help information for this class.
176
187
  #
@@ -178,32 +189,39 @@ class Thor
178
189
  # shell<Thor::Shell>
179
190
  #
180
191
  def help(shell, subcommand = false)
181
- list = printable_tasks(true, subcommand)
192
+ list = printable_commands(true, subcommand)
182
193
  Thor::Util.thor_classes_in(self).each do |klass|
183
- list += klass.printable_tasks(false)
194
+ list += klass.printable_commands(false)
184
195
  end
185
196
  list.sort!{ |a,b| a[0] <=> b[0] }
186
197
 
187
- shell.say "Tasks:"
198
+ if @package_name
199
+ shell.say "#{@package_name} commands:"
200
+ else
201
+ shell.say "Commands:"
202
+ end
203
+
188
204
  shell.print_table(list, :indent => 2, :truncate => true)
189
205
  shell.say
190
206
  class_options_help(shell)
191
207
  end
192
208
 
193
- # Returns tasks ready to be printed.
194
- def printable_tasks(all = true, subcommand = false)
195
- (all ? all_tasks : tasks).map do |_, task|
196
- next if task.hidden?
209
+ # Returns commands ready to be printed.
210
+ def printable_commands(all = true, subcommand = false)
211
+ (all ? all_commands : commands).map do |_, command|
212
+ next if command.hidden?
197
213
  item = []
198
- item << banner(task, false, subcommand)
199
- item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
214
+ item << banner(command, false, subcommand)
215
+ item << (command.description ? "# #{command.description.gsub(/\s+/m,' ')}" : "")
200
216
  item
201
217
  end.compact
202
218
  end
219
+ alias printable_tasks printable_commands
203
220
 
204
221
  def subcommands
205
222
  @subcommands ||= from_superclass(:subcommands, [])
206
223
  end
224
+ alias subtasks subcommands
207
225
 
208
226
  def subcommand(subcommand, subcommand_class)
209
227
  self.subcommands << subcommand.to_s
@@ -211,9 +229,10 @@ class Thor
211
229
 
212
230
  define_method(subcommand) do |*args|
213
231
  args, opts = Thor::Arguments.split(args)
214
- invoke subcommand_class, args, opts, :invoked_via_subcommand => true
232
+ invoke subcommand_class, args, opts, :invoked_via_subcommand => true, :class_options => options
215
233
  end
216
234
  end
235
+ alias subtask subcommand
217
236
 
218
237
  # Extend check unknown options to accept a hash of conditions.
219
238
  #
@@ -236,10 +255,10 @@ class Thor
236
255
  options = check_unknown_options
237
256
  return false unless options
238
257
 
239
- task = config[:current_task]
240
- return true unless task
258
+ command = config[:current_command]
259
+ return true unless command
241
260
 
242
- name = task.name
261
+ name = command.name
243
262
 
244
263
  if subcommands.include?(name)
245
264
  false
@@ -253,16 +272,16 @@ class Thor
253
272
  end
254
273
 
255
274
  # Stop parsing of options as soon as an unknown option or a regular
256
- # argument is encountered. All remaining arguments are passed to the task.
257
- # This is useful if you have a task that can receive arbitrary additional
275
+ # argument is encountered. All remaining arguments are passed to the command.
276
+ # This is useful if you have a command that can receive arbitrary additional
258
277
  # options, and where those additional options should not be handled by
259
278
  # Thor.
260
279
  #
261
280
  # ==== Example
262
281
  #
263
- # To better understand how this is useful, let's consider a task that calls
282
+ # To better understand how this is useful, let's consider a command that calls
264
283
  # an external command. A user may want to pass arbitrary options and
265
- # arguments to that command. The task itself also accepts some options,
284
+ # arguments to that command. The command itself also accepts some options,
266
285
  # which should be handled by Thor.
267
286
  #
268
287
  # class_option "verbose", :type => :boolean
@@ -288,163 +307,167 @@ class Thor
288
307
  # --verbose foo
289
308
  #
290
309
  # ==== Parameters
291
- # Symbol ...:: A list of tasks that should be affected.
292
- def stop_on_unknown_option!(*task_names)
310
+ # Symbol ...:: A list of commands that should be affected.
311
+ def stop_on_unknown_option!(*command_names)
293
312
  @stop_on_unknown_option ||= Set.new
294
- @stop_on_unknown_option.merge(task_names)
313
+ @stop_on_unknown_option.merge(command_names)
295
314
  end
296
315
 
297
- def stop_on_unknown_option?(task) #:nodoc:
298
- !!@stop_on_unknown_option && @stop_on_unknown_option.include?(task.name.to_sym)
316
+ def stop_on_unknown_option?(command) #:nodoc:
317
+ command && !@stop_on_unknown_option.nil? && @stop_on_unknown_option.include?(command.name.to_sym)
299
318
  end
300
319
 
301
- protected
302
-
303
- # The method responsible for dispatching given the args.
304
- def dispatch(meth, given_args, given_opts, config) #:nodoc:
305
- # There is an edge case when dispatching from a subcommand.
306
- # A problem occurs invoking the default task. This case occurs
307
- # when arguments are passed and a default task is defined, and
308
- # the first given_args does not match the default task.
309
- # Thor use "help" by default so we skip that case.
310
- # Note the call to retrieve_task_name. It's called with
311
- # given_args.dup since that method calls args.shift. Then lookup
312
- # the task normally. If the first item in given_args is not
313
- # a task then use the default task. The given_args will be
314
- # intact later since dup was used.
315
- if config[:invoked_via_subcommand] && given_args.size >= 1 && default_task != "help" && given_args.first != default_task
316
- meth ||= retrieve_task_name(given_args.dup)
317
- task = all_tasks[normalize_task_name(meth)]
318
- task ||= all_tasks[normalize_task_name(default_task)]
319
- else
320
- meth ||= retrieve_task_name(given_args)
321
- task = all_tasks[normalize_task_name(meth)]
322
- end
320
+ protected
321
+
322
+ # The method responsible for dispatching given the args.
323
+ def dispatch(meth, given_args, given_opts, config) #:nodoc:
324
+ # There is an edge case when dispatching from a subcommand.
325
+ # A problem occurs invoking the default command. This case occurs
326
+ # when arguments are passed and a default command is defined, and
327
+ # the first given_args does not match the default command.
328
+ # Thor use "help" by default so we skip that case.
329
+ # Note the call to retrieve_command_name. It's called with
330
+ # given_args.dup since that method calls args.shift. Then lookup
331
+ # the command normally. If the first item in given_args is not
332
+ # a command then use the default command. The given_args will be
333
+ # intact later since dup was used.
334
+ if config[:invoked_via_subcommand] && given_args.size >= 1 && default_command != "help" && given_args.first != default_command
335
+ meth ||= retrieve_command_name(given_args.dup)
336
+ command = all_commands[normalize_command_name(meth)]
337
+ command ||= all_commands[normalize_command_name(default_command)]
338
+ else
339
+ meth ||= retrieve_command_name(given_args)
340
+ command = all_commands[normalize_command_name(meth)]
341
+ end
323
342
 
324
- if task
325
- args, opts = Thor::Options.split(given_args)
326
- if stop_on_unknown_option?(task) && !args.empty?
327
- # given_args starts with a non-option, so we treat everything as
328
- # ordinary arguments
329
- args.concat opts
330
- opts.clear
331
- end
332
- else
333
- args, opts = given_args, nil
334
- task = Thor::DynamicTask.new(meth)
343
+ if command
344
+ args, opts = Thor::Options.split(given_args)
345
+ if stop_on_unknown_option?(command) && !args.empty?
346
+ # given_args starts with a non-option, so we treat everything as
347
+ # ordinary arguments
348
+ args.concat opts
349
+ opts.clear
335
350
  end
336
-
337
- opts = given_opts || opts || []
338
- config.merge!(:current_task => task, :task_options => task.options)
339
-
340
- instance = new(args, opts, config)
341
- yield instance if block_given?
342
- args = instance.args
343
- trailing = args[Range.new(arguments.size, -1)]
344
- instance.invoke_task(task, trailing || [])
351
+ else
352
+ args, opts = given_args, nil
353
+ command = Thor::DynamicCommand.new(meth)
345
354
  end
346
355
 
347
- # The banner for this class. You can customize it if you are invoking the
348
- # thor class by another ways which is not the Thor::Runner. It receives
349
- # the task that is going to be invoked and a boolean which indicates if
350
- # the namespace should be displayed as arguments.
351
- #
352
- def banner(task, namespace = nil, subcommand = false)
353
- "#{basename} #{task.formatted_usage(self, $thor_runner, subcommand)}"
354
- end
356
+ opts = given_opts || opts || []
357
+ config.merge!(:current_command => command, :command_options => command.options)
355
358
 
356
- def baseclass #:nodoc:
357
- Thor
358
- end
359
+ instance = new(args, opts, config)
360
+ yield instance if block_given?
361
+ args = instance.args
362
+ trailing = args[Range.new(arguments.size, -1)]
363
+ instance.invoke_command(command, trailing || [])
364
+ end
359
365
 
360
- def create_task(meth) #:nodoc:
361
- @long_desc ||= nil
362
- @usage ||= nil
363
- if @usage && @desc
364
- base_class = @hide ? Thor::HiddenTask : Thor::Task
365
- tasks[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
366
- @usage, @desc, @long_desc, @method_options, @hide = nil
367
- true
368
- elsif self.all_tasks[meth] || meth == "method_missing"
369
- true
370
- else
371
- puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " <<
372
- "Call desc if you want this method to be available as task or declare it inside a " <<
373
- "no_tasks{} block. Invoked from #{caller[1].inspect}."
374
- false
375
- end
376
- end
366
+ # The banner for this class. You can customize it if you are invoking the
367
+ # thor class by another ways which is not the Thor::Runner. It receives
368
+ # the command that is going to be invoked and a boolean which indicates if
369
+ # the namespace should be displayed as arguments.
370
+ #
371
+ def banner(command, namespace = nil, subcommand = false)
372
+ "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
373
+ end
377
374
 
378
- def initialize_added #:nodoc:
379
- class_options.merge!(method_options)
380
- @method_options = nil
381
- end
375
+ def baseclass #:nodoc:
376
+ Thor
377
+ end
382
378
 
383
- # Retrieve the task name from given args.
384
- def retrieve_task_name(args) #:nodoc:
385
- meth = args.first.to_s unless args.empty?
386
- if meth && (map[meth] || meth !~ /^\-/)
387
- args.shift
388
- else
389
- nil
390
- end
379
+ def create_command(meth) #:nodoc:
380
+ if @usage && @desc
381
+ base_class = @hide ? Thor::HiddenCommand : Thor::Command
382
+ commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
383
+ @usage, @desc, @long_desc, @method_options, @hide = nil
384
+ true
385
+ elsif self.all_commands[meth] || meth == "method_missing"
386
+ true
387
+ else
388
+ puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " <<
389
+ "Call desc if you want this method to be available as command or declare it inside a " <<
390
+ "no_commands{} block. Invoked from #{caller[1].inspect}."
391
+ false
391
392
  end
393
+ end
394
+ alias create_task create_command
392
395
 
393
- # receives a (possibly nil) task name and returns a name that is in
394
- # the tasks hash. In addition to normalizing aliases, this logic
395
- # will determine if a shortened command is an unambiguous substring of
396
- # a task or alias.
397
- #
398
- # +normalize_task_name+ also converts names like +animal-prison+
399
- # into +animal_prison+.
400
- def normalize_task_name(meth) #:nodoc:
401
- return default_task.to_s.gsub('-', '_') unless meth
402
-
403
- possibilities = find_task_possibilities(meth)
404
- if possibilities.size > 1
405
- raise ArgumentError, "Ambiguous task #{meth} matches [#{possibilities.join(', ')}]"
406
- elsif possibilities.size < 1
407
- meth = meth || default_task
408
- elsif map[meth]
409
- meth = map[meth]
410
- else
411
- meth = possibilities.first
412
- end
396
+ def initialize_added #:nodoc:
397
+ class_options.merge!(method_options)
398
+ @method_options = nil
399
+ end
413
400
 
414
- meth.to_s.gsub('-','_') # treat foo-bar as foo_bar
401
+ # Retrieve the command name from given args.
402
+ def retrieve_command_name(args) #:nodoc:
403
+ meth = args.first.to_s unless args.empty?
404
+ if meth && (map[meth] || meth !~ /^\-/)
405
+ args.shift
406
+ else
407
+ nil
415
408
  end
416
-
417
- # this is the logic that takes the task name passed in by the user
418
- # and determines whether it is an unambiguous substrings of a task or
419
- # alias name.
420
- def find_task_possibilities(meth)
421
- len = meth.to_s.length
422
- possibilities = all_tasks.merge(map).keys.select { |n| meth == n[0, len] }.sort
423
- unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
424
-
425
- if possibilities.include?(meth)
426
- [meth]
427
- elsif unique_possibilities.size == 1
428
- unique_possibilities
429
- else
430
- possibilities
431
- end
409
+ end
410
+ alias retrieve_task_name retrieve_command_name
411
+
412
+ # receives a (possibly nil) command name and returns a name that is in
413
+ # the commands hash. In addition to normalizing aliases, this logic
414
+ # will determine if a shortened command is an unambiguous substring of
415
+ # a command or alias.
416
+ #
417
+ # +normalize_command_name+ also converts names like +animal-prison+
418
+ # into +animal_prison+.
419
+ def normalize_command_name(meth) #:nodoc:
420
+ return default_command.to_s.gsub('-', '_') unless meth
421
+
422
+ possibilities = find_command_possibilities(meth)
423
+ if possibilities.size > 1
424
+ raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]"
425
+ elsif possibilities.size < 1
426
+ meth = meth || default_command
427
+ elsif map[meth]
428
+ meth = map[meth]
429
+ else
430
+ meth = possibilities.first
432
431
  end
433
432
 
434
- def subcommand_help(cmd)
435
- desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
436
- class_eval <<-RUBY
437
- def help(task = nil, subcommand = true); super; end
438
- RUBY
433
+ meth.to_s.gsub('-','_') # treat foo-bar as foo_bar
434
+ end
435
+ alias normalize_task_name normalize_command_name
436
+
437
+ # this is the logic that takes the command name passed in by the user
438
+ # and determines whether it is an unambiguous substrings of a command or
439
+ # alias name.
440
+ def find_command_possibilities(meth)
441
+ len = meth.to_s.length
442
+ possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort
443
+ unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
444
+
445
+ if possibilities.include?(meth)
446
+ [meth]
447
+ elsif unique_possibilities.size == 1
448
+ unique_possibilities
449
+ else
450
+ possibilities
439
451
  end
452
+ end
453
+ alias find_task_possibilities find_command_possibilities
454
+
455
+ def subcommand_help(cmd)
456
+ desc "help [COMMAND]", "Describe subcommands or one specific subcommand"
457
+ class_eval <<-RUBY
458
+ def help(command = nil, subcommand = true); super; end
459
+ RUBY
460
+ end
461
+ alias subtask_help subcommand_help
462
+
440
463
  end
441
464
 
442
465
  include Thor::Base
443
466
 
444
467
  map HELP_MAPPINGS => :help
445
468
 
446
- desc "help [TASK]", "Describe available tasks or one specific task"
447
- def help(task = nil, subcommand = false)
448
- task ? self.class.task_help(shell, task) : self.class.help(shell, subcommand)
469
+ desc "help [COMMAND]", "Describe available commands or one specific command"
470
+ def help(command = nil, subcommand = false)
471
+ command ? self.class.command_help(shell, command) : self.class.help(shell, subcommand)
449
472
  end
450
473
  end