mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
Add (instance_)resolution_order to Pry::Method
This commit is contained in:
parent
1c0a82f888
commit
fd26a55627
2 changed files with 103 additions and 0 deletions
|
@ -89,6 +89,29 @@ class Pry
|
||||||
all_from_common(obj, :method)
|
all_from_common(obj, :method)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get every `Class` and `Module`, in order, that will be checked when looking
|
||||||
|
# for an instance method to call on this object.
|
||||||
|
# @param [Object] obj
|
||||||
|
# @return [Array[Class, Module]]
|
||||||
|
def resolution_order(obj)
|
||||||
|
if obj.is_a?(Class)
|
||||||
|
singleton_class_resolution_order(obj) + instance_resolution_order(Class)
|
||||||
|
else
|
||||||
|
klass = singleton_class(obj) rescue obj.class
|
||||||
|
instance_resolution_order(klass)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get every `Class` and `Module`, in order, that will be checked when looking
|
||||||
|
# for methods on instances of the given `Class` or `Module`.
|
||||||
|
# This does not treat singleton classes of classes specially.
|
||||||
|
# @param [Class, Module] klass
|
||||||
|
# @return [Array[Class, Module]]
|
||||||
|
def instance_resolution_order(klass)
|
||||||
|
# include klass in case it is a singleton class,
|
||||||
|
([klass] + klass.ancestors).uniq
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# See all_from_class and all_from_obj.
|
# See all_from_class and all_from_obj.
|
||||||
|
@ -103,6 +126,20 @@ class Pry
|
||||||
end
|
end
|
||||||
end.flatten(1)
|
end.flatten(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get the singleton classes of superclasses that could define methods on
|
||||||
|
# the given class object, and any modules they include.
|
||||||
|
# If a module is included at multiple points in the ancestry, only
|
||||||
|
# the lowest copy will be returned.
|
||||||
|
def singleton_class_resolution_order(klass)
|
||||||
|
resolution_order = klass.ancestors.map do |anc|
|
||||||
|
[singleton_class(anc)] + singleton_class(anc).included_modules if anc.is_a?(Class)
|
||||||
|
end.compact.flatten(1)
|
||||||
|
|
||||||
|
resolution_order.reverse.uniq.reverse - Class.included_modules
|
||||||
|
end
|
||||||
|
|
||||||
|
def singleton_class(obj); class << obj; self; end end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A new instance of `Pry::Method` wrapping the given `::Method`, `UnboundMethod`, or `Proc`.
|
# A new instance of `Pry::Method` wrapping the given `::Method`, `UnboundMethod`, or `Proc`.
|
||||||
|
|
|
@ -215,6 +215,72 @@ describe Pry::Method do
|
||||||
Pry::Method.all_from_obj(@class).detect{ |x| x.name == 'allocate' }.owner.should == (class << @class; self; end)
|
Pry::Method.all_from_obj(@class).detect{ |x| x.name == 'allocate' }.owner.should == (class << @class; self; end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'method resolution order' do
|
||||||
|
module LS
|
||||||
|
class Top; end
|
||||||
|
|
||||||
|
class Next < Top; end
|
||||||
|
|
||||||
|
module M; end
|
||||||
|
module N; include M; end
|
||||||
|
module O; include M; end
|
||||||
|
module P; end
|
||||||
|
|
||||||
|
class Low < Next; include N; include P; end
|
||||||
|
class Lower < Low; extend N; end
|
||||||
|
class Bottom < Lower; extend O; end
|
||||||
|
end
|
||||||
|
|
||||||
|
def singleton_class(obj); class << obj; self; end; end
|
||||||
|
|
||||||
|
it "should look at a class and then its superclass" do
|
||||||
|
Pry::Method.instance_resolution_order(LS::Next).should == [LS::Next] + Pry::Method.instance_resolution_order(LS::Top)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include the included modules between a class and its superclass" do
|
||||||
|
Pry::Method.instance_resolution_order(LS::Low).should == [LS::Low, LS::P, LS::N, LS::M] + Pry::Method.instance_resolution_order(LS::Next)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not include modules extended into the class" do
|
||||||
|
Pry::Method.instance_resolution_order(LS::Bottom).should == [LS::Bottom] + Pry::Method.instance_resolution_order(LS::Lower)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include included modules for Modules" do
|
||||||
|
Pry::Method.instance_resolution_order(LS::O).should == [LS::O, LS::M]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include the singleton class of objects" do
|
||||||
|
obj = LS::Low.new
|
||||||
|
Pry::Method.resolution_order(obj).should == [singleton_class(obj)] + Pry::Method.instance_resolution_order(LS::Low)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not include singleton classes of numbers" do
|
||||||
|
Pry::Method.resolution_order(4).should == Pry::Method.instance_resolution_order(Fixnum)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include singleton classes for classes" do
|
||||||
|
Pry::Method.resolution_order(LS::Low).should == [singleton_class(LS::Low)] + Pry::Method.resolution_order(LS::Next)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include modules included into singleton classes" do
|
||||||
|
Pry::Method.resolution_order(LS::Lower).should == [singleton_class(LS::Lower), LS::N, LS::M] + Pry::Method.resolution_order(LS::Low)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include modules at most once" do
|
||||||
|
Pry::Method.resolution_order(LS::Bottom).count(LS::M).should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include modules at the point which they would be reached" do
|
||||||
|
Pry::Method.resolution_order(LS::Bottom).should == [singleton_class(LS::Bottom), LS::O] + (Pry::Method.resolution_order(LS::Lower))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include the Pry::Method.instance_resolution_order of Class after the singleton classes" do
|
||||||
|
Pry::Method.resolution_order(LS::Top).should ==
|
||||||
|
[singleton_class(LS::Top), singleton_class(Object), (defined? BasicObject) && singleton_class(BasicObject)].compact +
|
||||||
|
Pry::Method.instance_resolution_order(Class)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue