Reputation: 1779
My understanding is that private
means being private to an instance. Private methods can't be called with an explicit receiver, even self
. To call a private method, I have to go through a process like below:
class Sample
def foo
baz
end
private
def baz
end
end
Sample.new.foo
This will call the private baz
method. Is there a way to directly call a private method with an explicit receiver?
Upvotes: 45
Views: 38005
Reputation: 121000
Yes, this is possible with Kernel#send
:
receiver.send :method_name, parameters
Though there are workarounds like BasicObject#instance_eval
, or Kernel#binding
manipulations, the common way to call private method is to call send
on the receiver.
Upvotes: 79
Reputation: 369458
There are two levels on which your question can be answered: the base language level and the reflective meta-level.
On the base language level, the answer is blatantly obvious if you just contrast the definition of privacy with your question:
private methods can only be called without an explicit receiver
and
can I call a private without an explicit receiver?
The answer is obviously "No", the two sentences directly contradict each other.
On the reflective meta-level, however, the answer is "Yes". In fact, you can do pretty much anything on the meta-level and circumvent almost any sort of access restriction, protection, hiding, and encapsulation.
Object#send
, whose main purpose is to allow you to call a method whose name you don't know until runtime, also has the additional side-effect of circumventing access restrictions like private
and protected
. (It also ignores Refinements, which is an important restriction you should be aware of!)
Upvotes: 3
Reputation: 10898
As shown in the other answers, you can use send
to call a private method. However, this can make for some untidy code.
If you really need your method to be publicly accessible on instances of your class, you could just reset the visibility of the method. Assuming the class isn't defined within your code, simply reopen it and set the visibility of the method:
class Sample
public :baz
end
Now you can call it:
s = Sample.new
s.baz
# => 'baz called'
Upvotes: 1
Reputation: 369074
Using BasicObject#instance_eval
, you can call private method.
class Sample
private
def baz
'baz called'
end
end
Sample.new.instance_eval('baz')
# => 'baz called'
Sample.new.instance_eval { baz }
# => 'baz called'
Upvotes: 8