mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

This restores compatibility with previous versions. This behavior
was previously undefined, but it makes sense for the name of the
defined method to be returned.
a52ef3451e
402 lines
9.9 KiB
Ruby
402 lines
9.9 KiB
Ruby
# frozen_string_literal: false
|
|
require 'test/unit'
|
|
require 'forwardable'
|
|
|
|
class TestForwardable < Test::Unit::TestCase
|
|
INTEGER = 42
|
|
RECEIVER = BasicObject.new
|
|
RETURNED1 = BasicObject.new
|
|
RETURNED2 = BasicObject.new
|
|
|
|
class << RECEIVER
|
|
def delegated1
|
|
RETURNED1
|
|
end
|
|
|
|
def delegated2
|
|
RETURNED2
|
|
end
|
|
|
|
def delegated1_kw(**kw)
|
|
[RETURNED1, kw]
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegator
|
|
%i[def_delegator def_instance_delegator].each do |m|
|
|
ret = nil
|
|
cls = forwardable_class do
|
|
ret = __send__ m, :@receiver, :delegated1
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_equal :delegated1, ret
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegator_constant
|
|
%i[def_delegator def_instance_delegator].each do |m|
|
|
cls = forwardable_class do
|
|
__send__ m, 'TestForwardable::INTEGER', :to_i
|
|
end
|
|
|
|
assert_equal 42, cls.new.to_i
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegator_kw
|
|
%i[def_delegator def_instance_delegator].each do |m|
|
|
cls = forwardable_class do
|
|
__send__ m, :@receiver, :delegated1_kw
|
|
end
|
|
|
|
ary = cls.new.delegated1_kw b: 1
|
|
assert_same RETURNED1, ary[0]
|
|
assert_equal({b: 1}, ary[1])
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegator_using_args_method_as_receiver
|
|
%i[def_delegator def_instance_delegator].each do |m|
|
|
cls = forwardable_class(
|
|
receiver_name: :args,
|
|
type: :method,
|
|
visibility: :private
|
|
) do
|
|
__send__ m, :args, :delegated1
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegator_using_block_method_as_receiver
|
|
%i[def_delegator def_instance_delegator].each do |m|
|
|
cls = forwardable_class(
|
|
receiver_name: :block,
|
|
type: :method,
|
|
visibility: :private
|
|
) do
|
|
__send__ m, :block, :delegated1
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegators
|
|
%i[def_delegators def_instance_delegators].each do |m|
|
|
cls = forwardable_class do
|
|
__send__ m, :@receiver, :delegated1, :delegated2
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_same RETURNED2, cls.new.delegated2
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegators_using_args_method_as_receiver
|
|
%i[def_delegators def_instance_delegators].each do |m|
|
|
cls = forwardable_class(
|
|
receiver_name: :args,
|
|
type: :method,
|
|
visibility: :private
|
|
) do
|
|
__send__ m, :args, :delegated1, :delegated2
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_same RETURNED2, cls.new.delegated2
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegators_using_block_method_as_receiver
|
|
%i[def_delegators def_instance_delegators].each do |m|
|
|
cls = forwardable_class(
|
|
receiver_name: :block,
|
|
type: :method,
|
|
visibility: :private
|
|
) do
|
|
__send__ m, :block, :delegated1, :delegated2
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_same RETURNED2, cls.new.delegated2
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegators_send_id
|
|
%i[def_delegators def_instance_delegators].each do |m|
|
|
cls = forwardable_class do
|
|
attr_reader :receiver
|
|
__send__ m, :@receiver, :__send__, :__id__
|
|
end
|
|
|
|
assert_not_equal cls.new.__id__, cls.new.receiver.__id__
|
|
assert_not_equal cls.new.__send__(:__id__), cls.new.receiver.__send__(:__id__)
|
|
end
|
|
end
|
|
|
|
def test_instance_delegate
|
|
%i[delegate instance_delegate].each do |m|
|
|
cls = forwardable_class do
|
|
__send__ m, delegated1: :@receiver, delegated2: :@receiver
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_same RETURNED2, cls.new.delegated2
|
|
|
|
cls = forwardable_class do
|
|
__send__ m, %i[delegated1 delegated2] => :@receiver
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_same RETURNED2, cls.new.delegated2
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegate_using_args_method_as_receiver
|
|
%i[delegate instance_delegate].each do |m|
|
|
cls = forwardable_class(
|
|
receiver_name: :args,
|
|
type: :method,
|
|
visibility: :private
|
|
) do
|
|
__send__ m, delegated1: :args, delegated2: :args
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_same RETURNED2, cls.new.delegated2
|
|
end
|
|
end
|
|
|
|
def test_def_instance_delegate_using_block_method_as_receiver
|
|
%i[delegate instance_delegate].each do |m|
|
|
cls = forwardable_class(
|
|
receiver_name: :block,
|
|
type: :method,
|
|
visibility: :private
|
|
) do
|
|
__send__ m, delegated1: :block, delegated2: :block
|
|
end
|
|
|
|
assert_same RETURNED1, cls.new.delegated1
|
|
assert_same RETURNED2, cls.new.delegated2
|
|
end
|
|
end
|
|
|
|
def test_class_single_delegator
|
|
%i[def_delegator def_single_delegator].each do |m|
|
|
ret = nil
|
|
cls = single_forwardable_class do
|
|
ret = __send__ m, :@receiver, :delegated1
|
|
end
|
|
|
|
assert_same RETURNED1, cls.delegated1
|
|
assert_equal :delegated1, ret
|
|
end
|
|
end
|
|
|
|
def test_class_single_delegators
|
|
%i[def_delegators def_single_delegators].each do |m|
|
|
cls = single_forwardable_class do
|
|
__send__ m, :@receiver, :delegated1, :delegated2
|
|
end
|
|
|
|
assert_same RETURNED1, cls.delegated1
|
|
assert_same RETURNED2, cls.delegated2
|
|
end
|
|
end
|
|
|
|
def test_class_single_delegate
|
|
%i[delegate single_delegate].each do |m|
|
|
cls = single_forwardable_class do
|
|
__send__ m, delegated1: :@receiver, delegated2: :@receiver
|
|
end
|
|
|
|
assert_same RETURNED1, cls.delegated1
|
|
assert_same RETURNED2, cls.delegated2
|
|
|
|
cls = single_forwardable_class do
|
|
__send__ m, %i[delegated1 delegated2] => :@receiver
|
|
end
|
|
|
|
assert_same RETURNED1, cls.delegated1
|
|
assert_same RETURNED2, cls.delegated2
|
|
end
|
|
end
|
|
|
|
def test_obj_single_delegator
|
|
%i[def_delegator def_single_delegator].each do |m|
|
|
obj = single_forwardable_object do
|
|
__send__ m, :@receiver, :delegated1
|
|
end
|
|
|
|
assert_same RETURNED1, obj.delegated1
|
|
end
|
|
end
|
|
|
|
def test_obj_single_delegators
|
|
%i[def_delegators def_single_delegators].each do |m|
|
|
obj = single_forwardable_object do
|
|
__send__ m, :@receiver, :delegated1, :delegated2
|
|
end
|
|
|
|
assert_same RETURNED1, obj.delegated1
|
|
assert_same RETURNED2, obj.delegated2
|
|
end
|
|
end
|
|
|
|
def test_obj_single_delegators_send_id
|
|
%i[def_delegators def_single_delegators].each do |m|
|
|
obj = single_forwardable_object do
|
|
singleton_class.__send__ :attr_reader, :receiver
|
|
__send__ m, :@receiver, :__send__, :__id__
|
|
end
|
|
|
|
assert_not_equal obj.__id__, obj.receiver.__id__
|
|
assert_not_equal obj.__send__(:__id__), obj.receiver.__send__(:__id__)
|
|
end
|
|
end
|
|
|
|
def test_obj_single_delegate
|
|
%i[delegate single_delegate].each do |m|
|
|
obj = single_forwardable_object do
|
|
__send__ m, delegated1: :@receiver, delegated2: :@receiver
|
|
end
|
|
|
|
assert_same RETURNED1, obj.delegated1
|
|
assert_same RETURNED2, obj.delegated2
|
|
|
|
obj = single_forwardable_object do
|
|
__send__ m, %i[delegated1 delegated2] => :@receiver
|
|
end
|
|
|
|
assert_same RETURNED1, obj.delegated1
|
|
assert_same RETURNED2, obj.delegated2
|
|
end
|
|
end
|
|
|
|
class Foo
|
|
extend Forwardable
|
|
|
|
attr_accessor :bar
|
|
def_delegator :bar, :baz
|
|
def_delegator :caller, :itself, :c
|
|
end
|
|
|
|
def test_backtrace_adjustment
|
|
obj = Foo.new
|
|
def (obj.bar = Object.new).baz
|
|
foo
|
|
end
|
|
e = assert_raise(NameError) {
|
|
obj.baz
|
|
}
|
|
assert_not_match(/\/forwardable\.rb/, e.backtrace[0],
|
|
proc {RubyVM::InstructionSequence.of(obj.method(:baz)).disassemble})
|
|
assert_equal(caller(0, 1)[0], Foo.new.c[0])
|
|
end
|
|
|
|
class Foo2 < BasicObject
|
|
extend ::Forwardable
|
|
|
|
def_delegator :bar, :baz
|
|
end
|
|
|
|
def test_basicobject_subclass
|
|
bug11616 = '[ruby-core:71176] [Bug #11616]'
|
|
assert_raise_with_message(NameError, /`bar'/, bug11616) {
|
|
Foo2.new.baz
|
|
}
|
|
end
|
|
|
|
def test_aref
|
|
obj = Object.new.extend SingleForwardable
|
|
obj.instance_variable_set("@h", {foo: 42})
|
|
obj.def_delegator("@h", :[])
|
|
assert_equal(42, obj[:foo])
|
|
end
|
|
|
|
def test_aset
|
|
obj = Object.new.extend SingleForwardable
|
|
obj.instance_variable_set("@h", h = {foo: 0})
|
|
obj.def_delegator("@h", :[]=)
|
|
obj[:foo] = 42
|
|
assert_equal(42, h[:foo])
|
|
end
|
|
|
|
def test_binop
|
|
obj = Object.new.extend SingleForwardable
|
|
obj.instance_variable_set("@h", 40)
|
|
obj.def_delegator("@h", :+)
|
|
assert_equal(42, obj+2)
|
|
end
|
|
|
|
def test_uniop
|
|
obj = Object.new.extend SingleForwardable
|
|
obj.instance_variable_set("@h", -42)
|
|
obj.def_delegator("@h", :-@)
|
|
assert_equal(42, -obj)
|
|
end
|
|
|
|
def test_on_private_method
|
|
cls = Class.new do
|
|
private def foo; :foo end
|
|
extend Forwardable
|
|
def_delegator :itself, :foo, :bar
|
|
end
|
|
assert_warn(/forwarding to private method/) do
|
|
assert_equal(:foo, cls.new.bar)
|
|
end
|
|
end
|
|
|
|
def test_non_module
|
|
str = String.new
|
|
str.extend Forwardable
|
|
str.instance_variable_set("@h", 42)
|
|
str.def_delegator("@h", :to_s, :forty_two)
|
|
assert_equal("42", str.forty_two)
|
|
end
|
|
|
|
private
|
|
|
|
def forwardable_class(
|
|
receiver_name: :receiver,
|
|
type: :ivar,
|
|
visibility: :public,
|
|
&block
|
|
)
|
|
Class.new do
|
|
extend Forwardable
|
|
|
|
define_method(:initialize) do
|
|
instance_variable_set("@#{receiver_name}", RECEIVER)
|
|
end
|
|
|
|
if type == :method
|
|
attr_reader(receiver_name)
|
|
__send__(visibility, receiver_name)
|
|
end
|
|
|
|
class_exec(&block)
|
|
end
|
|
end
|
|
|
|
def single_forwardable_class(&block)
|
|
Class.new do
|
|
extend SingleForwardable
|
|
|
|
@receiver = RECEIVER
|
|
|
|
class_exec(&block)
|
|
end
|
|
end
|
|
|
|
def single_forwardable_object(&block)
|
|
obj = Object.new.extend SingleForwardable
|
|
obj.instance_variable_set(:@receiver, RECEIVER)
|
|
obj.instance_eval(&block)
|
|
obj
|
|
end
|
|
end
|