zhyt1985
5/22/2018 - 6:38 AM

Different ways to create the Singleton pattern in Ruby.

Different ways to create the Singleton pattern in Ruby.

##
# This files shows some possible implementations of the Singleton pattern
# in Ruby. I'm not a huge fan of the Singleton pattern, but it's nice
# in some cases. In this file I'm going to implement a simple logger.
#


##
# The first implementation that can come to our minds is to create a class
# that holds an instance as a class variable that can be accessed through
# the instance method called 'instance'. Then, with this instance you can
# log as usual. Furthermore, the 'new' class method is kept private to prevent
# the programmer of instantiating new logs.
class Log
  def initialize
    @log = File.new 'log.txt', 'a'
  end

  def log(msg)
    @log.puts msg
  end

  @@instance = Log.new

  def instance
    @@instance
  end

  private_class_method :new
end


##
# We can also achieve the above implementation by including the
# Singleton module.

require 'singleton'

class IncludedSingletonLog
  include Singleton

  def initialize
    @log = File.new 'log.txt', 'a'
  end

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


##
# And here is a more basic implementation. Here I want to show the idea of
# eager instantiation (which also applies to the techniques used above).
# Eager instantiation means that the instance is created when the class gets
# loaded, even if it's not used.
class EagerLog
  @@log = File.new 'log.txt', 'a'

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

  private_class_method :new
end


##
# On the other hand, lazy instantiation means that the instance is created
# only when the programmer wants to use this class for the first time.
class LazyLog
  def self.log
    @@log ||= File.new 'log.txt', 'a'
    @@log.puts msg
  end

  private_class_method :new
end


##
# In Ruby, the difference between a Module and a Class is just three methods:
# :new, :allocate and :superclass. So a Module is almost the same as a Class
# but it cannot be instantiated. Cool, we now can implement the above classes
# as modules but without calling the private_class_method method.
module ModuleLog
  @@log = File.new 'log.txt', 'a'

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