Back to Home

How do you create singletones in Ruby?

ruby · rails · singleton · singleton

How do you create singletones in Ruby?

    How to create a singleton in Ruby?
    Personally, 4 ways come to mind.


    image


    Method One: include Singleton


    The standard library defines a module Singletonthat performs
    some actions on the class, in particular:


    • Makes .newprivate
    • Adds .instancethat creates and / or returns an instance
    • Overrides #dupand #cloneso that they cause an error

    I guess this is a classic singleton implementation. In any case, I would write
    something like this if I were a Java programmer. Nothing special, everything works.


    Method two: module with module_function


    There Moduleis a method #module_functionthat allows you to use the
    defined methods on your own. This approach is used, for example, in
    Math. Example:


    module M
      module_function
      def f
        :f
      end
    end
    M.f # ==> :f

    I would not recommend such a singleton implementation for several reasons:


    • This still remains a mixin, which can be included in another
      class / module. This, of course, is not scary, but something is wrong here.
    • Private methods can only be made with crutches, as module_function
      creates a public copy of the method in itself. I immediately came up with just this:


      module M
        module_function
        def f
          g
        end
        def g
          'hello'
        end
        singleton_class.send(:private, :g)
      end
      M.f # ==> 'hello'
      M.g # ==> NoMethodError

    • (Personal reason) Modules using module_functionin my opinion should
      be a collection of stateless methods to help in something. Accordingly, it
      include MyModulewill be used only to make methods
      available in the current module without resorting to MyModule. This
      usage scenario is provided in "The Ruby Programming Language" withMath

    By the way, you can use for the same purpose extend selfinstead
    module_function. This will eliminate the problem of private methods. But let's say the
    notorious ruby-style-guide does not approve of this approach (link:
    https://github.com/bbatsov/ruby-style-guide#module-function )


    I think it’s obvious that it extend selfworks differently, but I’m not sure that there is
    any dangerous difference.


    upd. still not very obvious. extend selfmakes the module add itself to the list of connected modules (I don’t know how simple it is to write), but module_functioncreates copies of methods. Specifically, look at the code:


    module M
      module_function
      def f
        :f
      end
    end
    class A
      include M
    end
    M.f # ==> :f
    # module_function делает методы приватными в include
    A.new.send(:f) # ==> :f
    module M
      def f
        :TROLOLO
      end
    end
    A.new.send(:f) # ==> :TROLOLO
    M.f # ==> :f

    Method three: class / module with class methods only


    class MyClass
      def self.f
        :f
      end
      def self.g
        :g
      end
    end
    MyClass.f # ==> :f

    or so:


    class MyClass
      class << self
        def f
          :f
        end
        private
        def g
          :g
        end
      end
    end
    MyClass.f # ==> :f
    MyClass.g # ==> NoMethodError

    Of course, classyou can use instead module. In the aforementioned
    style guide, this approach is not recommended. They recommend it instead
    module_function.


    In my practice, this approach was most often encountered. Personally, it always seemed to me like
    some kind of scary crutch, but at the same time I like it more than use
    Singleton, because MySingleton.do_somethingIt looks more attractive to me
    MySingleton.instance.do_something.


    Create an instance of Object


    Recently, I constantly use this approach:


    MySingleton = Object.new
    class << MySingleton
      def f
        g
      end
      private
      def g
        puts 'hello'
      end
    end

    Now our singleton is just an instance Objectwith the methods we need:


    MySingleton.class # ==> Object

    Here are the problems:



    A small digression: class << self


    I think everyone saw the syntax:


    class MyClass
      class << self
        def do_something_on_class
          ...
        end
      end
      def do_something_on_instance
        ...
      end
    end

    At the same time, I have repeatedly noticed that a person does not know what this
    design means . Actually, in Ruby, objects actually have two classes: the one
    whose instance it is, and the so-called. "singleton class" - singleton class.


    Surely you saw examples where we define methods directly on objects.
    Something like this:


    x = Object.new
    def x.hey
      'hey'
    end
    x.hey # ==> 'hey'

    In a class-oriented OOP, an object does not have its own methods. The behavior of an object is
    determined by the class to which it belongs. Therefore, we cannot define a
    method on an object with def x.hey, we must define it in a class. But
    if we do this, then all instances Objectwill have to
    get a method #heythat we don’t want. Therefore, Ruby creates an “extra”
    class for the object, called a singleton class. You can get it using the method
    #singleton_class. In general, I got carried away and probably only confused those who did not
    know about the "singleton class". This is a very interesting side of Ruby, so I suggest
    reading about it yourself.


    Actually, in short, the design class << some_object"enters" into the
    singleton class . Compare:


    class A # enter class A scope
      def hey
        'hey'
      end
    end
    class << A # enter class A singleton class scope
      def hey
        'hello'
      end
    end
    A.new.hey # ==> 'hey'
    A.hey     # ==> 'hello'

    Only registered users can participate in the survey. Please come in.

    Well ... how?

    • 56.6% 1 option (Singleton) 34
    • 11.6% 2 option (module_function) 7
    • 23.3% 3 option (class methods) 14
    • 5% 4 option (Object.new) 3
    • 11.6% Custom 7

    Read Next