diff options
author | Jon Leighton <j@jonathanleighton.com> | 2011-07-18 15:50:02 +0100 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2011-08-15 12:08:23 +0100 |
commit | 6f4b405250157c76fea86c42c8b0854ca4a3c4b8 (patch) | |
tree | 64a3cf1ee7825fba9a0cb08a1b5e3001b5531528 /activesupport | |
parent | 652ab436db674a112bcbc72d8c73e21f2ced512a (diff) | |
download | rails-6f4b405250157c76fea86c42c8b0854ca4a3c4b8.tar.gz rails-6f4b405250157c76fea86c42c8b0854ca4a3c4b8.tar.bz2 rails-6f4b405250157c76fea86c42c8b0854ca4a3c4b8.zip |
Backport Object#public_send to 1.8 so that we can implement Module#delegate such that non-public methods raise
Diffstat (limited to 'activesupport')
3 files changed, 143 insertions, 0 deletions
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 9ad1e12699..249c2e93c5 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/inclusion' +require 'active_support/core_ext/object/public_send' require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/instance_variables' diff --git a/activesupport/lib/active_support/core_ext/object/public_send.rb b/activesupport/lib/active_support/core_ext/object/public_send.rb new file mode 100644 index 0000000000..233e69b4f8 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/public_send.rb @@ -0,0 +1,25 @@ +require 'active_support/core_ext/kernel/singleton_class' + +class Object + unless Object.public_method_defined?(:public_send) + # Backports Object#public_send from 1.9 + def public_send(method, *args, &block) # :nodoc: + # Don't create a singleton class for the object if it doesn't already have one + # (This also protects us from classes like Fixnum and Symbol, which cannot have a + # singleton class.) + klass = singleton_methods.any? ? self.singleton_class : self.class + + if klass.public_method_defined?(method) + send(method, *args, &block) + else + if klass.private_method_defined?(method) + raise NoMethodError, "private method `#{method}' called for #{inspect}" + elsif klass.protected_method_defined?(method) + raise NoMethodError, "protected method `#{method}' called for #{inspect}" + else + raise NoMethodError, "undefined method `#{method}' for #{inspect}" + end + end + end + end +end diff --git a/activesupport/test/core_ext/object/public_send_test.rb b/activesupport/test/core_ext/object/public_send_test.rb new file mode 100644 index 0000000000..7dc542e51c --- /dev/null +++ b/activesupport/test/core_ext/object/public_send_test.rb @@ -0,0 +1,117 @@ +require 'abstract_unit' +require 'active_support/core_ext/object/public_send' + +module PublicSendReceiver + def receive_public_method(*args) + return args + [yield] + end + + protected + + def receive_protected_method(*args) + return args + [yield] + end + + private + + def receive_private_method(*args) + return args + [yield] + end +end + +# Note, running this on 1.9 will be testing the Ruby core implementation, but it is good to +# do this to ensure that our backport functions the same as Ruby core in 1.9 +class PublicSendTest < Test::Unit::TestCase + def instance + @instance ||= begin + klass = Class.new do + include PublicSendReceiver + end + klass.new + end + end + + def singleton_instance + @singleton_instance ||= begin + object = Object.new + object.singleton_class.send(:include, PublicSendReceiver) + object + end + end + + def test_should_receive_public_method + assert_equal( + [:foo, :bar, :baz], + instance.public_send(:receive_public_method, :foo, :bar) { :baz } + ) + end + + def test_should_receive_public_singleton_method + assert_equal( + [:foo, :bar, :baz], + singleton_instance.public_send(:receive_public_method, :foo, :bar) { :baz } + ) + end + + def test_should_raise_on_protected_method + assert_raises(NoMethodError) do + instance.public_send(:receive_protected_method, :foo, :bar) { :baz } + end + end + + def test_should_raise_on_protected_singleton_method + assert_raises(NoMethodError) do + singleton_instance.public_send(:receive_protected_method, :foo, :bar) { :baz } + end + end + + def test_should_raise_on_private_method + assert_raises(NoMethodError) do + instance.public_send(:receive_private_method, :foo, :bar) { :baz } + end + end + + def test_should_raise_on_singleton_private_method + assert_raises(NoMethodError) do + singleton_instance.public_send(:receive_private_method, :foo, :bar) { :baz } + end + end + + def test_should_raise_on_undefined_method + assert_raises(NoMethodError) do + instance.public_send(:receive_undefined_method, :foo, :bar) { :baz } + end + end + + def test_protected_method_message + instance.public_send(:receive_protected_method, :foo, :bar) { :baz } + rescue NoMethodError => exception + assert_equal( + "protected method `receive_protected_method' called for #{instance.inspect}", + exception.message + ) + end + + def test_private_method_message + instance.public_send(:receive_private_method, :foo, :bar) { :baz } + rescue NoMethodError => exception + assert_equal( + "private method `receive_private_method' called for #{instance.inspect}", + exception.message + ) + end + + def test_undefined_method_message + instance.public_send(:receive_undefined_method, :foo, :bar) { :baz } + rescue NoMethodError => exception + assert_equal( + "undefined method `receive_undefined_method' for #{instance.inspect}", + exception.message + ) + end + + def test_receiver_with_no_singleton + assert_equal "5", 5.public_send(:to_s) + assert_equal "foo", :foo.public_send(:to_s) + end +end |