pry 0.12.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +162 -1
  3. data/LICENSE +1 -1
  4. data/README.md +331 -269
  5. data/bin/pry +5 -0
  6. data/lib/pry.rb +132 -119
  7. data/lib/pry/basic_object.rb +8 -4
  8. data/lib/pry/block_command.rb +22 -0
  9. data/lib/pry/class_command.rb +194 -0
  10. data/lib/pry/cli.rb +43 -51
  11. data/lib/pry/code.rb +40 -28
  12. data/lib/pry/code/code_file.rb +28 -24
  13. data/lib/pry/code/code_range.rb +4 -2
  14. data/lib/pry/code/loc.rb +15 -8
  15. data/lib/pry/code_object.rb +40 -38
  16. data/lib/pry/color_printer.rb +47 -46
  17. data/lib/pry/command.rb +166 -369
  18. data/lib/pry/command_set.rb +76 -73
  19. data/lib/pry/command_state.rb +31 -0
  20. data/lib/pry/commands/amend_line.rb +86 -81
  21. data/lib/pry/commands/bang.rb +18 -14
  22. data/lib/pry/commands/bang_pry.rb +15 -11
  23. data/lib/pry/commands/cat.rb +61 -54
  24. data/lib/pry/commands/cat/abstract_formatter.rb +23 -18
  25. data/lib/pry/commands/cat/exception_formatter.rb +71 -60
  26. data/lib/pry/commands/cat/file_formatter.rb +55 -49
  27. data/lib/pry/commands/cat/input_expression_formatter.rb +35 -30
  28. data/lib/pry/commands/cd.rb +40 -35
  29. data/lib/pry/commands/change_inspector.rb +29 -22
  30. data/lib/pry/commands/change_prompt.rb +44 -39
  31. data/lib/pry/commands/clear_screen.rb +16 -10
  32. data/lib/pry/commands/code_collector.rb +148 -133
  33. data/lib/pry/commands/disable_pry.rb +23 -19
  34. data/lib/pry/commands/easter_eggs.rb +19 -30
  35. data/lib/pry/commands/edit.rb +184 -161
  36. data/lib/pry/commands/edit/exception_patcher.rb +21 -17
  37. data/lib/pry/commands/edit/file_and_line_locator.rb +34 -23
  38. data/lib/pry/commands/exit.rb +39 -35
  39. data/lib/pry/commands/exit_all.rb +24 -20
  40. data/lib/pry/commands/exit_program.rb +20 -16
  41. data/lib/pry/commands/find_method.rb +168 -160
  42. data/lib/pry/commands/fix_indent.rb +16 -12
  43. data/lib/pry/commands/help.rb +140 -133
  44. data/lib/pry/commands/hist.rb +151 -150
  45. data/lib/pry/commands/import_set.rb +20 -16
  46. data/lib/pry/commands/jump_to.rb +25 -21
  47. data/lib/pry/commands/list_inspectors.rb +35 -28
  48. data/lib/pry/commands/ls.rb +124 -102
  49. data/lib/pry/commands/ls/constants.rb +59 -42
  50. data/lib/pry/commands/ls/formatter.rb +50 -46
  51. data/lib/pry/commands/ls/globals.rb +38 -34
  52. data/lib/pry/commands/ls/grep.rb +17 -13
  53. data/lib/pry/commands/ls/instance_vars.rb +29 -27
  54. data/lib/pry/commands/ls/interrogatable.rb +18 -12
  55. data/lib/pry/commands/ls/jruby_hacks.rb +47 -41
  56. data/lib/pry/commands/ls/local_names.rb +26 -22
  57. data/lib/pry/commands/ls/local_vars.rb +38 -28
  58. data/lib/pry/commands/ls/ls_entity.rb +47 -51
  59. data/lib/pry/commands/ls/methods.rb +44 -43
  60. data/lib/pry/commands/ls/methods_helper.rb +46 -42
  61. data/lib/pry/commands/ls/self_methods.rb +23 -22
  62. data/lib/pry/commands/nesting.rb +21 -17
  63. data/lib/pry/commands/play.rb +93 -82
  64. data/lib/pry/commands/pry_backtrace.rb +22 -17
  65. data/lib/pry/commands/pry_version.rb +15 -11
  66. data/lib/pry/commands/raise_up.rb +27 -22
  67. data/lib/pry/commands/reload_code.rb +60 -48
  68. data/lib/pry/commands/reset.rb +16 -12
  69. data/lib/pry/commands/ri.rb +55 -45
  70. data/lib/pry/commands/save_file.rb +45 -43
  71. data/lib/pry/commands/shell_command.rb +51 -51
  72. data/lib/pry/commands/shell_mode.rb +21 -17
  73. data/lib/pry/commands/show_doc.rb +80 -68
  74. data/lib/pry/commands/show_info.rb +189 -171
  75. data/lib/pry/commands/show_input.rb +16 -11
  76. data/lib/pry/commands/show_source.rb +110 -45
  77. data/lib/pry/commands/stat.rb +35 -31
  78. data/lib/pry/commands/switch_to.rb +21 -15
  79. data/lib/pry/commands/toggle_color.rb +20 -16
  80. data/lib/pry/commands/watch_expression.rb +89 -86
  81. data/lib/pry/commands/watch_expression/expression.rb +32 -27
  82. data/lib/pry/commands/whereami.rb +156 -148
  83. data/lib/pry/commands/wtf.rb +75 -50
  84. data/lib/pry/config.rb +307 -25
  85. data/lib/pry/config/attributable.rb +22 -0
  86. data/lib/pry/config/lazy_value.rb +29 -0
  87. data/lib/pry/config/memoized_value.rb +34 -0
  88. data/lib/pry/config/value.rb +24 -0
  89. data/lib/pry/control_d_handler.rb +28 -0
  90. data/lib/pry/core_extensions.rb +9 -7
  91. data/lib/pry/editor.rb +48 -21
  92. data/lib/pry/env.rb +18 -0
  93. data/lib/pry/exception_handler.rb +43 -0
  94. data/lib/pry/exceptions.rb +13 -16
  95. data/lib/pry/forwardable.rb +5 -1
  96. data/lib/pry/helpers.rb +2 -0
  97. data/lib/pry/helpers/base_helpers.rb +68 -197
  98. data/lib/pry/helpers/command_helpers.rb +50 -61
  99. data/lib/pry/helpers/documentation_helpers.rb +20 -13
  100. data/lib/pry/helpers/options_helpers.rb +14 -7
  101. data/lib/pry/helpers/platform.rb +7 -5
  102. data/lib/pry/helpers/table.rb +33 -26
  103. data/lib/pry/helpers/text.rb +17 -14
  104. data/lib/pry/history.rb +48 -56
  105. data/lib/pry/hooks.rb +21 -12
  106. data/lib/pry/indent.rb +54 -50
  107. data/lib/pry/input_completer.rb +248 -230
  108. data/lib/pry/input_lock.rb +8 -9
  109. data/lib/pry/inspector.rb +36 -24
  110. data/lib/pry/last_exception.rb +45 -45
  111. data/lib/pry/method.rb +141 -94
  112. data/lib/pry/method/disowned.rb +16 -4
  113. data/lib/pry/method/patcher.rb +12 -3
  114. data/lib/pry/method/weird_method_locator.rb +68 -44
  115. data/lib/pry/object_path.rb +33 -25
  116. data/lib/pry/output.rb +121 -35
  117. data/lib/pry/pager.rb +186 -180
  118. data/lib/pry/prompt.rb +123 -54
  119. data/lib/pry/pry_class.rb +61 -103
  120. data/lib/pry/pry_instance.rb +217 -215
  121. data/lib/pry/repl.rb +18 -22
  122. data/lib/pry/repl_file_loader.rb +27 -21
  123. data/lib/pry/ring.rb +11 -6
  124. data/lib/pry/slop.rb +574 -563
  125. data/lib/pry/slop/commands.rb +164 -169
  126. data/lib/pry/slop/option.rb +172 -168
  127. data/lib/pry/syntax_highlighter.rb +26 -0
  128. data/lib/pry/system_command_handler.rb +17 -0
  129. data/lib/pry/testable.rb +59 -61
  130. data/lib/pry/testable/evalable.rb +21 -12
  131. data/lib/pry/testable/mockable.rb +18 -10
  132. data/lib/pry/testable/pry_tester.rb +71 -56
  133. data/lib/pry/testable/utility.rb +29 -21
  134. data/lib/pry/testable/variables.rb +49 -43
  135. data/lib/pry/version.rb +3 -1
  136. data/lib/pry/warning.rb +27 -0
  137. data/lib/pry/wrapped_module.rb +51 -42
  138. data/lib/pry/wrapped_module/candidate.rb +21 -14
  139. metadata +35 -35
  140. data/lib/pry/commands.rb +0 -6
  141. data/lib/pry/commands/disabled_commands.rb +0 -2
  142. data/lib/pry/commands/gem_cd.rb +0 -26
  143. data/lib/pry/commands/gem_install.rb +0 -32
  144. data/lib/pry/commands/gem_list.rb +0 -33
  145. data/lib/pry/commands/gem_open.rb +0 -29
  146. data/lib/pry/commands/gem_readme.rb +0 -25
  147. data/lib/pry/commands/gem_search.rb +0 -40
  148. data/lib/pry/commands/gem_stats.rb +0 -83
  149. data/lib/pry/commands/gist.rb +0 -102
  150. data/lib/pry/commands/install_command.rb +0 -54
  151. data/lib/pry/config/behavior.rb +0 -255
  152. data/lib/pry/config/convenience.rb +0 -28
  153. data/lib/pry/config/default.rb +0 -159
  154. data/lib/pry/config/memoization.rb +0 -48
  155. data/lib/pry/platform.rb +0 -91
  156. data/lib/pry/plugins.rb +0 -122
  157. data/lib/pry/rubygem.rb +0 -84
  158. data/lib/pry/terminal.rb +0 -91
data/lib/pry/repl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Pry
2
4
  class REPL
3
5
  extend Pry::Forwardable
@@ -19,13 +21,11 @@ class Pry
19
21
  # @option options [Object] :target The initial target of the session.
20
22
  def initialize(pry, options = {})
21
23
  @pry = pry
22
- @indent = Pry::Indent.new
24
+ @indent = Pry::Indent.new(pry)
23
25
 
24
26
  @readline_output = nil
25
27
 
26
- if options[:target]
27
- @pry.push_binding options[:target]
28
- end
28
+ @pry.push_binding options[:target] if options[:target]
29
29
  end
30
30
 
31
31
  # Start the read-eval-print loop.
@@ -47,10 +47,10 @@ class Pry
47
47
  def prologue
48
48
  pry.exec_hook :before_session, pry.output, pry.current_binding, pry
49
49
 
50
+ return unless pry.config.correct_indent
51
+
50
52
  # Clear the line before starting Pry. This fixes issue #566.
51
- if pry.config.correct_indent
52
- Kernel.print(Helpers::Platform.windows_ansi? ? "\e[0F" : "\e[0G")
53
- end
53
+ output.print(Helpers::Platform.windows_ansi? ? "\e[0F" : "\e[0G")
54
54
  end
55
55
 
56
56
  # The actual read-eval-print loop.
@@ -98,13 +98,15 @@ class Pry
98
98
  val = read_line("#{current_prompt}#{indentation}")
99
99
 
100
100
  # Return nil for EOF, :no_more_input for error, or :control_c for <Ctrl-C>
101
- return val unless String === val
101
+ return val unless val.is_a?(String)
102
102
 
103
103
  if pry.config.auto_indent
104
104
  original_val = "#{indentation}#{val}"
105
105
  indented_val = @indent.indent(val)
106
106
 
107
- if output.tty? && pry.config.correct_indent && Pry::Helpers::BaseHelpers.use_ansi_codes?
107
+ if output.tty? &&
108
+ pry.config.correct_indent &&
109
+ Pry::Helpers::BaseHelpers.use_ansi_codes?
108
110
  output.print @indent.correct_indentation(
109
111
  current_prompt,
110
112
  indented_val,
@@ -130,7 +132,7 @@ class Pry
130
132
  yield
131
133
  rescue EOFError
132
134
  pry.config.input = Pry.config.input
133
- if !should_retry
135
+ unless should_retry
134
136
  output.puts "Error: Pry ran out of things to read from! " \
135
137
  "Attempting to break out of REPL."
136
138
  return :no_more_input
@@ -151,9 +153,7 @@ class Pry
151
153
  puts "Error: #{e.message}"
152
154
  output.puts e.backtrace
153
155
  exception_count += 1
154
- if exception_count < 5
155
- retry
156
- end
156
+ retry if exception_count < 5
157
157
  puts "FATAL: Pry failed to get user input using `#{input}`."
158
158
  puts "To fix this you may be able to pass input and output file " \
159
159
  "descriptors to pry directly. e.g."
@@ -185,12 +185,10 @@ class Pry
185
185
  input_readline(current_prompt, false) # false since we'll add it manually
186
186
  elsif coolline_available?
187
187
  input_readline(current_prompt)
188
+ elsif input.method(:readline).arity == 1
189
+ input_readline(current_prompt)
188
190
  else
189
- if input.method(:readline).arity == 1
190
- input_readline(current_prompt)
191
- else
192
- input_readline
193
- end
191
+ input_readline
194
192
  end
195
193
  end
196
194
  end
@@ -227,9 +225,7 @@ class Pry
227
225
  def set_readline_output
228
226
  return if @readline_output
229
227
 
230
- if piping?
231
- @readline_output = (Readline.output = Pry.config.output)
232
- end
228
+ @readline_output = (Readline.output = Pry.config.output) if piping?
233
229
  end
234
230
 
235
231
  # Calculates correct overhang for current line. Supports vi Readline
@@ -247,7 +243,7 @@ class Pry
247
243
  # rb-readline doesn't support this method:
248
244
  # https://github.com/ConnorAtherton/rb-readline/issues/152
249
245
  if Readline.vi_editing_mode?
250
- overhang += current_prompt.length - indented_val.length
246
+ overhang = output.width - current_prompt.size - indented_val.size
251
247
  end
252
248
  rescue NotImplementedError
253
249
  # VI editing mode is unsupported on JRuby.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Pry
2
4
  # A class to manage the loading of files through the REPL loop.
3
5
  # This is an interesting trick as it processes your file as if it
@@ -12,7 +14,7 @@ class Pry
12
14
  class REPLFileLoader
13
15
  def initialize(file_name)
14
16
  full_name = File.expand_path(file_name)
15
- raise RuntimeError, "No such file: #{full_name}" if !File.exist?(full_name)
17
+ raise "No such file: #{full_name}" unless File.exist?(full_name)
16
18
 
17
19
  define_additional_commands
18
20
  @content = File.read(full_name)
@@ -20,34 +22,36 @@ class Pry
20
22
 
21
23
  # Switch to interactive mode, i.e take input from the user
22
24
  # and use the regular print and exception handlers.
23
- # @param [Pry] _pry_ the Pry instance to make interactive.
24
- def interactive_mode(_pry_)
25
- _pry_.config.input = Pry.config.input
26
- _pry_.config.print = Pry.config.print
27
- _pry_.config.exception_handler = Pry.config.exception_handler
28
- Pry::REPL.new(_pry_).start
25
+ # @param [Pry] pry_instance the Pry instance to make interactive.
26
+ def interactive_mode(pry_instance)
27
+ pry_instance.config.input = Pry.config.input
28
+ pry_instance.config.print = Pry.config.print
29
+ pry_instance.config.exception_handler = Pry.config.exception_handler
30
+ Pry::REPL.new(pry_instance).start
29
31
  end
30
32
 
31
33
  # Switch to non-interactive mode. Essentially
32
34
  # this means there is no result output
33
35
  # and that the session becomes interactive when an exception is encountered.
34
- # @param [Pry] _pry_ the Pry instance to make non-interactive.
35
- def non_interactive_mode(_pry_, content)
36
- _pry_.print = proc {}
37
- _pry_.exception_handler = proc do |o, e, _p_|
38
- _p_.run_command "cat --ex"
36
+ # @param [Pry] pry_instance the Pry instance to make non-interactive.
37
+ def non_interactive_mode(pry_instance, content)
38
+ pry_instance.print = proc {}
39
+ pry_instance.exception_handler = proc do |o, _e, p|
40
+ p.run_command "cat --ex"
39
41
  o.puts "...exception encountered, going interactive!"
40
- interactive_mode(_pry_)
42
+ interactive_mode(pry_instance)
41
43
  end
42
44
 
43
45
  content.lines.each do |line|
44
- break unless _pry_.eval line, generated: true
46
+ break unless pry_instance.eval line, generated: true
45
47
  end
46
48
 
47
- unless _pry_.eval_string.empty?
48
- _pry_.output.puts "#{_pry_.eval_string}...exception encountered, going interactive!"
49
- interactive_mode(_pry_)
50
- end
49
+ return if pry_instance.eval_string.empty?
50
+
51
+ pry_instance.output.puts(
52
+ "#{pry_instance.eval_string}...exception encountered, going interactive!"
53
+ )
54
+ interactive_mode(pry_instance)
51
55
  end
52
56
 
53
57
  # Define a few extra commands useful for flipping back & forth
@@ -56,11 +60,13 @@ class Pry
56
60
  s = self
57
61
 
58
62
  Pry::Commands.command "make-interactive", "Make the session interactive" do
59
- s.interactive_mode(_pry_)
63
+ s.interactive_mode(pry_instance)
60
64
  end
61
65
 
62
- Pry::Commands.command "load-file", "Load another file through the repl" do |file_name|
63
- s.non_interactive_mode(_pry_, File.read(File.expand_path(file_name)))
66
+ Pry::Commands.command(
67
+ "load-file", "Load another file through the repl"
68
+ ) do |file_name|
69
+ s.non_interactive_mode(pry_instance, File.read(File.expand_path(file_name)))
64
70
  end
65
71
  end
66
72
 
data/lib/pry/ring.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Pry
2
4
  # A ring is a thread-safe fixed-capacity array to which you can only add
3
5
  # elements. Older entries are overwritten as you add new elements, so that the
@@ -57,10 +59,7 @@ class Pry
57
59
  return @buffer[(count + index) % max_size] if index.is_a?(Integer)
58
60
  return @buffer[index] if count <= max_size
59
61
 
60
- # Swap parts of array when the array turns page and starts overwriting
61
- # from the beginning, then apply the range.
62
- last_part = @buffer.slice([index.end, max_size - 1].min, count % max_size)
63
- (last_part + (@buffer - last_part))[index]
62
+ transpose_buffer_tail[index]
64
63
  end
65
64
  end
66
65
 
@@ -68,8 +67,7 @@ class Pry
68
67
  def to_a
69
68
  return @buffer.dup if count <= max_size
70
69
 
71
- last_part = @buffer.slice(count % max_size, @buffer.size)
72
- last_part + (@buffer - last_part)
70
+ transpose_buffer_tail
73
71
  end
74
72
 
75
73
  # Clear the buffer and reset count.
@@ -80,5 +78,12 @@ class Pry
80
78
  @count = 0
81
79
  end
82
80
  end
81
+
82
+ private
83
+
84
+ def transpose_buffer_tail
85
+ tail = @buffer.slice(count % max_size, @buffer.size)
86
+ tail.concat @buffer.slice(0, count % max_size)
87
+ end
83
88
  end
84
89
  end
data/lib/pry/slop.rb CHANGED
@@ -1,661 +1,672 @@
1
- class Pry::Slop
2
- require_relative 'slop/option'
3
- require_relative 'slop/commands'
4
- include Enumerable
5
- VERSION = '3.4.0'
6
-
7
- # The main Error class, all Exception classes inherit from this class.
8
- class Error < StandardError; end
9
-
10
- # Raised when an option argument is expected but none are given.
11
- class MissingArgumentError < Error; end
12
-
13
- # Raised when an option is expected/required but not present.
14
- class MissingOptionError < Error; end
15
-
16
- # Raised when an argument does not match its intended match constraint.
17
- class InvalidArgumentError < Error; end
18
-
19
- # Raised when an invalid option is found and the strict flag is enabled.
20
- class InvalidOptionError < Error; end
21
-
22
- # Raised when an invalid command is found and the strict flag is enabled.
23
- class InvalidCommandError < Error; end
24
-
25
- # Returns a default Hash of configuration options this Slop instance uses.
26
- DEFAULT_OPTIONS = {
27
- strict: false,
28
- help: false,
29
- banner: nil,
30
- ignore_case: false,
31
- autocreate: false,
32
- arguments: false,
33
- optional_arguments: false,
34
- multiple_switches: true,
35
- longest_flag: 0
36
- }
37
-
38
- class << self
39
- # items - The Array of items to extract options from (default: ARGV).
40
- # config - The Hash of configuration options to send to Slop.new().
41
- # block - An optional block used to add options.
42
- #
43
- # Examples:
44
- #
45
- # Slop.parse(ARGV, :help => true) do
46
- # on '-n', '--name', 'Your username', :argument => true
47
- # end
1
+ # frozen_string_literal: true
2
+
3
+ class Pry
4
+ # rubocop:disable Metrics/ClassLength
5
+ class Slop
6
+ require_relative 'slop/option'
7
+ require_relative 'slop/commands'
8
+ include Enumerable
9
+ VERSION = '3.4.0'.freeze
10
+
11
+ # The main Error class, all Exception classes inherit from this class.
12
+ class Error < StandardError; end
13
+
14
+ # Raised when an option argument is expected but none are given.
15
+ class MissingArgumentError < Error; end
16
+
17
+ # Raised when an option is expected/required but not present.
18
+ class MissingOptionError < Error; end
19
+
20
+ # Raised when an argument does not match its intended match constraint.
21
+ class InvalidArgumentError < Error; end
22
+
23
+ # Raised when an invalid option is found and the strict flag is enabled.
24
+ class InvalidOptionError < Error; end
25
+
26
+ # Raised when an invalid command is found and the strict flag is enabled.
27
+ class InvalidCommandError < Error; end
28
+
29
+ # Returns a default Hash of configuration options this Slop instance uses.
30
+ DEFAULT_OPTIONS = {
31
+ strict: false,
32
+ help: false,
33
+ banner: nil,
34
+ ignore_case: false,
35
+ autocreate: false,
36
+ arguments: false,
37
+ optional_arguments: false,
38
+ multiple_switches: true,
39
+ longest_flag: 0
40
+ }.freeze
41
+
42
+ class << self
43
+ # items - The Array of items to extract options from (default: ARGV).
44
+ # config - The Hash of configuration options to send to Slop.new().
45
+ # block - An optional block used to add options.
46
+ #
47
+ # Examples:
48
+ #
49
+ # Slop.parse(ARGV, :help => true) do
50
+ # on '-n', '--name', 'Your username', :argument => true
51
+ # end
52
+ #
53
+ # Returns a new instance of Slop.
54
+ def parse(items = ARGV, config = {}, &block)
55
+ parse! items.dup, config, &block
56
+ end
57
+
58
+ # items - The Array of items to extract options from (default: ARGV).
59
+ # config - The Hash of configuration options to send to Slop.new().
60
+ # block - An optional block used to add options.
61
+ #
62
+ # Returns a new instance of Slop.
63
+ def parse!(items = ARGV, config = {}, &block)
64
+ if items.is_a?(Hash) && config.empty?
65
+ config = items
66
+ items = ARGV
67
+ end
68
+ slop = Pry::Slop.new config, &block
69
+ slop.parse! items
70
+ slop
71
+ end
72
+
73
+ # Build a Slop object from a option specification.
74
+ #
75
+ # This allows you to design your options via a simple String rather
76
+ # than programatically. Do note though that with this method, you're
77
+ # unable to pass any advanced options to the on() method when creating
78
+ # options.
79
+ #
80
+ # string - The optspec String
81
+ # config - A Hash of configuration options to pass to Slop.new
82
+ #
83
+ # Examples:
84
+ #
85
+ # opts = Slop.optspec(<<-SPEC)
86
+ # ruby foo.rb [options]
87
+ # ---
88
+ # n,name= Your name
89
+ # a,age= Your age
90
+ # A,auth Sign in with auth
91
+ # p,passcode= Your secret pass code
92
+ # SPEC
93
+ #
94
+ # opts.fetch_option(:name).description #=> "Your name"
95
+ #
96
+ # Returns a new instance of Slop.
97
+ def optspec(string, config = {})
98
+ config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
99
+ lines = optspec.split("\n").reject(&:empty?)
100
+ opts = Slop.new(config)
101
+
102
+ lines.each do |line|
103
+ opt, description = line.split(' ', 2)
104
+ short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
105
+ opt = opts.on(short, long, description)
106
+
107
+ if long && long.end_with?('=')
108
+ long.sub!(/\=$/, '')
109
+ opt.config[:argument] = true
110
+ end
111
+ end
112
+
113
+ opts
114
+ end
115
+ end
116
+
117
+ # The Hash of configuration options for this Slop instance.
118
+ attr_reader :config
119
+
120
+ # The Array of Slop::Option objects tied to this Slop instance.
121
+ attr_reader :options
122
+
123
+ # Create a new instance of Slop and optionally build options via a block.
48
124
  #
49
- # Returns a new instance of Slop.
50
- def parse(items = ARGV, config = {}, &block)
51
- parse! items.dup, config, &block
125
+ # config - A Hash of configuration options.
126
+ # block - An optional block used to specify options.
127
+ def initialize(config = {}, &block)
128
+ @config = DEFAULT_OPTIONS.merge(config)
129
+ @options = []
130
+ @commands = {}
131
+ @trash = []
132
+ @triggered_options = []
133
+ @unknown_options = []
134
+ @callbacks = {}
135
+ @separators = {}
136
+ @runner = nil
137
+
138
+ if block_given?
139
+ block.arity == 1 ? yield(self) : instance_eval(&block)
140
+ end
141
+
142
+ return unless config[:help]
143
+
144
+ on('-h', '--help', 'Display this help message.', tail: true) do
145
+ warn help
146
+ end
52
147
  end
53
148
 
54
- # items - The Array of items to extract options from (default: ARGV).
55
- # config - The Hash of configuration options to send to Slop.new().
56
- # block - An optional block used to add options.
149
+ # Is strict mode enabled?
57
150
  #
58
- # Returns a new instance of Slop.
59
- def parse!(items = ARGV, config = {}, &block)
60
- config, items = items, ARGV if items.is_a?(Hash) && config.empty?
61
- slop = Pry::Slop.new config, &block
62
- slop.parse! items
63
- slop
151
+ # Returns true if strict mode is enabled, false otherwise.
152
+ def strict?
153
+ config[:strict]
64
154
  end
65
155
 
66
- # Build a Slop object from a option specification.
156
+ # Set the banner.
67
157
  #
68
- # This allows you to design your options via a simple String rather
69
- # than programatically. Do note though that with this method, you're
70
- # unable to pass any advanced options to the on() method when creating
71
- # options.
158
+ # banner - The String to set the banner.
159
+ def banner=(banner)
160
+ config[:banner] = banner
161
+ end
162
+
163
+ # Get or set the banner.
72
164
  #
73
- # string - The optspec String
74
- # config - A Hash of configuration options to pass to Slop.new
165
+ # banner - The String to set the banner.
75
166
  #
76
- # Examples:
77
- #
78
- # opts = Slop.optspec(<<-SPEC)
79
- # ruby foo.rb [options]
80
- # ---
81
- # n,name= Your name
82
- # a,age= Your age
83
- # A,auth Sign in with auth
84
- # p,passcode= Your secret pass code
85
- # SPEC
86
- #
87
- # opts.fetch_option(:name).description #=> "Your name"
88
- #
89
- # Returns a new instance of Slop.
90
- def optspec(string, config = {})
91
- config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
92
- lines = optspec.split("\n").reject(&:empty?)
93
- opts = Slop.new(config)
94
-
95
- lines.each do |line|
96
- opt, description = line.split(' ', 2)
97
- short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
98
- opt = opts.on(short, long, description)
99
-
100
- if long && long.end_with?('=')
101
- long.sub!(/\=$/, '')
102
- opt.config[:argument] = true
103
- end
104
- end
105
-
106
- opts
167
+ # Returns the banner String.
168
+ def banner(banner = nil)
169
+ config[:banner] = banner if banner
170
+ config[:banner]
107
171
  end
108
- end
109
-
110
- # The Hash of configuration options for this Slop instance.
111
- attr_reader :config
112
172
 
113
- # The Array of Slop::Option objects tied to this Slop instance.
114
- attr_reader :options
173
+ # Set the description (used for commands).
174
+ #
175
+ # desc - The String to set the description.
176
+ def description=(desc)
177
+ config[:description] = desc
178
+ end
115
179
 
116
- # Create a new instance of Slop and optionally build options via a block.
117
- #
118
- # config - A Hash of configuration options.
119
- # block - An optional block used to specify options.
120
- def initialize(config = {}, &block)
121
- @config = DEFAULT_OPTIONS.merge(config)
122
- @options = []
123
- @commands = {}
124
- @trash = []
125
- @triggered_options = []
126
- @unknown_options = []
127
- @callbacks = {}
128
- @separators = {}
129
- @runner = nil
180
+ # Get or set the description (used for commands).
181
+ #
182
+ # desc - The String to set the description.
183
+ #
184
+ # Returns the description String.
185
+ def description(desc = nil)
186
+ config[:description] = desc if desc
187
+ config[:description]
188
+ end
130
189
 
131
- if block_given?
132
- block.arity == 1 ? yield(self) : instance_eval(&block)
190
+ # Add a new command.
191
+ #
192
+ # command - The Symbol or String used to identify this command.
193
+ # options - A Hash of configuration options (see Slop::new)
194
+ #
195
+ # Returns a new instance of Slop mapped to this command.
196
+ def command(command, options = {}, &block)
197
+ @commands[command.to_s] = Pry::Slop.new(options, &block)
133
198
  end
134
199
 
135
- if config[:help]
136
- on('-h', '--help', 'Display this help message.', tail: true) do
137
- $stderr.puts help
138
- end
200
+ # Parse a list of items, executing and gathering options along the way.
201
+ #
202
+ # items - The Array of items to extract options from (default: ARGV).
203
+ # block - An optional block which when used will yield non options.
204
+ #
205
+ # Returns an Array of original items.
206
+ def parse(items = ARGV, &block)
207
+ parse! items.dup, &block
208
+ items
139
209
  end
140
- end
141
210
 
142
- # Is strict mode enabled?
143
- #
144
- # Returns true if strict mode is enabled, false otherwise.
145
- def strict?
146
- config[:strict]
147
- end
211
+ # Parse a list of items, executing and gathering options along the way.
212
+ # unlike parse() this method will remove any options and option arguments
213
+ # from the original Array.
214
+ #
215
+ # items - The Array of items to extract options from (default: ARGV).
216
+ # block - An optional block which when used will yield non options.
217
+ #
218
+ # Returns an Array of original items with options removed.
219
+ def parse!(items = ARGV, &block)
220
+ if items.empty? && @callbacks[:empty]
221
+ @callbacks[:empty].each { |cb| cb.call(self) }
222
+ return items
223
+ end
148
224
 
149
- # Set the banner.
150
- #
151
- # banner - The String to set the banner.
152
- def banner=(banner)
153
- config[:banner] = banner
154
- end
225
+ if (cmd = @commands[items[0]])
226
+ return cmd.parse! items[1..-1]
227
+ end
155
228
 
156
- # Get or set the banner.
157
- #
158
- # banner - The String to set the banner.
159
- #
160
- # Returns the banner String.
161
- def banner(banner = nil)
162
- config[:banner] = banner if banner
163
- config[:banner]
164
- end
229
+ items.each_with_index do |item, index|
230
+ @trash << index && break if item == '--'
231
+ autocreate(items, index) if config[:autocreate]
232
+ process_item(items, index, &block) unless @trash.include?(index)
233
+ end
234
+ items.reject!.with_index { |_item, index| @trash.include?(index) }
165
235
 
166
- # Set the description (used for commands).
167
- #
168
- # desc - The String to set the description.
169
- def description=(desc)
170
- config[:description] = desc
171
- end
236
+ missing_options = options.select { |opt| opt.required? && opt.count < 1 }
237
+ if missing_options.any?
238
+ raise MissingOptionError,
239
+ "Missing required option(s): #{missing_options.map(&:key).join(', ')}"
240
+ end
172
241
 
173
- # Get or set the description (used for commands).
174
- #
175
- # desc - The String to set the description.
176
- #
177
- # Returns the description String.
178
- def description(desc = nil)
179
- config[:description] = desc if desc
180
- config[:description]
181
- end
242
+ if @unknown_options.any?
243
+ raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
244
+ end
182
245
 
183
- # Add a new command.
184
- #
185
- # command - The Symbol or String used to identify this command.
186
- # options - A Hash of configuration options (see Slop::new)
187
- #
188
- # Returns a new instance of Slop mapped to this command.
189
- def command(command, options = {}, &block)
190
- @commands[command.to_s] = Pry::Slop.new(options, &block)
191
- end
246
+ if @triggered_options.empty? && @callbacks[:no_options]
247
+ @callbacks[:no_options].each { |cb| cb.call(self) }
248
+ end
192
249
 
193
- # Parse a list of items, executing and gathering options along the way.
194
- #
195
- # items - The Array of items to extract options from (default: ARGV).
196
- # block - An optional block which when used will yield non options.
197
- #
198
- # Returns an Array of original items.
199
- def parse(items = ARGV, &block)
200
- parse! items.dup, &block
201
- items
202
- end
250
+ @runner.call(self, items) if @runner.respond_to?(:call)
203
251
 
204
- # Parse a list of items, executing and gathering options along the way.
205
- # unlike parse() this method will remove any options and option arguments
206
- # from the original Array.
207
- #
208
- # items - The Array of items to extract options from (default: ARGV).
209
- # block - An optional block which when used will yield non options.
210
- #
211
- # Returns an Array of original items with options removed.
212
- def parse!(items = ARGV, &block)
213
- if items.empty? && @callbacks[:empty]
214
- @callbacks[:empty].each { |cb| cb.call(self) }
215
- return items
252
+ items
216
253
  end
217
254
 
218
- if (cmd = @commands[items[0]])
219
- return cmd.parse! items[1..-1]
255
+ # Add an Option.
256
+ #
257
+ # objects - An Array with an optional Hash as the last element.
258
+ #
259
+ # Examples:
260
+ #
261
+ # on '-u', '--username=', 'Your username'
262
+ # on :v, :verbose, 'Enable verbose mode'
263
+ #
264
+ # Returns the created instance of Slop::Option.
265
+ def on(*objects, &block)
266
+ option = build_option(objects, &block)
267
+ options << option
268
+ option
220
269
  end
270
+ alias option on
271
+ alias opt on
221
272
 
222
- items.each_with_index do |item, index|
223
- @trash << index && break if item == '--'
224
- autocreate(items, index) if config[:autocreate]
225
- process_item(items, index, &block) unless @trash.include?(index)
273
+ # Fetch an options argument value.
274
+ #
275
+ # key - The Symbol or String option short or long flag.
276
+ #
277
+ # Returns the Object value for this option, or nil.
278
+ def [](key)
279
+ option = fetch_option(key)
280
+ option.value if option
226
281
  end
227
- items.reject!.with_index { |item, index| @trash.include?(index) }
282
+ alias get []
228
283
 
229
- missing_options = options.select { |opt| opt.required? && opt.count < 1 }
230
- if missing_options.any?
231
- raise MissingOptionError,
232
- "Missing required option(s): #{missing_options.map(&:key).join(', ')}"
284
+ # Returns a new Hash with option flags as keys and option values as values.
285
+ #
286
+ # include_commands - If true, merge options from all sub-commands.
287
+ def to_hash(include_commands = false)
288
+ hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
289
+ if include_commands
290
+ @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
291
+ end
292
+ hash
233
293
  end
294
+ alias to_h to_hash
234
295
 
235
- if @unknown_options.any?
236
- raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
296
+ # Enumerable interface. Yields each Slop::Option.
297
+ def each(&block)
298
+ options.each(&block)
237
299
  end
238
300
 
239
- if @triggered_options.empty? && @callbacks[:no_options]
240
- @callbacks[:no_options].each { |cb| cb.call(self) }
301
+ # Specify code to be executed when these options are parsed.
302
+ #
303
+ # callable - An object responding to a call method.
304
+ #
305
+ # yields - The instance of Slop parsing these options
306
+ # An Array of unparsed arguments
307
+ #
308
+ # Example:
309
+ #
310
+ # Slop.parse do
311
+ # on :v, :verbose
312
+ #
313
+ # run do |opts, args|
314
+ # puts "Arguments: #{args.inspect}" if opts.verbose?
315
+ # end
316
+ # end
317
+ def run(callable = nil, &block)
318
+ @runner = callable || block
319
+ return if @runner.respond_to?(:call)
320
+
321
+ raise ArgumentError, "You must specify a callable object or a block to #run"
241
322
  end
242
323
 
243
- @runner.call(self, items) if @runner.respond_to?(:call)
324
+ # Check for an options presence.
325
+ #
326
+ # Examples:
327
+ #
328
+ # opts.parse %w( --foo )
329
+ # opts.present?(:foo) #=> true
330
+ # opts.present?(:bar) #=> false
331
+ #
332
+ # Returns true if all of the keys are present in the parsed arguments.
333
+ def present?(*keys)
334
+ keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
335
+ end
244
336
 
245
- items
246
- end
337
+ # Override this method so we can check if an option? method exists.
338
+ #
339
+ # Returns true if this option key exists in our list of options.
340
+ def respond_to_missing?(method_name, include_all = false)
341
+ options.any? { |o| o.key == method_name.to_s.chop } || super
342
+ end
247
343
 
248
- # Add an Option.
249
- #
250
- # objects - An Array with an optional Hash as the last element.
251
- #
252
- # Examples:
253
- #
254
- # on '-u', '--username=', 'Your username'
255
- # on :v, :verbose, 'Enable verbose mode'
256
- #
257
- # Returns the created instance of Slop::Option.
258
- def on(*objects, &block)
259
- option = build_option(objects, &block)
260
- options << option
261
- option
262
- end
263
- alias option on
264
- alias opt on
265
-
266
- # Fetch an options argument value.
267
- #
268
- # key - The Symbol or String option short or long flag.
269
- #
270
- # Returns the Object value for this option, or nil.
271
- def [](key)
272
- option = fetch_option(key)
273
- option.value if option
274
- end
275
- alias get []
344
+ # Fetch a list of options which were missing from the parsed list.
345
+ #
346
+ # Examples:
347
+ #
348
+ # opts = Slop.new do
349
+ # on :n, :name=
350
+ # on :p, :password=
351
+ # end
352
+ #
353
+ # opts.parse %w[ --name Lee ]
354
+ # opts.missing #=> ['password']
355
+ #
356
+ # Returns an Array of Strings representing missing options.
357
+ def missing
358
+ (options - @triggered_options).map(&:key)
359
+ end
276
360
 
277
- # Returns a new Hash with option flags as keys and option values as values.
278
- #
279
- # include_commands - If true, merge options from all sub-commands.
280
- def to_hash(include_commands = false)
281
- hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
282
- if include_commands
283
- @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
361
+ # Fetch a Slop::Option object.
362
+ #
363
+ # key - The Symbol or String option key.
364
+ #
365
+ # Examples:
366
+ #
367
+ # opts.on(:foo, 'Something fooey', :argument => :optional)
368
+ # opt = opts.fetch_option(:foo)
369
+ # opt.class #=> Slop::Option
370
+ # opt.accepts_optional_argument? #=> true
371
+ #
372
+ # Returns an Option or nil if none were found.
373
+ def fetch_option(key)
374
+ options.find { |option| [option.long, option.short].include?(clean(key)) }
284
375
  end
285
- hash
286
- end
287
- alias to_h to_hash
288
376
 
289
- # Enumerable interface. Yields each Slop::Option.
290
- def each(&block)
291
- options.each(&block)
292
- end
377
+ # Fetch a Slop object associated with this command.
378
+ #
379
+ # command - The String or Symbol name of the command.
380
+ #
381
+ # Examples:
382
+ #
383
+ # opts.command :foo do
384
+ # on :v, :verbose, 'Enable verbose mode'
385
+ # end
386
+ #
387
+ # # ruby run.rb foo -v
388
+ # opts.fetch_command(:foo).verbose? #=> true
389
+ def fetch_command(command)
390
+ @commands[command.to_s]
391
+ end
293
392
 
294
- # Specify code to be executed when these options are parsed.
295
- #
296
- # callable - An object responding to a call method.
297
- #
298
- # yields - The instance of Slop parsing these options
299
- # An Array of unparsed arguments
300
- #
301
- # Example:
302
- #
303
- # Slop.parse do
304
- # on :v, :verbose
305
- #
306
- # run do |opts, args|
307
- # puts "Arguments: #{args.inspect}" if opts.verbose?
308
- # end
309
- # end
310
- def run(callable = nil, &block)
311
- @runner = callable || block
312
- unless @runner.respond_to?(:call)
313
- raise ArgumentError, "You must specify a callable object or a block to #run"
393
+ # Add a callback.
394
+ #
395
+ # label - The Symbol identifier to attach this callback.
396
+ #
397
+ # Returns nothing.
398
+ def add_callback(label, &block)
399
+ (@callbacks[label] ||= []) << block
314
400
  end
315
- end
316
401
 
317
- # Check for an options presence.
318
- #
319
- # Examples:
320
- #
321
- # opts.parse %w( --foo )
322
- # opts.present?(:foo) #=> true
323
- # opts.present?(:bar) #=> false
324
- #
325
- # Returns true if all of the keys are present in the parsed arguments.
326
- def present?(*keys)
327
- keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
328
- end
402
+ # Add string separators between options.
403
+ #
404
+ # text - The String text to print.
405
+ def separator(text)
406
+ if @separators[options.size]
407
+ @separators[options.size] << "\n#{text}"
408
+ else
409
+ @separators[options.size] = text
410
+ end
411
+ end
329
412
 
330
- # Override this method so we can check if an option? method exists.
331
- #
332
- # Returns true if this option key exists in our list of options.
333
- def respond_to_missing?(method_name, include_all = false)
334
- options.any? { |o| o.key == method_name.to_s.chop } || super
335
- end
413
+ # Print a handy Slop help string.
414
+ #
415
+ # Returns the banner followed by available option help strings.
416
+ def to_s
417
+ heads = options.reject(&:tail?)
418
+ tails = (options - heads)
419
+ opts = (heads + tails).select(&:help).map(&:to_s)
420
+ optstr = opts.each_with_index.map do |o, i|
421
+ (str = @separators[i + 1]) ? [o, str].join("\n") : o
422
+ end.join("\n")
423
+
424
+ if @commands.any?
425
+ optstr << "\n" unless optstr.empty?
426
+ optstr << "\nAvailable commands:\n\n"
427
+ optstr << commands_to_help
428
+ optstr << "\n\nSee `<command> --help` for more information on a specific command."
429
+ end
336
430
 
337
- # Fetch a list of options which were missing from the parsed list.
338
- #
339
- # Examples:
340
- #
341
- # opts = Slop.new do
342
- # on :n, :name=
343
- # on :p, :password=
344
- # end
345
- #
346
- # opts.parse %w[ --name Lee ]
347
- # opts.missing #=> ['password']
348
- #
349
- # Returns an Array of Strings representing missing options.
350
- def missing
351
- (options - @triggered_options).map(&:key)
352
- end
431
+ banner = config[:banner]
432
+ banner ||= "Usage: #{File.basename($PROGRAM_NAME, '.*')}" \
433
+ "#{' [command]' if @commands.any?} [options]"
434
+ if banner
435
+ "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
436
+ else
437
+ optstr
438
+ end
439
+ end
440
+ alias help to_s
353
441
 
354
- # Fetch a Slop::Option object.
355
- #
356
- # key - The Symbol or String option key.
357
- #
358
- # Examples:
359
- #
360
- # opts.on(:foo, 'Something fooey', :argument => :optional)
361
- # opt = opts.fetch_option(:foo)
362
- # opt.class #=> Slop::Option
363
- # opt.accepts_optional_argument? #=> true
364
- #
365
- # Returns an Option or nil if none were found.
366
- def fetch_option(key)
367
- options.find { |option| [option.long, option.short].include?(clean(key)) }
368
- end
442
+ private
369
443
 
370
- # Fetch a Slop object associated with this command.
371
- #
372
- # command - The String or Symbol name of the command.
373
- #
374
- # Examples:
375
- #
376
- # opts.command :foo do
377
- # on :v, :verbose, 'Enable verbose mode'
378
- # end
379
- #
380
- # # ruby run.rb foo -v
381
- # opts.fetch_command(:foo).verbose? #=> true
382
- def fetch_command(command)
383
- @commands[command.to_s]
384
- end
444
+ # Convenience method for present?(:option).
445
+ #
446
+ # Examples:
447
+ #
448
+ # opts.parse %( --verbose )
449
+ # opts.verbose? #=> true
450
+ # opts.other? #=> false
451
+ #
452
+ # Returns true if this option is present. If this method does not end
453
+ # with a ? character it will instead call super().
454
+ def method_missing(method, *args, &block)
455
+ meth = method.to_s
456
+ if meth.end_with?('?')
457
+ meth = meth.chop
458
+ present?(meth) || present?(meth.tr('_', '-'))
459
+ else
460
+ super
461
+ end
462
+ end
385
463
 
386
- # Add a callback.
387
- #
388
- # label - The Symbol identifier to attach this callback.
389
- #
390
- # Returns nothing.
391
- def add_callback(label, &block)
392
- (@callbacks[label] ||= []) << block
393
- end
464
+ # Process a list item, figure out if it's an option, execute any
465
+ # callbacks, assign any option arguments, and do some sanity checks.
466
+ #
467
+ # items - The Array of items to process.
468
+ # index - The current Integer index of the item we want to process.
469
+ # block - An optional block which when passed will yield non options.
470
+ #
471
+ # Returns nothing.
472
+ def process_item(items, index, &block)
473
+ return unless (item = items[index])
394
474
 
395
- # Add string separators between options.
396
- #
397
- # text - The String text to print.
398
- def separator(text)
399
- if @separators[options.size]
400
- @separators[options.size] << "\n#{text}"
401
- else
402
- @separators[options.size] = text
403
- end
404
- end
475
+ option, argument = extract_option(item) if item.start_with?('-')
405
476
 
406
- # Print a handy Slop help string.
407
- #
408
- # Returns the banner followed by available option help strings.
409
- def to_s
410
- heads = options.reject(&:tail?)
411
- tails = (options - heads)
412
- opts = (heads + tails).select(&:help).map(&:to_s)
413
- optstr = opts.each_with_index.map { |o, i|
414
- (str = @separators[i + 1]) ? [o, str].join("\n") : o
415
- }.join("\n")
416
-
417
- if @commands.any?
418
- optstr << "\n" if !optstr.empty?
419
- optstr << "\nAvailable commands:\n\n"
420
- optstr << commands_to_help
421
- optstr << "\n\nSee `<command> --help` for more information on a specific command."
422
- end
423
-
424
- banner = config[:banner]
425
- banner = "Usage: #{File.basename($0, '.*')}#{' [command]' if @commands.any?} [options]" if banner.nil?
426
- if banner
427
- "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
428
- else
429
- optstr
430
- end
431
- end
432
- alias help to_s
433
-
434
- private
435
-
436
- # Convenience method for present?(:option).
437
- #
438
- # Examples:
439
- #
440
- # opts.parse %( --verbose )
441
- # opts.verbose? #=> true
442
- # opts.other? #=> false
443
- #
444
- # Returns true if this option is present. If this method does not end
445
- # with a ? character it will instead call super().
446
- def method_missing(method, *args, &block)
447
- meth = method.to_s
448
- if meth.end_with?('?')
449
- meth.chop!
450
- present?(meth) || present?(meth.gsub('_', '-'))
451
- else
452
- super
453
- end
454
- end
477
+ if option
478
+ option.count += 1 unless item.start_with?('--no-')
479
+ option.count += 1 if option.key[0, 3] == "no-"
480
+ @trash << index
481
+ @triggered_options << option
455
482
 
456
- # Process a list item, figure out if it's an option, execute any
457
- # callbacks, assign any option arguments, and do some sanity checks.
458
- #
459
- # items - The Array of items to process.
460
- # index - The current Integer index of the item we want to process.
461
- # block - An optional block which when passed will yield non options.
462
- #
463
- # Returns nothing.
464
- def process_item(items, index, &block)
465
- return unless (item = items[index])
466
-
467
- option, argument = extract_option(item) if item.start_with?('-')
468
-
469
- if option
470
- option.count += 1 unless item.start_with?('--no-')
471
- option.count += 1 if option.key[0, 3] == "no-"
472
- @trash << index
473
- @triggered_options << option
474
-
475
- if option.expects_argument?
476
- argument ||= items.at(index + 1)
477
-
478
- if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
479
- raise MissingArgumentError, "#{option.key} expects an argument"
480
- end
483
+ if option.expects_argument?
484
+ argument ||= items.at(index + 1)
481
485
 
482
- execute_option(option, argument, index, item)
483
- elsif option.accepts_optional_argument?
484
- argument ||= items.at(index + 1)
486
+ if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
487
+ raise MissingArgumentError, "#{option.key} expects an argument"
488
+ end
485
489
 
486
- if argument && argument =~ /\A([^\-?]|-\d)+/
487
490
  execute_option(option, argument, index, item)
491
+ elsif option.accepts_optional_argument?
492
+ argument ||= items.at(index + 1)
493
+
494
+ if argument && argument =~ /\A([^\-?]|-\d)+/
495
+ execute_option(option, argument, index, item)
496
+ else
497
+ option.call(nil)
498
+ end
499
+ elsif config[:multiple_switches] && argument
500
+ execute_multiple_switches(option, argument, index)
488
501
  else
502
+ option.value = option.count > 0
489
503
  option.call(nil)
490
504
  end
491
- elsif config[:multiple_switches] && argument
492
- execute_multiple_switches(option, argument, index)
493
505
  else
494
- option.value = option.count > 0
495
- option.call(nil)
506
+ @unknown_options << item if strict? && item =~ /\A--?/
507
+ yield(item) if block && !@trash.include?(index)
496
508
  end
497
- else
498
- @unknown_options << item if strict? && item =~ /\A--?/
499
- block.call(item) if block && !@trash.include?(index)
500
509
  end
501
- end
502
510
 
503
- # Execute an option, firing off callbacks and assigning arguments.
504
- #
505
- # option - The Slop::Option object found by #process_item.
506
- # argument - The argument Object to assign to this option.
507
- # index - The current Integer index of the object we're processing.
508
- # item - The optional String item we're processing.
509
- #
510
- # Returns nothing.
511
- def execute_option(option, argument, index, item = nil)
512
- if !option
513
- if config[:multiple_switches] && strict?
514
- raise InvalidOptionError, "Unknown option -#{item}"
511
+ # Execute an option, firing off callbacks and assigning arguments.
512
+ #
513
+ # option - The Slop::Option object found by #process_item.
514
+ # argument - The argument Object to assign to this option.
515
+ # index - The current Integer index of the object we're processing.
516
+ # item - The optional String item we're processing.
517
+ #
518
+ # Returns nothing.
519
+ def execute_option(option, argument, index, item = nil)
520
+ unless option
521
+ if config[:multiple_switches] && strict?
522
+ raise InvalidOptionError, "Unknown option -#{item}"
523
+ end
524
+
525
+ return
515
526
  end
516
527
 
517
- return
518
- end
528
+ if argument
529
+ unless item && item.end_with?("=#{argument}")
530
+ @trash << index + 1 unless option.argument_in_value
531
+ end
532
+ option.value = argument
533
+ else
534
+ option.value = option.count > 0
535
+ end
519
536
 
520
- if argument
521
- unless item && item.end_with?("=#{argument}")
522
- @trash << index + 1 unless option.argument_in_value
537
+ if option.match? && !argument.match(option.config[:match])
538
+ raise InvalidArgumentError, "#{argument} is an invalid argument"
523
539
  end
524
- option.value = argument
525
- else
526
- option.value = option.count > 0
527
- end
528
540
 
529
- if option.match? && !argument.match(option.config[:match])
530
- raise InvalidArgumentError, "#{argument} is an invalid argument"
541
+ option.call(option.value)
531
542
  end
532
543
 
533
- option.call(option.value)
534
- end
535
-
536
- # Execute a `-abc` type option where a, b and c are all options. This
537
- # method is only executed if the multiple_switches argument is true.
538
- #
539
- # option - The first Option object.
540
- # argument - The argument to this option. (Split into multiple Options).
541
- # index - The index of the current item being processed.
542
- #
543
- # Returns nothing.
544
- def execute_multiple_switches(option, argument, index)
545
- execute_option(option, nil, index)
546
- argument.split('').each do |key|
547
- next unless (opt = fetch_option(key))
548
-
549
- opt.count += 1
550
- execute_option(opt, nil, index, key)
544
+ # Execute a `-abc` type option where a, b and c are all options. This
545
+ # method is only executed if the multiple_switches argument is true.
546
+ #
547
+ # option - The first Option object.
548
+ # argument - The argument to this option. (Split into multiple Options).
549
+ # index - The index of the current item being processed.
550
+ #
551
+ # Returns nothing.
552
+ def execute_multiple_switches(option, argument, index)
553
+ execute_option(option, nil, index)
554
+ argument.split('').each do |key|
555
+ next unless (opt = fetch_option(key))
556
+
557
+ opt.count += 1
558
+ execute_option(opt, nil, index, key)
559
+ end
551
560
  end
552
- end
553
561
 
554
- # Extract an option from a flag.
555
- #
556
- # flag - The flag key used to extract an option.
557
- #
558
- # Returns an Array of [option, argument].
559
- def extract_option(flag)
560
- option = fetch_option(flag)
561
- option ||= fetch_option(flag.downcase) if config[:ignore_case]
562
- option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
563
-
564
- unless option
565
- case flag
566
- when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
567
- option, argument = fetch_option($1), ($2 || false)
568
- option.argument_in_value = true if option
562
+ # Extract an option from a flag.
563
+ #
564
+ # flag - The flag key used to extract an option.
565
+ #
566
+ # Returns an Array of [option, argument].
567
+ def extract_option(flag)
568
+ option = fetch_option(flag)
569
+ option ||= fetch_option(flag.downcase) if config[:ignore_case]
570
+ option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
571
+
572
+ unless option
573
+ case flag
574
+ when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
575
+ option = fetch_option(Regexp.last_match(1))
576
+ argument = Regexp.last_match(2) || false
577
+ option.argument_in_value = true if option
578
+ end
569
579
  end
580
+
581
+ [option, argument]
570
582
  end
571
583
 
572
- [option, argument]
573
- end
584
+ # Autocreate an option on the fly. See the :autocreate Slop config option.
585
+ #
586
+ # items - The Array of items we're parsing.
587
+ # index - The current Integer index for the item we're processing.
588
+ #
589
+ # Returns nothing.
590
+ def autocreate(items, index)
591
+ flag = items[index]
592
+ return if fetch_option(flag) || @trash.include?(index)
574
593
 
575
- # Autocreate an option on the fly. See the :autocreate Slop config option.
576
- #
577
- # items - The Array of items we're parsing.
578
- # index - The current Integer index for the item we're processing.
579
- #
580
- # Returns nothing.
581
- def autocreate(items, index)
582
- flag = items[index]
583
- if !fetch_option(flag) && !@trash.include?(index)
584
594
  option = build_option(Array(flag))
585
595
  argument = items[index + 1]
586
596
  option.config[:argument] = (argument && argument !~ /\A--?/)
587
597
  option.config[:autocreated] = true
588
598
  options << option
589
599
  end
590
- end
591
-
592
- # Build an option from a list of objects.
593
- #
594
- # objects - An Array of objects used to build this option.
595
- #
596
- # Returns a new instance of Slop::Option.
597
- def build_option(objects, &block)
598
- config = {}
599
- config[:argument] = true if @config[:arguments]
600
- config[:optional_argument] = true if @config[:optional_arguments]
601
-
602
- if objects.last.is_a?(Hash)
603
- config.merge!(objects.last)
604
- objects.pop
605
- end
606
- short = extract_short_flag(objects, config)
607
- long = extract_long_flag(objects, config)
608
- desc = objects[0].respond_to?(:to_str) ? objects.shift : nil
609
-
610
- Option.new(self, short, long, desc, config, &block)
611
- end
612
600
 
613
- # Extract the short flag from an item.
614
- #
615
- # objects - The Array of objects passed from #build_option.
616
- # config - The Hash of configuration options built in #build_option.
617
- def extract_short_flag(objects, config)
618
- flag = clean(objects.first)
601
+ # Build an option from a list of objects.
602
+ #
603
+ # objects - An Array of objects used to build this option.
604
+ #
605
+ # Returns a new instance of Slop::Option.
606
+ def build_option(objects, &block)
607
+ config = {}
608
+ config[:argument] = true if @config[:arguments]
609
+ config[:optional_argument] = true if @config[:optional_arguments]
610
+
611
+ if objects.last.is_a?(Hash)
612
+ config.merge!(objects.last)
613
+ objects.pop
614
+ end
615
+ short = extract_short_flag(objects, config)
616
+ long = extract_long_flag(objects, config)
617
+ desc = objects[0].respond_to?(:to_str) ? objects.shift : nil
619
618
 
620
- if flag.size == 2 && flag.end_with?('=')
621
- config[:argument] ||= true
622
- flag.chop!
619
+ Option.new(self, short, long, desc, config, &block)
623
620
  end
624
621
 
625
- if flag.size == 1
622
+ # Extract the short flag from an item.
623
+ #
624
+ # objects - The Array of objects passed from #build_option.
625
+ # config - The Hash of configuration options built in #build_option.
626
+ def extract_short_flag(objects, config)
627
+ flag = clean(objects.first)
628
+
629
+ if flag.size == 2 && flag.end_with?('=')
630
+ config[:argument] ||= true
631
+ flag.chop!
632
+ end
633
+
634
+ return unless flag.size == 1
635
+
626
636
  objects.shift
627
637
  flag
628
638
  end
629
- end
630
639
 
631
- # Extract the long flag from an item.
632
- #
633
- # objects - The Array of objects passed from #build_option.
634
- # config - The Hash of configuration options built in #build_option.
635
- def extract_long_flag(objects, config)
636
- flag = objects.first.to_s
637
- if flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/
640
+ # Extract the long flag from an item.
641
+ #
642
+ # objects - The Array of objects passed from #build_option.
643
+ # config - The Hash of configuration options built in #build_option.
644
+ def extract_long_flag(objects, config)
645
+ flag = objects.first.to_s
646
+ return unless flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/
647
+
638
648
  config[:argument] ||= true if flag.end_with?('=')
639
649
  config[:optional_argument] = true if flag.end_with?('=?')
640
650
  objects.shift
641
651
  clean(flag).sub(/\=\??\z/, '')
642
652
  end
643
- end
644
653
 
645
- # Remove any leading -- characters from a string.
646
- #
647
- # object - The Object we want to cast to a String and clean.
648
- #
649
- # Returns the newly cleaned String with leading -- characters removed.
650
- def clean(object)
651
- object.to_s.sub(/\A--?/, '')
652
- end
654
+ # Remove any leading -- characters from a string.
655
+ #
656
+ # object - The Object we want to cast to a String and clean.
657
+ #
658
+ # Returns the newly cleaned String with leading -- characters removed.
659
+ def clean(object)
660
+ object.to_s.sub(/\A--?/, '')
661
+ end
653
662
 
654
- def commands_to_help
655
- padding = 0
656
- @commands.each { |c, _| padding = c.size if c.size > padding }
657
- @commands.map do |cmd, opts|
658
- " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
659
- end.join("\n")
663
+ def commands_to_help
664
+ padding = 0
665
+ @commands.each { |c, _| padding = c.size if c.size > padding }
666
+ @commands.map do |cmd, opts|
667
+ " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
668
+ end.join("\n")
669
+ end
660
670
  end
671
+ # rubocop:enable Metrics/ClassLength
661
672
  end