Skip to content

Commit

Permalink
Backport Object#public_send to 1.8 so that we can implement Module#de…
Browse files Browse the repository at this point in the history
…legate such that non-public methods raise
  • Loading branch information
jonleighton committed Aug 15, 2011
1 parent 652ab43 commit 6f4b405
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 0 deletions.
1 change: 1 addition & 0 deletions activesupport/lib/active_support/core_ext/object.rb
Expand Up @@ -3,6 +3,7 @@
require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/inclusion' 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/conversions'
require 'active_support/core_ext/object/instance_variables' require 'active_support/core_ext/object/instance_variables'
Expand Down
25 changes: 25 additions & 0 deletions 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
117 changes: 117 additions & 0 deletions 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

3 comments on commit 6f4b405

@fxn
Copy link
Member

@fxn fxn commented on 6f4b405 Aug 15, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remember the AS guide :).

@jonleighton
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I nodoc'ed this because I really only backported it so that I could use it in Module#delegate. Do you think it should be public?

@fxn
Copy link
Member

@fxn fxn commented on 6f4b405 Aug 15, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, because it changes Object.

Please sign in to comment.