Instance methods can't call protected class methods?


#1

I am trying to understand this behavior.

From page 356 of the Pickaxe (2nd ed):

Protected. Can be invoked only by objects of the defining class and its
subclasses.

If I have

class Foo

end

f = Foo.new

Is f not an “object of the defining class”? If so, why should an
instance method of Foo not be able to call a protected class method?

I’m not looking for an answer of the form “because in class scope,
self.class.type.object.class # => Object, however in instance scope”
etc. Logically, why should it be this way?

In C++ it makes sense that a static class method cannot call an
non-static method, since static methods exist even when there are no
objects of the class in existence. However, they do not limit you on the
reverse and prevent a non-static method from calling a static method.


#2

where’s the problem?

class Test

protected
def self.call_me
puts “static and protected”
end

public
def invoke_me
puts “try to call”
Test.call_me
# this will not work: self.call_me
puts “done”
end

end

t = Test.new
t.invoke_me

try to call
static and protected
done

Sean Hermany schrieb:


#3

The problem is that self.call_me is not protected.

class Test

protected
def self.call_me
puts “static and protected”
end

public
def invoke_me
puts “try to call”
Test.call_me
# this will not work: self.call_me
puts “done”
end

end

t = Test.new

print “Output of p Test.protected_methods.sort:\n\n”

p Test.protected_methods.sort

print “\nOutput of p Test.public_methods.sort:\n\n”

p Test.public_methods.sort

print “\n”

t.invoke_me

OUTPUT:

sean@jefferson:~$ ruby protected_test.rb
Output of p Test.protected_methods.sort:

[]

Output of p Test.public_methods.sort:

["<", “<=”, “<=>”, “==”, “===”, “=~”, “>”, “>=”, “id”, “send”,
“allocate”, “ancestors”, “autoload”, “autoload?”, “call_me”, “class”,
“class_eval”, “class_variables”, “clone”, “const_defined?”, “const_get”,
“const_missing”, “const_set”, “constants”, “display”, “dup”, “eql?”,
“equal?”, “extend”, “freeze”, “frozen?”, “hash”, “id”, “include?”,
“included_modules”, “inspect”, “instance_eval”, “instance_method”,
“instance_methods”, “instance_of?”, “instance_variable_get”,
“instance_variable_set”, “instance_variables”, “is_a?”, “kind_of?”,
“method”, “method_defined?”, “methods”, “module_eval”, “name”, “new”,
“nil?”, “object_id”, “private_class_method”, “private_instance_methods”,
“private_method_defined?”, “private_methods”,
“protected_instance_methods”, “protected_method_defined?”,
“protected_methods”, “public_class_method”, “public_instance_methods”,
“public_method_defined?”, “public_methods”, “respond_to?”, “send”,
“singleton_methods”, “superclass”, “taint”, “tainted?”, “to_a”, “to_s”,
“type”, “untaint”]

try to call
static and protected
done

  • Sean

Pete wrote:

where’s the problem?

class Test

protected
def self.call_me
puts “static and protected”
end

public
def invoke_me
puts “try to call”
Test.call_me
# this will not work: self.call_me
puts “done”
end

end

t = Test.new
t.invoke_me

try to call
static and protected
done

Sean Hermany schrieb:


#4

Pete wrote:
[…]

protected
def self.call_me
puts “static and protected”
end
[…]

Unfortunately, this method is neither “static” nor “protected”.

Although class methods are often used for the same purposes as static
methods in Java/C++, they are quite a different animal.

And as Sean pointed out, the protected only applies to the methods
instance methods of Test, not instance methods of Test’s singleton
class.

– Jim W.


#5

Sean Hermany wrote:
[… example elided …]

Is f not an “object of the defining class”? If so, why should an
instance method of Foo not be able to call a protected class method?

I’m not looking for an answer of the form “because in class scope,
self.class.type.object.class # => Object, however in instance scope”
etc.

So, I’m guessing that saying “Because f and Foo have different defining
classes” isn’t the answer you are looking for, because you go on to say:

Logically, why should it be this way?

So you are looking for the reason the protected rule was chosen, rather
than an explaination of the rule itself.

Hmm, only Matz himself can only answer that question definitively,
however, I can make some guesses.

The first reason is a complexity/benefit tradeoff. The rule currently
is that only instances of the same class (or related subclass) can call
protected methods. Your suggestion would add additional cases to the
rule. Is the benefit gained from the extra conditions worth the extra
complexity of the rule? Perhaps not.

There may also be a runtime cost to allowing additional classes to call
protected methods. Since determining if a protected method call is
valid involves some runtime checking, making the list of possible
calling classes larger might impact performance. Note the heavy use of
“may” and “might” in this paragraph, I’m no expert on Ruby internals.

– Jim W.