Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access private class methods in Ruby?

Given a Class in Ruby:

class MyClass
  def self.my_class_method
    puts "class method"
  end

  private

  def my_method
    puts "regular method"
  end

  private_class_method :my_class_method
end

To access private methods I can call .send(:my_method) on the Class Object, but how does that work for class methods?

like image 583
Severin Avatar asked Jan 09 '15 11:01

Severin


Video Answer


2 Answers

You should do:

class MyClass
  def self.my_class_method
    puts "class method"
  end

  private

  def my_method
    puts "regular method"
  end

  private_class_method :my_class_method
end

# to call class method
MyClass.send :my_class_method # => class method
# to call instance method
MyClass.new.send :my_method # => regular method

In Ruby, class(s) are also objects, so you can call the #send method on the class also.

In Ruby, you can define private class methods as

class MyClass
  class << self
    private 

    def my_class_method
      puts "class method"
    end
  end
end

Or using thhis macro like method: private_class_method

like image 174
Arup Rakshit Avatar answered Oct 21 '22 16:10

Arup Rakshit


First off, MyClass.send(:my_method) would not work. You have to send it to an instance: MyClass.new.send(:my_method).

Then, your my_class_method is not really private.
Ruby's semantic of private are somewhat different from what you might be used to in other languages. Since Ruby allows you to bypass encapsulation if you choose to, private just means that a method can only be called implicitly, without sending a message to an actual object.

For example:

class Example
  def test
    'foobar'
  end

  def hello
    puts test        # implicit receiver
    puts self.test   # explicit receiver
  end
end

This is all good, but why is this important for your question?
Because you're declaring my_class_method explicitly on self. Doing so bypasses the private modifier, and the method is public. This means that you can just call it with:

MyClass.my_class_method

If you really need private class methods, then you can define them on the metaclass:

class MyClass

  class << self

    private

    def my_class_method
      puts "class method"
    end
  end


  private

  def my_method
    puts "regular method"
  end

end

This will make my_class_method actually private, and force you to invoke it with any of these:

MyClass.send :my_class_method
MyClass.class_exec { my_class_method }
MyClass.class_eval { my_class_method }
MyClass.class_eval "my_class_method"
like image 29
tompave Avatar answered Oct 21 '22 17:10

tompave