pry 0.12.2-java → 0.13.0-java
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +110 -1
- data/LICENSE +1 -1
- data/README.md +331 -269
- data/bin/pry +5 -0
- data/lib/pry.rb +133 -119
- data/lib/pry/basic_object.rb +8 -4
- data/lib/pry/block_command.rb +22 -0
- data/lib/pry/class_command.rb +194 -0
- data/lib/pry/cli.rb +40 -31
- data/lib/pry/code.rb +39 -27
- data/lib/pry/code/code_file.rb +28 -24
- data/lib/pry/code/code_range.rb +4 -2
- data/lib/pry/code/loc.rb +15 -8
- data/lib/pry/code_object.rb +40 -38
- data/lib/pry/color_printer.rb +47 -46
- data/lib/pry/command.rb +166 -369
- data/lib/pry/command_set.rb +76 -73
- data/lib/pry/command_state.rb +31 -0
- data/lib/pry/commands/amend_line.rb +86 -81
- data/lib/pry/commands/bang.rb +18 -14
- data/lib/pry/commands/bang_pry.rb +15 -11
- data/lib/pry/commands/cat.rb +61 -54
- data/lib/pry/commands/cat/abstract_formatter.rb +23 -18
- data/lib/pry/commands/cat/exception_formatter.rb +71 -60
- data/lib/pry/commands/cat/file_formatter.rb +55 -49
- data/lib/pry/commands/cat/input_expression_formatter.rb +35 -30
- data/lib/pry/commands/cd.rb +40 -35
- data/lib/pry/commands/change_inspector.rb +29 -22
- data/lib/pry/commands/change_prompt.rb +44 -39
- data/lib/pry/commands/clear_screen.rb +16 -10
- data/lib/pry/commands/code_collector.rb +148 -133
- data/lib/pry/commands/disable_pry.rb +23 -19
- data/lib/pry/commands/easter_eggs.rb +19 -30
- data/lib/pry/commands/edit.rb +184 -161
- data/lib/pry/commands/edit/exception_patcher.rb +21 -17
- data/lib/pry/commands/edit/file_and_line_locator.rb +34 -23
- data/lib/pry/commands/exit.rb +39 -35
- data/lib/pry/commands/exit_all.rb +24 -20
- data/lib/pry/commands/exit_program.rb +20 -16
- data/lib/pry/commands/find_method.rb +168 -160
- data/lib/pry/commands/fix_indent.rb +16 -12
- data/lib/pry/commands/help.rb +140 -133
- data/lib/pry/commands/hist.rb +151 -150
- data/lib/pry/commands/import_set.rb +20 -16
- data/lib/pry/commands/jump_to.rb +25 -21
- data/lib/pry/commands/list_inspectors.rb +35 -28
- data/lib/pry/commands/ls.rb +124 -102
- data/lib/pry/commands/ls/constants.rb +59 -42
- data/lib/pry/commands/ls/formatter.rb +50 -46
- data/lib/pry/commands/ls/globals.rb +38 -34
- data/lib/pry/commands/ls/grep.rb +17 -13
- data/lib/pry/commands/ls/instance_vars.rb +29 -27
- data/lib/pry/commands/ls/interrogatable.rb +18 -12
- data/lib/pry/commands/ls/jruby_hacks.rb +47 -41
- data/lib/pry/commands/ls/local_names.rb +26 -22
- data/lib/pry/commands/ls/local_vars.rb +38 -28
- data/lib/pry/commands/ls/ls_entity.rb +47 -51
- data/lib/pry/commands/ls/methods.rb +44 -43
- data/lib/pry/commands/ls/methods_helper.rb +46 -42
- data/lib/pry/commands/ls/self_methods.rb +23 -22
- data/lib/pry/commands/nesting.rb +21 -17
- data/lib/pry/commands/play.rb +93 -82
- data/lib/pry/commands/pry_backtrace.rb +24 -17
- data/lib/pry/commands/pry_version.rb +15 -11
- data/lib/pry/commands/raise_up.rb +27 -22
- data/lib/pry/commands/reload_code.rb +60 -48
- data/lib/pry/commands/reset.rb +16 -12
- data/lib/pry/commands/ri.rb +55 -45
- data/lib/pry/commands/save_file.rb +45 -43
- data/lib/pry/commands/shell_command.rb +51 -51
- data/lib/pry/commands/shell_mode.rb +21 -17
- data/lib/pry/commands/show_doc.rb +81 -68
- data/lib/pry/commands/show_info.rb +189 -171
- data/lib/pry/commands/show_input.rb +16 -11
- data/lib/pry/commands/show_source.rb +109 -45
- data/lib/pry/commands/stat.rb +35 -31
- data/lib/pry/commands/switch_to.rb +21 -15
- data/lib/pry/commands/toggle_color.rb +20 -16
- data/lib/pry/commands/watch_expression.rb +89 -86
- data/lib/pry/commands/watch_expression/expression.rb +32 -27
- data/lib/pry/commands/whereami.rb +156 -148
- data/lib/pry/commands/wtf.rb +75 -50
- data/lib/pry/config.rb +311 -25
- data/lib/pry/config/attributable.rb +22 -0
- data/lib/pry/config/lazy_value.rb +29 -0
- data/lib/pry/config/memoized_value.rb +34 -0
- data/lib/pry/config/value.rb +24 -0
- data/lib/pry/control_d_handler.rb +28 -0
- data/lib/pry/core_extensions.rb +9 -7
- data/lib/pry/editor.rb +48 -21
- data/lib/pry/env.rb +18 -0
- data/lib/pry/exception_handler.rb +43 -0
- data/lib/pry/exceptions.rb +13 -16
- data/lib/pry/forwardable.rb +5 -1
- data/lib/pry/helpers.rb +2 -0
- data/lib/pry/helpers/base_helpers.rb +68 -197
- data/lib/pry/helpers/command_helpers.rb +50 -61
- data/lib/pry/helpers/documentation_helpers.rb +20 -13
- data/lib/pry/helpers/options_helpers.rb +14 -7
- data/lib/pry/helpers/platform.rb +7 -5
- data/lib/pry/helpers/table.rb +33 -26
- data/lib/pry/helpers/text.rb +17 -14
- data/lib/pry/history.rb +48 -56
- data/lib/pry/hooks.rb +21 -12
- data/lib/pry/indent.rb +54 -50
- data/lib/pry/input_completer.rb +248 -230
- data/lib/pry/input_lock.rb +8 -9
- data/lib/pry/inspector.rb +36 -24
- data/lib/pry/last_exception.rb +45 -45
- data/lib/pry/method.rb +141 -94
- data/lib/pry/method/disowned.rb +16 -4
- data/lib/pry/method/patcher.rb +12 -3
- data/lib/pry/method/weird_method_locator.rb +68 -44
- data/lib/pry/object_path.rb +33 -25
- data/lib/pry/output.rb +121 -35
- data/lib/pry/pager.rb +41 -42
- data/lib/pry/plugins.rb +25 -8
- data/lib/pry/prompt.rb +123 -54
- data/lib/pry/pry_class.rb +61 -98
- data/lib/pry/pry_instance.rb +217 -215
- data/lib/pry/repl.rb +18 -22
- data/lib/pry/repl_file_loader.rb +27 -21
- data/lib/pry/ring.rb +11 -6
- data/lib/pry/slop.rb +574 -563
- data/lib/pry/slop/commands.rb +164 -169
- data/lib/pry/slop/option.rb +172 -168
- data/lib/pry/syntax_highlighter.rb +26 -0
- data/lib/pry/system_command_handler.rb +17 -0
- data/lib/pry/testable.rb +59 -61
- data/lib/pry/testable/evalable.rb +21 -12
- data/lib/pry/testable/mockable.rb +18 -10
- data/lib/pry/testable/pry_tester.rb +71 -56
- data/lib/pry/testable/utility.rb +29 -21
- data/lib/pry/testable/variables.rb +49 -43
- data/lib/pry/version.rb +3 -1
- data/lib/pry/warning.rb +27 -0
- data/lib/pry/wrapped_module.rb +51 -42
- data/lib/pry/wrapped_module/candidate.rb +21 -14
- metadata +31 -30
- data/lib/pry/commands.rb +0 -6
- data/lib/pry/commands/disabled_commands.rb +0 -2
- data/lib/pry/commands/gem_cd.rb +0 -26
- data/lib/pry/commands/gem_install.rb +0 -32
- data/lib/pry/commands/gem_list.rb +0 -33
- data/lib/pry/commands/gem_open.rb +0 -29
- data/lib/pry/commands/gem_readme.rb +0 -25
- data/lib/pry/commands/gem_search.rb +0 -40
- data/lib/pry/commands/gem_stats.rb +0 -83
- data/lib/pry/commands/gist.rb +0 -102
- data/lib/pry/commands/install_command.rb +0 -54
- data/lib/pry/config/behavior.rb +0 -255
- data/lib/pry/config/convenience.rb +0 -28
- data/lib/pry/config/default.rb +0 -159
- data/lib/pry/config/memoization.rb +0 -48
- data/lib/pry/platform.rb +0 -91
- data/lib/pry/rubygem.rb +0 -84
- 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
|
-
|
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
|
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? &&
|
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
|
-
|
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
|
-
|
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
|
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.
|
data/lib/pry/repl_file_loader.rb
CHANGED
@@ -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
|
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]
|
24
|
-
def interactive_mode(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
Pry::REPL.new(
|
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]
|
35
|
-
def non_interactive_mode(
|
36
|
-
|
37
|
-
|
38
|
-
|
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(
|
42
|
+
interactive_mode(pry_instance)
|
41
43
|
end
|
42
44
|
|
43
45
|
content.lines.each do |line|
|
44
|
-
break unless
|
46
|
+
break unless pry_instance.eval line, generated: true
|
45
47
|
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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(
|
63
|
+
s.interactive_mode(pry_instance)
|
60
64
|
end
|
61
65
|
|
62
|
-
Pry::Commands.command
|
63
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
#
|
50
|
-
|
51
|
-
|
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
|
-
#
|
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
|
59
|
-
def
|
60
|
-
config
|
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
|
-
#
|
156
|
+
# Set the banner.
|
67
157
|
#
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
#
|
74
|
-
# config - A Hash of configuration options to pass to Slop.new
|
165
|
+
# banner - The String to set the banner.
|
75
166
|
#
|
76
|
-
#
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
114
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
132
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
150
|
-
|
151
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
174
|
-
|
175
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
219
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
282
|
+
alias get []
|
228
283
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
236
|
-
|
296
|
+
# Enumerable interface. Yields each Slop::Option.
|
297
|
+
def each(&block)
|
298
|
+
options.each(&block)
|
237
299
|
end
|
238
300
|
|
239
|
-
|
240
|
-
|
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
|
-
|
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
|
-
|
246
|
-
|
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
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
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
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
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
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
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
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
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
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
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
|
-
|
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
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
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
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
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
|
-
|
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
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
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
|
-
|
457
|
-
|
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
|
-
|
483
|
-
|
484
|
-
|
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
|
-
|
495
|
-
|
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
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
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
|
-
|
518
|
-
|
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
|
-
|
521
|
-
|
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
|
-
|
530
|
-
raise InvalidArgumentError, "#{argument} is an invalid argument"
|
541
|
+
option.call(option.value)
|
531
542
|
end
|
532
543
|
|
533
|
-
option.
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
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
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
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
|
-
|
573
|
-
|
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
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
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
|
-
|
621
|
-
config[:argument] ||= true
|
622
|
-
flag.chop!
|
619
|
+
Option.new(self, short, long, desc, config, &block)
|
623
620
|
end
|
624
621
|
|
625
|
-
|
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
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
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
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
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
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
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
|