TheWebs
TheWebs

Reputation: 12923

Instantiating objects only once in singletons in ruby

I have been for the last couple of days working on this Factory pattern based class where you can register classes, give them unique names, then use them to create objects on the fly. My class looks as such:

module AisisWriter
  class ClassFactory
    class << self
      undef_method :new
      attr_accessor :registered_objects

      def register(class_name, klass, params = nil)
        if !params.nil? && !params.is_a?(Array)
          raise ArgumentError, "Params must be an array."
        end

        if registered?(class_name)
          raise ArgumentError, "Class name already registered."
        end

        @registered_object[class_name] = {:class_name => klass, :params => !params.nil? ? params.flatten : nil}
      end

      def registered?(class_name)
        if @registered_object.nil?
          return false
        end

        @registered_object.include? class_name
      end

      def create(class_name, params = nil)
        if !params.nil? && !params.is_a?(Array)
          raise ArgumentError, "Params must be an array."
        end

        if !registered?(class_name)
          raise ArgumentError, "Class does not exist in the registered classes."
        end

        klass = @registered_object[class_name]

        if !params.nil
          klass[:class_name].new(params.flatten)
        else
          flass[:class_name].new(*klass[:params])
        end
      end

    end
  end
end

But I have some questions, which arose as I was writing tests.

  1. in the register(...) function I do:

@registered_object[class_name] = {:class_name => klass, :params => !params.nil? ? params.flatten : nil} This fails because @registered_object is nil. How do I initialize it only once? In php I would write a getInstance() method and say if the class instance is set, don't set it again, while we set the class instance also set other variables that only need to be set once. Would I have a get_instance method here?

  1. In relation to question one, can I chain methods in ruby. so if I needed to get an instance of this class, which just sets @registered_object to {} once, could I then do: AisisWriter::ClassFactory.get_instance.register(...) Does ruby support that?

The basis here is to make sure that when this class is called upon, we check if @register_object is nil, and if so set it to a new instance of {}

If there is a easier solution I am all ears.

Upvotes: 1

Views: 864

Answers (2)

Chris Heald
Chris Heald

Reputation: 62648

If you want an actual singleton factory, then Ruby has the Singleton module which helps with that!

You can define your class like this:

module AisisWriter
  class ClassFactory
    include Singleton
    attr_accessor :registered_objects

    def initialize
      @registered_object = {}
    end

    def register(class_name, klass, params = nil)
      raise ArgumentError, "Class name already registered." if registered? class_name
      @registered_object[class_name] = {:class_name => klass, :params => params}
    end

    def registered?(class_name)
      @registered_object.key? class_name
    end

    def create(class_name, params = nil)
      raise ArgumentError, "Class does not exist in the registered classes." unless registered? class_name
      klass = @registered_object[class_name]
      klass[:class_name].new *Array(params || klass[:params])
    end
  end
end

This provides an #initialize method where you can set up all your singleton state, but ::new is uncallable as you'd expect. Instead, you use ::instance.

And then you'd use it like:

AisisWriter::ClassFactory.instance.register(...)
AisisWriter::ClassFactory.instance.create(...)

The factory will be instantiated only once when #instance is called for the first time.

Upvotes: 0

B Seven
B Seven

Reputation: 45943

You can do

def registered_object
  @registered_object ||= {}
end

Then call registered_object instead of @registered_object.

This is a common Ruby idiom. It means:

If @registered_object is falsy, then set it to {}.

Upvotes: 5

Related Questions