Object Oriented Programming and Method Privacy in Ruby!

Ruby is an Object-oriented programming language. As its name kind of suggests, this means that we build Ruby programs using things called objects. There are plenty of tutorials etc online but I will start by explaining some 'need to know' fundamentals… 

Object-Oriented Programming

To start building an object-oriented Ruby program a good place to start is with creating a class. A class is born via creating a new Ruby file and adding a class definition.

class

This is the class definition for a Student class, written in a file named student.rb

So you could have say, a Student class or a Car class or a Task class. Out there in the real world individually students, cars and tasks are going to differ from each other, but they have the category of that which they exist under in common.

We produce our objects by ‘initializing’ them from a class using the new method (otherwise known as the ‘constructor’). We can then personalise these individual objects by calling methods on them and by giving them attributes.

... I kind of picture a class like a factory, so for the Student class factory we press an imaginary button (aka initialize the object) and it spits out a fresh new student object that we can now personalise using attributes and methods.

Attributes

Attributes are passed into the object as an argument(s) when the object is initialized… for example:

1

Instance Variables - A Quick Detour

In Ruby we have local variables, instance variables, class variables and global variables.

Instance variables are easily recognisable because they are prefixed with an @. They are bound to an instance of a class and allow us to form the state of an object. This just means that each instance of the class will have its own set of information stored in its instance variables. So if I wanted to create a new student object called jerry, he would produce a different result outputted to the terminal than emma would. This is due to jerry being instilled with different attributes passed into his instance variables.

3

4

If I try and replace the instance variable with a local variable (named local_var… just for example’s sake… it is considered bad practice in Ruby to include ‘var’ in naming a variable as it describes what it is in programming terms rather than what it is representing)

5

I get the error

6

This is because within age we are unable to access local_var. Local variables are only available within the methods that they are defined. This is their ‘scope’. Because of this, we are not setting up our program to pass attribute information for an object around our Student class. All instance variables behave privately by default. This is because you don't have access to them outside the scope of the object itself.

Back to Attributes!

Getter and Setter Methods

Only the object's own methods can access its instance variables. Therefore we need methods known as ‘getter methods’ which return the value of the particular instance variable initialized in the initialize method.

The age method in my Student class is an example of a getter method:

7

Here we are telling the age attribute to be set to 10 when initializing the emma student object.

8

Writing out all these getter/setter methods looks kind of clunky and repetitive. Therefore Ruby gives us a nice short hand way through using attr_accessors.

The attribute accessor for getter methods is attr_reader. We simply pass the getter method name into the attr_reader and it will create the getter method for us, this looks much neater. Here is the Student class refactored to use an attr_reader:

9

Sometimes we may want to change the value of an instance variable from within a class method. To do this we need to use a setter method. To set this up I have added in a fav_colour getter method and a fav_color setter method (the one with the =) to our student class:

setter

To refactor the setter method I can get rid of it and define fav_color in an attr_writer:

11

Calling the setter method would be done a slightly strange looking way which uses an = and some brackets:

12

This results in:

13

The use of the = sign makes it easily recognisable as a setter as it gives the feel of assigning a value. Ruby also gives us syntactic sugar (basically a 'nice to read' way of writing the code) which could allow us to call our setter like this:

16

I can then refactor the code further using an attr_accessor which creates both reader and writer (alternative terms for getter and setter) methods. This means I can delete the pre-existing getter fav_color method definition:

attr_accessor

Method Privacy

Now we have had a brief overview of OOP (object-oriented programming) we can delve into method privacy.

So the gotcha for understanding the rules of method privacy is that the visibility (whether it is public, private or protected) of a method depends on what its receiver is allowed to be. The idea of a method is to send a message to an object, but sometimes we only want to allow certain objects to send messages.

Public

Methods by default are public meaning that they can be called on any of our student objects. For a public method the receiver can either be self, the name of the method or another object. So we can happily run emma.allowed_black_hoody?

Private

Private methods don’t let you define a specific receiver. So if I make the allowed_black_hoody? method private like so:

17

Then I get a no_method_error

Private methods can also be declared like so:

18

I can’t just remove the receiver because I would end up with a no method error as I have nothing to call the method on. If we don’t specify a receiver then the method would be called on self. The only way we can call the private method is if self is an instance of the Student class.

Self is an instance of Student when any Student instance method is executed. So if I add a method called hoody_check I can call the allowed_black_hoody? private method from there without a receiver and then call emma.hoody_check. This gives me the result of the private method allowed_black_hoody?

19

The idea is that the Student object can check_hoody for itself but no one else can tell it to do so. Private methods are used commonly in Rails for methods which require parameters. This boosts security as it only permits permissible attributes for that instance.

Protected

I haven’t really come across protected methods much as they are not used so often. They are a less hardcore version of private methods. You can call a protected method as long as self is an instance of the class or any of its defining subclasses. This is the same as private but if the class of self and the class of the object having the method called on it are the same or related by inheritance then it will let you call it. It is used when one instance of a class wants to do something with another instance of the class.

Here is an example:

20

If I changed the age method to be private as opposed to protected then I would get a no method error. If I call it as a protected method then it returns “Jerry is older than Emma”.

Two instances of the class are being compared, therefore we need them to both be able to call the age method which means that age can’t be private because private methods only let you call on an instance method that is self implied.

Understanding the concept of OOP and how scope and method privacy work are really vital when learning to build Ruby programs. I hope this post has helped to break down and explain at least an overview of these concepts to help you in building your own programs.

I would recommend reading The Well Grounded Rubyist by David Black as it provided by far the best breakdown I have found of how method privacy works in Ruby.