Public and Private Methods in Ruby

Method Access Rules

While the end user of your program is never going to be using the methods you define in a class directly, it is still helpful to control access to your methods. Envision a team situation in which multiple developers use instances of the same class; it is useful to control which methods can be used.

A simple way to think about the difference is to imagine our class is an airplane (I’m writing this aboard one, in fact.)

Public Methods

In our Plane class, we have a few public instance methods. We don’t need to specify that a method be public because that is their default.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Plane

  attr_reader :type, :fuel, :landed, :people, :personal_info_of_passengers, :burning_fuel

  def initialize(model)
      @model = model
      @fuel = 100
      @airborne = false
      @burning_fuel = false
  end

  def take_off
      puts "Firing up engines"
      puts "Fuel levels are #{fuel}."
      @airborne = true
      fire_up_engine
  end
  
  def fire_up_engine
      puts "firing up the engine"
      begin_gas_combustion
  end

  def begin_gas_combustion
      puts "combustin' the gas!"
      burning_fuel = true
  end

  def personal_info_of_passengers(people)
      people.map do |person|
          person.social_security_number
      end
  end

end    

Woah, wait a second. It’s all well and good that we’re able to see what type of plane we’re working with, the fuel levels, and even whether or not we’re airborne. However, there are a few things that we definitely don’t want to be usable outside our class.

An object is easy to think about as something that does tasks for you, and its methods are the actions of doing those things. Public methods are like the API – where calls are made to the instances of the object. Private methods, on the other hand, are tasks that you might not want to be used freely. For example, the method begin_gas_combustion is a component of the method fire_up_engine but it’s unclear whether or not you’d want to use it in any other context. In fact, it would probably be unsafe to be able to use it in other contexts. Luckily, Ruby enables use of private methods to get around this problem.

Private Methods

A private method cannot be called with an explicit receiver. This means that if we specify begin_gas_combustion to be a private method, we shouldn’t be able to call begin_gas_combustion on instances of our Plane class explicitly. Let’s rewrite a bit of our Plane class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Plane

...

  def fire_up_engine
      puts "firing up the engine"
      begin_gas_combustion
  end

  private
  
      def begin_gas_combustion
          "combustin' da gas!"
          burning_fuel = true
      end

end

Ok, that seems to make a bit more sense. Let’s test out our methods. If you try, you’ll get the following:

1
2
3
4
wills_plane = Plane.new(747)
wills_plane.begin_gas_combustion

#=> '<main>': private method 'begin_gas_combustion' called for #<Plane:0x00000002aeae50> (NoMethodError)`

This makes sense based on our definition of private methods – the instance will is now the explicit receiver of the method as called. However, when we try the following:

1
2
3
4
wills_plane.fire_up_engine

#=> "firing up the engine"
#=> "combustin' da gas!"

Here, since the method gets called within a public method, there is an implicit receiver – the specific instance of the object – and we are ok.

Private Setter (=) Methods

Private methods with the “no explicit receiver” rule do hit problems, however, with writer / setter methods. In these methods, you can’t use the implicit receiver because Ruby would assume you were using a local variable instead of a method without the keyword self. Therefore, there is an exception – Ruby doesn’t apply the rule to setter methods as long as the explicit receiver is self.

You Can Bypass Private Methods with ‘send’

Ruby has given us a way to get around our rule, however. You can always just use the send method to send any method to any object.

Hopefully this clears up the value / usage of private methods, and see you next time.