Easy Tutorial
❮ Ruby Comment Ruby Rubygems ❯

Ruby Object-Oriented

Ruby is a pure object-oriented language, where everything appears in the form of objects. Every value in Ruby is an object, even the most primitive things: strings, numbers, and even true and false. The class itself is also an object, an instance of the Class class. This chapter will explain all the main features related to Ruby object-oriented programming.

Classes are used to specify the form of an object, combining data representation and methods into a neat package. Data and methods within a class are known as class members.

Ruby Class Definitions

When you define a class, you are essentially defining a blueprint for a data type. This does not define any data, but it defines what the class name means, i.e., what an object of the class will consist of and what operations can be performed on that object.

A class definition starts with the keyword class followed by the class name and ends with the keyword end. For example, we use the keyword class to define the Box class as follows:

class Box
   code
end

By convention, the name must start with a capital letter and if it consists of multiple words, each word should start with a capital letter without separators (e.g., CamelCase).

Defining Ruby Objects

A class provides the blueprint for objects, so essentially, an object is created from a class. We declare objects of a class using the new keyword. The following statements declare two objects of the class Box:

box1 = Box.new
box2 = Box.new

The initialize Method

The initialize method is a standard Ruby class method and serves as the constructor of the class, similar to constructors in other object-oriented programming languages. When you want to initialize some class variables at the time of creating objects, the initialize method comes in handy. This method takes a series of parameters and, like any Ruby method, must be prefixed with the def keyword, as shown below:

class Box
   def initialize(w,h)
      @width, @height = w, h
   end
end

Instance Variables

Instance variables are class attributes that become object attributes when objects are created using the class. Each object's attributes are assigned individually and do not share values with other objects. Inside the class, these attributes are accessed using the @ operator, and outside the class, they are accessed using accessor methods that are public. Below, we use @width and @height as instance variables for the class Box.

class Box
   def initialize(w,h)
      # assign instance variables
      @width, @height = w, h
   end
end

Accessor (Getter) & Setter Methods

To read the defined variables outside the class, we can define accessor (getter) methods. The following example demonstrates the use of accessor methods:

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor
   def initialize(w,h)
      @width, @height = w, h
   end

   # Accessor methods
   def printWidth
      @width
   end

   def printHeight
      @height
   end
end

# Create an object
box = Box.new(10, 20)

# Use accessor methods
x = box.printWidth()
y = box.printHeight()

puts "Box width : #{x}"
puts "Box height : #{y}"

When the above code is executed, it produces the following result:

Box width : 10
Box height : 20

Similar to accessor methods for accessing variable values, Ruby provides a way to set values for defined variables outside the class, known as setter methods, defined as follows:

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # Accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # Setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# Create an object
box = Box.new(10, 20)

# Use setter methods
box.setWidth = 30
box.setHeight = 50

# Use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Box width : #{x}"
puts "Box height : #{y}"

When the above code is executed, it produces the following result:

Box width : 30
Box height : 50

>

Since both methods are very common, Ruby defines three attribute declaration methods: attraccessor :variablename, attr_reader :variable_name, and attr_writer :variable_name. Where accessor = reader + writer.

Also note: The variable name must be prefixed with :, and variable names must be separated by ,.

Instance Methods

Instance methods are defined like any other method using the def keyword, but they can only be used through a class instance, as shown in the following example. They are not limited to accessing instance variables and can perform other tasks as needed.

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # Instance method
   def getArea
      @width * @height
   end
end

# Create an object
box = Box.new(10, 20)

# Call instance method
a = box.getArea()
puts "Area of the box is : #{a}"

When the above code is executed, it produces the following result:

Area of the box is : 200

Class Methods & Class Variables

Class variables are variables shared among all instances of a class. In other words, instances of class variables can be accessed by all object instances. Class variables are prefixed with two @ symbols (@@) and must be initialized within the class definition, as shown in the following example.

Class methods are defined using def self.methodname() and end with the end delimiter. Class methods can be called using the classname.methodname format, as shown in the following example:

#!/usr/bin/ruby -w

class Box
   # Initialize class variable
   @@count = 0
   def initialize(w,h)
      # Assign instance variables
      @width, @height = w, h

      @@count += 1
   end

   def self.printCount()
      puts "Box count is : #@@count"
   end
end

# Create two objects
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)

# Call class method to print box count
Box.printCount()

When the above code is executed, it produces the following result:

Box count is : 2

The to_s Method

Any class you define has a to_s instance method to return a string representation of the object. The following is a simple example that represents a Box object based on width and height:

#!/usr/bin/ruby -w

class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # Define to_s method
   def to_s
      "(w:#@width,h:#@height)"  # String formatting of the object
   end
end

# Create an object
box = Box.new(10, 20)

# Automatically calls to_s method
puts "String representation of box is : #{box}"

When the above code is executed, it produces the following result:

String representation of box is : (w:10,h:20)

Access Control

Ruby provides three levels of instance method protection: public, private, or protected. Ruby does not apply any access control on instance and class variables.

The following is a simple example demonstrating the syntax of these modifiers:

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # Instance method by default it is public
   def getArea
      getWidth() * getHeight
   end

   # Define private accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end
   # Make them private
   private :getWidth, :getHeight

   # Instance method to print area
   def printArea
      @area = getWidth() * getHeight
      puts "Big box area is : #@area"
   end
   # Make it protected
   protected :printArea
end

# Create an object
box = Box.new(10, 20)

# Call instance method
a = box.getArea()
puts "Area of the box is : #{a}"

# Try to call protected instance method
box.printArea()

When the above code is executed, it produces the following result. Here, the first method call succeeds, but the second method generates an issue.

Area of the box is : 200
test.rb:42: protected method `printArea' called for #<Box:0xb7f11280 @height=20, @width=10> (NoMethodError)

Class Inheritance

Inheritance is one of the most important concepts in object-oriented programming. Inheritance allows us to define a class based on another class, making application creation and maintenance easier.

Inheritance helps in reusing code and executing quickly. Unfortunately, Ruby does not support multiple inheritance, but Ruby supports mixins. A mixin is like a specific implementation of multiple inheritance, where only the interface part is inherited.

#!/usr/bin/ruby -w

class Box
   # Initialize class variable
   @@count = 0
   def initialize(w,h)
      # Assign instance variables
      @width, @height = w, h

      @@count += 1
   end

   def self.printCount()
      puts "Box count is : #@@count"
   end
end

# Create two objects
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)

# Call class method to print box count
Box.printCount()

When the above code is executed, it produces the following result:

Box count is : 2

The to_s Method

Any class you define has a to_s instance method to return a string representation of the object. The following is a simple example that represents a Box object based on width and height:

#!/usr/bin/ruby -w

class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # Define to_s method
   def to_s
      "(w:#@width,h:#@height)"  # String formatting of the object
   end
end

# Create an object
box = Box.new(10, 20)

# Automatically calls to_s method
puts "String representation of box is : #{box}"

When the above code is executed, it produces the following result:

String representation of box is : (w:10,h:20)

Access Control

Ruby provides three levels of instance method protection: public, private, or protected. Ruby does not apply any access control on instance and class variables.

The following is a simple example demonstrating the syntax of these modifiers:

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # Instance method by default it is public
   def getArea
      getWidth() * getHeight
   end

   # Define private accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end
   # Make them private
   private :getWidth, :getHeight

   # Instance method to print area
   def printArea
      @area = getWidth() * getHeight
      puts "Big box area is : #@area"
   end
   # Make it protected
   protected :printArea
end

# Create an object
box = Box.new(10, 20)

# Call instance method
a = box.getArea()
puts "Area of the box is : #{a}"

# Try to call protected instance method
box.printArea()

When the above code is executed, it produces the following result. Here, the first method call succeeds, but the second method generates an issue.

Area of the box is : 200
test.rb:42: protected method `printArea' called for #<Box:0xb7f11280 @height=20, @width=10> (NoMethodError)

Class Inheritance

Inheritance is one of the most important concepts in object-oriented programming. Inheritance allows us to define a class based on another class, making application creation and maintenance easier.

Inheritance helps in reusing code and executing quickly. Unfortunately, Ruby does not support multiple inheritance, but Ruby supports mixins. A mixin is like a specific implementation of multiple inheritance, where only the interface part is inherited. When creating a class, programmers can directly specify that the new class inherits members from an existing class, thus avoiding the need to write new data members and member functions from scratch. This existing class is called the base class or parent class, and the new class is called the derived class or subclass.

Ruby also provides the concept of subclassing, which is inheritance. The following example explains this concept. The syntax for extending a class is very simple. Just add a < character and the name of the parent class to the class statement. For example, the following defines the class BigBox as a subclass of Box:

Example

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # Instance method
   def getArea
      @width * @height
   end
end

# Define subclass
class BigBox < Box

   # Add a new instance method
   def printArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# Create object
box = BigBox.new(10, 20)

# Output area
box.printArea()

When the above code is executed, it produces the following result:

Big box area is : 200

Method Overloading

Although you can add new functionality in a derived class, sometimes you may want to change the behavior of a method already defined in the parent class. In this case, you can keep the method name the same and overload the method's functionality, as shown in the following example:

Example

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # Instance method
   def getArea
      @width * @height
   end
end

# Define subclass
class BigBox < Box

   # Change existing getArea method
   def getArea
      @area = @width * @height
      puts "Big box area is : #@area"
   end
end

# Create object
box = BigBox.new(10, 20)

# Use overloaded method to output area
box.getArea()

The above example outputs the following result:

Big box area is : 200

Operator Overloading

We want to use the + operator to perform vector addition of two Box objects, the * operator to multiply the width and height of a Box, and the unary operator - to negate the width and height of a Box. Below is a version of the Box class with mathematical operator definitions:

class Box
  def initialize(w,h) # Initialize width and height
    @width,@height = w, h
  end

  def +(other)         # Define + to perform vector addition
    Box.new(@width + other.width, @height + other.height)
  end

  def -@               # Define unary operator - to negate width and height
    Box.new(-@width, -@height)
  end

  def *(scalar)        # Perform scalar multiplication
    Box.new(@width*scalar, @height*scalar)
  end
end

Freezing Objects

Sometimes, we want to prevent objects from being changed. In Object, the freeze method can achieve this by effectively making an object constant. Any object can be frozen by calling Object.freeze. A frozen object cannot be modified, meaning you cannot change its instance variables.

You can check if a given object is frozen using the Object.frozen? method. If the object is frozen, this method returns true; otherwise, it returns false. The following example explains this concept:

Example

#!/usr/bin/ruby -w

# Define class
class Box
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # Accessor methods
   def getWidth
      @width
   end
   def getHeight
      @height
   end

   # Setter methods
   def setWidth=(value)
      @width = value
   end
   def setHeight=(value)
      @height = value
   end
end

# Create object
box = Box.new(10, 20)

# Let's freeze the object
box.freeze
if( box.frozen? )
   puts "Box object is frozen object"
else
   puts "Box object is normal object"
end

# Now try using setter methods
box.setWidth = 30
box.setHeight = 50

# Use accessor methods
x = box.getWidth()
y = box.getHeight()

puts "Width of the box is : #{x}"
puts "Height of the box is : #{y}"

When the above code is executed, it produces the following result:

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
        from test.rb:39

Class Constants

You can define a constant inside a class by assigning a direct numeric or string value to a variable. Constants do not require the use of @ or @@. By convention, constant names are in uppercase.

Once a constant is defined, you cannot change its value. You can access the constant inside the class directly, as if accessing a variable. However, if you want to access the constant outside the class, you must use classname::constant, as shown in the following example:

Example

#!/usr/bin/ruby -w

# Define class
class Box
   BOX_COMPANY = "TATA Inc"
   BOXWEIGHT = 10
   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end
   # Instance method
   def getArea
      @width * @height
   end
end

# Create object
box = Box.new(10, 20)

# Call instance method
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"

When the above code is executed, it produces the following result:

Area of the box is : 200
TATA Inc
Box weight is: 10

Class constants can be inherited and can also be overridden like instance methods.

Creating Objects Using allocate

There may be a situation where you want to create an object without calling its constructor initialize, i.e., without using the new method. In such cases, you can call allocate to create an uninitialized object, as shown in the following example:

Example

#!/usr/bin/ruby -w

# Define class
class Box
   attr_accessor :width, :height

   # Constructor method
   def initialize(w,h)
      @width, @height = w, h
   end

   # Instance method
   def getArea
      @width * @height
   end
end

# Create object using new
box1 = Box.new(10, 20)

# Create another object using allocate
box2 = Box.allocate

# Call instance method using box1
a = box1.getArea()
puts "Area of the box is : #{a}"

# Call instance method using box2
a = box2.getArea()
puts "Area of the box is : #{a}"

When the above code is executed, it produces the following result:

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*' 
   for nil:NilClass (NoMethodError) from test.rb:29

Class Information

Ruby's self is similar to Java's this but quite different. In Java, methods are referenced in instance methods, so this generally points to the current object. Ruby executes code line by line, so self has different meanings in different contexts. Let's look at the following example:

Example

#!/usr/bin/ruby -w

class Box
   # Output class information
   puts "Class of self = #{self.class}"
   puts "Name of self = #{self.name}"
end

When the above code is executed, it produces the following result:

Class of self = Class
Name of self = Box

This means that the class definition can be executed by treating the class as the current object, and also means that the method in the meta-class and superclass is available during the method definition execution.

❮ Ruby Comment Ruby Rubygems ❯