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.

Shift Perspective

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

Beetle Car

Let's check the singleton methods for this specific string object.

p car.singleton_methods

This prints:

[:drive]

Key Takeaway

  • 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.

results matching ""

    No results matching ""