pry 0.10.0.pre2-universal-mingw32
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 +7 -0
- data/CHANGELOG.md +702 -0
- data/LICENSE +25 -0
- data/README.md +406 -0
- data/bin/pry +16 -0
- data/lib/pry.rb +161 -0
- data/lib/pry/cli.rb +220 -0
- data/lib/pry/code.rb +346 -0
- data/lib/pry/code/code_file.rb +103 -0
- data/lib/pry/code/code_range.rb +71 -0
- data/lib/pry/code/loc.rb +92 -0
- data/lib/pry/code_object.rb +172 -0
- data/lib/pry/color_printer.rb +55 -0
- data/lib/pry/command.rb +692 -0
- data/lib/pry/command_set.rb +443 -0
- data/lib/pry/commands.rb +6 -0
- data/lib/pry/commands/amend_line.rb +99 -0
- data/lib/pry/commands/bang.rb +20 -0
- data/lib/pry/commands/bang_pry.rb +17 -0
- data/lib/pry/commands/cat.rb +62 -0
- data/lib/pry/commands/cat/abstract_formatter.rb +27 -0
- data/lib/pry/commands/cat/exception_formatter.rb +77 -0
- data/lib/pry/commands/cat/file_formatter.rb +67 -0
- data/lib/pry/commands/cat/input_expression_formatter.rb +43 -0
- data/lib/pry/commands/cd.rb +41 -0
- data/lib/pry/commands/change_inspector.rb +27 -0
- data/lib/pry/commands/change_prompt.rb +26 -0
- data/lib/pry/commands/code_collector.rb +165 -0
- data/lib/pry/commands/disable_pry.rb +27 -0
- data/lib/pry/commands/disabled_commands.rb +2 -0
- data/lib/pry/commands/easter_eggs.rb +112 -0
- data/lib/pry/commands/edit.rb +195 -0
- data/lib/pry/commands/edit/exception_patcher.rb +25 -0
- data/lib/pry/commands/edit/file_and_line_locator.rb +36 -0
- data/lib/pry/commands/exit.rb +42 -0
- data/lib/pry/commands/exit_all.rb +29 -0
- data/lib/pry/commands/exit_program.rb +23 -0
- data/lib/pry/commands/find_method.rb +193 -0
- data/lib/pry/commands/fix_indent.rb +19 -0
- data/lib/pry/commands/gem_cd.rb +26 -0
- data/lib/pry/commands/gem_install.rb +32 -0
- data/lib/pry/commands/gem_list.rb +33 -0
- data/lib/pry/commands/gem_open.rb +29 -0
- data/lib/pry/commands/gist.rb +101 -0
- data/lib/pry/commands/help.rb +164 -0
- data/lib/pry/commands/hist.rb +180 -0
- data/lib/pry/commands/import_set.rb +22 -0
- data/lib/pry/commands/install_command.rb +53 -0
- data/lib/pry/commands/jump_to.rb +29 -0
- data/lib/pry/commands/list_inspectors.rb +35 -0
- data/lib/pry/commands/list_prompts.rb +35 -0
- data/lib/pry/commands/ls.rb +114 -0
- data/lib/pry/commands/ls/constants.rb +47 -0
- data/lib/pry/commands/ls/formatter.rb +49 -0
- data/lib/pry/commands/ls/globals.rb +48 -0
- data/lib/pry/commands/ls/grep.rb +21 -0
- data/lib/pry/commands/ls/instance_vars.rb +39 -0
- data/lib/pry/commands/ls/interrogatable.rb +18 -0
- data/lib/pry/commands/ls/jruby_hacks.rb +49 -0
- data/lib/pry/commands/ls/local_names.rb +35 -0
- data/lib/pry/commands/ls/local_vars.rb +39 -0
- data/lib/pry/commands/ls/ls_entity.rb +70 -0
- data/lib/pry/commands/ls/methods.rb +57 -0
- data/lib/pry/commands/ls/methods_helper.rb +46 -0
- data/lib/pry/commands/ls/self_methods.rb +32 -0
- data/lib/pry/commands/nesting.rb +25 -0
- data/lib/pry/commands/play.rb +103 -0
- data/lib/pry/commands/pry_backtrace.rb +25 -0
- data/lib/pry/commands/pry_version.rb +17 -0
- data/lib/pry/commands/raise_up.rb +32 -0
- data/lib/pry/commands/reload_code.rb +62 -0
- data/lib/pry/commands/reset.rb +18 -0
- data/lib/pry/commands/ri.rb +60 -0
- data/lib/pry/commands/save_file.rb +61 -0
- data/lib/pry/commands/shell_command.rb +48 -0
- data/lib/pry/commands/shell_mode.rb +25 -0
- data/lib/pry/commands/show_doc.rb +83 -0
- data/lib/pry/commands/show_info.rb +195 -0
- data/lib/pry/commands/show_input.rb +17 -0
- data/lib/pry/commands/show_source.rb +50 -0
- data/lib/pry/commands/simple_prompt.rb +22 -0
- data/lib/pry/commands/stat.rb +40 -0
- data/lib/pry/commands/switch_to.rb +23 -0
- data/lib/pry/commands/toggle_color.rb +24 -0
- data/lib/pry/commands/watch_expression.rb +105 -0
- data/lib/pry/commands/watch_expression/expression.rb +38 -0
- data/lib/pry/commands/whereami.rb +190 -0
- data/lib/pry/commands/wtf.rb +57 -0
- data/lib/pry/config.rb +24 -0
- data/lib/pry/config/behavior.rb +139 -0
- data/lib/pry/config/convenience.rb +26 -0
- data/lib/pry/config/default.rb +165 -0
- data/lib/pry/core_extensions.rb +131 -0
- data/lib/pry/editor.rb +133 -0
- data/lib/pry/exceptions.rb +77 -0
- data/lib/pry/helpers.rb +5 -0
- data/lib/pry/helpers/base_helpers.rb +113 -0
- data/lib/pry/helpers/command_helpers.rb +156 -0
- data/lib/pry/helpers/documentation_helpers.rb +75 -0
- data/lib/pry/helpers/options_helpers.rb +27 -0
- data/lib/pry/helpers/table.rb +109 -0
- data/lib/pry/helpers/text.rb +107 -0
- data/lib/pry/history.rb +125 -0
- data/lib/pry/history_array.rb +121 -0
- data/lib/pry/hooks.rb +230 -0
- data/lib/pry/indent.rb +406 -0
- data/lib/pry/input_completer.rb +242 -0
- data/lib/pry/input_lock.rb +132 -0
- data/lib/pry/inspector.rb +27 -0
- data/lib/pry/last_exception.rb +61 -0
- data/lib/pry/method.rb +546 -0
- data/lib/pry/method/disowned.rb +53 -0
- data/lib/pry/method/patcher.rb +125 -0
- data/lib/pry/method/weird_method_locator.rb +186 -0
- data/lib/pry/module_candidate.rb +136 -0
- data/lib/pry/object_path.rb +82 -0
- data/lib/pry/output.rb +50 -0
- data/lib/pry/pager.rb +234 -0
- data/lib/pry/plugins.rb +103 -0
- data/lib/pry/prompt.rb +26 -0
- data/lib/pry/pry_class.rb +375 -0
- data/lib/pry/pry_instance.rb +654 -0
- data/lib/pry/rbx_path.rb +22 -0
- data/lib/pry/repl.rb +202 -0
- data/lib/pry/repl_file_loader.rb +74 -0
- data/lib/pry/rubygem.rb +82 -0
- data/lib/pry/terminal.rb +79 -0
- data/lib/pry/test/helper.rb +170 -0
- data/lib/pry/version.rb +3 -0
- data/lib/pry/wrapped_module.rb +373 -0
- metadata +248 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
# in case the tests call reset_defaults, ensure we reset them to
|
4
|
+
# amended (test friendly) values
|
5
|
+
class << Pry
|
6
|
+
alias_method :orig_reset_defaults, :reset_defaults
|
7
|
+
def reset_defaults
|
8
|
+
orig_reset_defaults
|
9
|
+
|
10
|
+
Pry.config.color = false
|
11
|
+
Pry.config.pager = false
|
12
|
+
Pry.config.should_load_rc = false
|
13
|
+
Pry.config.should_load_local_rc= false
|
14
|
+
Pry.config.should_load_plugins = false
|
15
|
+
Pry.config.history.should_load = false
|
16
|
+
Pry.config.history.should_save = false
|
17
|
+
Pry.config.correct_indent = false
|
18
|
+
Pry.config.hooks = Pry::Hooks.new
|
19
|
+
Pry.config.collision_warning = false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
Pry.reset_defaults
|
23
|
+
|
24
|
+
# A global space for storing temporary state during tests.
|
25
|
+
|
26
|
+
module PryTestHelpers
|
27
|
+
|
28
|
+
module_function
|
29
|
+
|
30
|
+
# inject a variable into a binding
|
31
|
+
def inject_var(name, value, b)
|
32
|
+
Pry.current[:pry_local] = value
|
33
|
+
b.eval("#{name} = ::Pry.current[:pry_local]")
|
34
|
+
ensure
|
35
|
+
Pry.current[:pry_local] = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def constant_scope(*names)
|
39
|
+
names.each do |name|
|
40
|
+
Object.remove_const name if Object.const_defined?(name)
|
41
|
+
end
|
42
|
+
|
43
|
+
yield
|
44
|
+
ensure
|
45
|
+
names.each do |name|
|
46
|
+
Object.remove_const name if Object.const_defined?(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Open a temp file and yield it to the block, closing it after
|
51
|
+
# @return [String] The path of the temp file
|
52
|
+
def temp_file(ext='.rb')
|
53
|
+
file = Tempfile.new(['pry', ext])
|
54
|
+
yield file
|
55
|
+
ensure
|
56
|
+
file.close(true) if file
|
57
|
+
File.unlink("#{file.path}c") if File.exists?("#{file.path}c") # rbx
|
58
|
+
end
|
59
|
+
|
60
|
+
def unindent(*args)
|
61
|
+
Pry::Helpers::CommandHelpers.unindent(*args)
|
62
|
+
end
|
63
|
+
|
64
|
+
def mock_command(cmd, args=[], opts={})
|
65
|
+
output = StringIO.new
|
66
|
+
pry = Pry.new(output: output)
|
67
|
+
ret = cmd.new(opts.merge(pry_instance: pry, :output => output)).call_safely(*args)
|
68
|
+
Struct.new(:output, :return).new(output.string, ret)
|
69
|
+
end
|
70
|
+
|
71
|
+
def mock_exception(*mock_backtrace)
|
72
|
+
StandardError.new.tap do |e|
|
73
|
+
e.define_singleton_method(:backtrace) { mock_backtrace }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def pry_tester(*args, &block)
|
79
|
+
if args.length == 0 || args[0].is_a?(Hash)
|
80
|
+
args.unshift(Pry.toplevel_binding)
|
81
|
+
end
|
82
|
+
|
83
|
+
PryTester.new(*args).tap do |t|
|
84
|
+
(class << t; self; end).class_eval(&block) if block
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def pry_eval(*eval_strs)
|
89
|
+
if eval_strs.first.is_a? String
|
90
|
+
binding = Pry.toplevel_binding
|
91
|
+
else
|
92
|
+
binding = Pry.binding_for(eval_strs.shift)
|
93
|
+
end
|
94
|
+
|
95
|
+
pry_tester(binding).eval(*eval_strs)
|
96
|
+
end
|
97
|
+
|
98
|
+
class PryTester
|
99
|
+
extend Forwardable
|
100
|
+
|
101
|
+
attr_reader :pry, :out
|
102
|
+
|
103
|
+
def_delegators :@pry, :eval_string, :eval_string=
|
104
|
+
|
105
|
+
def initialize(target = TOPLEVEL_BINDING, options = {})
|
106
|
+
@pry = Pry.new(options.merge(:target => target))
|
107
|
+
@history = options[:history]
|
108
|
+
|
109
|
+
@pry.inject_sticky_locals!
|
110
|
+
reset_output
|
111
|
+
end
|
112
|
+
|
113
|
+
def eval(*strs)
|
114
|
+
reset_output
|
115
|
+
result = nil
|
116
|
+
|
117
|
+
strs.flatten.each do |str|
|
118
|
+
str = "#{str.strip}\n"
|
119
|
+
@history.push str if @history
|
120
|
+
|
121
|
+
if @pry.process_command(str)
|
122
|
+
result = last_command_result_or_output
|
123
|
+
else
|
124
|
+
result = @pry.evaluate_ruby(str)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
result
|
129
|
+
end
|
130
|
+
|
131
|
+
def push(*lines)
|
132
|
+
Array(lines).flatten.each do |line|
|
133
|
+
@pry.eval(line)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def push_binding(context)
|
138
|
+
@pry.push_binding context
|
139
|
+
end
|
140
|
+
|
141
|
+
def last_output
|
142
|
+
@out.string if @out
|
143
|
+
end
|
144
|
+
|
145
|
+
def process_command(command_str)
|
146
|
+
@pry.process_command(command_str) or raise "Not a valid command"
|
147
|
+
last_command_result_or_output
|
148
|
+
end
|
149
|
+
|
150
|
+
def last_command_result
|
151
|
+
result = Pry.current[:pry_cmd_result]
|
152
|
+
result.retval if result
|
153
|
+
end
|
154
|
+
|
155
|
+
protected
|
156
|
+
|
157
|
+
def last_command_result_or_output
|
158
|
+
result = last_command_result
|
159
|
+
if result != Pry::Command::VOID_VALUE
|
160
|
+
result
|
161
|
+
else
|
162
|
+
last_output
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def reset_output
|
167
|
+
@out = StringIO.new
|
168
|
+
@pry.output = @out
|
169
|
+
end
|
170
|
+
end
|
data/lib/pry/version.rb
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
require 'pry/module_candidate'
|
2
|
+
|
3
|
+
class Pry
|
4
|
+
class << self
|
5
|
+
# If the given object is a `Pry::WrappedModule`, return it unaltered. If it's
|
6
|
+
# anything else, return it wrapped in a `Pry::WrappedModule` instance.
|
7
|
+
def WrappedModule(obj)
|
8
|
+
if obj.is_a? Pry::WrappedModule
|
9
|
+
obj
|
10
|
+
else
|
11
|
+
Pry::WrappedModule.new(obj)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class WrappedModule
|
17
|
+
include Helpers::BaseHelpers
|
18
|
+
include CodeObject::Helpers
|
19
|
+
|
20
|
+
attr_reader :wrapped
|
21
|
+
|
22
|
+
# Convert a string to a module.
|
23
|
+
#
|
24
|
+
# @param [String] mod_name
|
25
|
+
# @param [Binding] target The binding where the lookup takes place.
|
26
|
+
# @return [Module, nil] The module or `nil` (if conversion failed).
|
27
|
+
# @example
|
28
|
+
# Pry::WrappedModule.from_str("Pry::Code")
|
29
|
+
def self.from_str(mod_name, target=TOPLEVEL_BINDING)
|
30
|
+
if safe_to_evaluate?(mod_name, target)
|
31
|
+
Pry::WrappedModule.new(target.eval(mod_name))
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
rescue RescuableException
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
private
|
41
|
+
|
42
|
+
# We use this method to decide whether code is safe to eval. Method's are
|
43
|
+
# generally not, but everything else is.
|
44
|
+
# TODO: is just checking != "method" enough??
|
45
|
+
# TODO: see duplication of this method in Pry::CodeObject
|
46
|
+
# @param [String] str The string to lookup.
|
47
|
+
# @param [Binding] target Where the lookup takes place.
|
48
|
+
# @return [Boolean]
|
49
|
+
def safe_to_evaluate?(str, target)
|
50
|
+
return true if str.strip == "self"
|
51
|
+
kind = target.eval("defined?(#{str})")
|
52
|
+
kind =~ /variable|constant/
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @raise [ArgumentError] if the argument is not a `Module`
|
57
|
+
# @param [Module] mod
|
58
|
+
def initialize(mod)
|
59
|
+
raise ArgumentError, "Tried to initialize a WrappedModule with a non-module #{mod.inspect}" unless ::Module === mod
|
60
|
+
@wrapped = mod
|
61
|
+
@memoized_candidates = []
|
62
|
+
@host_file_lines = nil
|
63
|
+
@source = nil
|
64
|
+
@source_location = nil
|
65
|
+
@doc = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns an array of the names of the constants accessible in the wrapped
|
69
|
+
# module. This avoids the problem of accidentally calling the singleton
|
70
|
+
# method `Module.constants`.
|
71
|
+
# @param [Boolean] inherit Include the names of constants from included
|
72
|
+
# modules?
|
73
|
+
def constants(inherit = true)
|
74
|
+
Module.instance_method(:constants).bind(@wrapped).call(inherit)
|
75
|
+
end
|
76
|
+
|
77
|
+
# The prefix that would appear before methods defined on this class.
|
78
|
+
#
|
79
|
+
# i.e. the "String." or "String#" in String.new and String#initialize.
|
80
|
+
#
|
81
|
+
# @return String
|
82
|
+
def method_prefix
|
83
|
+
if singleton_class?
|
84
|
+
if Module === singleton_instance
|
85
|
+
"#{WrappedModule.new(singleton_instance).nonblank_name}."
|
86
|
+
else
|
87
|
+
"self."
|
88
|
+
end
|
89
|
+
else
|
90
|
+
"#{nonblank_name}#"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# The name of the Module if it has one, otherwise #<Class:0xf00>.
|
95
|
+
#
|
96
|
+
# @return [String]
|
97
|
+
def nonblank_name
|
98
|
+
if name.to_s == ""
|
99
|
+
wrapped.inspect
|
100
|
+
else
|
101
|
+
name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Is this a singleton class?
|
106
|
+
# @return [Boolean]
|
107
|
+
def singleton_class?
|
108
|
+
if Pry::Method.safe_send(wrapped, :respond_to?, :singleton_class?)
|
109
|
+
Pry::Method.safe_send(wrapped, :singleton_class?)
|
110
|
+
else
|
111
|
+
wrapped != Pry::Method.safe_send(wrapped, :ancestors).first
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Is this strictly a module? (does not match classes)
|
116
|
+
# @return [Boolean]
|
117
|
+
def module?
|
118
|
+
wrapped.instance_of?(Module)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Is this strictly a class?
|
122
|
+
# @return [Boolean]
|
123
|
+
def class?
|
124
|
+
wrapped.instance_of?(Class)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Get the instance associated with this singleton class.
|
128
|
+
#
|
129
|
+
# @raise ArgumentError: tried to get instance of non singleton class
|
130
|
+
#
|
131
|
+
# @return [Object]
|
132
|
+
def singleton_instance
|
133
|
+
raise ArgumentError, "tried to get instance of non singleton class" unless singleton_class?
|
134
|
+
|
135
|
+
if Helpers::BaseHelpers.jruby?
|
136
|
+
wrapped.to_java.attached
|
137
|
+
else
|
138
|
+
@singleton_instance ||= ObjectSpace.each_object(wrapped).detect{ |x| (class << x; self; end) == wrapped }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Forward method invocations to the wrapped module
|
143
|
+
def method_missing(method_name, *args, &block)
|
144
|
+
wrapped.send(method_name, *args, &block)
|
145
|
+
end
|
146
|
+
|
147
|
+
def respond_to?(method_name)
|
148
|
+
super || wrapped.respond_to?(method_name)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Retrieve the source location of a module. Return value is in same
|
152
|
+
# format as Method#source_location. If the source location
|
153
|
+
# cannot be found this method returns `nil`.
|
154
|
+
#
|
155
|
+
# @param [Module] mod The module (or class).
|
156
|
+
# @return [Array<String, Fixnum>, nil] The source location of the
|
157
|
+
# module (or class), or `nil` if no source location found.
|
158
|
+
def source_location
|
159
|
+
@source_location ||= primary_candidate.source_location
|
160
|
+
rescue Pry::RescuableException
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
|
164
|
+
# @return [String, nil] The associated file for the module (i.e
|
165
|
+
# the primary candidate: highest ranked monkeypatch).
|
166
|
+
def file
|
167
|
+
Array(source_location).first
|
168
|
+
end
|
169
|
+
alias_method :source_file, :file
|
170
|
+
|
171
|
+
# @return [Fixnum, nil] The associated line for the module (i.e
|
172
|
+
# the primary candidate: highest ranked monkeypatch).
|
173
|
+
def line
|
174
|
+
Array(source_location).last
|
175
|
+
end
|
176
|
+
alias_method :source_line, :line
|
177
|
+
|
178
|
+
# Returns documentation for the module.
|
179
|
+
# This documentation is for the primary candidate, if
|
180
|
+
# you would like documentation for other candidates use
|
181
|
+
# `WrappedModule#candidate` to select the candidate you're
|
182
|
+
# interested in.
|
183
|
+
# @raise [Pry::CommandError] If documentation cannot be found.
|
184
|
+
# @return [String] The documentation for the module.
|
185
|
+
def doc
|
186
|
+
@doc ||= primary_candidate.doc
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns the source for the module.
|
190
|
+
# This source is for the primary candidate, if
|
191
|
+
# you would like source for other candidates use
|
192
|
+
# `WrappedModule#candidate` to select the candidate you're
|
193
|
+
# interested in.
|
194
|
+
# @raise [Pry::CommandError] If source cannot be found.
|
195
|
+
# @return [String] The source for the module.
|
196
|
+
def source
|
197
|
+
@source ||= primary_candidate.source
|
198
|
+
end
|
199
|
+
|
200
|
+
# @return [String] Return the associated file for the
|
201
|
+
# module from YARD, if one exists.
|
202
|
+
def yard_file
|
203
|
+
YARD::Registry.at(name).file if yard_docs?
|
204
|
+
end
|
205
|
+
|
206
|
+
# @return [Fixnum] Return the associated line for the
|
207
|
+
# module from YARD, if one exists.
|
208
|
+
def yard_line
|
209
|
+
YARD::Registry.at(name).line if yard_docs?
|
210
|
+
end
|
211
|
+
|
212
|
+
# @return [String] Return the YARD docs for this module.
|
213
|
+
def yard_doc
|
214
|
+
YARD::Registry.at(name).docstring.to_s if yard_docs?
|
215
|
+
end
|
216
|
+
|
217
|
+
# Return a candidate for this module of specified rank. A `rank`
|
218
|
+
# of 0 is equivalent to the 'primary candidate', which is the
|
219
|
+
# module definition with the highest number of methods. A `rank`
|
220
|
+
# of 1 is the module definition with the second highest number of
|
221
|
+
# methods, and so on. Module candidates are necessary as modules
|
222
|
+
# can be reopened multiple times and in multiple places in Ruby,
|
223
|
+
# the candidate API gives you access to the module definition
|
224
|
+
# representing each of those reopenings.
|
225
|
+
# @raise [Pry::CommandError] If the `rank` is out of range. That
|
226
|
+
# is greater than `number_of_candidates - 1`.
|
227
|
+
# @param [Fixnum] rank
|
228
|
+
# @return [Pry::WrappedModule::Candidate]
|
229
|
+
def candidate(rank)
|
230
|
+
@memoized_candidates[rank] ||= Candidate.new(self, rank)
|
231
|
+
end
|
232
|
+
|
233
|
+
# @return [Fixnum] The number of candidate definitions for the
|
234
|
+
# current module.
|
235
|
+
def number_of_candidates
|
236
|
+
method_candidates.count
|
237
|
+
end
|
238
|
+
|
239
|
+
# @note On JRuby 1.9 and higher, in certain conditions, this method chucks
|
240
|
+
# away its ability to be quick (when there are lots of monkey patches,
|
241
|
+
# like in Rails). However, it should be efficient enough on other rubies.
|
242
|
+
# @see https://github.com/jruby/jruby/issues/525
|
243
|
+
# @return [Enumerator, Array] on JRuby 1.9 and higher returns Array, on
|
244
|
+
# other rubies returns Enumerator
|
245
|
+
def candidates
|
246
|
+
enum = Enumerator.new do |y|
|
247
|
+
(0...number_of_candidates).each do |num|
|
248
|
+
y.yield candidate(num)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
Pry::Helpers::BaseHelpers.jruby_19? ? enum.to_a : enum
|
252
|
+
end
|
253
|
+
|
254
|
+
# @return [Boolean] Whether YARD docs are available for this module.
|
255
|
+
def yard_docs?
|
256
|
+
!!(defined?(YARD) && YARD::Registry.at(name))
|
257
|
+
end
|
258
|
+
|
259
|
+
# @param [Fixnum] times How far to travel up the ancestor chain.
|
260
|
+
# @return [Pry::WrappedModule, nil] The wrapped module that is the
|
261
|
+
# superclass.
|
262
|
+
# When `self` is a `Module` then return the
|
263
|
+
# nth ancestor, otherwise (in the case of classes) return the
|
264
|
+
# nth ancestor that is a class.
|
265
|
+
def super(times=1)
|
266
|
+
return self if times.zero?
|
267
|
+
|
268
|
+
if wrapped.is_a?(Class)
|
269
|
+
sup = ancestors.select { |v| v.is_a?(Class) }[times]
|
270
|
+
else
|
271
|
+
sup = ancestors[times]
|
272
|
+
end
|
273
|
+
|
274
|
+
Pry::WrappedModule(sup) if sup
|
275
|
+
end
|
276
|
+
|
277
|
+
private
|
278
|
+
|
279
|
+
# @return [Pry::WrappedModule::Candidate] The candidate with the
|
280
|
+
# highest rank, that is the 'monkey patch' of this module with the
|
281
|
+
# highest number of methods, which contains a source code line that
|
282
|
+
# defines the module. It is considered the 'canonical' definition
|
283
|
+
# for the module. In the absense of a suitable candidate, the
|
284
|
+
# candidate of rank 0 will be returned, or a CommandError raised if
|
285
|
+
# there are no candidates at all.
|
286
|
+
def primary_candidate
|
287
|
+
@primary_candidate ||= candidates.find { |c| c.file } ||
|
288
|
+
# This will raise an exception if there is no candidate at all.
|
289
|
+
candidate(0)
|
290
|
+
end
|
291
|
+
|
292
|
+
# @return [Array<Array<Pry::Method>>] The array of `Pry::Method` objects,
|
293
|
+
# there are two associated with each candidate. The first is the 'base
|
294
|
+
# method' for a candidate and it serves as the start point for
|
295
|
+
# the search in uncovering the module definition. The second is
|
296
|
+
# the last method defined for that candidate and it is used to
|
297
|
+
# speed up source code extraction.
|
298
|
+
def method_candidates
|
299
|
+
@method_candidates ||= all_source_locations_by_popularity.map do |group|
|
300
|
+
methods_sorted_by_source_line = group.last.sort_by(&:source_line)
|
301
|
+
[methods_sorted_by_source_line.first, methods_sorted_by_source_line.last]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# A helper method.
|
306
|
+
def all_source_locations_by_popularity
|
307
|
+
return @all_source_locations_by_popularity if @all_source_locations_by_popularity
|
308
|
+
|
309
|
+
ims = all_relevant_methods_for(wrapped)
|
310
|
+
@all_source_locations_by_popularity = ims.group_by { |v| Array(v.source_location).first }.
|
311
|
+
sort_by do |path, methods|
|
312
|
+
expanded = File.expand_path(path)
|
313
|
+
load_order = $LOADED_FEATURES.index{ |file| expanded.end_with?(file) }
|
314
|
+
|
315
|
+
[-methods.size, load_order || (1.0 / 0.0)]
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# We only want methods that have a non-nil `source_location`. We also
|
320
|
+
# skip some spooky internal methods.
|
321
|
+
# (i.e we skip `__class_init__` because it's an odd rbx specific thing that causes tests to fail.)
|
322
|
+
# @return [Array<Pry::Method>]
|
323
|
+
def all_relevant_methods_for(mod)
|
324
|
+
methods = all_methods_for(mod).select(&:source_location).
|
325
|
+
reject{ |x| x.name == '__class_init__' || method_defined_by_forwardable_module?(x) }
|
326
|
+
|
327
|
+
return methods unless methods.empty?
|
328
|
+
|
329
|
+
safe_send(mod, :constants).map do |const_name|
|
330
|
+
if const = nested_module?(mod, const_name)
|
331
|
+
all_relevant_methods_for(const)
|
332
|
+
else
|
333
|
+
[]
|
334
|
+
end
|
335
|
+
end.flatten
|
336
|
+
end
|
337
|
+
|
338
|
+
# Return all methods (instance methods and class methods) for a
|
339
|
+
# given module.
|
340
|
+
# @return [Array<Pry::Method>]
|
341
|
+
def all_methods_for(mod)
|
342
|
+
Pry::Method.all_from_obj(mod, false) + Pry::Method.all_from_class(mod, false)
|
343
|
+
end
|
344
|
+
|
345
|
+
def nested_module?(parent, name)
|
346
|
+
return if safe_send(parent, :autoload?, name)
|
347
|
+
child = safe_send(parent, :const_get, name)
|
348
|
+
return unless Module === child
|
349
|
+
return unless safe_send(child, :name) == "#{safe_send(parent, :name)}::#{name}"
|
350
|
+
child
|
351
|
+
end
|
352
|
+
|
353
|
+
# Detect methods that are defined with `def_delegator` from the Forwardable
|
354
|
+
# module. We want to reject these methods as they screw up module
|
355
|
+
# extraction since the `source_location` for such methods points at forwardable.rb
|
356
|
+
# TODO: make this more robust as valid user-defined files called
|
357
|
+
# forwardable.rb are also skipped.
|
358
|
+
def method_defined_by_forwardable_module?(method)
|
359
|
+
method.source_location.first =~ /forwardable\.rb/
|
360
|
+
end
|
361
|
+
|
362
|
+
# memoized lines for file
|
363
|
+
def lines_for_file(file)
|
364
|
+
@lines_for_file ||= {}
|
365
|
+
|
366
|
+
if file == Pry.eval_path
|
367
|
+
@lines_for_file[file] ||= Pry.line_buffer.drop(1)
|
368
|
+
else
|
369
|
+
@lines_for_file[file] ||= File.readlines(file)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|