pry 0.10.3 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +439 -16
  3. data/LICENSE +1 -1
  4. data/README.md +362 -302
  5. data/bin/pry +4 -7
  6. data/lib/pry/basic_object.rb +10 -0
  7. data/lib/pry/block_command.rb +22 -0
  8. data/lib/pry/class_command.rb +194 -0
  9. data/lib/pry/cli.rb +84 -97
  10. data/lib/pry/code/code_file.rb +37 -26
  11. data/lib/pry/code/code_range.rb +7 -5
  12. data/lib/pry/code/loc.rb +26 -13
  13. data/lib/pry/code.rb +42 -31
  14. data/lib/pry/code_object.rb +53 -28
  15. data/lib/pry/color_printer.rb +46 -35
  16. data/lib/pry/command.rb +197 -369
  17. data/lib/pry/command_set.rb +89 -114
  18. data/lib/pry/command_state.rb +31 -0
  19. data/lib/pry/commands/amend_line.rb +86 -82
  20. data/lib/pry/commands/bang.rb +18 -14
  21. data/lib/pry/commands/bang_pry.rb +15 -11
  22. data/lib/pry/commands/cat/abstract_formatter.rb +23 -18
  23. data/lib/pry/commands/cat/exception_formatter.rb +85 -72
  24. data/lib/pry/commands/cat/file_formatter.rb +56 -46
  25. data/lib/pry/commands/cat/input_expression_formatter.rb +35 -30
  26. data/lib/pry/commands/cat.rb +62 -54
  27. data/lib/pry/commands/cd.rb +40 -35
  28. data/lib/pry/commands/change_inspector.rb +29 -22
  29. data/lib/pry/commands/change_prompt.rb +48 -23
  30. data/lib/pry/commands/clear_screen.rb +20 -0
  31. data/lib/pry/commands/code_collector.rb +148 -131
  32. data/lib/pry/commands/disable_pry.rb +23 -19
  33. data/lib/pry/commands/easter_eggs.rb +23 -34
  34. data/lib/pry/commands/edit/exception_patcher.rb +21 -17
  35. data/lib/pry/commands/edit/file_and_line_locator.rb +34 -23
  36. data/lib/pry/commands/edit.rb +185 -157
  37. data/lib/pry/commands/exit.rb +40 -35
  38. data/lib/pry/commands/exit_all.rb +24 -20
  39. data/lib/pry/commands/exit_program.rb +20 -16
  40. data/lib/pry/commands/find_method.rb +168 -162
  41. data/lib/pry/commands/fix_indent.rb +16 -12
  42. data/lib/pry/commands/help.rb +140 -133
  43. data/lib/pry/commands/hist.rb +151 -149
  44. data/lib/pry/commands/import_set.rb +20 -15
  45. data/lib/pry/commands/jump_to.rb +25 -21
  46. data/lib/pry/commands/list_inspectors.rb +35 -28
  47. data/lib/pry/commands/ls/constants.rb +59 -31
  48. data/lib/pry/commands/ls/formatter.rb +42 -36
  49. data/lib/pry/commands/ls/globals.rb +38 -36
  50. data/lib/pry/commands/ls/grep.rb +17 -15
  51. data/lib/pry/commands/ls/instance_vars.rb +29 -28
  52. data/lib/pry/commands/ls/interrogatable.rb +18 -12
  53. data/lib/pry/commands/ls/jruby_hacks.rb +47 -41
  54. data/lib/pry/commands/ls/local_names.rb +26 -24
  55. data/lib/pry/commands/ls/local_vars.rb +38 -30
  56. data/lib/pry/commands/ls/ls_entity.rb +47 -52
  57. data/lib/pry/commands/ls/methods.rb +49 -51
  58. data/lib/pry/commands/ls/methods_helper.rb +46 -42
  59. data/lib/pry/commands/ls/self_methods.rb +23 -21
  60. data/lib/pry/commands/ls.rb +124 -103
  61. data/lib/pry/commands/nesting.rb +21 -17
  62. data/lib/pry/commands/play.rb +92 -82
  63. data/lib/pry/commands/pry_backtrace.rb +22 -17
  64. data/lib/pry/commands/pry_version.rb +15 -11
  65. data/lib/pry/commands/raise_up.rb +33 -27
  66. data/lib/pry/commands/reload_code.rb +60 -48
  67. data/lib/pry/commands/reset.rb +16 -12
  68. data/lib/pry/commands/ri.rb +57 -42
  69. data/lib/pry/commands/save_file.rb +45 -43
  70. data/lib/pry/commands/shell_command.rb +56 -29
  71. data/lib/pry/commands/shell_mode.rb +22 -18
  72. data/lib/pry/commands/show_doc.rb +80 -70
  73. data/lib/pry/commands/show_info.rb +194 -155
  74. data/lib/pry/commands/show_input.rb +16 -11
  75. data/lib/pry/commands/show_source.rb +110 -42
  76. data/lib/pry/commands/stat.rb +35 -31
  77. data/lib/pry/commands/switch_to.rb +21 -15
  78. data/lib/pry/commands/toggle_color.rb +20 -16
  79. data/lib/pry/commands/watch_expression/expression.rb +32 -27
  80. data/lib/pry/commands/watch_expression.rb +89 -84
  81. data/lib/pry/commands/whereami.rb +156 -141
  82. data/lib/pry/commands/wtf.rb +78 -40
  83. data/lib/pry/config/attributable.rb +22 -0
  84. data/lib/pry/config/lazy_value.rb +29 -0
  85. data/lib/pry/config/memoized_value.rb +34 -0
  86. data/lib/pry/config/value.rb +24 -0
  87. data/lib/pry/config.rb +310 -20
  88. data/lib/pry/control_d_handler.rb +28 -0
  89. data/lib/pry/core_extensions.rb +22 -9
  90. data/lib/pry/editor.rb +56 -34
  91. data/lib/pry/env.rb +18 -0
  92. data/lib/pry/exception_handler.rb +43 -0
  93. data/lib/pry/exceptions.rb +13 -18
  94. data/lib/pry/forwardable.rb +27 -0
  95. data/lib/pry/helpers/base_helpers.rb +20 -62
  96. data/lib/pry/helpers/command_helpers.rb +52 -62
  97. data/lib/pry/helpers/documentation_helpers.rb +21 -12
  98. data/lib/pry/helpers/options_helpers.rb +15 -8
  99. data/lib/pry/helpers/platform.rb +55 -0
  100. data/lib/pry/helpers/table.rb +44 -32
  101. data/lib/pry/helpers/text.rb +96 -85
  102. data/lib/pry/helpers.rb +3 -0
  103. data/lib/pry/history.rb +81 -55
  104. data/lib/pry/hooks.rb +60 -110
  105. data/lib/pry/indent.rb +74 -68
  106. data/lib/pry/input_completer.rb +199 -158
  107. data/lib/pry/input_lock.rb +7 -10
  108. data/lib/pry/inspector.rb +36 -24
  109. data/lib/pry/last_exception.rb +45 -45
  110. data/lib/pry/method/disowned.rb +19 -5
  111. data/lib/pry/method/patcher.rb +14 -8
  112. data/lib/pry/method/weird_method_locator.rb +79 -45
  113. data/lib/pry/method.rb +178 -124
  114. data/lib/pry/object_path.rb +37 -28
  115. data/lib/pry/output.rb +102 -16
  116. data/lib/pry/pager.rb +187 -174
  117. data/lib/pry/prompt.rb +213 -25
  118. data/lib/pry/pry_class.rb +119 -98
  119. data/lib/pry/pry_instance.rb +261 -224
  120. data/lib/pry/repl.rb +83 -29
  121. data/lib/pry/repl_file_loader.rb +27 -22
  122. data/lib/pry/ring.rb +89 -0
  123. data/lib/pry/slop/LICENSE +20 -0
  124. data/lib/pry/slop/commands.rb +190 -0
  125. data/lib/pry/slop/option.rb +210 -0
  126. data/lib/pry/slop.rb +672 -0
  127. data/lib/pry/syntax_highlighter.rb +26 -0
  128. data/lib/pry/system_command_handler.rb +17 -0
  129. data/lib/pry/testable/evalable.rb +24 -0
  130. data/lib/pry/testable/mockable.rb +22 -0
  131. data/lib/pry/testable/pry_tester.rb +88 -0
  132. data/lib/pry/testable/utility.rb +34 -0
  133. data/lib/pry/testable/variables.rb +52 -0
  134. data/lib/pry/testable.rb +68 -0
  135. data/lib/pry/version.rb +3 -1
  136. data/lib/pry/warning.rb +20 -0
  137. data/lib/pry/{module_candidate.rb → wrapped_module/candidate.rb} +35 -32
  138. data/lib/pry/wrapped_module.rb +68 -63
  139. data/lib/pry.rb +133 -149
  140. metadata +58 -69
  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/gist.rb +0 -101
  147. data/lib/pry/commands/install_command.rb +0 -53
  148. data/lib/pry/commands/list_prompts.rb +0 -35
  149. data/lib/pry/commands/simple_prompt.rb +0 -22
  150. data/lib/pry/commands.rb +0 -6
  151. data/lib/pry/config/behavior.rb +0 -139
  152. data/lib/pry/config/convenience.rb +0 -25
  153. data/lib/pry/config/default.rb +0 -161
  154. data/lib/pry/history_array.rb +0 -121
  155. data/lib/pry/plugins.rb +0 -103
  156. data/lib/pry/rbx_path.rb +0 -22
  157. data/lib/pry/rubygem.rb +0 -82
  158. data/lib/pry/terminal.rb +0 -79
  159. data/lib/pry/test/helper.rb +0 -170
data/lib/pry/repl.rb CHANGED
@@ -1,8 +1,8 @@
1
- require 'forwardable'
1
+ # frozen_string_literal: true
2
2
 
3
3
  class Pry
4
4
  class REPL
5
- extend Forwardable
5
+ extend Pry::Forwardable
6
6
  def_delegators :@pry, :input, :output
7
7
 
8
8
  # @return [Pry] The instance of {Pry} that the user is controlling.
@@ -21,11 +21,11 @@ class Pry
21
21
  # @option options [Object] :target The initial target of the session.
22
22
  def initialize(pry, options = {})
23
23
  @pry = pry
24
- @indent = Pry::Indent.new
24
+ @indent = Pry::Indent.new(pry)
25
25
 
26
- if options[:target]
27
- @pry.push_binding options[:target]
28
- end
26
+ @readline_output = nil
27
+
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 Pry::Helpers::BaseHelpers.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,16 +98,19 @@ 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
- current_prompt, indented_val,
110
- original_val.length - indented_val.length
111
+ current_prompt,
112
+ indented_val,
113
+ calculate_overhang(current_prompt, original_val, indented_val)
111
114
  )
112
115
  output.flush
113
116
  end
@@ -129,7 +132,7 @@ class Pry
129
132
  yield
130
133
  rescue EOFError
131
134
  pry.config.input = Pry.config.input
132
- if !should_retry
135
+ unless should_retry
133
136
  output.puts "Error: Pry ran out of things to read from! " \
134
137
  "Attempting to break out of REPL."
135
138
  return :no_more_input
@@ -138,8 +141,7 @@ class Pry
138
141
  retry
139
142
 
140
143
  # Handle <Ctrl+C> like Bash: empty the current input buffer, but don't
141
- # quit. This is only for MRI 1.9; other versions of Ruby don't let you
142
- # send Interrupt from within Readline.
144
+ # quit.
143
145
  rescue Interrupt
144
146
  return :control_c
145
147
 
@@ -150,9 +152,7 @@ class Pry
150
152
  puts "Error: #{e.message}"
151
153
  output.puts e.backtrace
152
154
  exception_count += 1
153
- if exception_count < 5
154
- retry
155
- end
155
+ retry if exception_count < 5
156
156
  puts "FATAL: Pry failed to get user input using `#{input}`."
157
157
  puts "To fix this you may be able to pass input and output file " \
158
158
  "descriptors to pry directly. e.g."
@@ -168,27 +168,26 @@ class Pry
168
168
  # @return [String?] The next line of input, or `nil` on <Ctrl-D>.
169
169
  def read_line(current_prompt)
170
170
  handle_read_errors do
171
- if defined? Coolline and input.is_a? Coolline
171
+ if coolline_available?
172
172
  input.completion_proc = proc do |cool|
173
173
  completions = @pry.complete cool.completed_word
174
174
  completions.compact
175
175
  end
176
176
  elsif input.respond_to? :completion_proc=
177
- input.completion_proc = proc do |input|
178
- @pry.complete input
177
+ input.completion_proc = proc do |inp|
178
+ @pry.complete inp
179
179
  end
180
180
  end
181
181
 
182
- if defined?(Readline) and input == Readline
182
+ if readline_available?
183
+ set_readline_output
183
184
  input_readline(current_prompt, false) # false since we'll add it manually
184
- elsif defined? Coolline and input.is_a? Coolline
185
+ elsif coolline_available?
186
+ input_readline(current_prompt)
187
+ elsif input.method(:readline).arity == 1
185
188
  input_readline(current_prompt)
186
189
  else
187
- if input.method(:readline).arity == 1
188
- input_readline(current_prompt)
189
- else
190
- input_readline
191
- end
190
+ input_readline
192
191
  end
193
192
  end
194
193
  end
@@ -198,5 +197,60 @@ class Pry
198
197
  input.readline(*args)
199
198
  end
200
199
  end
200
+
201
+ def readline_available?
202
+ defined?(Readline) && input == Readline
203
+ end
204
+
205
+ def coolline_available?
206
+ defined?(Coolline) && input.is_a?(Coolline)
207
+ end
208
+
209
+ # If `$stdout` is not a tty, it's probably a pipe.
210
+ # @example
211
+ # # `piping?` returns `false`
212
+ # % pry
213
+ # [1] pry(main)
214
+ #
215
+ # # `piping?` returns `true`
216
+ # % pry | tee log
217
+ def piping?
218
+ return false unless $stdout.respond_to?(:tty?)
219
+
220
+ !$stdout.tty? && $stdin.tty? && !Helpers::Platform.windows?
221
+ end
222
+
223
+ # @return [void]
224
+ def set_readline_output
225
+ return if @readline_output
226
+
227
+ @readline_output = (Readline.output = Pry.config.output) if piping?
228
+ end
229
+
230
+ # Calculates correct overhang for current line. Supports vi Readline
231
+ # mode and its indicators such as "(ins)" or "(cmd)".
232
+ #
233
+ # @return [Integer]
234
+ # @note This doesn't calculate overhang for Readline's emacs mode with an
235
+ # indicator because emacs is the default mode and it doesn't use
236
+ # indicators in 99% of cases.
237
+ def calculate_overhang(current_prompt, original_val, indented_val)
238
+ overhang = original_val.length - indented_val.length
239
+
240
+ if readline_available? && Readline.respond_to?(:vi_editing_mode?)
241
+ begin
242
+ # rb-readline doesn't support this method:
243
+ # https://github.com/ConnorAtherton/rb-readline/issues/152
244
+ if Readline.vi_editing_mode?
245
+ overhang = output.width - current_prompt.size - indented_val.size
246
+ end
247
+ rescue NotImplementedError
248
+ # VI editing mode is unsupported on JRuby.
249
+ # https://github.com/pry/pry/issues/1840
250
+ nil
251
+ end
252
+ end
253
+ [0, overhang].max
254
+ end
201
255
  end
202
256
  end
@@ -1,5 +1,6 @@
1
- class Pry
1
+ # frozen_string_literal: true
2
2
 
3
+ class Pry
3
4
  # A class to manage the loading of files through the REPL loop.
4
5
  # This is an interesting trick as it processes your file as if it
5
6
  # was user input in an interactive session. As a result, all Pry
@@ -13,7 +14,7 @@ class Pry
13
14
  class REPLFileLoader
14
15
  def initialize(file_name)
15
16
  full_name = File.expand_path(file_name)
16
- raise RuntimeError, "No such file: #{full_name}" if !File.exists?(full_name)
17
+ raise "No such file: #{full_name}" unless File.exist?(full_name)
17
18
 
18
19
  define_additional_commands
19
20
  @content = File.read(full_name)
@@ -21,34 +22,36 @@ class Pry
21
22
 
22
23
  # Switch to interactive mode, i.e take input from the user
23
24
  # and use the regular print and exception handlers.
24
- # @param [Pry] _pry_ the Pry instance to make interactive.
25
- def interactive_mode(_pry_)
26
- _pry_.config.input = Pry.config.input
27
- _pry_.config.print = Pry.config.print
28
- _pry_.config.exception_handler = Pry.config.exception_handler
29
- 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
30
31
  end
31
32
 
32
33
  # Switch to non-interactive mode. Essentially
33
34
  # this means there is no result output
34
35
  # and that the session becomes interactive when an exception is encountered.
35
- # @param [Pry] _pry_ the Pry instance to make non-interactive.
36
- def non_interactive_mode(_pry_, content)
37
- _pry_.print = proc {}
38
- _pry_.exception_handler = proc do |o, e, _p_|
39
- _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"
40
41
  o.puts "...exception encountered, going interactive!"
41
- interactive_mode(_pry_)
42
+ interactive_mode(pry_instance)
42
43
  end
43
44
 
44
45
  content.lines.each do |line|
45
- break unless _pry_.eval line, :generated => true
46
+ break unless pry_instance.eval line, generated: true
46
47
  end
47
48
 
48
- unless _pry_.eval_string.empty?
49
- _pry_.output.puts "#{_pry_.eval_string}...exception encountered, going interactive!"
50
- interactive_mode(_pry_)
51
- 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)
52
55
  end
53
56
 
54
57
  # Define a few extra commands useful for flipping back & forth
@@ -57,11 +60,13 @@ class Pry
57
60
  s = self
58
61
 
59
62
  Pry::Commands.command "make-interactive", "Make the session interactive" do
60
- s.interactive_mode(_pry_)
63
+ s.interactive_mode(pry_instance)
61
64
  end
62
65
 
63
- Pry::Commands.command "load-file", "Load another file through the repl" do |file_name|
64
- 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)))
65
70
  end
66
71
  end
67
72
 
data/lib/pry/ring.rb ADDED
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pry
4
+ # A ring is a thread-safe fixed-capacity array to which you can only add
5
+ # elements. Older entries are overwritten as you add new elements, so that the
6
+ # ring can never contain more than `max_size` elemens.
7
+ #
8
+ # @example
9
+ # ring = Pry::Ring.new(3)
10
+ # ring << 1 << 2 << 3
11
+ # ring.to_a #=> [1, 2, 3]
12
+ # ring << 4
13
+ # ring.to_a #=> [2, 3, 4]
14
+ #
15
+ # ring[0] #=> 2
16
+ # ring[-1] #=> 4
17
+ # ring.clear
18
+ # ring[0] #=> nil
19
+ #
20
+ # @api public
21
+ # @since v0.12.0
22
+ class Ring
23
+ # @return [Integer] maximum buffer size
24
+ attr_reader :max_size
25
+
26
+ # @return [Integer] how many objects were added during the lifetime of the
27
+ # ring
28
+ attr_reader :count
29
+ alias size count
30
+
31
+ # @param [Integer] max_size Maximum buffer size. The buffer will start
32
+ # overwriting elements once its reaches its maximum capacity
33
+ def initialize(max_size)
34
+ @max_size = max_size
35
+ @mutex = Mutex.new
36
+ clear
37
+ end
38
+
39
+ # Push `value` to the current index.
40
+ #
41
+ # @param [Object] value
42
+ # @return [self]
43
+ def <<(value)
44
+ @mutex.synchronize do
45
+ @buffer[count % max_size] = value
46
+ @count += 1
47
+ self
48
+ end
49
+ end
50
+
51
+ # Read the value stored at `index`.
52
+ #
53
+ # @param [Integer, Range] index The element (if Integer) or elements
54
+ # (if Range) associated with `index`
55
+ # @return [Object, Array<Object>, nil] element(s) at `index`, `nil` if none
56
+ # exist
57
+ def [](index)
58
+ @mutex.synchronize do
59
+ return @buffer[index] if count <= max_size
60
+ return @buffer[(count + index) % max_size] if index.is_a?(Integer)
61
+
62
+ transpose_buffer_tail[index]
63
+ end
64
+ end
65
+
66
+ # @return [Array<Object>] the buffer as unwinded array
67
+ def to_a
68
+ return @buffer.dup if count <= max_size
69
+
70
+ transpose_buffer_tail
71
+ end
72
+
73
+ # Clear the buffer and reset count.
74
+ # @return [void]
75
+ def clear
76
+ @mutex.synchronize do
77
+ @buffer = []
78
+ @count = 0
79
+ end
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
88
+ end
89
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Lee Jarvis
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pry
4
+ class Slop
5
+ class Commands
6
+ include Enumerable
7
+
8
+ attr_reader :config, :commands, :arguments
9
+ attr_writer :banner
10
+
11
+ # Create a new instance of Slop::Commands and optionally build
12
+ # Slop instances via a block. Any configuration options used in
13
+ # this method will be the default configuration options sent to
14
+ # each Slop object created.
15
+ #
16
+ # config - An optional configuration Hash.
17
+ # block - Optional block used to define commands.
18
+ #
19
+ # Examples:
20
+ #
21
+ # commands = Slop::Commands.new do
22
+ # on :new do
23
+ # on '-o', '--outdir=', 'The output directory'
24
+ # on '-v', '--verbose', 'Enable verbose mode'
25
+ # end
26
+ #
27
+ # on :generate do
28
+ # on '--assets', 'Generate assets', :default => true
29
+ # end
30
+ #
31
+ # global do
32
+ # on '-D', '--debug', 'Enable debug mode', :default => false
33
+ # end
34
+ # end
35
+ #
36
+ # commands[:new].class #=> Slop
37
+ # commands.parse
38
+ #
39
+ def initialize(config = {}, &block)
40
+ @config = config
41
+ @commands = {}
42
+ @banner = nil
43
+ @triggered_command = nil
44
+
45
+ warn "[DEPRECATED] Slop::Commands is deprecated and will be removed in "\
46
+ "Slop version 4. Check out http://injekt.github.com/slop/#commands for "\
47
+ "a new implementation of commands."
48
+
49
+ return unless block_given?
50
+
51
+ block.arity == 1 ? yield(self) : instance_eval(&block)
52
+ end
53
+
54
+ # Optionally set the banner for this command help output.
55
+ #
56
+ # banner - The String text to set the banner.
57
+ #
58
+ # Returns the String banner if one is set.
59
+ def banner(banner = nil)
60
+ @banner = banner if banner
61
+ @banner
62
+ end
63
+
64
+ # Add a Slop instance for a specific command.
65
+ #
66
+ # command - A String or Symbol key used to identify this command.
67
+ # config - A Hash of configuration options to pass to Slop.
68
+ # block - An optional block used to pass options to Slop.
69
+ #
70
+ # Returns the newly created Slop instance mapped to command.
71
+ def on(command, config = {}, &block)
72
+ commands[command.to_s] = Slop.new(@config.merge(config), &block)
73
+ end
74
+
75
+ # Add a Slop instance used when no other commands exist.
76
+ #
77
+ # config - A Hash of configuration options to pass to Slop.
78
+ # block - An optional block used to pass options to Slop.
79
+ #
80
+ # Returns the newly created Slop instance mapped to default.
81
+ def default(config = {}, &block)
82
+ on('default', config, &block)
83
+ end
84
+
85
+ # Add a global Slop instance.
86
+ #
87
+ # config - A Hash of configuration options to pass to Slop.
88
+ # block - An optional block used to pass options to Slop.
89
+ #
90
+ # Returns the newly created Slop instance mapped to global.
91
+ def global(config = {}, &block)
92
+ on('global', config, &block)
93
+ end
94
+
95
+ # Fetch the instance of Slop tied to a command.
96
+ #
97
+ # key - The String or Symbol key used to locate this command.
98
+ #
99
+ # Returns the Slop instance if this key is found, nil otherwise.
100
+ def [](key)
101
+ commands[key.to_s]
102
+ end
103
+ alias get []
104
+
105
+ # Check for a command presence.
106
+ #
107
+ # Examples:
108
+ #
109
+ # cmds.parse %w( foo )
110
+ # cmds.present?(:foo) #=> true
111
+ # cmds.present?(:bar) #=> false
112
+ #
113
+ # Returns true if the given key is present in the parsed arguments.
114
+ def present?(key)
115
+ key.to_s == @triggered_command
116
+ end
117
+
118
+ # Enumerable interface.
119
+ def each(&block)
120
+ @commands.each(&block)
121
+ end
122
+
123
+ # Parse a list of items.
124
+ #
125
+ # items - The Array of items to parse.
126
+ #
127
+ # Returns the original Array of items.
128
+ def parse(items = ARGV)
129
+ parse! items.dup
130
+ items
131
+ end
132
+
133
+ # Parse a list of items, removing any options or option arguments found.
134
+ #
135
+ # items - The Array of items to parse.
136
+ #
137
+ # Returns the original Array of items with options removed.
138
+ def parse!(items = ARGV)
139
+ if (opts = commands[items[0].to_s])
140
+ @triggered_command = items.shift
141
+ execute_arguments! items
142
+ opts.parse! items
143
+ elsif (opts = commands['default'])
144
+ opts.parse! items
145
+ elsif config[:strict] && items[0]
146
+ raise InvalidCommandError, "Unknown command `#{items[0]}`"
147
+ end
148
+ execute_global_opts! items
149
+ items
150
+ end
151
+
152
+ # Returns a nested Hash with Slop options and values. See Slop#to_hash.
153
+ def to_hash
154
+ Hash[commands.map { |k, v| [k.to_sym, v.to_hash] }]
155
+ end
156
+
157
+ # Returns the help String.
158
+ def to_s
159
+ defaults = commands.delete('default')
160
+ globals = commands.delete('global')
161
+ helps = commands.reject { |_, v| v.options.none? }
162
+ helps['Global options'] = globals.to_s if globals && globals.options.any?
163
+ helps['Other options'] = defaults.to_s if defaults && defaults.options.any?
164
+ banner = @banner ? "#{@banner}\n" : ""
165
+ banner + helps.map { |key, opts| " #{key}\n#{opts}" }.join("\n\n")
166
+ end
167
+ alias help to_s
168
+
169
+ # Returns the inspection String.
170
+ def inspect
171
+ "#<Slop::Commands #{config.inspect} #{commands.values.map(&:inspect)}>"
172
+ end
173
+
174
+ private
175
+
176
+ # Returns nothing.
177
+ def execute_arguments!(items)
178
+ @arguments = items.take_while { |arg| !arg.start_with?('-') }
179
+ items.shift @arguments.size
180
+ end
181
+
182
+ # Returns nothing.
183
+ def execute_global_opts!(items)
184
+ return unless (global_opts = commands['global'])
185
+
186
+ global_opts.parse!(items)
187
+ end
188
+ end
189
+ end
190
+ end