1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

Pry::Method.from_binding support for super methods. [Fixes #378]

This commit is contained in:
Conrad Irwin 2011-12-23 22:44:01 +00:00
parent 322460f06d
commit 26cef7e411
2 changed files with 92 additions and 4 deletions

View file

@ -49,11 +49,43 @@ class Pry
if [:__script__, nil, :__binding__, :__binding_impl__].include?(meth_name)
nil
else
begin
method = begin
new(b.eval("method(#{meth_name.to_s.inspect})"))
rescue NameError, NoMethodError
Disowned.new(b.eval('self'), meth_name.to_s)
end
# it's possible in some cases that the method we find by this approach is a sub-method of
# the one we're currently in, consider:
#
# class A; def b; binding.pry; end; end
# class B < A; def b; super; end; end
#
# Given that we can normally find the source_range of methods, and that we know which
# __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
#
# This obviously won't work if the source is unavaiable for some reason, or if both
# methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
# is broken.
#
guess = method
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
if (guess.source_file && guess.source_range rescue false) &&
File.expand_path(guess.source_file) == File.expand_path(b.eval('__FILE__')) &&
guess.source_range.include?(b.eval('__LINE__'))
return guess
else
guess = guess.super
end
end
# Uhoh... none of the methods in the chain had the right __FILE__ and __LINE__
# This may be caused by rbx https://github.com/rubinius/rubinius/issues/953,
# or other unknown circumstances (TODO: we should warn the user when this happens)
method
end
end
@ -256,6 +288,12 @@ class Pry
source_location.nil? ? nil : source_location.last
end
# @return [Range, nil] The range of lines in `source_file` which contain
# the method's definition, or `nil` if that information is unavailable.
def source_range
source_location.nil? ? nil : (source_line)...(source_line + source.lines.count)
end
# @return [Symbol] The visibility of the method. May be `:public`,
# `:protected`, or `:private`.
def visibility

View file

@ -74,6 +74,56 @@ describe Pry::Method do
end
end
describe '.from_binding' do
it 'should be able to pick a method out of a binding' do
Pry::Method.from_binding(Class.new{ def self.foo; binding; end }.foo).name.should == "foo"
end
it 'should NOT find a method from the special pry bindings' do
Pry::Method.from_binding(5.__binding__).should == nil
end
it 'should NOT find a method from the toplevel binding' do
Pry::Method.from_binding(TOPLEVEL_BINDING).should == nil
end
it "should find methods that have been undef'd" do
m = Pry::Method.from_binding(Class.new do
def self.bar
class << self; undef bar; end
binding
end
end.bar)
m.name.should == "bar"
end
# Our source_location trick doesn't work, due to https://github.com/rubinius/rubinius/issues/953
unless defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/
it 'should find the super method correctly' do
a = Class.new{ def gag; binding; end; def self.line; __LINE__; end }
b = Class.new(a){ def gag; super; end }
g = b.new.gag
m = Pry::Method.from_binding(g)
m.owner.should == a
m.source_line.should == a.line
m.name.should == "gag"
end
end
it 'should find the right method if a super method exists' do
a = Class.new{ def gag; binding; end; }
b = Class.new(a){ def gag; super; binding; end; def self.line; __LINE__; end }
m = Pry::Method.from_binding(b.new.gag)
m.owner.should == b
m.source_line.should == b.line
m.name.should == "gag"
end
end
describe 'all_from_class' do
def should_find_method(name)
Pry::Method.all_from_class(@class).map(&:name).should.include(name)