1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
# frozen_string_literal: true
class Pry
# This class is responsible for taking a string (identifying a
# command/class/method/etc) and returning the relevant type of object.
# For example, if the user looks up "show-source" then a
# `Pry::Command` will be returned. Alternatively, if the user passes in "Pry#repl" then
# a `Pry::Method` object will be returned.
#
# The `CodeObject.lookup` method is responsible for 1. figuring out what kind of
# object the user wants (applying precedence rules in doing so -- i.e methods
# get precedence over commands with the same name) and 2. Returning
# the appropriate object. If the user fails to provide a string
# identifer for the object (i.e they pass in `nil` or "") then the
# object looked up will be the 'current method' or 'current class'
# associated with the Binding.
#
# TODO: This class is a clusterfuck. We need a much more robust
# concept of what a "Code Object" really is. Currently
# commands/classes/candidates/methods and so on just share a very
# ill-defined interface.
class CodeObject
module Helpers
# we need this helper as some Pry::Method objects can wrap Procs
# @return [Boolean]
def real_method_object?
is_a?(::Method) || is_a?(::UnboundMethod)
end
def c_method?
real_method_object? && source_type == :c
end
def module_with_yard_docs?
is_a?(WrappedModule) && yard_docs?
end
def command?
is_a?(Module) && self <= Pry::Command
end
# @return [Boolean] `true` if this module was defined by means of the C API,
# `false` if it's a Ruby module.
# @note If a module defined by C was extended with a lot of methods written
# in Ruby, this method would fail.
def c_module?
return unless is_a?(WrappedModule)
method_locations = wrapped.methods(false).map do |m|
wrapped.method(m).source_location
end
method_locations.concat(
wrapped.instance_methods(false).map do |m|
wrapped.instance_method(m).source_location
end
)
c_methods = method_locations.grep(nil).count
ruby_methods = method_locations.count - c_methods
c_methods > ruby_methods
end
end
include Pry::Helpers::CommandHelpers
class << self
def lookup(str, pry_instance, options = {})
co = new(str, pry_instance, options)
co.default_lookup || co.method_or_class_lookup ||
co.command_lookup || co.empty_lookup
end
end
attr_accessor :str
attr_accessor :target
attr_accessor :pry_instance
attr_accessor :super_level
def initialize(str, pry_instance, options = {})
options = {
super: 0
}.merge!(options)
@str = str
@pry_instance = pry_instance
@target = pry_instance.current_context
@super_level = options[:super]
end
# TODO: just make it so find_command_by_match_or_listing doesn't raise?
def command_lookup
pry_instance.commands.find_command_by_match_or_listing(str)
rescue StandardError
nil
end
# when no paramter is given (i.e CodeObject.lookup(nil)), then we
# lookup the 'current object' from the binding.
def empty_lookup
return nil if str && !str.empty?
obj = if internal_binding?(target)
mod = target_self.is_a?(Module) ? target_self : target_self.class
Pry::WrappedModule(mod)
else
Pry::Method.from_binding(target)
end
# respect the super level (i.e user might have specified a
# --super flag to show-source)
lookup_super(obj, super_level)
end
# lookup variables and constants and `self` that are not modules
def default_lookup
# we skip instance methods as we want those to fall through to
# method_or_class_lookup()
if safe_to_evaluate?(str) && !looks_like_an_instance_method?(str)
obj = target.eval(str)
# restrict to only objects we KNOW for sure support the full API
# Do NOT support just any object that responds to source_location
if sourcable_object?(obj)
Pry::Method(obj)
elsif !obj.is_a?(Module)
Pry::WrappedModule(obj.class)
end
end
rescue Pry::RescuableException
nil
end
def method_or_class_lookup
obj =
case str
when /\S+\(\)\z/
Pry::Method.from_str(str.sub(/\(\)\z/, ''), target) ||
Pry::WrappedModule.from_str(str, target)
else
Pry::WrappedModule.from_str(str, target) ||
Pry::Method.from_str(str, target)
end
lookup_super(obj, super_level)
end
private
def sourcable_object?(obj)
[::Proc, ::Method, ::UnboundMethod].any? { |o| obj.is_a?(o) }
end
# Returns true if `str` looks like a method, i.e Klass#method
# We need to consider this case because method lookups should fall
# through to the `method_or_class_lookup()` method but a
# defined?() on a "Klass#method` string will see the `#` as a
# comment and only evaluate the `Klass` part.
# @param [String] str
# @return [Boolean] Whether the string looks like an instance method.
def looks_like_an_instance_method?(str)
str =~ /\S#\S/
end
# We use this method to decide whether code is safe to eval. Method's are
# generally not, but everything else is.
# TODO: is just checking != "method" enough??
# TODO: see duplication of this method in Pry::WrappedModule
# @param [String] str The string to lookup
# @return [Boolean]
def safe_to_evaluate?(str)
return true if str.strip == "self"
return false if str =~ /%/
kind = target.eval("defined?(#{str})")
kind =~ /variable|constant/
end
def target_self
target.eval('self')
end
# grab the nth (`super_level`) super of `obj
# @param [Object] obj
# @param [Fixnum] super_level How far up the super chain to ascend.
def lookup_super(obj, super_level)
return unless obj
sup = obj.super(super_level)
raise Pry::CommandError, "No superclass found for #{obj.wrapped}" unless sup
sup
end
end
end
|