Ruby Classes

Background

Most of the languages today have the object oriented principles support (like abstraction, inheritance, polymorphism, overload, override etc.) and Ruby is both a scripting and object oriented language. When we have to represent a system, we classify a pattern of things into a template called class. Class when instantiated is an object. So a class (when instantiated) has a state (through its member variables) and behavior (member methods modifying the state of the object). We will see how Ruby supports OOPS through classes and what is relevant to us in Test Automation in this post.

Ruby Classes are like modules in that there is an initialize function to do something everytime an object of class is created. The difference is that we can create as many objects of the class whereas there is generally one module in the included program code.

Create a Class

A class is generally created using the keyword class as follows: class <class_name>..end

An object i.e. an instance of class is created using <class_name>.new

It is also important to understand the scope of variables w.r.t a class and this link has explained it all very well.

Ruby provides four types of variables:

  • Local Variables: Local variables are the variables that are defined in a method. Local variables are not available outside the method. You will see more details about method in subsequent chapter. Local variables begin with a lowercase letter or _.
  • Instance Variables: Instance variables are available across methods for any particular instance or object. That means that instance variables change from object to object. Instance variables are preceded by the at sign (@) followed by the variable name.
  • Class Variables: Class variables are available across different objects. A class variable belongs to the class and is a characteristic of a class. They are preceded by the sign @@ and are followed by the variable name.
  • Global Variables: Class variables are not available across classes. If you want to have a single variable, which is available across classes, you need to define a global variable. The global variables are always preceded by the dollar sign ($).

Getters and Setters

Generally we define a getter method and setter method for each member variable of a class. An example is below where we defined a Human class and defined getter method “hands” and setter method “hands=x” for the instance variable hands

Alternately, Ruby provides a much easier way for the getters and setters (yep, meta programming creates the methods behind for you as below)

The methods attr_accessor, attr_writer and attr_reader are self explantory

Visibility of methods

Ruby has public, protected and private visibility for methods. By default if we don’t declare any visibility scope, then all methods are public

private methods can only be called from the instance of that class

When calling a protected method the sender must be a subclass of the receiver or the receiver must be a subclass of the sender. Otherwise a NoMethodError will be raised

This post also explains it very well

The concept of private, protected and public methods in Ruby is somewhat different than it is in languages like Java. In Java if a method is declared private, it can only be accessed from other methods in the same class. When a method is declared protected it can be accessed by other classes in the same package as well as by subclasses of its class in a different package. When a method is public it is – of course – always visible to everyone. So, in essence with Java, these keywords protect the various members from access by classes, depending on where these classes are in the inheritance/package hierarchy.

In Ruby, the inheritance hierarchy or the package/module don’t really enter into the equation, it is rather all about which object is the receiver of a particular method call. When a method is declared private in Ruby, it means this method can never be called with an explicit receiver. Any time we’re able to call a private method with an implicit receiver it will always succeed. This means we can call a private method from within a class it is declared in as well as all subclasses of this class