Singleton Methods
In this chapter, you will learn about singleton methods and class methods and how they relate to each other.
Class Method
Shifting Perspective
Let's define a class method drive in Car class.
class Car
def self.drive
p 'driving'
end
end
How can we ask Ruby for the class methods defined in Car class? We cannot do:
Car.class_methods
We will get NoMethodError. We have to use singleton_methods.
class Car
def self.drive
p 'driving'
end
end
p Car.singleton_methods
This prints:
[:drive]
We can call the class method like this:
Car.drive
To view the drive() class method as a singleton method, we need to shift our perspective. We shift our perspective from Car class to Car as an instance of Class.
We can define the drive class method like this:
Car = Class.new
class << Car
def drive
p 'driving'
end
end
Car.drive
This also prints:
driving
We now see that Car is an instance of class Class so it is a singleton method from that perspective. To make this concept clear, if we create a Bus class that is an instance of Class:
Bus = Class.new
Bus.drive
This prints:
NoMethodError: undefined method ‘drive’ for Bus:Class
The drive class method is not available for Bus class or any other instances of Class.
Alternative Way to Define Class Method
Instead of using class << syntax, we can also define a class method like this:
Car = Class.new
def Car.drive
p 'driving'
end
Car.drive
Singleton Class and Class Method
We can also define a class method by defining a method inside the singleton class like this:
class Car
class << self
def drive
p 'driving'
end
end
end
p Car.singleton_methods
This prints:
[:drive]
The effect is the same as Class Method section, we can still call the drive() method like this:
Car.drive
Singleton Method for an Object
Let's define a singleton method called drive for a specific instance of Car class like this:
class Car
end
c = Car.new
def c.drive
'driving'
end
p c.singleton_methods
This prints:
[:drive]
We can call the singleton method drive like this:
p c.drive
This prints:
driving
Since this is a singleton method, the drive() method is not available for other instances of Car. This implies that we cannot do this:
b = Car.new
b.drive
We get the error:
NoMethodError: undefined method ‘drive’ for Car.
Define a Method in Singleton Class
We can do what we did in previous section like this:
class Car
end
c = Car.new
class << c
def drive
'driving'
end
end
p c.drive
This prints:
driving
Let's look at the singleton methods for Car class.
p c.singleton_methods
This prints:
[:drive]
This is the same as the previous section. They both illustrate different ways to define a method in the singleton class.
Mixin a Module
An alternative to defining a singleton method using:
class << obj
construct is to mix-in the method from a module. Here is an example:
module Driveable
def drive
'driving'
end
end
class Car
end
c = Car.new
class << c
include Driveable
end
p c.drive
This prints driving.
p c.singleton_methods
This prints [:drive]. This does the same thing we did in the previous section.
Different Approaches
We have seen three different approaches:
- Defining a class method in a Class.
- Defining a singleton method for a specific car object.
- Using mix-in to define a singleton method.
Let's now combine them all into one grand example:
module Stoppable
def stop
'brake failure, cannot stop'
end
end
class Car
def self.start
'starting'
end
end
c = Car.new
def c.fly
'flying'
end
class << c
include Stoppable
def drive
'driving'
end
end
p Car.singleton_methods
This prints [:start]. Let's print singleton methods for c, the specific instance of car object.
p c.singleton_methods
This prints:
[:fly, :drive, :stop]
We can filter out the methods included in the module by passing false to the singleton_methods().
p c.singleton_methods(false)
This prints:
[:fly, :drive]
These statements:
p Car.start
p c.drive
p c.stop
p c.fly
will print:
starting
driving
brake failure, cannot stop
flying
The first call is a class method call and the other three are singleton method calls.
Introspect Singleton Class
We can also ask Ruby for the singleton_class of a class like this:
class Car
end
p Car.singleton_class
This prints:
#Class:Car
Display Singleton Class
Let's look at a simple example for displaying the name of the singleton class:
class Car
class << self
def class_name
to_s
end
end
end
p Car.class_name
This prints:
Car
Dynamic Singleton Method
We can also use define_singleton_method() to dynamically define singleton method. Here is an example that defines to_s singleton method in Car class:
class Car
end
Car.define_singleton_method(:class_name) do
to_s
end
p Car.class_name
This still prints:
Car
Combo Example
Let's combine the two above examples into one example:
class Car
class << self
def class_name
to_s
end
end
end
Car.define_singleton_method(:whoami) do
"I am : #{class_name}"
end
p Car.whoami
This prints:
I am : Car
Singleton Method for String
As a last example, let's define a singleton method on Ruby's built-in string class.
car = 'Beetle'
car.define_singleton_method(:drive) { "You are driving : #{self}"}
p car.drive
This prints:
You are driving : Beetle
Let's check the singleton methods for this specific string object.
p car.singleton_methods
This prints:
[:drive]
- Class methods and singleton methods are the same.
Summary
In this chapter, we saw different ways to define class methods and singleton methods. Class methods are just methods on the singleton class. We also learned that the singleton methods live in singleton class.