mhaseeb
mhaseeb

Reputation: 1779

How to access private methods

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

Answers (4)

Aleksei Matiushkin
Aleksei Matiushkin

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

Jörg W Mittag
Jörg W Mittag

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

Jon
Jon

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

falsetru
falsetru

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

Related Questions