thor 0.16.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +15 -0
  3. data/README.md +23 -6
  4. data/bin/thor +1 -1
  5. data/lib/thor/actions/create_file.rb +34 -35
  6. data/lib/thor/actions/create_link.rb +9 -5
  7. data/lib/thor/actions/directory.rb +33 -23
  8. data/lib/thor/actions/empty_directory.rb +75 -85
  9. data/lib/thor/actions/file_manipulation.rb +103 -36
  10. data/lib/thor/actions/inject_into_file.rb +46 -36
  11. data/lib/thor/actions.rb +90 -68
  12. data/lib/thor/base.rb +302 -244
  13. data/lib/thor/command.rb +142 -0
  14. data/lib/thor/core_ext/hash_with_indifferent_access.rb +52 -24
  15. data/lib/thor/error.rb +90 -10
  16. data/lib/thor/group.rb +70 -74
  17. data/lib/thor/invocation.rb +63 -55
  18. data/lib/thor/line_editor/basic.rb +37 -0
  19. data/lib/thor/line_editor/readline.rb +88 -0
  20. data/lib/thor/line_editor.rb +17 -0
  21. data/lib/thor/nested_context.rb +29 -0
  22. data/lib/thor/parser/argument.rb +24 -28
  23. data/lib/thor/parser/arguments.rb +110 -102
  24. data/lib/thor/parser/option.rb +53 -15
  25. data/lib/thor/parser/options.rb +174 -97
  26. data/lib/thor/parser.rb +4 -4
  27. data/lib/thor/rake_compat.rb +12 -11
  28. data/lib/thor/runner.rb +159 -155
  29. data/lib/thor/shell/basic.rb +216 -93
  30. data/lib/thor/shell/color.rb +53 -40
  31. data/lib/thor/shell/html.rb +61 -58
  32. data/lib/thor/shell.rb +29 -36
  33. data/lib/thor/util.rb +231 -213
  34. data/lib/thor/version.rb +1 -1
  35. data/lib/thor.rb +303 -166
  36. data/thor.gemspec +27 -24
  37. metadata +36 -226
  38. data/.gitignore +0 -44
  39. data/.rspec +0 -2
  40. data/.travis.yml +0 -7
  41. data/CHANGELOG.rdoc +0 -134
  42. data/Gemfile +0 -15
  43. data/Thorfile +0 -30
  44. data/bin/rake2thor +0 -86
  45. data/lib/thor/core_ext/dir_escape.rb +0 -0
  46. data/lib/thor/core_ext/file_binary_read.rb +0 -9
  47. data/lib/thor/core_ext/ordered_hash.rb +0 -100
  48. data/lib/thor/task.rb +0 -132
  49. data/spec/actions/create_file_spec.rb +0 -170
  50. data/spec/actions/create_link_spec.rb +0 -81
  51. data/spec/actions/directory_spec.rb +0 -149
  52. data/spec/actions/empty_directory_spec.rb +0 -130
  53. data/spec/actions/file_manipulation_spec.rb +0 -370
  54. data/spec/actions/inject_into_file_spec.rb +0 -135
  55. data/spec/actions_spec.rb +0 -331
  56. data/spec/base_spec.rb +0 -279
  57. data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
  58. data/spec/core_ext/ordered_hash_spec.rb +0 -115
  59. data/spec/exit_condition_spec.rb +0 -19
  60. data/spec/fixtures/application.rb +0 -2
  61. data/spec/fixtures/app{1}/README +0 -3
  62. data/spec/fixtures/bundle/execute.rb +0 -6
  63. data/spec/fixtures/bundle/main.thor +0 -1
  64. data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
  65. data/spec/fixtures/doc/COMMENTER +0 -10
  66. data/spec/fixtures/doc/README +0 -3
  67. data/spec/fixtures/doc/block_helper.rb +0 -3
  68. data/spec/fixtures/doc/components/.empty_directory +0 -0
  69. data/spec/fixtures/doc/config.rb +0 -1
  70. data/spec/fixtures/doc/config.yaml.tt +0 -1
  71. data/spec/fixtures/enum.thor +0 -10
  72. data/spec/fixtures/group.thor +0 -114
  73. data/spec/fixtures/invoke.thor +0 -112
  74. data/spec/fixtures/path with spaces +0 -0
  75. data/spec/fixtures/script.thor +0 -190
  76. data/spec/fixtures/task.thor +0 -10
  77. data/spec/group_spec.rb +0 -216
  78. data/spec/invocation_spec.rb +0 -100
  79. data/spec/parser/argument_spec.rb +0 -53
  80. data/spec/parser/arguments_spec.rb +0 -66
  81. data/spec/parser/option_spec.rb +0 -202
  82. data/spec/parser/options_spec.rb +0 -330
  83. data/spec/rake_compat_spec.rb +0 -72
  84. data/spec/register_spec.rb +0 -135
  85. data/spec/runner_spec.rb +0 -241
  86. data/spec/shell/basic_spec.rb +0 -300
  87. data/spec/shell/color_spec.rb +0 -81
  88. data/spec/shell/html_spec.rb +0 -32
  89. data/spec/shell_spec.rb +0 -47
  90. data/spec/spec_helper.rb +0 -59
  91. data/spec/task_spec.rb +0 -80
  92. data/spec/thor_spec.rb +0 -418
  93. data/spec/util_spec.rb +0 -196
data/lib/thor/base.rb CHANGED
@@ -1,15 +1,17 @@
1
- require 'thor/core_ext/hash_with_indifferent_access'
2
- require 'thor/core_ext/ordered_hash'
3
- require 'thor/error'
4
- require 'thor/shell'
5
- require 'thor/invocation'
6
- require 'thor/parser'
7
- require 'thor/task'
8
- require 'thor/util'
1
+ require_relative "command"
2
+ require_relative "core_ext/hash_with_indifferent_access"
3
+ require_relative "error"
4
+ require_relative "invocation"
5
+ require_relative "nested_context"
6
+ require_relative "parser"
7
+ require_relative "shell"
8
+ require_relative "line_editor"
9
+ require_relative "util"
9
10
 
10
11
  class Thor
11
- autoload :Actions, 'thor/actions'
12
- autoload :RakeCompat, 'thor/rake_compat'
12
+ autoload :Actions, File.expand_path("actions", __dir__)
13
+ autoload :RakeCompat, File.expand_path("rake_compat", __dir__)
14
+ autoload :Group, File.expand_path("group", __dir__)
13
15
 
14
16
  # Shortcuts for help.
15
17
  HELP_MAPPINGS = %w(-h -? --help -D)
@@ -18,6 +20,17 @@ class Thor
18
20
  THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
19
21
  action add_file create_file in_root inside run run_ruby_script)
20
22
 
23
+ TEMPLATE_EXTNAME = ".tt"
24
+
25
+ class << self
26
+ def deprecation_warning(message) #:nodoc:
27
+ unless ENV['THOR_SILENCE_DEPRECATION']
28
+ warn "Deprecation warning: #{message}\n" +
29
+ 'You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.'
30
+ end
31
+ end
32
+ end
33
+
21
34
  module Base
22
35
  attr_accessor :options, :parent_options, :args
23
36
 
@@ -37,7 +50,7 @@ class Thor
37
50
  #
38
51
  # config<Hash>:: Configuration for this Thor class.
39
52
  #
40
- def initialize(args=[], options={}, config={})
53
+ def initialize(args = [], local_options = {}, config = {})
41
54
  parse_options = self.class.class_options
42
55
 
43
56
  # The start method splits inbound arguments at the first argument
@@ -45,21 +58,26 @@ class Thor
45
58
  # new, passing in the two halves of the arguments Array as the
46
59
  # first two parameters.
47
60
 
48
- if options.is_a?(Array)
49
- task_options = config.delete(:task_options) # hook for start
50
- parse_options = parse_options.merge(task_options) if task_options
51
- array_options, hash_options = options, {}
61
+ command_options = config.delete(:command_options) # hook for start
62
+ parse_options = parse_options.merge(command_options) if command_options
63
+ if local_options.is_a?(Array)
64
+ array_options = local_options
65
+ hash_options = {}
52
66
  else
53
67
  # Handle the case where the class was explicitly instantiated
54
68
  # with pre-parsed options.
55
- array_options, hash_options = [], options
69
+ array_options = []
70
+ hash_options = local_options
56
71
  end
57
72
 
58
73
  # Let Thor::Options parse the options first, so it can remove
59
74
  # declared options from the array. This will leave us with
60
75
  # a list of arguments that weren't declared.
61
- opts = Thor::Options.new(parse_options, hash_options)
76
+ stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
77
+ disable_required_check = self.class.disable_required_check? config[:current_command]
78
+ opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check)
62
79
  self.options = opts.parse(array_options)
80
+ self.options = config[:class_options].merge(options) if config[:class_options]
63
81
 
64
82
  # If unknown options are disallowed, make sure that none of the
65
83
  # remaining arguments looks like an option.
@@ -74,13 +92,14 @@ class Thor
74
92
  to_parse += opts.remaining unless self.class.strict_args_position?(config)
75
93
 
76
94
  thor_args = Thor::Arguments.new(self.class.arguments)
77
- thor_args.parse(to_parse).each { |k,v| send("#{k}=", v) }
95
+ thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) }
78
96
  @args = thor_args.remaining
79
97
  end
80
98
 
81
99
  class << self
82
100
  def included(base) #:nodoc:
83
- base.send :extend, ClassMethods
101
+ super(base)
102
+ base.extend ClassMethods
84
103
  base.send :include, Invocation
85
104
  base.send :include, Shell
86
105
  end
@@ -100,11 +119,11 @@ class Thor
100
119
  # Hash[path<String> => Class]
101
120
  #
102
121
  def subclass_files
103
- @subclass_files ||= Hash.new{ |h,k| h[k] = [] }
122
+ @subclass_files ||= Hash.new { |h, k| h[k] = [] }
104
123
  end
105
124
 
106
125
  # Whenever a class inherits from Thor or Thor::Group, we should track the
107
- # class and the file on Thor::Base. This is the method responsable for it.
126
+ # class and the file on Thor::Base. This is the method responsible for it.
108
127
  #
109
128
  def register_klass_file(klass) #:nodoc:
110
129
  file = caller[1].match(/(.*):\d+/)[1]
@@ -117,15 +136,15 @@ class Thor
117
136
 
118
137
  module ClassMethods
119
138
  def attr_reader(*) #:nodoc:
120
- no_tasks { super }
139
+ no_commands { super }
121
140
  end
122
141
 
123
142
  def attr_writer(*) #:nodoc:
124
- no_tasks { super }
143
+ no_commands { super }
125
144
  end
126
145
 
127
146
  def attr_accessor(*) #:nodoc:
128
- no_tasks { super }
147
+ no_commands { super }
129
148
  end
130
149
 
131
150
  # If you want to raise an error for unknown options, call check_unknown_options!
@@ -142,6 +161,37 @@ class Thor
142
161
  !!check_unknown_options
143
162
  end
144
163
 
164
+ # If you want to raise an error when the default value of an option does not match
165
+ # the type call check_default_type!
166
+ # This will be the default; for compatibility a deprecation warning is issued if necessary.
167
+ def check_default_type!
168
+ @check_default_type = true
169
+ end
170
+
171
+ # If you want to use defaults that don't match the type of an option,
172
+ # either specify `check_default_type: false` or call `allow_incompatible_default_type!`
173
+ def allow_incompatible_default_type!
174
+ @check_default_type = false
175
+ end
176
+
177
+ def check_default_type #:nodoc:
178
+ @check_default_type = from_superclass(:check_default_type, nil) unless defined?(@check_default_type)
179
+ @check_default_type
180
+ end
181
+
182
+ # If true, option parsing is suspended as soon as an unknown option or a
183
+ # regular argument is encountered. All remaining arguments are passed to
184
+ # the command as regular arguments.
185
+ def stop_on_unknown_option?(command_name) #:nodoc:
186
+ false
187
+ end
188
+
189
+ # If true, option set will not suspend the execution of the command when
190
+ # a required option is not provided.
191
+ def disable_required_check?(command_name) #:nodoc:
192
+ false
193
+ end
194
+
145
195
  # If you want only strict string args (useful when cascading thor classes),
146
196
  # call strict_args_position! This is disabled by default to allow dynamic
147
197
  # invocations.
@@ -163,11 +213,11 @@ class Thor
163
213
  # is how they are parsed from the command line, arguments are retrieved
164
214
  # from position:
165
215
  #
166
- # thor task NAME
216
+ # thor command NAME
167
217
  #
168
218
  # Instead of:
169
219
  #
170
- # thor task --name=NAME
220
+ # thor command --name=NAME
171
221
  #
172
222
  # Besides, arguments are used inside your code as an accessor (self.argument),
173
223
  # while options are all kept in a hash (self.options).
@@ -192,9 +242,9 @@ class Thor
192
242
  # ==== Errors
193
243
  # ArgumentError:: Raised if you supply a required argument after a non required one.
194
244
  #
195
- def argument(name, options={})
245
+ def argument(name, options = {})
196
246
  is_thor_reserved_word?(name, :argument)
197
- no_tasks { attr_accessor name }
247
+ no_commands { attr_accessor name }
198
248
 
199
249
  required = if options.key?(:optional)
200
250
  !options[:optional]
@@ -206,11 +256,13 @@ class Thor
206
256
 
207
257
  remove_argument name
208
258
 
209
- arguments.each do |argument|
210
- next if argument.required?
211
- raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
212
- "the non-required argument #{argument.human_name.inspect}."
213
- end if required
259
+ if required
260
+ arguments.each do |argument|
261
+ next if argument.required?
262
+ raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " \
263
+ "the non-required argument #{argument.human_name.inspect}."
264
+ end
265
+ end
214
266
 
215
267
  options[:required] = required
216
268
 
@@ -235,7 +287,7 @@ class Thor
235
287
  # ==== Parameters
236
288
  # Hash[Symbol => Object]
237
289
  #
238
- def class_options(options=nil)
290
+ def class_options(options = nil)
239
291
  @class_options ||= from_superclass(:class_options, {})
240
292
  build_options(options, @class_options) if options
241
293
  @class_options
@@ -257,7 +309,7 @@ class Thor
257
309
  # :banner:: -- String to show on usage notes.
258
310
  # :hide:: -- If you want to hide this option from the help.
259
311
  #
260
- def class_option(name, options={})
312
+ def class_option(name, options = {})
261
313
  build_option(name, options, class_options)
262
314
  end
263
315
 
@@ -298,87 +350,96 @@ class Thor
298
350
  end
299
351
 
300
352
  # Defines the group. This is used when thor list is invoked so you can specify
301
- # that only tasks from a pre-defined group will be shown. Defaults to standard.
353
+ # that only commands from a pre-defined group will be shown. Defaults to standard.
302
354
  #
303
355
  # ==== Parameters
304
356
  # name<String|Symbol>
305
357
  #
306
- def group(name=nil)
307
- case name
308
- when nil
309
- @group ||= from_superclass(:group, 'standard')
310
- else
311
- @group = name.to_s
358
+ def group(name = nil)
359
+ if name
360
+ @group = name.to_s
361
+ else
362
+ @group ||= from_superclass(:group, "standard")
312
363
  end
313
364
  end
314
365
 
315
- # Returns the tasks for this Thor class.
366
+ # Returns the commands for this Thor class.
316
367
  #
317
368
  # ==== Returns
318
- # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task
319
- # objects as values.
369
+ # Hash:: An ordered hash with commands names as keys and Thor::Command
370
+ # objects as values.
320
371
  #
321
- def tasks
322
- @tasks ||= Thor::CoreExt::OrderedHash.new
372
+ def commands
373
+ @commands ||= Hash.new
323
374
  end
375
+ alias_method :tasks, :commands
324
376
 
325
- # Returns the tasks for this Thor class and all subclasses.
377
+ # Returns the commands for this Thor class and all subclasses.
326
378
  #
327
379
  # ==== Returns
328
- # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task
329
- # objects as values.
380
+ # Hash:: An ordered hash with commands names as keys and Thor::Command
381
+ # objects as values.
330
382
  #
331
- def all_tasks
332
- @all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new)
333
- @all_tasks.merge(tasks)
383
+ def all_commands
384
+ @all_commands ||= from_superclass(:all_commands, Hash.new)
385
+ @all_commands.merge!(commands)
334
386
  end
387
+ alias_method :all_tasks, :all_commands
335
388
 
336
- # Removes a given task from this Thor class. This is usually done if you
389
+ # Removes a given command from this Thor class. This is usually done if you
337
390
  # are inheriting from another class and don't want it to be available
338
391
  # anymore.
339
392
  #
340
- # By default it only remove the mapping to the task. But you can supply
393
+ # By default it only remove the mapping to the command. But you can supply
341
394
  # :undefine => true to undefine the method from the class as well.
342
395
  #
343
396
  # ==== Parameters
344
- # name<Symbol|String>:: The name of the task to be removed
345
- # options<Hash>:: You can give :undefine => true if you want tasks the method
397
+ # name<Symbol|String>:: The name of the command to be removed
398
+ # options<Hash>:: You can give :undefine => true if you want commands the method
346
399
  # to be undefined from the class as well.
347
400
  #
348
- def remove_task(*names)
401
+ def remove_command(*names)
349
402
  options = names.last.is_a?(Hash) ? names.pop : {}
350
403
 
351
404
  names.each do |name|
352
- tasks.delete(name.to_s)
353
- all_tasks.delete(name.to_s)
405
+ commands.delete(name.to_s)
406
+ all_commands.delete(name.to_s)
354
407
  undef_method name if options[:undefine]
355
408
  end
356
409
  end
410
+ alias_method :remove_task, :remove_command
357
411
 
358
- # All methods defined inside the given block are not added as tasks.
412
+ # All methods defined inside the given block are not added as commands.
359
413
  #
360
414
  # So you can do:
361
415
  #
362
416
  # class MyScript < Thor
363
- # no_tasks do
364
- # def this_is_not_a_task
417
+ # no_commands do
418
+ # def this_is_not_a_command
365
419
  # end
366
420
  # end
367
421
  # end
368
422
  #
369
- # You can also add the method and remove it from the task list:
423
+ # You can also add the method and remove it from the command list:
370
424
  #
371
425
  # class MyScript < Thor
372
- # def this_is_not_a_task
426
+ # def this_is_not_a_command
373
427
  # end
374
- # remove_task :this_is_not_a_task
428
+ # remove_command :this_is_not_a_command
375
429
  # end
376
430
  #
377
- def no_tasks
378
- @no_tasks = true
379
- yield
380
- ensure
381
- @no_tasks = false
431
+ def no_commands(&block)
432
+ no_commands_context.enter(&block)
433
+ end
434
+
435
+ alias_method :no_tasks, :no_commands
436
+
437
+ def no_commands_context
438
+ @no_commands_context ||= NestedContext.new
439
+ end
440
+
441
+ def no_commands?
442
+ no_commands_context.entered?
382
443
  end
383
444
 
384
445
  # Sets the namespace for the Thor or Thor::Group class. By default the
@@ -391,7 +452,7 @@ class Thor
391
452
  #
392
453
  # namespace :my_scripts
393
454
  #
394
- # You change how your tasks are invoked:
455
+ # You change how your commands are invoked:
395
456
  #
396
457
  # thor my_scripts -h
397
458
  #
@@ -399,243 +460,240 @@ class Thor
399
460
  #
400
461
  # namespace :default
401
462
  #
402
- # Your tasks can be invoked with a shortcut. Instead of:
463
+ # Your commands can be invoked with a shortcut. Instead of:
403
464
  #
404
- # thor :my_task
465
+ # thor :my_command
405
466
  #
406
- def namespace(name=nil)
407
- case name
408
- when nil
409
- @namespace ||= Thor::Util.namespace_from_thor_class(self)
410
- else
467
+ def namespace(name = nil)
468
+ if name
411
469
  @namespace = name.to_s
470
+ else
471
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
412
472
  end
413
473
  end
414
474
 
415
- # Parses the task and options from the given args, instantiate the class
416
- # and invoke the task. This method is used when the arguments must be parsed
475
+ # Parses the command and options from the given args, instantiate the class
476
+ # and invoke the command. This method is used when the arguments must be parsed
417
477
  # from an array. If you are inside Ruby and want to use a Thor class, you
418
478
  # can simply initialize it:
419
479
  #
420
480
  # script = MyScript.new(args, options, config)
421
- # script.invoke(:task, first_arg, second_arg, third_arg)
481
+ # script.invoke(:command, first_arg, second_arg, third_arg)
422
482
  #
423
- def start(given_args=ARGV, config={})
483
+ def start(given_args = ARGV, config = {})
424
484
  config[:shell] ||= Thor::Base.shell.new
425
485
  dispatch(nil, given_args.dup, nil, config)
426
486
  rescue Thor::Error => e
427
- ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
428
- exit(1) if exit_on_failure?
487
+ config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
488
+ exit(false) if exit_on_failure?
429
489
  rescue Errno::EPIPE
430
- # This happens if a thor task is piped to something like `head`,
490
+ # This happens if a thor command is piped to something like `head`,
431
491
  # which closes the pipe when it's done reading. This will also
432
492
  # mean that if the pipe is closed, further unnecessary
433
493
  # computation will not occur.
434
- exit(0)
494
+ exit(true)
435
495
  end
436
496
 
437
- # Allows to use private methods from parent in child classes as tasks.
497
+ # Allows to use private methods from parent in child classes as commands.
438
498
  #
439
499
  # ==== Parameters
440
- # names<Array>:: Method names to be used as tasks
500
+ # names<Array>:: Method names to be used as commands
441
501
  #
442
502
  # ==== Examples
443
503
  #
444
- # public_task :foo
445
- # public_task :foo, :bar, :baz
504
+ # public_command :foo
505
+ # public_command :foo, :bar, :baz
446
506
  #
447
- def public_task(*names)
507
+ def public_command(*names)
448
508
  names.each do |name|
449
509
  class_eval "def #{name}(*); super end"
450
510
  end
451
511
  end
512
+ alias_method :public_task, :public_command
452
513
 
453
- def handle_no_task_error(task, has_namespace = $thor_runner) #:nodoc:
454
- if has_namespace
455
- raise UndefinedTaskError, "Could not find task #{task.inspect} in #{namespace.inspect} namespace."
456
- else
457
- raise UndefinedTaskError, "Could not find task #{task.inspect}."
458
- end
514
+ def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
515
+ raise UndefinedCommandError.new(command, all_commands.keys, (namespace if has_namespace))
459
516
  end
460
-
461
- def handle_argument_error(task, error, arity=nil) #:nodoc:
462
- msg = "#{basename} #{task.name}"
463
- if arity
464
- required = arity < 0 ? (-1 - arity) : arity
465
- msg << " requires at least #{required} argument"
466
- msg << "s" if required > 1
467
- else
468
- msg = "call #{msg} as"
469
- end
470
-
471
- msg << ": #{self.banner(task).inspect}."
517
+ alias_method :handle_no_task_error, :handle_no_command_error
518
+
519
+ def handle_argument_error(command, error, args, arity) #:nodoc:
520
+ name = [command.ancestor_name, command.name].compact.join(" ")
521
+ msg = "ERROR: \"#{basename} #{name}\" was called with ".dup
522
+ msg << "no arguments" if args.empty?
523
+ msg << "arguments " << args.inspect unless args.empty?
524
+ msg << "\nUsage: \"#{banner(command).split("\n").join("\"\n \"")}\""
472
525
  raise InvocationError, msg
473
526
  end
474
527
 
475
- protected
476
-
477
- # Prints the class options per group. If an option does not belong to
478
- # any group, it's printed as Class option.
479
- #
480
- def class_options_help(shell, groups={}) #:nodoc:
481
- # Group options by group
482
- class_options.each do |_, value|
483
- groups[value.group] ||= []
484
- groups[value.group] << value
485
- end
528
+ # A flag that makes the process exit with status 1 if any error happens.
529
+ def exit_on_failure?
530
+ Thor.deprecation_warning "Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `#{self.name}`"
531
+ false
532
+ end
486
533
 
487
- # Deal with default group
488
- global_options = groups.delete(nil) || []
489
- print_options(shell, global_options)
534
+ protected
490
535
 
491
- # Print all others
492
- groups.each do |group_name, options|
493
- print_options(shell, options, group_name)
494
- end
536
+ # Prints the class options per group. If an option does not belong to
537
+ # any group, it's printed as Class option.
538
+ #
539
+ def class_options_help(shell, groups = {}) #:nodoc:
540
+ # Group options by group
541
+ class_options.each do |_, value|
542
+ groups[value.group] ||= []
543
+ groups[value.group] << value
495
544
  end
496
545
 
497
- # Receives a set of options and print them.
498
- def print_options(shell, options, group_name=nil)
499
- return if options.empty?
546
+ # Deal with default group
547
+ global_options = groups.delete(nil) || []
548
+ print_options(shell, global_options)
500
549
 
501
- list = []
502
- padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
550
+ # Print all others
551
+ groups.each do |group_name, options|
552
+ print_options(shell, options, group_name)
553
+ end
554
+ end
503
555
 
504
- options.each do |option|
505
- unless option.hide
506
- item = [ option.usage(padding) ]
507
- item.push(option.description ? "# #{option.description}" : "")
556
+ # Receives a set of options and print them.
557
+ def print_options(shell, options, group_name = nil)
558
+ return if options.empty?
508
559
 
509
- list << item
510
- list << [ "", "# Default: #{option.default}" ] if option.show_default?
511
- list << [ "", "# Possible values: #{option.enum.join(', ')}" ] if option.enum
512
- end
513
- end
560
+ list = []
561
+ padding = options.map { |o| o.aliases.size }.max.to_i * 4
514
562
 
515
- shell.say(group_name ? "#{group_name} options:" : "Options:")
516
- shell.print_table(list, :indent => 2)
517
- shell.say ""
518
- end
563
+ options.each do |option|
564
+ next if option.hide
565
+ item = [option.usage(padding)]
566
+ item.push(option.description ? "# #{option.description}" : "")
519
567
 
520
- # Raises an error if the word given is a Thor reserved word.
521
- def is_thor_reserved_word?(word, type) #:nodoc:
522
- return false unless THOR_RESERVED_WORDS.include?(word.to_s)
523
- raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
568
+ list << item
569
+ list << ["", "# Default: #{option.default}"] if option.show_default?
570
+ list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
524
571
  end
525
572
 
526
- # Build an option and adds it to the given scope.
527
- #
528
- # ==== Parameters
529
- # name<Symbol>:: The name of the argument.
530
- # options<Hash>:: Described in both class_option and method_option.
531
- # scope<Hash>:: Options hash that is being built up
532
- def build_option(name, options, scope) #:nodoc:
533
- scope[name] = Thor::Option.new(name, options)
534
- end
573
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
574
+ shell.print_table(list, :indent => 2)
575
+ shell.say ""
576
+ end
535
577
 
536
- # Receives a hash of options, parse them and add to the scope. This is a
537
- # fast way to set a bunch of options:
538
- #
539
- # build_options :foo => true, :bar => :required, :baz => :string
540
- #
541
- # ==== Parameters
542
- # Hash[Symbol => Object]
543
- def build_options(options, scope) #:nodoc:
544
- options.each do |key, value|
545
- scope[key] = Thor::Option.parse(key, value)
546
- end
547
- end
578
+ # Raises an error if the word given is a Thor reserved word.
579
+ def is_thor_reserved_word?(word, type) #:nodoc:
580
+ return false unless THOR_RESERVED_WORDS.include?(word.to_s)
581
+ raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
582
+ end
548
583
 
549
- # Finds a task with the given name. If the task belongs to the current
550
- # class, just return it, otherwise dup it and add the fresh copy to the
551
- # current task hash.
552
- def find_and_refresh_task(name) #:nodoc:
553
- task = if task = tasks[name.to_s]
554
- task
555
- elsif task = all_tasks[name.to_s]
556
- tasks[name.to_s] = task.clone
557
- else
558
- raise ArgumentError, "You supplied :for => #{name.inspect}, but the task #{name.inspect} could not be found."
559
- end
584
+ # Build an option and adds it to the given scope.
585
+ #
586
+ # ==== Parameters
587
+ # name<Symbol>:: The name of the argument.
588
+ # options<Hash>:: Described in both class_option and method_option.
589
+ # scope<Hash>:: Options hash that is being built up
590
+ def build_option(name, options, scope) #:nodoc:
591
+ scope[name] = Thor::Option.new(name, {:check_default_type => check_default_type}.merge!(options))
592
+ end
593
+
594
+ # Receives a hash of options, parse them and add to the scope. This is a
595
+ # fast way to set a bunch of options:
596
+ #
597
+ # build_options :foo => true, :bar => :required, :baz => :string
598
+ #
599
+ # ==== Parameters
600
+ # Hash[Symbol => Object]
601
+ def build_options(options, scope) #:nodoc:
602
+ options.each do |key, value|
603
+ scope[key] = Thor::Option.parse(key, value)
560
604
  end
605
+ end
561
606
 
562
- # Everytime someone inherits from a Thor class, register the klass
563
- # and file into baseclass.
564
- def inherited(klass)
565
- Thor::Base.register_klass_file(klass)
566
- klass.instance_variable_set(:@no_tasks, false)
607
+ # Finds a command with the given name. If the command belongs to the current
608
+ # class, just return it, otherwise dup it and add the fresh copy to the
609
+ # current command hash.
610
+ def find_and_refresh_command(name) #:nodoc:
611
+ if commands[name.to_s]
612
+ commands[name.to_s]
613
+ elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
614
+ commands[name.to_s] = command.clone
615
+ else
616
+ raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
567
617
  end
618
+ end
619
+ alias_method :find_and_refresh_task, :find_and_refresh_command
620
+
621
+ # Everytime someone inherits from a Thor class, register the klass
622
+ # and file into baseclass.
623
+ def inherited(klass)
624
+ super(klass)
625
+ Thor::Base.register_klass_file(klass)
626
+ klass.instance_variable_set(:@no_commands, 0)
627
+ end
568
628
 
569
- # Fire this callback whenever a method is added. Added methods are
570
- # tracked as tasks by invoking the create_task method.
571
- def method_added(meth)
572
- meth = meth.to_s
629
+ # Fire this callback whenever a method is added. Added methods are
630
+ # tracked as commands by invoking the create_command method.
631
+ def method_added(meth)
632
+ super(meth)
633
+ meth = meth.to_s
573
634
 
574
- if meth == "initialize"
575
- initialize_added
576
- return
577
- end
635
+ if meth == "initialize"
636
+ initialize_added
637
+ return
638
+ end
578
639
 
579
- # Return if it's not a public instance method
580
- return unless public_instance_methods.include?(meth) ||
581
- public_instance_methods.include?(meth.to_sym)
640
+ # Return if it's not a public instance method
641
+ return unless public_method_defined?(meth.to_sym)
582
642
 
583
- return if @no_tasks || !create_task(meth)
643
+ return if no_commands? || !create_command(meth)
584
644
 
585
- is_thor_reserved_word?(meth, :task)
586
- Thor::Base.register_klass_file(self)
587
- end
645
+ is_thor_reserved_word?(meth, :command)
646
+ Thor::Base.register_klass_file(self)
647
+ end
588
648
 
589
- # Retrieves a value from superclass. If it reaches the baseclass,
590
- # returns default.
591
- def from_superclass(method, default=nil)
592
- if self == baseclass || !superclass.respond_to?(method, true)
593
- default
594
- else
595
- value = superclass.send(method)
596
-
597
- if value
598
- if value.is_a?(TrueClass) || value.is_a?(Symbol)
599
- value
600
- else
601
- value.dup
602
- end
603
- end
649
+ # Retrieves a value from superclass. If it reaches the baseclass,
650
+ # returns default.
651
+ def from_superclass(method, default = nil)
652
+ if self == baseclass || !superclass.respond_to?(method, true)
653
+ default
654
+ else
655
+ value = superclass.send(method)
656
+
657
+ # Ruby implements `dup` on Object, but raises a `TypeError`
658
+ # if the method is called on immediates. As a result, we
659
+ # don't have a good way to check whether dup will succeed
660
+ # without calling it and rescuing the TypeError.
661
+ begin
662
+ value.dup
663
+ rescue TypeError
664
+ value
604
665
  end
605
- end
606
-
607
- # A flag that makes the process exit with status 1 if any error happens.
608
- def exit_on_failure?
609
- false
610
- end
611
666
 
612
- #
613
- # The basename of the program invoking the thor class.
614
- #
615
- def basename
616
- File.basename($0).split(' ').first
617
667
  end
668
+ end
618
669
 
619
- # SIGNATURE: Sets the baseclass. This is where the superclass lookup
620
- # finishes.
621
- def baseclass #:nodoc:
622
- end
670
+ #
671
+ # The basename of the program invoking the thor class.
672
+ #
673
+ def basename
674
+ File.basename($PROGRAM_NAME).split(" ").first
675
+ end
623
676
 
624
- # SIGNATURE: Creates a new task if valid_task? is true. This method is
625
- # called when a new method is added to the class.
626
- def create_task(meth) #:nodoc:
627
- end
677
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
678
+ # finishes.
679
+ def baseclass #:nodoc:
680
+ end
628
681
 
629
- # SIGNATURE: Defines behavior when the initialize method is added to the
630
- # class.
631
- def initialize_added #:nodoc:
632
- end
682
+ # SIGNATURE: Creates a new command if valid_command? is true. This method is
683
+ # called when a new method is added to the class.
684
+ def create_command(meth) #:nodoc:
685
+ end
686
+ alias_method :create_task, :create_command
633
687
 
634
- # SIGNATURE: The hook invoked by start.
635
- def dispatch(task, given_args, given_opts, config) #:nodoc:
636
- raise NotImplementedError
637
- end
688
+ # SIGNATURE: Defines behavior when the initialize method is added to the
689
+ # class.
690
+ def initialize_added #:nodoc:
691
+ end
638
692
 
693
+ # SIGNATURE: The hook invoked by start.
694
+ def dispatch(command, given_args, given_opts, config) #:nodoc:
695
+ raise NotImplementedError
696
+ end
639
697
  end
640
698
  end
641
699
  end