diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2016-06-10 12:54:25 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2016-06-10 12:54:25 +0000 |
commit | b661824e75787548d47e1af649727a617a964bd4 (patch) | |
tree | 4f47c71d71cfa638ddefa441d033ed3e3a78a889 | |
parent | 10174c295b91ef1370d3c910755d79c895d0ef32 (diff) |
forwardable.rb: fix for non-module objects
* lib/forwardable.rb (Forwardable._delegator_method): extract
method generator and deal with non-module objects.
[ruby-dev:49656] [Bug #12478]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55366 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | lib/forwardable.rb | 77 | ||||
-rw-r--r-- | test/test_forwardable.rb | 52 |
3 files changed, 87 insertions, 48 deletions
@@ -1,3 +1,9 @@ +Fri Jun 10 21:54:24 2016 Nobuyoshi Nakada <[email protected]> + + * lib/forwardable.rb (Forwardable._delegator_method): extract + method generator and deal with non-module objects. + [ruby-dev:49656] [Bug #12478] + Fri Jun 10 17:35:11 2016 Martin Duerst <[email protected]> * string.c: Special-case :ascii option in rb_str_capitalize_bang and diff --git a/lib/forwardable.rb b/lib/forwardable.rb index 2ea0aa1d77..e78b9ed1e0 100644 --- a/lib/forwardable.rb +++ b/lib/forwardable.rb @@ -178,38 +178,44 @@ module Forwardable # q.push 23 #=> NoMethodError # def def_instance_delegator(accessor, method, ali = method) - accessor = accessor.to_s - if method_defined?(accessor) || private_method_defined?(accessor) - accessor = "#{accessor}()" - end - - line_no = __LINE__; str = %{proc do - def #{ali}(*args, &block) - begin - #{accessor} - ensure - [email protected]_if {|s| ::Forwardable::FILE_REGEXP =~ s} if $@ and !::Forwardable::debug - end.__send__(:#{method}, *args, &block) - end - end} - - gen = RubyVM::InstructionSequence - .compile(str, __FILE__, __FILE__, line_no, - trace_instruction: false, - tailcall_optimization: true) - .eval + gen = Forwardable._delegator_method(self, accessor, method, ali) # If it's not a class or module, it's an instance - begin - module_eval(&gen) - rescue - instance_eval(&gen) - end + (Module === self ? self : singleton_class).module_eval(&gen) end alias delegate instance_delegate alias def_delegators def_instance_delegators alias def_delegator def_instance_delegator + + def self._delegator_method(obj, accessor, method, ali) + accessor = accessor.to_s unless Symbol === accessor + + if Module === obj ? + obj.method_defined?(accessor) || obj.private_method_defined?(accessor) : + obj.respond_to?(accessor, true) + accessor = "#{accessor}()" + end + + line_no = __LINE__+1; str = "#{<<-"begin;"}\n#{<<-"end;"}" + begin; + proc do + def #{ali}(*args, &block) + begin + #{accessor} + ensure + [email protected]_if {|s| ::Forwardable::FILE_REGEXP =~ s} if $@ and !::Forwardable::debug + end.__send__ :#{method}, *args, &block + end + end + end; + + RubyVM::InstructionSequence + .compile(str, __FILE__, __FILE__, line_no, + trace_instruction: false, + tailcall_optimization: true) + .eval + end end # SingleForwardable can be used to setup delegation at the object level as well. @@ -280,26 +286,7 @@ module SingleForwardable # the method of the same name in _accessor_). If _new_name_ is # provided, it is used as the name for the delegate method. def def_single_delegator(accessor, method, ali = method) - accessor = accessor.to_s - if method_defined?(accessor) || private_method_defined?(accessor) - accessor = "#{accessor}()" - end - - line_no = __LINE__; str = %{proc do - def #{ali}(*args, &block) - begin - #{accessor} - ensure - [email protected]_if {|s| ::Forwardable::FILE_REGEXP =~ s} if $@ and !::Forwardable::debug - end.__send__(:#{method}, *args, &block) - end - end} - - gen = RubyVM::InstructionSequence - .compile(str, __FILE__, __FILE__, line_no, - trace_instruction: false, - tailcall_optimization: true) - .eval + gen = Forwardable._delegator_method(self, accessor, method, ali) instance_eval(&gen) end diff --git a/test/test_forwardable.rb b/test/test_forwardable.rb index 550280de19..f678ca4fc7 100644 --- a/test/test_forwardable.rb +++ b/test/test_forwardable.rb @@ -144,7 +144,7 @@ class TestForwardable < Test::Unit::TestCase end end - def test_def_single_delegator + def test_class_single_delegator %i[def_delegator def_single_delegator].each do |m| cls = single_forwardable_class do __send__ m, :@receiver, :delegated1 @@ -154,7 +154,7 @@ class TestForwardable < Test::Unit::TestCase end end - def test_def_single_delegators + def test_class_single_delegators %i[def_delegators def_single_delegators].each do |m| cls = single_forwardable_class do __send__ m, :@receiver, :delegated1, :delegated2 @@ -165,7 +165,7 @@ class TestForwardable < Test::Unit::TestCase end end - def test_single_delegate + def test_class_single_delegate %i[delegate single_delegate].each do |m| cls = single_forwardable_class do __send__ m, delegated1: :@receiver, delegated2: :@receiver @@ -183,6 +183,45 @@ class TestForwardable < Test::Unit::TestCase 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_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 @@ -247,4 +286,11 @@ class TestForwardable < Test::Unit::TestCase 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 |