summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog24
-rw-r--r--lib/forwardable.rb14
-rw-r--r--test/test_forwardable.rb229
-rw-r--r--version.h2
4 files changed, 266 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index a147d27643..abb2fc38f2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+Fri Apr 22 14:13:28 2016 sorah (Shota Fukumori) <her@sorah.jp>
+
+ * lib/forwardable.rb: Convert given accessors to String.
+
+ r53381 changed to accept only Symbol or String for accessors, but
+ there are several rubygems that pass classes (e.g. Array,
+ Hash, ...) as accessors. Prior r53381, it was accepted because Class#to_s
+ returns its class name. After r53381 given accessors are checked
+ with define_method, but it accepts only Symbol or String, otherwise
+ raises TypeError.
+
+ def_delegator Foo, :some_method
+
+ This change is to revert unexpected incompatibility. But this behavior
+ may change in the future.
+
+Fri Apr 22 14:13:28 2016 Elliot Winkler <elliot.winkler@gmail.com>
+
+ * lib/forwardable.rb (def_instance_delegator) fix delegating to
+ 'args' and 'block', clashing with local variables in generated
+ methods. [ruby-core:72579] [Bug #11916]
+
+ * lib/forwardable.rb (def_single_delegator): ditto.
+
Fri Apr 22 13:41:38 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
* compile.c (compile_massign_lhs): when index ends with splat,
diff --git a/lib/forwardable.rb b/lib/forwardable.rb
index ecc5f03843..602cd8b815 100644
--- a/lib/forwardable.rb
+++ b/lib/forwardable.rb
@@ -177,6 +177,11 @@ 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 = %{
def #{ali}(*args, &block)
begin
@@ -269,7 +274,12 @@ 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)
- str = %{
+ accessor = accessor.to_s
+ if method_defined?(accessor) || private_method_defined?(accessor)
+ accessor = "#{accessor}()"
+ end
+
+ line_no = __LINE__; str = %{
def #{ali}(*args, &block)
begin
#{accessor}.__send__(:#{method}, *args, &block)
@@ -280,7 +290,7 @@ module SingleForwardable
end
}
- instance_eval(str, __FILE__, __LINE__)
+ instance_eval(str, __FILE__, line_no)
end
alias delegate single_delegate
diff --git a/test/test_forwardable.rb b/test/test_forwardable.rb
new file mode 100644
index 0000000000..69372d6936
--- /dev/null
+++ b/test/test_forwardable.rb
@@ -0,0 +1,229 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'forwardable'
+
+class TestForwardable < Test::Unit::TestCase
+ RECEIVER = BasicObject.new
+ RETURNED1 = BasicObject.new
+ RETURNED2 = BasicObject.new
+
+ class << RECEIVER
+ def delegated1
+ RETURNED1
+ end
+
+ def delegated2
+ RETURNED2
+ end
+ end
+
+ def test_def_instance_delegator
+ %i[def_delegator def_instance_delegator].each do |m|
+ cls = forwardable_class do
+ __send__ m, :@receiver, :delegated1
+ end
+
+ assert_same RETURNED1, cls.new.delegated1
+ 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_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_def_single_delegator
+ %i[def_delegator def_single_delegator].each do |m|
+ cls = single_forwardable_class do
+ __send__ m, :@receiver, :delegated1
+ end
+
+ assert_same RETURNED1, cls.delegated1
+ end
+ end
+
+ def test_def_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_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
+
+ class Foo
+ extend Forwardable
+
+ def_delegator :bar, :baz
+ def_delegator :caller, :itself, :c
+
+ class Exception
+ end
+ 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
+end
diff --git a/version.h b/version.h
index f824f0be63..694a8be767 100644
--- a/version.h
+++ b/version.h
@@ -1,6 +1,6 @@
#define RUBY_VERSION "2.2.5"
#define RUBY_RELEASE_DATE "2016-04-22"
-#define RUBY_PATCHLEVEL 294
+#define RUBY_PATCHLEVEL 295
#define RUBY_RELEASE_YEAR 2016
#define RUBY_RELEASE_MONTH 4