class Pry::Method::WeirdMethodLocator
Pry::Method and return it, or return nil if we fail.
When we locate the method that matches the Binding we wrap it in
method table.
inheritance chain, and for 2. we search laterally along the object’s
was renamed to something else. For 1. we search vertically up the
2. The Pry::Method represents a method of the same name while the original
1. The Pry::Method is from a subclass
the Binding:
there are primarily two situations where the seed method doesn’t match
Given a ‘Binding` from inside a method and a ’seed’ Pry::Method object,
object captured by a binding.
This class is responsible for locating the real ‘Pry::Method`
def all_methods_for(obj)
def all_methods_for(obj) obj.public_methods(false) + obj.private_methods(false) + obj.protected_methods(false) end
def expanded_source_location(source_location)
def expanded_source_location(source_location) return unless source_location if pry_file? source_location else [File.expand_path(source_location.first), source_location.last] end end
def find_method
-
(Pry::Method, nil)- The Pry::Method that matches the
def find_method find_method_in_superclass || find_renamed_method end
def find_method_in_superclass
-
(Pry::Method, nil)- The Pry::Method representing the
def find_method_in_superclass guess = method return guess if skip_superclass_search? while guess # needs rescue if this is a Disowned method or a C method or something... # TODO: Fix up the exception handling so we don't need a bare rescue return guess if normal_method?(guess) break if guess == guess.super guess = guess.super end # Uhoh... none of the methods in the chain had the right `__FILE__` and # `__LINE__` due to unknown circumstances. # TODO: we should warn the user when this happens. nil end
def find_renamed_method
-
(Pry::Method, nil)- The Pry::Method representing the
def find_renamed_method return unless valid_file?(target_file) alias_name = all_methods_for(target_self).find do |v| location = target_self.method(v).source_location location && expanded_source_location(location) == renamed_method_source_location end alias_name && Pry::Method(target_self.method(alias_name)) end
def index_to_line_number(index)
def index_to_line_number(index) # Pry.line_buffer is 0-indexed pry_file? ? index : index + 1 end
def initialize(method, target)
-
target(Binding) -- The Binding that captures the method -
method(Pry::Method) -- The seed method.
def initialize(method, target) @method = method @target = target end
def lines_for_file(file)
def lines_for_file(file) @lines_for_file ||= {} @lines_for_file[file] ||= if Pry.eval_path == file Pry.line_buffer else File.readlines(file) end end
def lost_method?
-
(Boolean)- Whether the Pry::Method is unrecoverable
def lost_method? !!(find_method.nil? && renamed_method_source_location) end
def normal_method?(method, binding)
-
(Boolean)-
Parameters:
-
binding(Binding) -- -
method(Pry::Method) --
def normal_method?(method, binding) if method && method.source_file && method.source_range if binding.respond_to?(:source_location) binding_file, binding_line = binding.source_location else binding_file = binding.eval('__FILE__') binding_line = binding.eval('__LINE__') end (File.expand_path(method.source_file) == File.expand_path(binding_file)) && method.source_range.include?(binding_line) end rescue StandardError false end
def normal_method?(method)
def normal_method?(method) self.class.normal_method?(method, target) end
def pry_file?
def pry_file? file = if target.respond_to?(:source_location) target.source_location.first else target.eval('__FILE__') end Pry.eval_path == file end
def renamed_method_source_location
-
(Array- The `source_location` of the)
def renamed_method_source_location if defined?(@original_method_source_location) return @original_method_source_location end source_index = lines_for_file(target_file)[0..(target_line - 1)].rindex do |v| Pry::Method.method_definition?(method.name, v) end @original_method_source_location = source_index && [target_file, index_to_line_number(source_index)] end
def skip_superclass_search?
def skip_superclass_search? target_mod = @target.eval('self').class target_mod.ancestors.take_while { |mod| mod != target_mod }.any? end
def target_file
def target_file file = if target.respond_to?(:source_location) target.source_location.first else target.eval('__FILE__') end pry_file? ? file : File.expand_path(file) end
def target_line
def target_line if target.respond_to?(:source_location) target.source_location.last else target.eval('__LINE__') end end
def target_self
def target_self target.eval('self') end
def valid_file?(file)
def valid_file?(file) (File.exist?(file) && !File.directory?(file)) || Pry.eval_path == file end
def weird_method?(method, binding)
def weird_method?(method, binding) !normal_method?(method, binding) end