pry 0.10.4 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +360 -16
  3. data/LICENSE +1 -1
  4. data/README.md +352 -306
  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 +81 -74
  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 +48 -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 +24 -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 +81 -70
  73. data/lib/pry/commands/show_info.rb +193 -160
  74. data/lib/pry/commands/show_input.rb +16 -11
  75. data/lib/pry/commands/show_source.rb +109 -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 +155 -146
  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 +317 -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 +53 -33
  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 +20 -12
  98. data/lib/pry/helpers/options_helpers.rb +15 -8
  99. data/lib/pry/helpers/platform.rb +60 -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 +72 -66
  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 +177 -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 -177
  117. data/lib/pry/plugins.rb +49 -13
  118. data/lib/pry/prompt.rb +213 -25
  119. data/lib/pry/pry_class.rb +106 -93
  120. data/lib/pry/pry_instance.rb +261 -224
  121. data/lib/pry/repl.rb +82 -27
  122. data/lib/pry/repl_file_loader.rb +27 -22
  123. data/lib/pry/ring.rb +89 -0
  124. data/lib/pry/slop/LICENSE +20 -0
  125. data/lib/pry/slop/commands.rb +190 -0
  126. data/lib/pry/slop/option.rb +210 -0
  127. data/lib/pry/slop.rb +672 -0
  128. data/lib/pry/syntax_highlighter.rb +26 -0
  129. data/lib/pry/system_command_handler.rb +17 -0
  130. data/lib/pry/testable/evalable.rb +24 -0
  131. data/lib/pry/testable/mockable.rb +22 -0
  132. data/lib/pry/testable/pry_tester.rb +88 -0
  133. data/lib/pry/testable/utility.rb +34 -0
  134. data/lib/pry/testable/variables.rb +52 -0
  135. data/lib/pry/testable.rb +68 -0
  136. data/lib/pry/version.rb +3 -1
  137. data/lib/pry/warning.rb +27 -0
  138. data/lib/pry/{module_candidate.rb → wrapped_module/candidate.rb} +28 -27
  139. data/lib/pry/wrapped_module.rb +66 -57
  140. data/lib/pry.rb +134 -149
  141. metadata +49 -59
  142. data/lib/pry/commands/disabled_commands.rb +0 -2
  143. data/lib/pry/commands/gem_cd.rb +0 -26
  144. data/lib/pry/commands/gem_install.rb +0 -32
  145. data/lib/pry/commands/gem_list.rb +0 -33
  146. data/lib/pry/commands/gem_open.rb +0 -29
  147. data/lib/pry/commands/gist.rb +0 -101
  148. data/lib/pry/commands/install_command.rb +0 -53
  149. data/lib/pry/commands/list_prompts.rb +0 -35
  150. data/lib/pry/commands/simple_prompt.rb +0 -22
  151. data/lib/pry/commands.rb +0 -6
  152. data/lib/pry/config/behavior.rb +0 -139
  153. data/lib/pry/config/convenience.rb +0 -25
  154. data/lib/pry/config/default.rb +0 -161
  155. data/lib/pry/history_array.rb +0 -121
  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
@@ -150,9 +153,7 @@ class Pry
150
153
  puts "Error: #{e.message}"
151
154
  output.puts e.backtrace
152
155
  exception_count += 1
153
- if exception_count < 5
154
- retry
155
- end
156
+ retry if exception_count < 5
156
157
  puts "FATAL: Pry failed to get user input using `#{input}`."
157
158
  puts "To fix this you may be able to pass input and output file " \
158
159
  "descriptors to pry directly. e.g."
@@ -168,27 +169,26 @@ class Pry
168
169
  # @return [String?] The next line of input, or `nil` on <Ctrl-D>.
169
170
  def read_line(current_prompt)
170
171
  handle_read_errors do
171
- if defined? Coolline and input.is_a? Coolline
172
+ if coolline_available?
172
173
  input.completion_proc = proc do |cool|
173
174
  completions = @pry.complete cool.completed_word
174
175
  completions.compact
175
176
  end
176
177
  elsif input.respond_to? :completion_proc=
177
- input.completion_proc = proc do |input|
178
- @pry.complete input
178
+ input.completion_proc = proc do |inp|
179
+ @pry.complete inp
179
180
  end
180
181
  end
181
182
 
182
- if defined?(Readline) and input == Readline
183
+ if readline_available?
184
+ set_readline_output
183
185
  input_readline(current_prompt, false) # false since we'll add it manually
184
- elsif defined? Coolline and input.is_a? Coolline
186
+ elsif coolline_available?
187
+ input_readline(current_prompt)
188
+ elsif input.method(:readline).arity == 1
185
189
  input_readline(current_prompt)
186
190
  else
187
- if input.method(:readline).arity == 1
188
- input_readline(current_prompt)
189
- else
190
- input_readline
191
- end
191
+ input_readline
192
192
  end
193
193
  end
194
194
  end
@@ -198,5 +198,60 @@ class Pry
198
198
  input.readline(*args)
199
199
  end
200
200
  end
201
+
202
+ def readline_available?
203
+ defined?(Readline) && input == Readline
204
+ end
205
+
206
+ def coolline_available?
207
+ defined?(Coolline) && input.is_a?(Coolline)
208
+ end
209
+
210
+ # If `$stdout` is not a tty, it's probably a pipe.
211
+ # @example
212
+ # # `piping?` returns `false`
213
+ # % pry
214
+ # [1] pry(main)
215
+ #
216
+ # # `piping?` returns `true`
217
+ # % pry | tee log
218
+ def piping?
219
+ return false unless $stdout.respond_to?(:tty?)
220
+
221
+ !$stdout.tty? && $stdin.tty? && !Helpers::Platform.windows?
222
+ end
223
+
224
+ # @return [void]
225
+ def set_readline_output
226
+ return if @readline_output
227
+
228
+ @readline_output = (Readline.output = Pry.config.output) if piping?
229
+ end
230
+
231
+ # Calculates correct overhang for current line. Supports vi Readline
232
+ # mode and its indicators such as "(ins)" or "(cmd)".
233
+ #
234
+ # @return [Integer]
235
+ # @note This doesn't calculate overhang for Readline's emacs mode with an
236
+ # indicator because emacs is the default mode and it doesn't use
237
+ # indicators in 99% of cases.
238
+ def calculate_overhang(current_prompt, original_val, indented_val)
239
+ overhang = original_val.length - indented_val.length
240
+
241
+ if readline_available? && Readline.respond_to?(:vi_editing_mode?)
242
+ begin
243
+ # rb-readline doesn't support this method:
244
+ # https://github.com/ConnorAtherton/rb-readline/issues/152
245
+ if Readline.vi_editing_mode?
246
+ overhang = output.width - current_prompt.size - indented_val.size
247
+ end
248
+ rescue NotImplementedError
249
+ # VI editing mode is unsupported on JRuby.
250
+ # https://github.com/pry/pry/issues/1840
251
+ nil
252
+ end
253
+ end
254
+ [0, overhang].max
255
+ end
201
256
  end
202
257
  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[(count + index) % max_size] if index.is_a?(Integer)
60
+ return @buffer[index] if count <= max_size
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