Support delegates for BasicObject
For BasicObject, bind the Kernel respond_to? instance method to the object and call it instead of calling the method directly. Also, use bind_call(recv, ...) for better performance. Fixes [Bug #16127]
This commit is contained in:
parent
4171909695
commit
2322c94dd6
Notes:
git
2019-10-11 05:15:39 +09:00
@ -79,10 +79,10 @@ class Delegator < BasicObject
|
|||||||
r = true
|
r = true
|
||||||
target = self.__getobj__ {r = false}
|
target = self.__getobj__ {r = false}
|
||||||
|
|
||||||
if r && target.respond_to?(m)
|
if r && target_respond_to?(target, m, false)
|
||||||
target.__send__(m, *args, &block)
|
target.__send__(m, *args, &block)
|
||||||
elsif ::Kernel.method_defined?(m) || ::Kernel.private_method_defined?(m)
|
elsif ::Kernel.method_defined?(m) || ::Kernel.private_method_defined?(m)
|
||||||
::Kernel.instance_method(m).bind(self).(*args, &block)
|
::Kernel.instance_method(m).bind_call(self, *args, &block)
|
||||||
else
|
else
|
||||||
super(m, *args, &block)
|
super(m, *args, &block)
|
||||||
end
|
end
|
||||||
@ -95,14 +95,24 @@ class Delegator < BasicObject
|
|||||||
def respond_to_missing?(m, include_private)
|
def respond_to_missing?(m, include_private)
|
||||||
r = true
|
r = true
|
||||||
target = self.__getobj__ {r = false}
|
target = self.__getobj__ {r = false}
|
||||||
r &&= target.respond_to?(m, include_private)
|
r &&= target_respond_to?(target, m, include_private)
|
||||||
if r && include_private && !target.respond_to?(m, false)
|
if r && include_private && !target_respond_to?(target, m, false)
|
||||||
warn "delegator does not forward private method \##{m}", uplevel: 3
|
warn "delegator does not forward private method \##{m}", uplevel: 3
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
r
|
r
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Handle BasicObject instances
|
||||||
|
private def target_respond_to?(target, m, include_private)
|
||||||
|
case target
|
||||||
|
when Object
|
||||||
|
target.respond_to?(m, include_private)
|
||||||
|
else
|
||||||
|
::Kernel.instance_method(:respond_to?).bind_call(target, m, include_private)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Returns the methods available to this delegate object as the union
|
# Returns the methods available to this delegate object as the union
|
||||||
# of this object's and \_\_getobj\_\_ methods.
|
# of this object's and \_\_getobj\_\_ methods.
|
||||||
|
@ -322,4 +322,12 @@ class TestDelegateClass < Test::Unit::TestCase
|
|||||||
delegate.constants
|
delegate.constants
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_basicobject
|
||||||
|
o = BasicObject.new
|
||||||
|
def o.bar; 1; end
|
||||||
|
delegate = SimpleDelegator.new(o)
|
||||||
|
assert_equal(1, delegate.bar)
|
||||||
|
assert_raise(NoMethodError, /undefined method `foo' for/) { delegate.foo }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user