Dalibor Nasevic

Ruby Singleton Pattern

Singleton is perhaps the most hated of all programming patterns. You can read some of the reasons for this in Why Singletons are Evil. But, I think it has some good sides. I will first start by describing what a singleton pattern is, walk through different ways of implementing it in Ruby, and point to how it's used in Rails.

Singleton is a design pattern that restricts instantiation of a class to only one instance that is globally available. It is useful when you need that instance to be accessible in different parts of the application, usually for logging functionality, communication with external systems, database access, etc. There are few ways of implementing singleton pattern in Ruby:

Single Instance of a class

class Logger
  def initialize
    @log = File.open("log.txt", "a")
  end
  
  @@instance = Logger.new

  def self.instance
    return @@instance
  end

  def log(msg)
    @log.puts(msg)
  end

  private_class_method :new
end

Logger.instance.log('message 1')

In this code example, inside class Logger we create instance of the very same class Logger and we can access that instance with class method Logger.instance whenever we need to write something to the log file using the instance method "log". In the "initialize" method we just opened a log file for appending, and at the end of Logger class, we made method "new" private so that we cannot create new instances of class Logger. That is Singleton Pattern: only one instance, globally available.

Ruby Singleton module

Ruby Standard Library has a Singleton module which implements the Singleton pattern. Previous example when using the Singleton module would translate to:

require 'singleton'

class Logger
  include Singleton
  
  def initialize
    @log = File.open("log.txt", "a")
  end

  def log(msg)
    @log.puts(msg)
  end
end

Logger.instance.log('message 2')

Here we require and include Singleton module inside Logger class, define "initialize" method which opens the log file for appending and instance method "log" for writing to that log file. Ruby Singleton module does lazy instantiation (creates instance from Logger class at the moment when we call Logger.instance method) and not during load time (like in the previous example). Also, Ruby Singleton module makes "new" method private, so we don't have to call private_class_method.

Now, we can look at some of the alternative ways of implementing similar functionality to Singleton Pattern in Ruby:

Global variable

Because singleton instance should be globally available, we can create a global variable $logger and use it as reference to Logger instance. But, global variables are usually a bad idea, the problem is that they can be redefined during runtime without noticing that.

  $logger = Logger.new
  $logger.log("message 3")

Constant

On the other side, constants cannot be redefined, but we still have problems with lazy instantiation as in the case with a global variable.

  LOGGER = Logger.new
  LOGGER.log("message 4")

Class

Singleton pattern can also be implemented using class methods and class variables. Using class methods we are sure that we have a single instance.

class Logger

  def self.log(msg)
    @@log ||= File.open("log.txt", "a")
    @@log.puts(msg)
  end
end

Logger.log('message 5')

Module

Similar to class implementation, with the advantage that modules can't be instantiated. I generally prefer this way when using singleton pattern.

module Logger
  def self.log(msg)
    @@log ||= File.open("log.txt", "a")
    @@log.puts(msg)
  end
end

Singleton Pattern used in Rails

Rails is using the singleton pattern for the implementation of class Inflections. It is a single instance (Inflections.instance) that gives global access to all inflection rules used in different parts of Rails. Here is a code example (methods are empty for simplification):

module ActiveSupport
  module Inflector
    class Inflections
      def self.instance
        @__instance__ ||= new
      end

      attr_reader :plurals, :singulars, :uncountables, :humans

      def initialize
        @plurals, @singulars, @uncountables, @humans = [], [], [], []
      end

      def plural(rule, replacement)
      end

      def singular(rule, replacement)
      end

      def irregular(singular, plural)
      end

      def uncountable(*words)
      end

      def human(rule, replacement)
      end

      def clear(scope = :all)
      end
    end

    # Yields a singleton instance of Inflector::Inflections 
    # so you can specify additional inflector rules.
    def inflections
      if block_given?
        yield Inflections.instance
      else
        Inflections.instance
      end
    end

    def pluralize(word)
    end

    def singularize(word)
    end

    def humanize(lower_case_and_underscored_word)
    end

    def titleize(word)
    end

    def tableize(class_name)
    end

    def classify(table_name)
    end
  end
end

ActiveSupport::Inflector::Inflections class implements the singleton pattern. Singleton instance is returned or yielded by inflections method (defined in ActiveSupport::Inflector module) depending if it is called with a block or not. Methods like: pluralize, singularize, humanize, titleize, tableize and classify defined in ActiveSupport::Inflector module calls inflections method to get singleton instance and implement theirs functionality, and also inflections method is called in other parts of Rails like in config/initializers/inflections.rb with a block for adding other inflections that are not added by default:

ActiveSupport::Inflector.inflections do |inflect|
  #   inflect.plural /^(ox)$/i, '\1en'
  #   inflect.singular /^(ox)en/i, '\1'
  #   inflect.irregular 'person', 'people'
  #   inflect.uncountable %w( fish sheep )
end

Maybe you can point to other usages of Singleton Pattern in Ruby?