thor 0.18.1 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +13 -7
  3. data/Thorfile +4 -5
  4. data/bin/thor +1 -1
  5. data/lib/thor.rb +78 -67
  6. data/lib/thor/actions.rb +57 -56
  7. data/lib/thor/actions/create_file.rb +33 -35
  8. data/lib/thor/actions/create_link.rb +2 -3
  9. data/lib/thor/actions/directory.rb +37 -38
  10. data/lib/thor/actions/empty_directory.rb +67 -69
  11. data/lib/thor/actions/file_manipulation.rb +17 -15
  12. data/lib/thor/actions/inject_into_file.rb +27 -29
  13. data/lib/thor/base.rb +193 -189
  14. data/lib/thor/command.rb +20 -23
  15. data/lib/thor/core_ext/hash_with_indifferent_access.rb +21 -24
  16. data/lib/thor/core_ext/io_binary_read.rb +2 -4
  17. data/lib/thor/core_ext/ordered_hash.rb +9 -11
  18. data/lib/thor/error.rb +5 -1
  19. data/lib/thor/group.rb +53 -54
  20. data/lib/thor/invocation.rb +44 -38
  21. data/lib/thor/line_editor.rb +17 -0
  22. data/lib/thor/line_editor/basic.rb +35 -0
  23. data/lib/thor/line_editor/readline.rb +88 -0
  24. data/lib/thor/parser.rb +4 -4
  25. data/lib/thor/parser/argument.rb +28 -29
  26. data/lib/thor/parser/arguments.rb +102 -98
  27. data/lib/thor/parser/option.rb +26 -22
  28. data/lib/thor/parser/options.rb +86 -86
  29. data/lib/thor/rake_compat.rb +9 -10
  30. data/lib/thor/runner.rb +141 -141
  31. data/lib/thor/shell.rb +27 -34
  32. data/lib/thor/shell/basic.rb +91 -63
  33. data/lib/thor/shell/color.rb +44 -43
  34. data/lib/thor/shell/html.rb +59 -60
  35. data/lib/thor/util.rb +24 -27
  36. data/lib/thor/version.rb +1 -1
  37. data/spec/actions/create_file_spec.rb +25 -27
  38. data/spec/actions/create_link_spec.rb +19 -18
  39. data/spec/actions/directory_spec.rb +31 -31
  40. data/spec/actions/empty_directory_spec.rb +18 -18
  41. data/spec/actions/file_manipulation_spec.rb +38 -28
  42. data/spec/actions/inject_into_file_spec.rb +13 -13
  43. data/spec/actions_spec.rb +43 -43
  44. data/spec/base_spec.rb +45 -38
  45. data/spec/command_spec.rb +13 -14
  46. data/spec/core_ext/hash_with_indifferent_access_spec.rb +19 -19
  47. data/spec/core_ext/ordered_hash_spec.rb +6 -6
  48. data/spec/exit_condition_spec.rb +4 -4
  49. data/spec/fixtures/invoke.thor +19 -0
  50. data/spec/fixtures/script.thor +1 -1
  51. data/spec/group_spec.rb +30 -24
  52. data/spec/helper.rb +28 -15
  53. data/spec/invocation_spec.rb +39 -19
  54. data/spec/line_editor/basic_spec.rb +28 -0
  55. data/spec/line_editor/readline_spec.rb +69 -0
  56. data/spec/line_editor_spec.rb +43 -0
  57. data/spec/parser/argument_spec.rb +12 -12
  58. data/spec/parser/arguments_spec.rb +11 -11
  59. data/spec/parser/option_spec.rb +33 -25
  60. data/spec/parser/options_spec.rb +66 -52
  61. data/spec/quality_spec.rb +75 -0
  62. data/spec/rake_compat_spec.rb +10 -10
  63. data/spec/register_spec.rb +60 -30
  64. data/spec/runner_spec.rb +67 -62
  65. data/spec/sandbox/application.rb +2 -0
  66. data/spec/sandbox/app{1}/README +3 -0
  67. data/spec/sandbox/bundle/execute.rb +6 -0
  68. data/spec/sandbox/bundle/main.thor +1 -0
  69. data/spec/sandbox/command.thor +10 -0
  70. data/spec/sandbox/doc/%file_name%.rb.tt +1 -0
  71. data/spec/sandbox/doc/COMMENTER +11 -0
  72. data/spec/sandbox/doc/README +3 -0
  73. data/spec/sandbox/doc/block_helper.rb +3 -0
  74. data/spec/sandbox/doc/config.rb +1 -0
  75. data/spec/sandbox/doc/config.yaml.tt +1 -0
  76. data/spec/sandbox/doc/excluding/%file_name%.rb.tt +1 -0
  77. data/spec/sandbox/enum.thor +10 -0
  78. data/spec/sandbox/group.thor +128 -0
  79. data/spec/sandbox/invoke.thor +131 -0
  80. data/spec/sandbox/path with spaces b/data/spec/sandbox/path with → spaces +0 -0
  81. data/spec/sandbox/preserve/script.sh +3 -0
  82. data/spec/sandbox/script.thor +220 -0
  83. data/spec/sandbox/subcommand.thor +17 -0
  84. data/spec/shell/basic_spec.rb +107 -86
  85. data/spec/shell/color_spec.rb +32 -8
  86. data/spec/shell/html_spec.rb +3 -4
  87. data/spec/shell_spec.rb +7 -7
  88. data/spec/subcommand_spec.rb +20 -2
  89. data/spec/thor_spec.rb +111 -97
  90. data/spec/util_spec.rb +30 -30
  91. data/thor.gemspec +14 -14
  92. metadata +69 -25
@@ -1,8 +1,7 @@
1
- require 'thor/actions/empty_directory'
1
+ require "thor/actions/empty_directory"
2
2
 
3
3
  class Thor
4
4
  module Actions
5
-
6
5
  # Injects the given content into a file. Different from gsub_file, this
7
6
  # method is reversible.
8
7
  #
@@ -36,13 +35,13 @@ class Thor
36
35
  attr_reader :replacement, :flag, :behavior
37
36
 
38
37
  def initialize(base, destination, data, config)
39
- super(base, destination, { :verbose => true }.merge(config))
38
+ super(base, destination, {:verbose => true}.merge(config))
40
39
 
41
40
  @behavior, @flag = if @config.key?(:after)
42
41
  [:after, @config.delete(:after)]
43
- else
44
- [:before, @config.delete(:before)]
45
- end
42
+ else
43
+ [:before, @config.delete(:before)]
44
+ end
46
45
 
47
46
  @replacement = data.is_a?(Proc) ? data.call : data
48
47
  @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp)
@@ -74,36 +73,35 @@ class Thor
74
73
  replace!(regexp, content, true)
75
74
  end
76
75
 
77
- protected
76
+ protected
78
77
 
79
- def say_status(behavior)
80
- status = if behavior == :invoke
81
- if flag == /\A/
82
- :prepend
83
- elsif flag == /\z/
84
- :append
85
- else
86
- :insert
87
- end
78
+ def say_status(behavior)
79
+ status = if behavior == :invoke
80
+ if flag == /\A/
81
+ :prepend
82
+ elsif flag == /\z/
83
+ :append
88
84
  else
89
- :subtract
85
+ :insert
90
86
  end
91
-
92
- super(status, config[:verbose])
87
+ else
88
+ :subtract
93
89
  end
94
90
 
95
- # Adds the content to the file.
96
- #
97
- def replace!(regexp, string, force)
98
- unless base.options[:pretend]
99
- content = File.binread(destination)
100
- if force || !content.include?(replacement)
101
- content.gsub!(regexp, string)
102
- File.open(destination, 'wb') { |file| file.write(content) }
103
- end
91
+ super(status, config[:verbose])
92
+ end
93
+
94
+ # Adds the content to the file.
95
+ #
96
+ def replace!(regexp, string, force)
97
+ unless base.options[:pretend]
98
+ content = File.binread(destination)
99
+ if force || !content.include?(replacement)
100
+ content.gsub!(regexp, string)
101
+ File.open(destination, "wb") { |file| file.write(content) }
104
102
  end
105
103
  end
106
-
104
+ end
107
105
  end
108
106
  end
109
107
  end
@@ -1,23 +1,26 @@
1
- require 'thor/command'
2
- require 'thor/core_ext/hash_with_indifferent_access'
3
- require 'thor/core_ext/ordered_hash'
4
- require 'thor/error'
5
- require 'thor/invocation'
6
- require 'thor/parser'
7
- require 'thor/shell'
8
- require 'thor/util'
1
+ require "thor/command"
2
+ require "thor/core_ext/hash_with_indifferent_access"
3
+ require "thor/core_ext/ordered_hash"
4
+ require "thor/error"
5
+ require "thor/invocation"
6
+ require "thor/parser"
7
+ require "thor/shell"
8
+ require "thor/line_editor"
9
+ require "thor/util"
9
10
 
10
11
  class Thor
11
- autoload :Actions, 'thor/actions'
12
- autoload :RakeCompat, 'thor/rake_compat'
13
- autoload :Group, 'thor/group'
12
+ autoload :Actions, "thor/actions"
13
+ autoload :RakeCompat, "thor/rake_compat"
14
+ autoload :Group, "thor/group"
14
15
 
15
16
  # Shortcuts for help.
16
- HELP_MAPPINGS = %w(-h -? --help -D)
17
+ HELP_MAPPINGS = %w[-h -? --help -D]
17
18
 
18
19
  # Thor methods that should not be overwritten by the user.
19
- THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root
20
- action add_file create_file in_root inside run run_ruby_script)
20
+ THOR_RESERVED_WORDS = %w[invoke shell options behavior root destination_root relative_root
21
+ action add_file create_file in_root inside run run_ruby_script]
22
+
23
+ TEMPLATE_EXTNAME = ".tt"
21
24
 
22
25
  module Base
23
26
  attr_accessor :options, :parent_options, :args
@@ -38,7 +41,7 @@ class Thor
38
41
  #
39
42
  # config<Hash>:: Configuration for this Thor class.
40
43
  #
41
- def initialize(args=[], options={}, config={})
44
+ def initialize(args = [], local_options = {}, config = {}) # rubocop:disable MethodLength
42
45
  parse_options = self.class.class_options
43
46
 
44
47
  # The start method splits inbound arguments at the first argument
@@ -46,14 +49,14 @@ class Thor
46
49
  # new, passing in the two halves of the arguments Array as the
47
50
  # first two parameters.
48
51
 
49
- if options.is_a?(Array)
50
- command_options = config.delete(:command_options) # hook for start
51
- parse_options = parse_options.merge(command_options) if command_options
52
- array_options, hash_options = options, {}
52
+ command_options = config.delete(:command_options) # hook for start
53
+ parse_options = parse_options.merge(command_options) if command_options
54
+ if local_options.is_a?(Array)
55
+ array_options, hash_options = local_options, {}
53
56
  else
54
57
  # Handle the case where the class was explicitly instantiated
55
58
  # with pre-parsed options.
56
- array_options, hash_options = [], options
59
+ array_options, hash_options = [], local_options
57
60
  end
58
61
 
59
62
  # Let Thor::Options parse the options first, so it can remove
@@ -62,7 +65,7 @@ class Thor
62
65
  stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
63
66
  opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown)
64
67
  self.options = opts.parse(array_options)
65
- self.options = config[:class_options].merge(self.options) if config[:class_options]
68
+ self.options = config[:class_options].merge(options) if config[:class_options]
66
69
 
67
70
  # If unknown options are disallowed, make sure that none of the
68
71
  # remaining arguments looks like an option.
@@ -77,13 +80,13 @@ class Thor
77
80
  to_parse += opts.remaining unless self.class.strict_args_position?(config)
78
81
 
79
82
  thor_args = Thor::Arguments.new(self.class.arguments)
80
- thor_args.parse(to_parse).each { |k,v| __send__("#{k}=", v) }
83
+ thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) }
81
84
  @args = thor_args.remaining
82
85
  end
83
86
 
84
87
  class << self
85
88
  def included(base) #:nodoc:
86
- base.send :extend, ClassMethods
89
+ base.extend ClassMethods
87
90
  base.send :include, Invocation
88
91
  base.send :include, Shell
89
92
  end
@@ -103,7 +106,7 @@ class Thor
103
106
  # Hash[path<String> => Class]
104
107
  #
105
108
  def subclass_files
106
- @subclass_files ||= Hash.new{ |h,k| h[k] = [] }
109
+ @subclass_files ||= Hash.new { |h, k| h[k] = [] }
107
110
  end
108
111
 
109
112
  # Whenever a class inherits from Thor or Thor::Group, we should track the
@@ -202,7 +205,7 @@ class Thor
202
205
  # ==== Errors
203
206
  # ArgumentError:: Raised if you supply a required argument after a non required one.
204
207
  #
205
- def argument(name, options={})
208
+ def argument(name, options = {}) # rubocop:disable MethodLength
206
209
  is_thor_reserved_word?(name, :argument)
207
210
  no_commands { attr_accessor name }
208
211
 
@@ -218,7 +221,7 @@ class Thor
218
221
 
219
222
  arguments.each do |argument|
220
223
  next if argument.required?
221
- raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
224
+ fail ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " <<
222
225
  "the non-required argument #{argument.human_name.inspect}."
223
226
  end if required
224
227
 
@@ -245,7 +248,7 @@ class Thor
245
248
  # ==== Parameters
246
249
  # Hash[Symbol => Object]
247
250
  #
248
- def class_options(options=nil)
251
+ def class_options(options = nil)
249
252
  @class_options ||= from_superclass(:class_options, {})
250
253
  build_options(options, @class_options) if options
251
254
  @class_options
@@ -267,7 +270,7 @@ class Thor
267
270
  # :banner:: -- String to show on usage notes.
268
271
  # :hide:: -- If you want to hide this option from the help.
269
272
  #
270
- def class_option(name, options={})
273
+ def class_option(name, options = {})
271
274
  build_option(name, options, class_options)
272
275
  end
273
276
 
@@ -313,12 +316,11 @@ class Thor
313
316
  # ==== Parameters
314
317
  # name<String|Symbol>
315
318
  #
316
- def group(name=nil)
317
- @group = case name
318
- when nil
319
- @group || from_superclass(:group, 'standard')
319
+ def group(name = nil)
320
+ if name
321
+ @group = name.to_s
320
322
  else
321
- name.to_s
323
+ @group ||= from_superclass(:group, "standard")
322
324
  end
323
325
  end
324
326
 
@@ -331,7 +333,7 @@ class Thor
331
333
  def commands
332
334
  @commands ||= Thor::CoreExt::OrderedHash.new
333
335
  end
334
- alias tasks commands
336
+ alias_method :tasks, :commands
335
337
 
336
338
  # Returns the commands for this Thor class and all subclasses.
337
339
  #
@@ -343,7 +345,7 @@ class Thor
343
345
  @all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new)
344
346
  @all_commands.merge(commands)
345
347
  end
346
- alias all_tasks all_commands
348
+ alias_method :all_tasks, :all_commands
347
349
 
348
350
  # Removes a given command from this Thor class. This is usually done if you
349
351
  # are inheriting from another class and don't want it to be available
@@ -366,7 +368,7 @@ class Thor
366
368
  undef_method name if options[:undefine]
367
369
  end
368
370
  end
369
- alias remove_task remove_command
371
+ alias_method :remove_task, :remove_command
370
372
 
371
373
  # All methods defined inside the given block are not added as commands.
372
374
  #
@@ -393,7 +395,7 @@ class Thor
393
395
  ensure
394
396
  @no_commands = false
395
397
  end
396
- alias no_tasks no_commands
398
+ alias_method :no_tasks, :no_commands
397
399
 
398
400
  # Sets the namespace for the Thor or Thor::Group class. By default the
399
401
  # namespace is retrieved from the class name. If your Thor class is named
@@ -417,12 +419,11 @@ class Thor
417
419
  #
418
420
  # thor :my_command
419
421
  #
420
- def namespace(name=nil)
421
- @namespace = case name
422
- when nil
423
- @namespace || Thor::Util.namespace_from_thor_class(self)
424
- else
422
+ def namespace(name = nil)
423
+ if name
425
424
  @namespace = name.to_s
425
+ else
426
+ @namespace ||= Thor::Util.namespace_from_thor_class(self)
426
427
  end
427
428
  end
428
429
 
@@ -434,11 +435,11 @@ class Thor
434
435
  # script = MyScript.new(args, options, config)
435
436
  # script.invoke(:command, first_arg, second_arg, third_arg)
436
437
  #
437
- def start(given_args=ARGV, config={})
438
+ def start(given_args = ARGV, config = {})
438
439
  config[:shell] ||= Thor::Base.shell.new
439
440
  dispatch(nil, given_args.dup, nil, config)
440
441
  rescue Thor::Error => e
441
- ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
442
+ config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
442
443
  exit(1) if exit_on_failure?
443
444
  rescue Errno::EPIPE
444
445
  # This happens if a thor command is piped to something like `head`,
@@ -463,190 +464,193 @@ class Thor
463
464
  class_eval "def #{name}(*); super end"
464
465
  end
465
466
  end
466
- alias public_task public_command
467
+ alias_method :public_task, :public_command
467
468
 
468
469
  def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc:
469
470
  if has_namespace
470
- raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
471
+ fail UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
471
472
  else
472
- raise UndefinedCommandError, "Could not find command #{command.inspect}."
473
+ fail UndefinedCommandError, "Could not find command #{command.inspect}."
473
474
  end
474
475
  end
475
- alias handle_no_task_error handle_no_command_error
476
+ alias_method :handle_no_task_error, :handle_no_command_error
476
477
 
477
478
  def handle_argument_error(command, error, args, arity) #:nodoc:
478
- msg = "ERROR: #{basename} #{command.name} was called with "
479
- msg << 'no arguments' if args.empty?
480
- msg << 'arguments ' << args.inspect if !args.empty?
481
- msg << "\nUsage: #{self.banner(command).inspect}."
482
- raise InvocationError, msg
483
- end
484
-
485
- protected
486
-
487
- # Prints the class options per group. If an option does not belong to
488
- # any group, it's printed as Class option.
489
- #
490
- def class_options_help(shell, groups={}) #:nodoc:
491
- # Group options by group
492
- class_options.each do |_, value|
493
- groups[value.group] ||= []
494
- groups[value.group] << value
495
- end
479
+ msg = "ERROR: \"#{basename} #{command.name}\" was called with "
480
+ msg << "no arguments" if args.empty?
481
+ msg << "arguments " << args.inspect unless args.empty?
482
+ msg << "\nUsage: #{banner(command).inspect}"
483
+ fail InvocationError, msg
484
+ end
496
485
 
497
- # Deal with default group
498
- global_options = groups.delete(nil) || []
499
- print_options(shell, global_options)
486
+ protected
500
487
 
501
- # Print all others
502
- groups.each do |group_name, options|
503
- print_options(shell, options, group_name)
504
- end
488
+ # Prints the class options per group. If an option does not belong to
489
+ # any group, it's printed as Class option.
490
+ #
491
+ def class_options_help(shell, groups = {}) #:nodoc:
492
+ # Group options by group
493
+ class_options.each do |_, value|
494
+ groups[value.group] ||= []
495
+ groups[value.group] << value
505
496
  end
506
497
 
507
- # Receives a set of options and print them.
508
- def print_options(shell, options, group_name=nil)
509
- return if options.empty?
498
+ # Deal with default group
499
+ global_options = groups.delete(nil) || []
500
+ print_options(shell, global_options)
510
501
 
511
- list = []
512
- padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
502
+ # Print all others
503
+ groups.each do |group_name, options|
504
+ print_options(shell, options, group_name)
505
+ end
506
+ end
513
507
 
514
- options.each do |option|
515
- unless option.hide
516
- item = [ option.usage(padding) ]
517
- item.push(option.description ? "# #{option.description}" : "")
508
+ # Receives a set of options and print them.
509
+ def print_options(shell, options, group_name = nil)
510
+ return if options.empty?
518
511
 
519
- list << item
520
- list << [ "", "# Default: #{option.default}" ] if option.show_default?
521
- list << [ "", "# Possible values: #{option.enum.join(', ')}" ] if option.enum
522
- end
523
- end
512
+ list = []
513
+ padding = options.map { |o| o.aliases.size }.max.to_i * 4
524
514
 
525
- shell.say(group_name ? "#{group_name} options:" : "Options:")
526
- shell.print_table(list, :indent => 2)
527
- shell.say ""
528
- end
515
+ options.each do |option|
516
+ unless option.hide
517
+ item = [option.usage(padding)]
518
+ item.push(option.description ? "# #{option.description}" : "")
529
519
 
530
- # Raises an error if the word given is a Thor reserved word.
531
- def is_thor_reserved_word?(word, type) #:nodoc:
532
- return false unless THOR_RESERVED_WORDS.include?(word.to_s)
533
- raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
520
+ list << item
521
+ list << ["", "# Default: #{option.default}"] if option.show_default?
522
+ list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum
523
+ end
534
524
  end
535
525
 
536
- # Build an option and adds it to the given scope.
537
- #
538
- # ==== Parameters
539
- # name<Symbol>:: The name of the argument.
540
- # options<Hash>:: Described in both class_option and method_option.
541
- # scope<Hash>:: Options hash that is being built up
542
- def build_option(name, options, scope) #:nodoc:
543
- scope[name] = Thor::Option.new(name, options)
544
- end
526
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
527
+ shell.print_table(list, :indent => 2)
528
+ shell.say ""
529
+ end
545
530
 
546
- # Receives a hash of options, parse them and add to the scope. This is a
547
- # fast way to set a bunch of options:
548
- #
549
- # build_options :foo => true, :bar => :required, :baz => :string
550
- #
551
- # ==== Parameters
552
- # Hash[Symbol => Object]
553
- def build_options(options, scope) #:nodoc:
554
- options.each do |key, value|
555
- scope[key] = Thor::Option.parse(key, value)
556
- end
557
- end
531
+ # Raises an error if the word given is a Thor reserved word.
532
+ def is_thor_reserved_word?(word, type) #:nodoc:
533
+ return false unless THOR_RESERVED_WORDS.include?(word.to_s)
534
+ fail "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}"
535
+ end
558
536
 
559
- # Finds a command with the given name. If the command belongs to the current
560
- # class, just return it, otherwise dup it and add the fresh copy to the
561
- # current command hash.
562
- def find_and_refresh_command(name) #:nodoc:
563
- command = if command = commands[name.to_s]
564
- command
565
- elsif command = all_commands[name.to_s]
566
- commands[name.to_s] = command.clone
567
- else
568
- raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
569
- end
537
+ # Build an option and adds it to the given scope.
538
+ #
539
+ # ==== Parameters
540
+ # name<Symbol>:: The name of the argument.
541
+ # options<Hash>:: Described in both class_option and method_option.
542
+ # scope<Hash>:: Options hash that is being built up
543
+ def build_option(name, options, scope) #:nodoc:
544
+ scope[name] = Thor::Option.new(name, options)
545
+ end
546
+
547
+ # Receives a hash of options, parse them and add to the scope. This is a
548
+ # fast way to set a bunch of options:
549
+ #
550
+ # build_options :foo => true, :bar => :required, :baz => :string
551
+ #
552
+ # ==== Parameters
553
+ # Hash[Symbol => Object]
554
+ def build_options(options, scope) #:nodoc:
555
+ options.each do |key, value|
556
+ scope[key] = Thor::Option.parse(key, value)
570
557
  end
571
- alias find_and_refresh_task find_and_refresh_command
558
+ end
572
559
 
573
- # Everytime someone inherits from a Thor class, register the klass
574
- # and file into baseclass.
575
- def inherited(klass)
576
- Thor::Base.register_klass_file(klass)
577
- klass.instance_variable_set(:@no_commands, false)
560
+ # Finds a command with the given name. If the command belongs to the current
561
+ # class, just return it, otherwise dup it and add the fresh copy to the
562
+ # current command hash.
563
+ def find_and_refresh_command(name) #:nodoc:
564
+ if commands[name.to_s]
565
+ commands[name.to_s]
566
+ elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition
567
+ commands[name.to_s] = command.clone
568
+ else
569
+ fail ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found."
578
570
  end
571
+ end
572
+ alias_method :find_and_refresh_task, :find_and_refresh_command
579
573
 
580
- # Fire this callback whenever a method is added. Added methods are
581
- # tracked as commands by invoking the create_command method.
582
- def method_added(meth)
583
- meth = meth.to_s
574
+ # Everytime someone inherits from a Thor class, register the klass
575
+ # and file into baseclass.
576
+ def inherited(klass)
577
+ Thor::Base.register_klass_file(klass)
578
+ klass.instance_variable_set(:@no_commands, false)
579
+ end
584
580
 
585
- if meth == "initialize"
586
- initialize_added
587
- return
588
- end
581
+ # Fire this callback whenever a method is added. Added methods are
582
+ # tracked as commands by invoking the create_command method.
583
+ def method_added(meth)
584
+ meth = meth.to_s
589
585
 
590
- # Return if it's not a public instance method
591
- return unless public_method_defined?(meth.to_sym)
586
+ if meth == "initialize"
587
+ initialize_added
588
+ return
589
+ end
592
590
 
593
- return if @no_commands || !create_command(meth)
591
+ # Return if it's not a public instance method
592
+ return unless public_method_defined?(meth.to_sym)
594
593
 
595
- is_thor_reserved_word?(meth, :command)
596
- Thor::Base.register_klass_file(self)
597
- end
594
+ @no_commands ||= false
595
+ return if @no_commands || !create_command(meth)
598
596
 
599
- # Retrieves a value from superclass. If it reaches the baseclass,
600
- # returns default.
601
- def from_superclass(method, default=nil)
602
- if self == baseclass || !superclass.respond_to?(method, true)
603
- default
604
- else
605
- value = superclass.send(method)
606
-
607
- if value
608
- if value.is_a?(TrueClass) || value.is_a?(Symbol)
609
- value
610
- else
611
- value.dup
612
- end
613
- end
597
+ is_thor_reserved_word?(meth, :command)
598
+ Thor::Base.register_klass_file(self)
599
+ end
600
+
601
+ # Retrieves a value from superclass. If it reaches the baseclass,
602
+ # returns default.
603
+ def from_superclass(method, default = nil)
604
+ if self == baseclass || !superclass.respond_to?(method, true)
605
+ default
606
+ else
607
+ value = superclass.send(method)
608
+
609
+ # Ruby implements `dup` on Object, but raises a `TypeError`
610
+ # if the method is called on immediates. As a result, we
611
+ # don't have a good way to check whether dup will succeed
612
+ # without calling it and rescuing the TypeError.
613
+ begin
614
+ value.dup
615
+ rescue TypeError
616
+ value
614
617
  end
615
- end
616
618
 
617
- # A flag that makes the process exit with status 1 if any error happens.
618
- def exit_on_failure?
619
- false
620
619
  end
620
+ end
621
621
 
622
- #
623
- # The basename of the program invoking the thor class.
624
- #
625
- def basename
626
- File.basename($0).split(' ').first
627
- end
622
+ # A flag that makes the process exit with status 1 if any error happens.
623
+ def exit_on_failure?
624
+ false
625
+ end
628
626
 
629
- # SIGNATURE: Sets the baseclass. This is where the superclass lookup
630
- # finishes.
631
- def baseclass #:nodoc:
632
- end
627
+ #
628
+ # The basename of the program invoking the thor class.
629
+ #
630
+ def basename
631
+ File.basename($PROGRAM_NAME).split(" ").first
632
+ end
633
633
 
634
- # SIGNATURE: Creates a new command if valid_command? is true. This method is
635
- # called when a new method is added to the class.
636
- def create_command(meth) #:nodoc:
637
- end
638
- alias create_task create_command
634
+ # SIGNATURE: Sets the baseclass. This is where the superclass lookup
635
+ # finishes.
636
+ def baseclass #:nodoc:
637
+ end
639
638
 
640
- # SIGNATURE: Defines behavior when the initialize method is added to the
641
- # class.
642
- def initialize_added #:nodoc:
643
- end
639
+ # SIGNATURE: Creates a new command if valid_command? is true. This method is
640
+ # called when a new method is added to the class.
641
+ def create_command(meth) #:nodoc:
642
+ end
643
+ alias_method :create_task, :create_command
644
644
 
645
- # SIGNATURE: The hook invoked by start.
646
- def dispatch(command, given_args, given_opts, config) #:nodoc:
647
- raise NotImplementedError
648
- end
645
+ # SIGNATURE: Defines behavior when the initialize method is added to the
646
+ # class.
647
+ def initialize_added #:nodoc:
648
+ end
649
649
 
650
+ # SIGNATURE: The hook invoked by start.
651
+ def dispatch(command, given_args, given_opts, config) #:nodoc:
652
+ fail NotImplementedError
653
+ end
650
654
  end
651
655
  end
652
656
  end