Public, Private and Protected methods in Ruby

Subscribe to receive new articles. No spam. Quality content.

Yes, this topic has been discussed many times and almost all people know that methods in Ruby can be public, private and protected. But it's not enough to know which access levels we have. Main point here to understand a difference. For example difference between private and protected levels of an access is not that obvious. So today I would like to talk about that difference.

Let's start from the simplest one - public.

Public

By default all methods in Ruby are public. It means that if we create method hi for a User class, it will be accessible for all User objects at any place in your code:

class User
  def hi
    "hi"
  end
end

u = User.new
puts u.hi # => hi

This very simple example shows: if we don't mark that method as private or protected - it will be public by default.

Private

To make methods private we can use private method. If we don't pass any params to it - all methods below private will be private:

class User

  def hi
    "hi"
  end

  def hello
    "hello"
  end

  private

  def secret
    "secret"
  end

  def internal
    "internal"
  end
end

u = User.new
u.hi # => "hi"
u.hello # => "hello"
u.secret # => error: private method `secret' called
u.internal # => error: private method `internal' called

As we see from example methods secret and internal which placed after private are not accessible for object or User class for outside world. If we try to call those methods we will get an error:

private method `secret' called for #User:0x007f9a8284bd30 (NoMethodError)

To get list of object's private methods we can use method private_methods:

u = User.new
puts u.private_methods(false).inspect # => [:secret, :internal]

Argument false shows that we want to see private methods only of User class.

Another way to make method private - pass method's name to private method explicitly. This approach is used rarely, but still it's better to know about such possibility.

Our previous example could look like this:

class User

  def hi
    "hi"
  end

  def hello
    "hello"
  end

  def secret
    "secret"
  end

  def internal
    "internal"
  end

  private :secret, :internal
end

u = User.new
u.private_methods(false) # => [:secret, :internal]

Line private :secret, :internal shows that we want to make secret and internal methods private. As I mentioned earlier Ruby developers use this approach not that often. The main usage is to write private and place all private methods below.

So private methods can not be used from outside, but we still can use it inside a class:

class User

  def hi
    "hi, i'll not tell you the #{secret}"
  end

  private

  def secret
    "secret"
  end
end

u = User.new
u.hi # => hi, i'll not tell you the secret

In hi method we can call secret method and display its content properly.

What's the main idea of splitting methods by private, protected and public? Why it's bad idea to make all methods public?

The answer lies in the proper object-oriented design. Each class should have simple and nice interface. By class interface I mean methods and their arguments. Public methods define interface of class. Private methods responsible for internal logic and there is no need to expose them for outside world. Keep an eye on interfaces of your classes. It's a good idea to make methods private if they used only inside methods of class.

Writing stable code means that you write classes with interfaces that change really rarely and people can rely on those interfaces.

We can change internal implementation as many times as we want because that will not affect interface of class and will not break other parts of application. But as soon as we change public method of class - we have to go through all code and find usages of that method and change it to support new interface.

Let's get back to private methods and consider one more interesting idea. At first sight it looks complicated, but: private methods can not be called with explicitly defined receiver. Receiver it's an object on which we call that method. Even inside a class if we pass self as a receiver that will not work.

It's easy to understand this idea by simple example:

class User

  def say_secret_with_self
    self.secret
  end

  def say_secret
    secret
  end

  private

  def secret
    "secret"
  end
end

u = User.new
u.say_secret_with_self # => `say_secret_with_self': private method `secret' called for #User:0x007ffdcc037e48 (NoMethodError)
u.say_secret # => "secret"

Inside class we tried to call private method as self.select - and we got an error, the same one that we got when tried to call secret from outside: private method secret' called for #User:0x007ffdcc037e48.

But if we call just secret inside a class - that will work. So we will be able to call private methods just without explicit receiver. In our case it's just a secret, but not a self.secret.

Protected

I hope that you're not so frustrated by last example and continue reading. That's good because we are at the most interesting part of this article :)

What we know about protected methods: they're not accessible for outside world and we can define them using protected method:

class User

  def secret
    "secret"
  end

  protected :secret
end

u = User.new
u.secret # => protected method `secret' called for #User:0x007fc7409384d8 (NoMethodError)

Or more convenient way:

class User

  protected

  def secret
    "secret"
  end
end

u = User.new
u.secret # => protected method `secret' called for #User:0x007fc7409384d8 (NoMethodError)

The question is: what's the difference between protected and private?

There is one main difference: protected methods support explicit definition of receiver inside class where it's being called:

class User

  def say_secret_with_self
    self.secret
  end

  protected

  def secret
    "secret"
  end
end

u = User.new
u.say_secret_with_self # => "secret"

If I change protected to private - self.secret will throw an error. But everything works with protected.

It means that inside User class we can call secret for each object of User.

Let's consider following example:

class User

  attr_reader :name

  def initialize(name)
    @name = name
  end

  def ==(other_user)
    self.secret == other_user.secret
  end

  protected

  def secret
    "#{name}.#{name.length}"
  end
end

bob = User.new("Bob")
john = User.new("John")
bob == john # => false

User class accepts name param on initialization. Also we defined method == which we will use for comparing two objects. This check will compare two secrets for users - current user and user we receive as a param other_user. Since protected methods allow us to define receiver - we will use that: self.secret == other_user.secret. Notice, that inside definition of User class we have an access to secret method for other_user user as well.

I've changed secret method a bit. It just preforms a secret string from name and length, so for Bob it would be "Bob.3" and for John it's "John.4". Logic is not that important because more interesting part that we can call protected method secret inside definition of class User, also we can explicitly set receiver of that method.

You did it! You've read this long article to the end! Public, Protected and Private it's a really important topic. Once it settled in your mind it will not look complicated anymore.

Those who read this article to this part I want to show one hack in Ruby that allows us to call private and protected methods on objects.

We can do that not by calling method straight away, but using send method:

class User

  private

  def secret
    "sic! it's a secret"
  end
end

u = User.new
u.send(:secret) # => sic! it's a secret

Enjoy! :)

Subscribe to receive new articles. No spam. Quality content.

Comments