In Ruby’s completely object-oriented world, even the simplest elements numbers, strings, true/false values are treated as objects. This consistent approach extends to classes themselves, which are objects instantiated from the Class
class. This chapter delves into the rich tapestry of object-oriented programming within Ruby, revealing its unique and powerful capabilities.
Think of a class as a meticulously crafted mold: it dictates the shape and function of the objects created from it. This mold seamlessly integrates data (what the object is) with methods (what the object does), creating a self-contained, efficient unit. The data and methods within this unit are known as class members.
Defining Classes in Ruby
A data type’s blueprint is defined when a class is defined. The meaning of the class name that is, what an object of the class will contain and what operations may be carried out on it is defined here, even though no data is actually defined.
A class definition begins with the term class, then the class name, and ends with a delimiter. The Box class, for instance, was defined using the keyword class as follows –
class Box
code
end
The first letter of the name must be capitalized, and names with multiple words are typically put together without any separating letters (CamelCase).
Describe the Ruby Objects
The blueprints for things are provided by classes, therefore in essence, an object is made from a class. A new keyword is used to declare objects of a class. The statements that follow declare two Box-class objects –
box1 = Box.new
box2 = Box.new
The initialize Method in Ruby
The initialize method is a common class method in Ruby that functions similarly to constructors in other object-oriented programming languages. If you wish to set up certain class variables when an object is created, the initialize method can be helpful. This method can accept a list of parameters, and as demonstrated below, it would be prefixed by the def keyword, just like any other Ruby method.
class Box
def initialize(w,h)
@width, @height = w, h
end
end
Instance Variables in Ruby
Instance variables in Ruby act like personalized attributes for each object created from a class. Unlike class variables, each object gets its own unique set of instance variables; there’s no sharing of values between objects. Inside the class, the @
symbol designates an instance variable. To access these variables from outside the class, you need public accessor methods. For example, in a Box class, @width
and @height
would be instance variables, each object having its own distinct width and height.
class Box
def initialize(w,h)
# assign instance variables
@width, @height = w, h
end
end
Accessor and Mutator Methods
The variables must be defined inside accessor methods, sometimes referred to as getter methods, in order for them to be accessible from outside the class. The use of accessor methods is demonstrated in the following example –
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
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 "Width of the box is : #{x}"
puts "Height of the box is : #{y}"
The following outcome is obtained when the aforementioned code is run –
Width of the box is : 10
Height of the box is : 20
Ruby offers setter methods, which are described as follows, as a means of setting the values of variables from outside the class, much like accessor methods, which are used to access the values of the variables.
#!/usr/bin/ruby -w
# define a 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 "Width of the box is : #{x}"
puts "Height of the box is : #{y}"
The following outcome is produced when the aforementioned code is run –
Width of the box is : 30
Height of the box is : 50
Instance Methods in Ruby
The methods instance are defined using the def keyword just like any other method, and they can only be used with a class instance, as demonstrated below. Accessing instance variables is just one aspect of their capabilities; they can perform many other tasks based on your needs.
#!/usr/bin/ruby -w
# define a 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 methods
a = box.getArea()
puts "Area of the box is : #{a}"
The following outcome is obtained when the aforementioned code is run –
Area of the box is : 200
Class Methods and Variables in Ruby
In Ruby, class variables are communal resources shared across all objects of a class—a single instance accessible to all. They’re identified by a double @
prefix (@@
). Crucially, class variables must be initialized within the class definition itself.
Class methods, conversely, are invoked directly on the class itself (e.g., ClassName.methodName), not on individual objects. They’re defined using the def self.methodName syntax.
#!/usr/bin/ruby -w
class Box
# Initialize our class variables
@@count = 0
def initialize(w,h)
# assign instance avriables
@width, @height = w, h
@@count += 1
end
def self.printCount()
puts "Box count is : #@@count"
end
end
# create two object
box1 = Box.new(10, 20)
box2 = Box.new(30, 100)
# call class method to print box count
Box.printCount()
The following outcome is obtained when the aforementioned code is run –
Box count is : 2
Understanding to_s in Ruby
Every Ruby class should ideally include a to_s
method that provides a string representation of its objects. For instance, a Box object might use to_s
to return a string describing its 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)
# to_s method will be called in reference of string automatically.
puts "String representation of box is : #{box}"
The following outcome is obtained when the aforementioned code is run –
String representation of box is : (w:10,h:20)
Access Modifiers in Ruby
When it comes to instance methods, Ruby offers you three different levels of protection: public, private, and protected. Access control over instance and class variables is not implemented by Ruby.
- Public Methods − Anyone can invoke public methods. With the exception of initialize, which is always private, methods are by default public.
- Private Methods − From outside the class, private methods are not accessible or even visible. Private members can only be accessed by the class methods.
- Protected Methods − Only objects belonging to the defining class and its subclasses are able to invoke a protected method. Family members are the only ones with access.
Here is a basic example that illustrates the syntax of each of the three access modifiers –
#!/usr/bin/ruby -w
# define a 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 methods
a = box.getArea()
puts "Area of the box is : #{a}"
# try to call protected or methods
box.printArea()
The following outcome is obtained upon execution of the aforementioned code. The first method in this case is called successfully, while the second method got an error.
Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height = 20, @width = 10> (NoMethodError)
Inheritance in Ruby
Inheritance is one of the most crucial ideas in object-oriented programming. The ability to define a class in terms of another class through inheritance facilitates the development and upkeep of applications.
Although Ruby does not enable several levels of inheritance, it does support mixins, which allows for the reuse of code functionality and quick implementation times. Just the interface part is inherited in a mixin, which is similar to a specialized implementation of multiple inheritance.
Instead of developing entirely new member methods and data members when creating a class, the programmer can specify that the new class should inherit the members of an existing class. The derived class or sub-class is the new class, and the base class or superclass is the current class.
The idea of subclassing, or inheritance, is also supported by Ruby, and the example that follows illustrates it. A class can be extended using a straightforward syntax. Simply include the name of the superclass and a < character in your class statement. As an illustration, the following defines the class Big Box as a Box − subclass.
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method
def getArea
@width * @height
end
end
# define a subclass
class BigBox < Box
# add a new instance method
def printArea
@area = @width * @height
puts "Big box area is : #@area"
end
end
# create an object
box = BigBox.new(10, 20)
# print the area
box.printArea()
The following outcome is obtained when the aforementioned code is run –
Big box area is : 200
Overriding Methods in Ruby
Sometimes you want to alter the behavior of a parent class’s declared methods, even though you can add new functionality to a derived class. You can accomplish this by simply overriding the method’s functionality while maintaining the same method name, as demonstrated in the example below.
#!/usr/bin/ruby -w
# define a class
class Box
# constructor method
def initialize(w,h)
@width, @height = w, h
end
# instance method
def getArea
@width * @height
end
end
# define a subclass
class BigBox < Box
# change existing getArea method as follows
def getArea
@area = @width * @height
puts "Big box area is : #@area"
end
end
# create an object
box = BigBox.new(10, 20)
# print the area using overriden method.
box.getArea()
Operator Overloading in Ruby
We want the *
operator to multiply the width and height of a Box by a scalar, the unary – operator to negate the width and height of the Box, and the +
operator to perform vector addition of two Box objects using +
. A variant of the Box class with defined mathematical operators is shown here.
class Box
def initialize(w,h) # Initialize the width and height
@width,@height = w, h
end
def +(other) # Define + to do vector addition
Box.new(@width + other.width, @height + other.height)
end
def -@ # Define unary minus to negate width and height
Box.new(-@width, -@height)
end
def *(scalar) # To perform scalar multiplication
Box.new(@width*scalar, @height*scalar)
end
end
Freezing Objects in Ruby
There are moments when we wish to stop something from changing. In Object, we can accomplish this by using the freeze method, which essentially makes an object a constant. You can use Object. Freeze to freeze any object. A frozen object cannot be altered; its instance variables cannot be changed.
You can use Object. Frozen to see if an object is already frozen or not. method that, if the object is frozen, returns true; otherwise, it produces a false value. The idea is explained in the following example:
#!/usr/bin/ruby -w
# define a 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)
# let us freez this 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}"
The following outcome is obtained when the aforementioned code is run.
Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
from test.rb:39
Constants in Ruby Classes
Within a class, a constant can be defined by directly assigning a text or numeric value to a variable that is defined without the use of @ or @@. We typically use uppercase for constant names.
You cannot alter a constant’s value once it has been defined, but you can access it directly within a class, just like you do with a variable. However, classname::constant must be used if you wish to access a constant outside of the class, as demonstrated in the example below.
#!/usr/bin/ruby -w
# define a 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 an object
box = Box.new(10, 20)
# call instance methods
a = box.getArea()
puts "Area of the box is : #{a}"
puts Box::BOX_COMPANY
puts "Box weight is: #{Box::BOXWEIGHT}"
The following outcome is obtained when the aforementioned code is run –
Area of the box is : 200
TATA Inc
Box weight is: 10
Like instance methods, class constants are inheritable and overridable.
Object Allocation in Ruby
In some circumstances, such as when you want to create an object using a new method without using its constructor initialize, you can call allocation to create an uninitialized object for you, as seen in the example below.
#!/usr/bin/ruby -w
# define a 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 an 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}"
The following outcome is obtained when the aforementioned code is run –
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
Retrieving Class Information in Ruby
Self must refer to something if class definitions are executable code, which suggests that they run within an object. We’ll see what it is.
#!/usr/bin/ruby -w
class Box
# print class information
puts "Type of self = #{self.type}"
puts "Name of self = #{self.name}"
end
The following outcome is obtained when the aforementioned code is run –
Type of self = Class
Name of self = Box
This indicates that the class is the current object when a class definition is executed. This implies that while the method definition is being executed, methods in the metaclass and its superclasses will be accessible.