thor 0.18.1 → 0.19.0

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 (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
@@ -19,12 +19,17 @@ class Thor
19
19
  end
20
20
 
21
21
  # Make initializer aware of invocations and the initialization args.
22
- def initialize(args=[], options={}, config={}, &block) #:nodoc:
23
- @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
24
- @_initializer = [ args, options, config ]
22
+ def initialize(args = [], options = {}, config = {}, &block) #:nodoc:
23
+ @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] }
24
+ @_initializer = [args, options, config]
25
25
  super
26
26
  end
27
27
 
28
+ # Make the current command chain accessible with in a Thor-(sub)command
29
+ def current_command_chain
30
+ @_invocations.values.flatten.map(&:to_sym)
31
+ end
32
+
28
33
  # Receives a name and invokes it. The name can be a string (either "command" or
29
34
  # "namespace:command"), a Thor::Command, a Class or a Thor instance. If the
30
35
  # command cannot be guessed by name, it can also be supplied as second argument.
@@ -40,11 +45,11 @@ class Thor
40
45
  # class A < Thor
41
46
  # def foo
42
47
  # invoke :bar
43
- # invoke "b:hello", ["José"]
48
+ # invoke "b:hello", ["Erik"]
44
49
  # end
45
50
  #
46
51
  # def bar
47
- # invoke "b:hello", ["José"]
52
+ # invoke "b:hello", ["Erik"]
48
53
  # end
49
54
  # end
50
55
  #
@@ -93,17 +98,18 @@ class Thor
93
98
  #
94
99
  # invoke Rspec::RR, [], :style => :foo
95
100
  #
96
- def invoke(name=nil, *args)
101
+ def invoke(name = nil, *args)
97
102
  if name.nil?
98
103
  warn "[Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}"
99
104
  return invoke_all
100
105
  end
101
106
 
102
- args.unshift(nil) if Array === args.first || NilClass === args.first
107
+ args.unshift(nil) if args.first.is_a?(Array) || args.first.nil?
103
108
  command, args, opts, config = args
104
109
 
105
110
  klass, command = _retrieve_class_and_command(name, command)
106
- raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
111
+ fail "Missing Thor class for invoke #{name}" unless klass
112
+ fail "Expected Thor class, got #{klass}" unless klass <= Thor::Base
107
113
 
108
114
  args, opts, config = _parse_initialization_options(args, opts, config)
109
115
  klass.send(:dispatch, command, args, opts, config) do |instance|
@@ -120,7 +126,7 @@ class Thor
120
126
  command.run(self, *args)
121
127
  end
122
128
  end
123
- alias invoke_task invoke_command
129
+ alias_method :invoke_task, :invoke_command
124
130
 
125
131
  # Invoke all commands for the current instance.
126
132
  def invoke_all #:nodoc:
@@ -132,41 +138,41 @@ class Thor
132
138
  with_padding { invoke(*args) }
133
139
  end
134
140
 
135
- protected
141
+ protected
136
142
 
137
- # Configuration values that are shared between invocations.
138
- def _shared_configuration #:nodoc:
139
- { :invocations => @_invocations }
140
- end
143
+ # Configuration values that are shared between invocations.
144
+ def _shared_configuration #:nodoc:
145
+ {:invocations => @_invocations}
146
+ end
141
147
 
142
- # This method simply retrieves the class and command to be invoked.
143
- # If the name is nil or the given name is a command in the current class,
144
- # use the given name and return self as class. Otherwise, call
145
- # prepare_for_invocation in the current class.
146
- def _retrieve_class_and_command(name, sent_command=nil) #:nodoc:
147
- case
148
- when name.nil?
149
- [self.class, nil]
150
- when self.class.all_commands[name.to_s]
151
- [self.class, name.to_s]
152
- else
153
- klass, command = self.class.prepare_for_invocation(nil, name)
154
- [klass, command || sent_command]
155
- end
148
+ # This method simply retrieves the class and command to be invoked.
149
+ # If the name is nil or the given name is a command in the current class,
150
+ # use the given name and return self as class. Otherwise, call
151
+ # prepare_for_invocation in the current class.
152
+ def _retrieve_class_and_command(name, sent_command = nil) #:nodoc:
153
+ case
154
+ when name.nil?
155
+ [self.class, nil]
156
+ when self.class.all_commands[name.to_s]
157
+ [self.class, name.to_s]
158
+ else
159
+ klass, command = self.class.prepare_for_invocation(nil, name)
160
+ [klass, command || sent_command]
156
161
  end
157
- alias _retrieve_class_and_task _retrieve_class_and_command
162
+ end
163
+ alias_method :_retrieve_class_and_task, :_retrieve_class_and_command
158
164
 
159
- # Initialize klass using values stored in the @_initializer.
160
- def _parse_initialization_options(args, opts, config) #:nodoc:
161
- stored_args, stored_opts, stored_config = @_initializer
165
+ # Initialize klass using values stored in the @_initializer.
166
+ def _parse_initialization_options(args, opts, config) #:nodoc:
167
+ stored_args, stored_opts, stored_config = @_initializer
162
168
 
163
- args ||= stored_args.dup
164
- opts ||= stored_opts.dup
169
+ args ||= stored_args.dup
170
+ opts ||= stored_opts.dup
165
171
 
166
- config ||= {}
167
- config = stored_config.merge(_shared_configuration).merge!(config)
172
+ config ||= {}
173
+ config = stored_config.merge(_shared_configuration).merge!(config)
168
174
 
169
- [ args, opts, config ]
170
- end
175
+ [args, opts, config]
176
+ end
171
177
  end
172
178
  end
@@ -0,0 +1,17 @@
1
+ require "thor/line_editor/basic"
2
+ require "thor/line_editor/readline"
3
+
4
+ class Thor
5
+ module LineEditor
6
+ def self.readline(prompt, options = {})
7
+ best_available.new(prompt, options).readline
8
+ end
9
+
10
+ def self.best_available
11
+ [
12
+ Thor::LineEditor::Readline,
13
+ Thor::LineEditor::Basic
14
+ ].detect(&:available?)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ class Thor
2
+ module LineEditor
3
+ class Basic
4
+ attr_reader :prompt, :options
5
+
6
+ def self.available?
7
+ true
8
+ end
9
+
10
+ def initialize(prompt, options)
11
+ @prompt = prompt
12
+ @options = options
13
+ end
14
+
15
+ def readline
16
+ $stdout.print(prompt)
17
+ get_input
18
+ end
19
+
20
+ private
21
+
22
+ def get_input
23
+ if echo?
24
+ $stdin.gets
25
+ else
26
+ $stdin.noecho(&:gets)
27
+ end
28
+ end
29
+
30
+ def echo?
31
+ options.fetch(:echo, true)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,88 @@
1
+ begin
2
+ require "readline"
3
+ rescue LoadError
4
+ end
5
+
6
+ class Thor
7
+ module LineEditor
8
+ class Readline < Basic
9
+ def self.available?
10
+ Object.const_defined?(:Readline)
11
+ end
12
+
13
+ def readline
14
+ if echo?
15
+ ::Readline.completion_append_character = nil
16
+ # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil.
17
+ if complete = completion_proc
18
+ ::Readline.completion_proc = complete
19
+ end
20
+ ::Readline.readline(prompt, add_to_history?)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def add_to_history?
29
+ options.fetch(:add_to_history, true)
30
+ end
31
+
32
+ def completion_proc
33
+ if use_path_completion?
34
+ proc { |text| PathCompletion.new(text).matches }
35
+ elsif completion_options.any?
36
+ proc do |text|
37
+ completion_options.select { |option| option.start_with?(text) }
38
+ end
39
+ end
40
+ end
41
+
42
+ def completion_options
43
+ options.fetch(:limited_to, [])
44
+ end
45
+
46
+ def use_path_completion?
47
+ options.fetch(:path, false)
48
+ end
49
+
50
+ class PathCompletion
51
+ attr_reader :text
52
+ private :text
53
+
54
+ def initialize(text)
55
+ @text = text
56
+ end
57
+
58
+ def matches
59
+ relative_matches
60
+ end
61
+
62
+ private
63
+
64
+ def relative_matches
65
+ absolute_matches.map { |path| path.sub(base_path, "") }
66
+ end
67
+
68
+ def absolute_matches
69
+ Dir[glob_pattern].map do |path|
70
+ if File.directory?(path)
71
+ "#{path}/"
72
+ else
73
+ path
74
+ end
75
+ end
76
+ end
77
+
78
+ def glob_pattern
79
+ "#{base_path}#{text}*"
80
+ end
81
+
82
+ def base_path
83
+ "#{Dir.pwd}/"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,4 +1,4 @@
1
- require 'thor/parser/argument'
2
- require 'thor/parser/arguments'
3
- require 'thor/parser/option'
4
- require 'thor/parser/options'
1
+ require "thor/parser/argument"
2
+ require "thor/parser/arguments"
3
+ require "thor/parser/option"
4
+ require "thor/parser/options"
@@ -1,17 +1,17 @@
1
1
  class Thor
2
2
  class Argument #:nodoc:
3
- VALID_TYPES = [ :numeric, :hash, :array, :string ]
3
+ VALID_TYPES = [:numeric, :hash, :array, :string]
4
4
 
5
5
  attr_reader :name, :description, :enum, :required, :type, :default, :banner
6
- alias :human_name :name
6
+ alias_method :human_name, :name
7
7
 
8
- def initialize(name, options={})
8
+ def initialize(name, options = {})
9
9
  class_name = self.class.name.split("::").last
10
10
 
11
11
  type = options[:type]
12
12
 
13
- raise ArgumentError, "#{class_name} name can't be nil." if name.nil?
14
- raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
13
+ fail ArgumentError, "#{class_name} name can't be nil." if name.nil?
14
+ fail ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
15
15
 
16
16
  @name = name.to_s
17
17
  @description = options[:desc]
@@ -41,34 +41,33 @@ class Thor
41
41
  end
42
42
  end
43
43
 
44
- protected
44
+ protected
45
45
 
46
- def validate!
47
- if required? && !default.nil?
48
- raise ArgumentError, "An argument cannot be required and have default value."
49
- elsif @enum && !@enum.is_a?(Array)
50
- raise ArgumentError, "An argument cannot have an enum other than an array."
51
- end
46
+ def validate!
47
+ if required? && !default.nil?
48
+ fail ArgumentError, "An argument cannot be required and have default value."
49
+ elsif @enum && !@enum.is_a?(Array)
50
+ fail ArgumentError, "An argument cannot have an enum other than an array."
52
51
  end
52
+ end
53
53
 
54
- def valid_type?(type)
55
- self.class::VALID_TYPES.include?(type.to_sym)
56
- end
54
+ def valid_type?(type)
55
+ self.class::VALID_TYPES.include?(type.to_sym)
56
+ end
57
57
 
58
- def default_banner
59
- case type
60
- when :boolean
61
- nil
62
- when :string, :default
63
- human_name.upcase
64
- when :numeric
65
- "N"
66
- when :hash
67
- "key:value"
68
- when :array
69
- "one two three"
70
- end
58
+ def default_banner
59
+ case type
60
+ when :boolean
61
+ nil
62
+ when :string, :default
63
+ human_name.upcase
64
+ when :numeric
65
+ "N"
66
+ when :hash
67
+ "key:value"
68
+ when :array
69
+ "one two three"
71
70
  end
72
-
71
+ end
73
72
  end
74
73
  end
@@ -1,5 +1,5 @@
1
1
  class Thor
2
- class Arguments #:nodoc:
2
+ class Arguments #:nodoc: # rubocop:disable ClassLength
3
3
  NUMERIC = /(\d*\.\d+|\d+)/
4
4
 
5
5
  # Receives an array of args and returns two arrays, one with arguments
@@ -13,7 +13,7 @@ class Thor
13
13
  arguments << item
14
14
  end
15
15
 
16
- return arguments, args[Range.new(arguments.size, -1)]
16
+ [arguments, args[Range.new(arguments.size, -1)]]
17
17
  end
18
18
 
19
19
  def self.parse(*args)
@@ -23,12 +23,12 @@ class Thor
23
23
 
24
24
  # Takes an array of Thor::Argument objects.
25
25
  #
26
- def initialize(arguments=[])
26
+ def initialize(arguments = [])
27
27
  @assigns, @non_assigned_required = {}, []
28
28
  @switches = arguments
29
29
 
30
30
  arguments.each do |argument|
31
- if argument.default != nil
31
+ if !argument.default.nil?
32
32
  @assigns[argument.human_name] = argument.default
33
33
  elsif argument.required?
34
34
  @non_assigned_required << argument
@@ -49,123 +49,127 @@ class Thor
49
49
  @assigns
50
50
  end
51
51
 
52
- def remaining
52
+ def remaining # rubocop:disable TrivialAccessors
53
53
  @pile
54
54
  end
55
55
 
56
- private
56
+ private
57
57
 
58
- def no_or_skip?(arg)
59
- arg =~ /^--(no|skip)-([-\w]+)$/
60
- $2
61
- end
58
+ def no_or_skip?(arg)
59
+ arg =~ /^--(no|skip)-([-\w]+)$/
60
+ $2
61
+ end
62
62
 
63
- def last?
64
- @pile.empty?
65
- end
63
+ def last?
64
+ @pile.empty?
65
+ end
66
66
 
67
- def peek
68
- @pile.first
69
- end
67
+ def peek
68
+ @pile.first
69
+ end
70
70
 
71
- def shift
72
- @pile.shift
73
- end
71
+ def shift
72
+ @pile.shift
73
+ end
74
74
 
75
- def unshift(arg)
76
- unless arg.kind_of?(Array)
77
- @pile.unshift(arg)
78
- else
79
- @pile = arg + @pile
80
- end
75
+ def unshift(arg)
76
+ if arg.kind_of?(Array)
77
+ @pile = arg + @pile
78
+ else
79
+ @pile.unshift(arg)
81
80
  end
81
+ end
82
82
 
83
- def current_is_value?
84
- peek && peek.to_s !~ /^-/
85
- end
83
+ def current_is_value?
84
+ peek && peek.to_s !~ /^-/
85
+ end
86
86
 
87
- # Runs through the argument array getting strings that contains ":" and
88
- # mark it as a hash:
89
- #
90
- # [ "name:string", "age:integer" ]
91
- #
92
- # Becomes:
93
- #
94
- # { "name" => "string", "age" => "integer" }
95
- #
96
- def parse_hash(name)
97
- return shift if peek.is_a?(Hash)
98
- hash = {}
99
-
100
- while current_is_value? && peek.include?(?:)
101
- key, value = shift.split(':',2)
102
- hash[key] = value
103
- end
104
- hash
105
- end
87
+ # Runs through the argument array getting strings that contains ":" and
88
+ # mark it as a hash:
89
+ #
90
+ # [ "name:string", "age:integer" ]
91
+ #
92
+ # Becomes:
93
+ #
94
+ # { "name" => "string", "age" => "integer" }
95
+ #
96
+ def parse_hash(name)
97
+ return shift if peek.is_a?(Hash)
98
+ hash = {}
106
99
 
107
- # Runs through the argument array getting all strings until no string is
108
- # found or a switch is found.
109
- #
110
- # ["a", "b", "c"]
111
- #
112
- # And returns it as an array:
113
- #
114
- # ["a", "b", "c"]
115
- #
116
- def parse_array(name)
117
- return shift if peek.is_a?(Array)
118
- array = []
119
-
120
- while current_is_value?
121
- array << shift
122
- end
123
- array
100
+ while current_is_value? && peek.include?(":")
101
+ key, value = shift.split(":", 2)
102
+ hash[key] = value
124
103
  end
104
+ hash
105
+ end
125
106
 
126
- # Check if the peek is numeric format and return a Float or Integer.
127
- # Otherwise raises an error.
128
- #
129
- def parse_numeric(name)
130
- return shift if peek.is_a?(Numeric)
107
+ # Runs through the argument array getting all strings until no string is
108
+ # found or a switch is found.
109
+ #
110
+ # ["a", "b", "c"]
111
+ #
112
+ # And returns it as an array:
113
+ #
114
+ # ["a", "b", "c"]
115
+ #
116
+ def parse_array(name)
117
+ return shift if peek.is_a?(Array)
118
+ array = []
119
+ array << shift while current_is_value?
120
+ array
121
+ end
131
122
 
132
- unless peek =~ NUMERIC && $& == peek
133
- raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
134
- end
123
+ # Check if the peek is numeric format and return a Float or Integer.
124
+ # Check if the peek is included in enum if enum is provided.
125
+ # Otherwise raises an error.
126
+ #
127
+ def parse_numeric(name)
128
+ return shift if peek.is_a?(Numeric)
135
129
 
136
- $&.index('.') ? shift.to_f : shift.to_i
130
+ unless peek =~ NUMERIC && $& == peek
131
+ fail MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
137
132
  end
138
133
 
139
- # Parse string:
140
- # for --string-arg, just return the current value in the pile
141
- # for --no-string-arg, nil
142
- #
143
- def parse_string(name)
144
- if no_or_skip?(name)
145
- nil
146
- else
147
- value = shift
148
- if @switches.is_a?(Hash) && switch = @switches[name]
149
- if switch.enum && !switch.enum.include?(value)
150
- raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
151
- end
152
- end
153
- value
134
+ value = $&.index(".") ? shift.to_f : shift.to_i
135
+ if @switches.is_a?(Hash) && switch = @switches[name]
136
+ if switch.enum && !switch.enum.include?(value)
137
+ fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
154
138
  end
155
139
  end
140
+ value
141
+ end
156
142
 
157
- # Raises an error if @non_assigned_required array is not empty.
158
- #
159
- def check_requirement!
160
- unless @non_assigned_required.empty?
161
- names = @non_assigned_required.map do |o|
162
- o.respond_to?(:switch_name) ? o.switch_name : o.human_name
163
- end.join("', '")
164
-
165
- class_name = self.class.name.split('::').last.downcase
166
- raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
143
+ # Parse string:
144
+ # for --string-arg, just return the current value in the pile
145
+ # for --no-string-arg, nil
146
+ # Check if the peek is included in enum if enum is provided. Otherwise raises an error.
147
+ #
148
+ def parse_string(name)
149
+ if no_or_skip?(name)
150
+ nil
151
+ else
152
+ value = shift
153
+ if @switches.is_a?(Hash) && switch = @switches[name] # rubocop:disable AssignmentInCondition
154
+ if switch.enum && !switch.enum.include?(value)
155
+ fail MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}"
156
+ end
167
157
  end
158
+ value
168
159
  end
160
+ end
169
161
 
162
+ # Raises an error if @non_assigned_required array is not empty.
163
+ #
164
+ def check_requirement!
165
+ unless @non_assigned_required.empty?
166
+ names = @non_assigned_required.map do |o|
167
+ o.respond_to?(:switch_name) ? o.switch_name : o.human_name
168
+ end.join("', '")
169
+
170
+ class_name = self.class.name.split("::").last.downcase
171
+ fail RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
172
+ end
173
+ end
170
174
  end
171
175
  end