TL;DR: respond_to? will return false for protected methods in Ruby 2.0

Let’s check out how protected and private methods behave in Ruby. After that, we’ll look at how Ruby 2.0 changes could possibly break your code (and what to do about it).

Method Visibility

In Ruby, we have three visibilities: public, protected, and private. Let’s define a class with all three:

class Heart def public_method ; end protected def protected_method ; end private def private_method ; end end

First, let’s see how these differ from within the Heart class.

Internal Visibility

Inside the Heart class, we can call any of these methods with an implicit recipient. In other words, this method will not raise exceptions (note that I’m just reopening the Heart class for demonstration):

class Heart def ok! public_method protected_method private_method end end

Public and protected methods can be called with an explicit recipient, but private methods cannot. So the following code will raise an exception on the third line of the method body:

class Heart def not_ok! self .public_method self .protected_method self .private_method end end

External Visibility

Outside the Heart class, we can only call the public methods:

irb(main):032:0> heart = Heart.new => #<Heart:0x007fdad1952f78> irb(main):033:0> heart.public_method # => nil irb(main):034:0> heart.protected_method # => raises NoMethodError irb(main):035:0> heart.private_method # => raises NoMethodError

One notable exception is if the object sending the message is of the same type as the object receiving the message, then it’s OK to call protected methods.

Here is an example:

class Hands < Heart def call_stuff r r.public_method r.protected_method r.private_method end end

I find this behavior to be most useful when implementing equality operators. For example:

class A def == other if self .class == other.class internal == other.internal else super end end protected def internal ; :a ; end end

Introspection

Finally, let’s look at respond_to? . The behavior of this method is changing in Ruby 2.0.0. First we’ll look at the behavior in 1.9, then how it changes in Ruby 2.0.0.

The respond_to? method will return true if the object responds to the given method. Let’s call respond_to? on our Heart object (with Ruby 1.9) and see what it returns:

1.9.3-p194 :010 > heart = Heart.new => #<Heart:0x007faaaa14e450> 1.9.3-p194 :011 > heart.respond_to? :public_method # => true 1.9.3-p194 :012 > heart.respond_to? :protected_method # => true 1.9.3-p194 :013 > heart.respond_to? :private_method # => false

Ruby 1.9 will return true for public and protected methods, but false for private methods. If we compare this to actually calling the method, we’ll see an inconsistent behavior. Let’s interleave respond_to? checks along with calling the method to see what happens:

1.9.3-p194 :014 > heart = Heart.new => #<Heart:0x007faaaa16d080> 1.9.3-p194 :015 > heart.respond_to? :public_method # => true 1.9.3-p194 :016 > heart.public_method # => nil 1.9.3-p194 :017 > heart.respond_to? :protected_method # => true 1.9.3-p194 :018 > heart.protected_method # => NoMethodError 1.9.3-p194 :019 > heart.respond_to? :private_method # => false 1.9.3-p194 :020 > heart.private_method # => NoMethodError

So, despite the fact that respond_to? returns true for the protected method, we cannot actually call that method.

Introspection (in Ruby 2.0.0)

In Ruby 2.0.0, respond_to? has changed. It no longer returns true for protected methods. Let’s look at our Heart example again, but this time with Ruby 2.0.0:

irb(main):013:0> heart = Heart.new => #<Heart:0x007fce0b09a188> irb(main):014:0> heart.respond_to? :public_method # => true irb(main):015:0> heart.public_method # => nil irb(main):016:0> heart.respond_to? :protected_method # => false irb(main):017:0> heart.protected_method # => NoMethodError irb(main):018:0> heart.respond_to? :private_method # => false irb(main):019:0> heart.private_method # => NoMethodError

The behavior of respond_to? lines up with the reality of calling the method in Ruby 2.0.0.

Caveats on Reality

The changes to respond_to? also apply inside our “same instances” case. Let’s use this class as an example:

class A def == a puts a.respond_to? :zoom! puts a.zoom! end protected def zoom! ; :a ; end end

If we run the following code in Ruby 2.0.0, the call to respond_to? will return false despite the fact that we can actually call the method:

irb(main):029:0> A.new == A.new false a => nil

I’m not sure this is a big problem because we should be checking ancestors in the comparator methods. If we check that the ancestors are the same, then the respond_to? calls become unnecessary. Also 99% of the objects I write don’t implement object comparator methods.

Compatibility

Most of the problems I’ve found in the Rails code base relating to respond_to? were fixed by either changing the visibility of the method, or calling respond_to? with a true as the second argument. In 1.9, the true tells Ruby to search private methods, and in 2.0, private and protected methods.

For library authors, dealing with this change depends on the situation. For example, if you have code like this:

def some_method other if other.respond_to?( :foo ) other.foo else some_default_behavior end end

Consider forcing the other object to have the method foo , and the super class of the foo instance implementing some_default_behavior .

If you expect foo to be a protected method, consider changing to is_a? checks, or passing true to respond_to? . Passing true could result in false positives, but I haven’t personally encountered that as a problem (yet).

Happy Hacking! <3<3<3<3