I\'ve been having this bothering recurring theme; let\'s just say, I have a class which defines an instance method and a protected class method. The instance method must call th
Let's consider a private class method (since protected class methods don't make sense).
We know it's possible for an instance to call a private method on itself, as long as it isn't using an explicit receiver (self.call_something_private
). It seems you also expect that an instance can call a private class method on its own class, but that is not the case.
Let's look at a way to do this without using send
.
The private
and protected
macros only affect instance methods of the current scope, not class methods. Here are three ways to rewrite your original code:
class Bang
def instance_bang
self.class.class_bang
end
# declare method visibility after
def self.class_bang
puts "bang"
end
private_class_method :class_bang
# inline
private_class_method def self.class_bang
puts "bang"
end
# class scope
class << self
# the private macro works here because we're inside the class scope
private
def class_bang
puts "bang"
end
end
end
So now we want to expose an interface on the class to call class_bang
, but only if it's called by an instance of Bang
.
class Bang
def instance_bang
self.class.invoke_class_bang(self)
end
class << self
private
def class_bang
puts "bang"
end
public
# we ask the receiver to pass itself as an argument ...
def invoke_class_bang(receiver)
# ... so that we can check whether it's
class_bang if receiver.is_a?(Bang)
end
end
end
That's not a very nice looking solution though. Here's a sneakier way:
class Bang
def initialize
def self.instance_bang() self.class.method(:class_bang).call end
end
class << self
private
def class_bang
puts "bang"
end
end
end
It is forbidden to call private methods with explicit receiver. You either have to use implicit receiver (private_bang
, without self
) or use send
. Please see my another answer for more information.
By the way, the original question is about calling class instance methods from instance methods. Your clarification doesn't include that. But if that's still true, you have to use self.class.send
or make the method public (so that you can use explicit receiver).
"The class method should be used inside the class scope, therefore should remain visible and callable within it, right?" Yes, that's correct, and that's the behavior Ruby exhibits. (As a point of clarification, instance scope is not "within" class scope. They are, appropriately, separate.)
The non-send
solution is to subclass or reopen the class to add a public class method to access the protected class method.