pry 0.9.8.4 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG +26 -0
- data/README.markdown +3 -3
- data/lib/pry.rb +9 -1
- data/lib/pry/code.rb +93 -0
- data/lib/pry/command.rb +35 -22
- data/lib/pry/command_set.rb +97 -67
- data/lib/pry/config.rb +63 -10
- data/lib/pry/core_extensions.rb +24 -18
- data/lib/pry/default_commands/context.rb +72 -12
- data/lib/pry/default_commands/easter_eggs.rb +4 -0
- data/lib/pry/default_commands/editing.rb +43 -15
- data/lib/pry/default_commands/find_method.rb +171 -0
- data/lib/pry/default_commands/hist.rb +10 -6
- data/lib/pry/default_commands/introspection.rb +183 -30
- data/lib/pry/default_commands/ls.rb +77 -7
- data/lib/pry/default_commands/misc.rb +1 -0
- data/lib/pry/default_commands/navigating_pry.rb +1 -8
- data/lib/pry/helpers/base_helpers.rb +10 -2
- data/lib/pry/helpers/command_helpers.rb +23 -40
- data/lib/pry/helpers/documentation_helpers.rb +65 -0
- data/lib/pry/indent.rb +17 -4
- data/lib/pry/method.rb +61 -45
- data/lib/pry/pry_class.rb +9 -3
- data/lib/pry/pry_instance.rb +99 -65
- data/lib/pry/rbx_method.rb +2 -9
- data/lib/pry/version.rb +1 -1
- data/lib/pry/wrapped_module.rb +236 -1
- data/pry.gemspec +5 -5
- data/test/helper.rb +22 -0
- data/test/test_command.rb +29 -0
- data/test/test_command_integration.rb +193 -10
- data/test/test_command_set.rb +82 -17
- data/test/test_default_commands/test_cd.rb +16 -0
- data/test/test_default_commands/test_context.rb +61 -0
- data/test/test_default_commands/test_documentation.rb +163 -43
- data/test/test_default_commands/test_find_method.rb +42 -0
- data/test/test_default_commands/test_input.rb +32 -0
- data/test/test_default_commands/test_introspection.rb +50 -197
- data/test/test_default_commands/test_ls.rb +22 -0
- data/test/test_default_commands/test_show_source.rb +306 -0
- data/test/test_pry.rb +3 -3
- data/test/test_pry_defaults.rb +21 -0
- data/test/test_sticky_locals.rb +81 -1
- data/test/test_syntax_checking.rb +7 -6
- metadata +22 -14
data/lib/pry/config.rb
CHANGED
@@ -4,44 +4,82 @@ class Pry
|
|
4
4
|
class Config < OpenStruct
|
5
5
|
|
6
6
|
# Get/Set the object to use for input by default by all Pry instances.
|
7
|
+
# Pry.config.input is an option determining the input object - the object from
|
8
|
+
# which Pry retrieves its lines of input. Pry accepts any object that implements the readline method.
|
9
|
+
# This includes IO objects, StringIO, Readline, File and custom objects.
|
7
10
|
# @return [#readline] The object to use for input by default by all
|
8
11
|
# Pry instances.
|
12
|
+
# @example
|
13
|
+
# Pry.config.input = StringIO.new("@x = 10\nexit")
|
9
14
|
attr_accessor :input
|
10
15
|
|
11
16
|
# Get/Set the object to use for output by default by all Pry instances.
|
17
|
+
# Pry.config.output is an option determining the output object - the object to which
|
18
|
+
# Pry writes its output. Pry accepts any object that implements the puts method. This
|
19
|
+
# includes IO objects, StringIO, File and custom objects.
|
12
20
|
# @return [#puts] The object to use for output by default by all
|
13
21
|
# Pry instances.
|
22
|
+
# @example
|
23
|
+
# Pry.config.output = StringIO.new
|
14
24
|
attr_accessor :output
|
15
25
|
|
16
26
|
# Get/Set the object to use for commands by default by all Pry instances.
|
17
27
|
# @return [Pry::CommandBase] The object to use for commands by default by all
|
18
28
|
# Pry instances.
|
29
|
+
# @example
|
30
|
+
# Pry.config.commands = Pry::CommandSet.new do
|
31
|
+
# import_from Pry::Commands, "ls"
|
32
|
+
#
|
33
|
+
# command "greet" do |name|
|
34
|
+
# output.puts "hello #{name}"
|
35
|
+
# end
|
36
|
+
# end
|
19
37
|
attr_accessor :commands
|
20
38
|
|
21
39
|
# Get/Set the Proc to use for printing by default by all Pry
|
22
40
|
# instances.
|
41
|
+
# Two parameters are passed to the print Proc: these are (1) the
|
42
|
+
# output object for the current session and (2) the expression value to print. It is important
|
43
|
+
# that you write to the output object instead of just stdout so that all Pry output can be redirected if necessary.
|
23
44
|
# This is the 'print' component of the REPL.
|
24
45
|
# @return [Proc] The Proc to use for printing by default by all
|
25
46
|
# Pry instances.
|
47
|
+
# @example
|
48
|
+
# Pry.config.print = proc { |output, value| output.puts "=> #{value.inspect}" }
|
26
49
|
attr_accessor :print
|
27
50
|
|
51
|
+
# Pry.config.exception_handler is an option determining the exception handler object - the
|
52
|
+
# Proc responsible for dealing with exceptions raised by user input to the REPL.
|
53
|
+
# Three parameters are passed to the exception handler Proc: these
|
54
|
+
# are (1) the output object for the current session, (2) the
|
55
|
+
# exception object that was raised inside the Pry session, and (3)
|
56
|
+
# a reference to the associated Pry instance.
|
28
57
|
# @return [Proc] The Proc to use for printing exceptions by default by all
|
29
58
|
# Pry instances.
|
59
|
+
# @example
|
60
|
+
# Pry.config.exception_handler = proc do |output, exception, _|
|
61
|
+
# output.puts "#{exception.class}: #{exception.message}"
|
62
|
+
# output.puts "from #{exception.backtrace.first}"
|
63
|
+
# end
|
30
64
|
attr_accessor :exception_handler
|
31
65
|
|
32
66
|
# @return [Array] The classes of exception that will not be caught by Pry.
|
67
|
+
# @example
|
68
|
+
# Pry.config.exception_whitelist = [SystemExit, SignalException]
|
33
69
|
attr_accessor :exception_whitelist
|
34
70
|
|
35
71
|
# @return [Fixnum] The number of lines of context to show before and after
|
36
72
|
# exceptions, etc.
|
73
|
+
# @example
|
74
|
+
# Pry.config.default_window_size = 10
|
37
75
|
attr_accessor :default_window_size
|
38
76
|
|
39
|
-
# Get/Set the
|
77
|
+
# Get/Set the `Pry::Hooks` instance that defines Pry hooks used by default by all Pry
|
40
78
|
# instances.
|
41
|
-
# @return [
|
79
|
+
# @return [Pry::Hooks] The hooks used by default by all Pry instances.
|
42
80
|
# @example
|
43
|
-
# Pry.hooks :before_session
|
44
|
-
#
|
81
|
+
# Pry.config.hooks = Pry::Hooks.new.add_hook(:before_session,
|
82
|
+
# :default) { |output, target, _pry_| output.puts "Good morning!" }
|
45
83
|
attr_reader :hooks
|
46
84
|
|
47
85
|
# FIXME:
|
@@ -63,23 +101,32 @@ class Pry
|
|
63
101
|
# Pry.config.input_stack = [StringIO.new("puts 'hello world'\nexit")]
|
64
102
|
attr_accessor :input_stack
|
65
103
|
|
66
|
-
# Get the array of Procs to be used for the prompts by default by
|
104
|
+
# Get the array of Procs (or single Proc) to be used for the prompts by default by
|
67
105
|
# all Pry instances.
|
68
|
-
#
|
106
|
+
# Three parameters are passed into the prompt procs, (1) the
|
107
|
+
# object that is the target of the session, (2) the current
|
108
|
+
# nesting level, and (3) a reference to the associated Pry instance. These objects can be used in the prompt, if desired.
|
109
|
+
# @return [Array<Proc>, Proc] The array of Procs to be used for the
|
69
110
|
# prompts by default by all Pry instances.
|
111
|
+
# @example
|
112
|
+
# Pry.config.prompt = proc { |obj, nest_level, _pry_| "#{obj}:#{nest_level}> " }
|
70
113
|
attr_accessor :prompt
|
71
114
|
|
72
|
-
# The default editor to use. Defaults to $EDITOR or
|
73
|
-
#
|
115
|
+
# The default editor to use. Defaults to $VISUAL, $EDITOR, or a sensible fallback
|
116
|
+
# for the platform.
|
74
117
|
# If `editor` is a String then that string is used as the shell
|
75
118
|
# command to invoke the editor. If `editor` is callable (e.g a
|
76
|
-
# Proc) then `file` and `
|
119
|
+
# Proc) then `file`, `line`, and `reloading` are passed in as parameters and the
|
77
120
|
# return value of that callable invocation is used as the exact
|
78
|
-
# shell command to invoke the editor.
|
121
|
+
# shell command to invoke the editor. `reloading` indicates whether Pry will be
|
122
|
+
# reloading code after the shell command returns. Any or all of these parameters
|
123
|
+
# can be omitted from the callable's signature.
|
79
124
|
# @example String
|
80
125
|
# Pry.config.editor = "emacsclient"
|
81
126
|
# @example Callable
|
82
127
|
# Pry.config.editor = proc { |file, line| "emacsclient #{file} +#{line}" }
|
128
|
+
# @example Callable waiting only if reloading
|
129
|
+
# Pry.config.editor = proc { |file, line, reloading| "subl #{'--wait' if reloading} #{file}:#{line}" }
|
83
130
|
# @return [String, #call]
|
84
131
|
attr_accessor :editor
|
85
132
|
|
@@ -171,6 +218,12 @@ class Pry
|
|
171
218
|
# Pry.config.gist.inspecter = proc &:inspect
|
172
219
|
# @return [OpenStruct]
|
173
220
|
attr_accessor :gist
|
221
|
+
|
222
|
+
# @return [Hash] Additional sticky locals (to the standard ones) to use in Pry sessions.
|
223
|
+
# @example Inject `random_number` sticky local into Pry session
|
224
|
+
# Pry.config.extra_sticky_locals = { :random_number => proc {
|
225
|
+
# rand(10) } }
|
226
|
+
attr_accessor :extra_sticky_locals
|
174
227
|
end
|
175
228
|
end
|
176
229
|
|
data/lib/pry/core_extensions.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
1
1
|
class Object
|
2
|
-
# Start a Pry REPL.
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# @param [Object, Binding] target The receiver of the Pry session.
|
13
|
-
# @example First form
|
2
|
+
# Start a Pry REPL. This method only differs from Pry.start in that it
|
3
|
+
# assumes that the target is `self`. It also accepts and passses the same
|
4
|
+
# exact options hash that Pry.start accepts. POSSIBLE DEPRICATION WARNING:
|
5
|
+
# In the future the backwards compatibility with pry(binding) could be
|
6
|
+
# removed so please properly use Object.pry or if you use pry(binding)
|
7
|
+
# switch Pry.start(binding).
|
8
|
+
|
9
|
+
# @param [Binding] the binding or options hash if no binding needed.
|
10
|
+
# @param [Hash] the options hash.
|
11
|
+
# @example First
|
14
12
|
# "dummy".pry
|
15
|
-
# @example Second
|
16
|
-
# pry
|
17
|
-
# @example
|
18
|
-
#
|
19
|
-
|
20
|
-
|
13
|
+
# @example Second
|
14
|
+
# binding.pry
|
15
|
+
# @example An example with options
|
16
|
+
# def my_method
|
17
|
+
# binding.pry :quiet => true
|
18
|
+
# end
|
19
|
+
# my_method()
|
20
|
+
|
21
|
+
def pry(*args)
|
22
|
+
if args.first.is_a?(Hash) || args.length == 0
|
23
|
+
args.unshift(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
Pry.start(*args)
|
21
27
|
end
|
22
28
|
|
23
29
|
# Return a binding object for the receiver.
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "pry/default_commands/ls"
|
2
2
|
require "pry/default_commands/cd"
|
3
|
+
require "pry/default_commands/find_method"
|
3
4
|
|
4
5
|
class Pry
|
5
6
|
module DefaultCommands
|
@@ -7,24 +8,62 @@ class Pry
|
|
7
8
|
Context = Pry::CommandSet.new do
|
8
9
|
import Ls
|
9
10
|
import Cd
|
11
|
+
import FindMethod
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
create_command "whereami" do
|
14
|
+
description "Show code surrounding the current context."
|
15
|
+
banner <<-BANNER
|
16
|
+
Usage: whereami [OPTIONS]
|
17
|
+
BANNER
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
def setup
|
20
|
+
@method = Pry::Method.from_binding(target)
|
17
21
|
end
|
18
22
|
|
19
|
-
|
23
|
+
def process
|
24
|
+
if show_method?
|
25
|
+
file = @method.source_file
|
26
|
+
start = @method.source_range.begin
|
27
|
+
finish = @method.source_range.end
|
28
|
+
marker = target.eval("__LINE__")
|
29
|
+
else
|
30
|
+
file = target.eval("__FILE__")
|
31
|
+
start = target.eval("__LINE__")
|
32
|
+
finish = (args.first && args.first.to_i) || 5
|
33
|
+
marker = start
|
34
|
+
end
|
35
|
+
|
36
|
+
if invalid_file?(file)
|
37
|
+
raise Pry::CommandError,
|
38
|
+
"Cannot find local context. Did you use binding.pry?"
|
39
|
+
end
|
20
40
|
|
21
|
-
|
22
|
-
|
23
|
-
|
41
|
+
# TODO: refactor.
|
42
|
+
if show_method?
|
43
|
+
code = Pry::Code.from_file(file).between(start, finish)
|
44
|
+
else
|
45
|
+
code = Pry::Code.from_file(file).around(start, finish)
|
46
|
+
end
|
47
|
+
|
48
|
+
desc = (@method && @method.name_with_owner) || ""
|
49
|
+
|
50
|
+
if !code.empty?
|
51
|
+
output.puts "\n#{text.bold('From:')} #{file} @ line #{start} #{desc}:\n\n"
|
52
|
+
output.puts code.with_line_numbers.with_marker(marker)
|
53
|
+
output.puts
|
54
|
+
end
|
55
|
+
end
|
24
56
|
|
25
|
-
|
26
|
-
|
27
|
-
|
57
|
+
private
|
58
|
+
|
59
|
+
def show_method?
|
60
|
+
args.empty? && @method && !@method.instance_of?(Pry::Method::Disowned) && @method.source_range.count < 20
|
61
|
+
end
|
62
|
+
|
63
|
+
def invalid_file?(file)
|
64
|
+
file != Pry.eval_path &&
|
65
|
+
(file =~ /(\(.*\))|<.*>/ || file == "" || file == "-e")
|
66
|
+
end
|
28
67
|
end
|
29
68
|
|
30
69
|
create_command "pry-backtrace", "Show the backtrace for the Pry session." do
|
@@ -87,6 +126,27 @@ class Pry
|
|
87
126
|
end
|
88
127
|
end
|
89
128
|
|
129
|
+
# N.B. using a regular expresion here so that "raise-up 'foo'" does the right thing.
|
130
|
+
create_command /raise-up(!?\b.*)/, :listing => 'raise-up' do
|
131
|
+
description "Raise an exception out of the current pry instance."
|
132
|
+
banner <<-BANNER
|
133
|
+
Raise up, like exit, allows you to quit pry. Instead of returning a value however, it raises an exception.
|
134
|
+
If you don't provide the exception to be raised, it will use the most recent exception (in pry _ex_).
|
135
|
+
|
136
|
+
e.g. `raise-up "get-me-out-of-here"` is equivalent to:
|
137
|
+
`raise "get-me-out-of-here"
|
138
|
+
raise-up`
|
139
|
+
|
140
|
+
When called as raise-up! (with an exclamation mark), this command raises the exception through
|
141
|
+
any nested prys you have created by "cd"ing into objects.
|
142
|
+
BANNER
|
143
|
+
|
144
|
+
def process
|
145
|
+
return stagger_output help if captures[0] =~ /(-h|--help)\b/
|
146
|
+
# Handle 'raise-up', 'raise-up "foo"', 'raise-up RuntimeError, 'farble' in a rubyesque manner
|
147
|
+
target.eval("_pry_.raise_up#{captures[0]}")
|
148
|
+
end
|
149
|
+
end
|
90
150
|
end
|
91
151
|
end
|
92
152
|
end
|
@@ -88,8 +88,9 @@ class Pry
|
|
88
88
|
temp_file do |f|
|
89
89
|
f.puts(content)
|
90
90
|
f.flush
|
91
|
-
|
92
|
-
|
91
|
+
reload = !opts.present?(:'no-reload') && !Pry.config.disable_auto_reload
|
92
|
+
invoke_editor(f.path, line, reload)
|
93
|
+
if reload
|
93
94
|
silence_warnings do
|
94
95
|
eval_string.replace(File.read(f.path))
|
95
96
|
end
|
@@ -138,10 +139,11 @@ class Pry
|
|
138
139
|
|
139
140
|
line = opts[:l].to_i if opts.present?(:line)
|
140
141
|
|
141
|
-
|
142
|
+
reload = opts.present?(:reload) || ((opts.present?(:ex) || file_name.end_with?(".rb")) && !opts.present?(:'no-reload')) && !Pry.config.disable_auto_reload
|
143
|
+
invoke_editor(file_name, line, reload)
|
142
144
|
set_file_and_dir_locals(file_name)
|
143
145
|
|
144
|
-
if
|
146
|
+
if reload
|
145
147
|
silence_warnings do
|
146
148
|
TOPLEVEL_BINDING.eval(File.read(file_name), file_name)
|
147
149
|
end
|
@@ -202,17 +204,12 @@ class Pry
|
|
202
204
|
def process_patch
|
203
205
|
lines = @method.source.lines.to_a
|
204
206
|
|
205
|
-
|
206
|
-
lines[0] =~ /^def (?:.*?\.)?#{original_name}(?=[\(\s;]|$)/)
|
207
|
-
lines[0] = "def #{original_name}#{$'}"
|
208
|
-
else
|
209
|
-
raise CommandError, "Pry can only patch methods created with the `def` keyword."
|
210
|
-
end
|
207
|
+
lines[0] = definition_line_for_owner(lines[0])
|
211
208
|
|
212
209
|
temp_file do |f|
|
213
210
|
f.puts lines.join
|
214
211
|
f.flush
|
215
|
-
invoke_editor(f.path, 0)
|
212
|
+
invoke_editor(f.path, 0, true)
|
216
213
|
|
217
214
|
if @method.alias?
|
218
215
|
with_method_transaction(original_name, @method.owner) do
|
@@ -228,9 +225,10 @@ class Pry
|
|
228
225
|
def process_file
|
229
226
|
file, line = extract_file_and_line
|
230
227
|
|
231
|
-
|
228
|
+
reload = !opts.present?(:'no-reload') && !Pry.config.disable_auto_reload
|
229
|
+
invoke_editor(file, opts["no-jump"] ? 0 : line, reload)
|
232
230
|
silence_warnings do
|
233
|
-
load file
|
231
|
+
load file if reload
|
234
232
|
end
|
235
233
|
end
|
236
234
|
|
@@ -257,6 +255,34 @@ class Pry
|
|
257
255
|
ensure
|
258
256
|
target.eval("undef #{temp_name}") rescue nil
|
259
257
|
end
|
258
|
+
|
259
|
+
# The original name of the method, if it's not present raise an error telling
|
260
|
+
# the user why we don't work.
|
261
|
+
#
|
262
|
+
def original_name
|
263
|
+
@method.original_name or raise CommandError, "Pry can only patch methods created with the `def` keyword."
|
264
|
+
end
|
265
|
+
|
266
|
+
# Update the definition line so that it can be eval'd directly on the Method's
|
267
|
+
# owner instead of from the original context.
|
268
|
+
#
|
269
|
+
# In particular this takes `def self.foo` and turns it into `def foo` so that we
|
270
|
+
# don't end up creating the method on the singleton class of the singleton class
|
271
|
+
# by accident.
|
272
|
+
#
|
273
|
+
# This is necessarily done by String manipulation because we can't find out what
|
274
|
+
# syntax is needed for the argument list by ruby-level introspection.
|
275
|
+
#
|
276
|
+
# @param String The original definition line. e.g. def self.foo(bar, baz=1)
|
277
|
+
# @return String The new definition line. e.g. def foo(bar, baz=1)
|
278
|
+
#
|
279
|
+
def definition_line_for_owner(line)
|
280
|
+
if line =~ /^def (?:.*?\.)?#{Regexp.escape(original_name)}(?=[\(\s;]|$)/
|
281
|
+
"def #{original_name}#{$'}"
|
282
|
+
else
|
283
|
+
raise CommandError, "Could not find original `def #{original_name}` line to patch."
|
284
|
+
end
|
285
|
+
end
|
260
286
|
end
|
261
287
|
|
262
288
|
create_command(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/) do
|
@@ -301,6 +327,8 @@ class Pry
|
|
301
327
|
end
|
302
328
|
|
303
329
|
create_command "play" do
|
330
|
+
include Helpers::DocumentationHelpers
|
331
|
+
|
304
332
|
description "Play back a string variable or a method or a file as input."
|
305
333
|
|
306
334
|
banner <<-BANNER
|
@@ -343,7 +371,7 @@ class Pry
|
|
343
371
|
self.content << File.read(File.expand_path(file))
|
344
372
|
end
|
345
373
|
opt.on :l, :lines, "Only play a subset of lines.", :optional => true, :as => Range, :default => 1..-1
|
346
|
-
opt.on :i, :in, "Play entries from Pry's input expression history. Takes an index or range.", :optional => true,
|
374
|
+
opt.on :i, :in, "Play entries from Pry's input expression history. Takes an index or range. Note this can only replay pure Ruby code, not Pry commands.", :optional => true,
|
347
375
|
:as => Range, :default => -5..-1 do |range|
|
348
376
|
input_expressions = _pry_.input_array[range] || []
|
349
377
|
Array(input_expressions).each { |v| self.content << v }
|
@@ -353,7 +381,7 @@ class Pry
|
|
353
381
|
|
354
382
|
def process
|
355
383
|
perform_play
|
356
|
-
run "show-input" unless
|
384
|
+
run "show-input" unless Pry::Code.complete_expression?(eval_string)
|
357
385
|
end
|
358
386
|
|
359
387
|
def process_non_opt
|
@@ -0,0 +1,171 @@
|
|
1
|
+
class Pry
|
2
|
+
module DefaultCommands
|
3
|
+
FindMethod = Pry::CommandSet.new do
|
4
|
+
|
5
|
+
create_command "find-method" do
|
6
|
+
extend Helpers::BaseHelpers
|
7
|
+
|
8
|
+
group "Context"
|
9
|
+
|
10
|
+
options :requires_gem => "ruby18_source_location" if mri_18?
|
11
|
+
|
12
|
+
description "Recursively search for a method within a Class/Module or the current namespace. find-method [-n | -c] METHOD [NAMESPACE]"
|
13
|
+
|
14
|
+
banner <<-BANNER
|
15
|
+
Usage: find-method [-n | -c] METHOD [NAMESPACE]
|
16
|
+
|
17
|
+
Recursively search for a method within a Class/Module or the current namespace.
|
18
|
+
Use the `-n` switch (the default) to search for methods whose name matches the given regex.
|
19
|
+
Use the `-c` switch to search for methods that contain the given code.
|
20
|
+
|
21
|
+
e.g find re Pry # find all methods whose name match /re/ inside the Pry namespace. Matches Pry#repl, etc.
|
22
|
+
e.g find -c 'output.puts' Pry # find all methods that contain the code: output.puts inside the Pry namepsace.
|
23
|
+
BANNER
|
24
|
+
|
25
|
+
def setup
|
26
|
+
require 'ruby18_source_location' if mri_18?
|
27
|
+
end
|
28
|
+
|
29
|
+
def options(opti)
|
30
|
+
opti.on :n, :name, "Search for a method by name"
|
31
|
+
opti.on :c, :content, "Search for a method based on content in Regex form"
|
32
|
+
end
|
33
|
+
|
34
|
+
def process
|
35
|
+
return if args.size < 1
|
36
|
+
pattern = ::Regexp.new args[0]
|
37
|
+
if args[1]
|
38
|
+
klass = target.eval(args[1])
|
39
|
+
if !klass.is_a?(Module)
|
40
|
+
klass = klass.class
|
41
|
+
end
|
42
|
+
else
|
43
|
+
klass = (target_self.is_a?(Module)) ? target_self : target_self.class
|
44
|
+
end
|
45
|
+
|
46
|
+
matches = if opts.content?
|
47
|
+
content_search(pattern, klass)
|
48
|
+
else
|
49
|
+
name_search(pattern, klass)
|
50
|
+
end
|
51
|
+
|
52
|
+
if matches.empty?
|
53
|
+
output.puts text.bold("No Methods Matched")
|
54
|
+
else
|
55
|
+
print_matches(matches, pattern)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# pretty-print a list of matching methods.
|
63
|
+
#
|
64
|
+
# @param Array[Method]
|
65
|
+
def print_matches(matches, pattern)
|
66
|
+
grouped = matches.group_by(&:owner)
|
67
|
+
order = grouped.keys.sort_by{ |x| x.name || x.to_s }
|
68
|
+
|
69
|
+
order.each do |klass|
|
70
|
+
output.puts text.bold(klass.name)
|
71
|
+
grouped[klass].each do |method|
|
72
|
+
header = method.name_with_owner
|
73
|
+
|
74
|
+
extra = if opts.content?
|
75
|
+
header += ": "
|
76
|
+
colorize_code((method.source.split(/\n/).select {|x| x =~ pattern }).join("\n#{' ' * header.length}"))
|
77
|
+
else
|
78
|
+
""
|
79
|
+
end
|
80
|
+
|
81
|
+
output.puts header + extra
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Run the given block against every constant in the provided namespace.
|
87
|
+
#
|
88
|
+
# @param Module The namespace in which to start the search.
|
89
|
+
# @param Hash[Module,Boolean] The namespaces we've already visited (private)
|
90
|
+
# @yieldparam klazz Each class/module in the namespace.
|
91
|
+
#
|
92
|
+
def recurse_namespace(klass, done={}, &block)
|
93
|
+
if done[klass] || !(Module === klass)
|
94
|
+
return
|
95
|
+
end
|
96
|
+
|
97
|
+
done[klass] = true
|
98
|
+
|
99
|
+
yield klass
|
100
|
+
|
101
|
+
klass.constants.each do |name|
|
102
|
+
next if klass.autoload?(name)
|
103
|
+
begin
|
104
|
+
const = klass.const_get(name)
|
105
|
+
rescue RescuableException => e
|
106
|
+
# constant loading is an inexact science at the best of times,
|
107
|
+
# this often happens when a constant was .autoload? but someone
|
108
|
+
# tried to load it. It's now not .autoload? but will still raise
|
109
|
+
# a NameError when you access it.
|
110
|
+
else
|
111
|
+
recurse_namespace(const, done, &block)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Gather all the methods in a namespace that pass the given block.
|
117
|
+
#
|
118
|
+
# @param Module The namespace in which to search.
|
119
|
+
# @yieldparam Method The method to test
|
120
|
+
# @yieldreturn Boolean
|
121
|
+
# @return Array[Method]
|
122
|
+
#
|
123
|
+
def search_all_methods(namespace)
|
124
|
+
done = Hash.new{ |h,k| h[k] = {} }
|
125
|
+
matches = []
|
126
|
+
|
127
|
+
recurse_namespace(namespace) do |klass|
|
128
|
+
(Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass)).each do |method|
|
129
|
+
next if done[method.owner][method.name]
|
130
|
+
done[method.owner][method.name] = true
|
131
|
+
|
132
|
+
matches << method if yield method
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
matches
|
137
|
+
end
|
138
|
+
|
139
|
+
# Search for all methods with a name that matches the given regex
|
140
|
+
# within a namespace.
|
141
|
+
#
|
142
|
+
# @param Regex The regex to search for
|
143
|
+
# @param Module The namespace to search
|
144
|
+
# @return Array[Method]
|
145
|
+
#
|
146
|
+
def name_search(regex, namespace)
|
147
|
+
search_all_methods(namespace) do |meth|
|
148
|
+
meth.name =~ regex
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Search for all methods who's implementation matches the given regex
|
153
|
+
# within a namespace.
|
154
|
+
#
|
155
|
+
# @param Regex The regex to search for
|
156
|
+
# @param Module The namespace to search
|
157
|
+
# @return Array[Method]
|
158
|
+
#
|
159
|
+
def content_search(regex, namespace)
|
160
|
+
search_all_methods(namespace) do |meth|
|
161
|
+
begin
|
162
|
+
meth.source =~ regex
|
163
|
+
rescue RescuableException => e
|
164
|
+
false
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|