lib/volt/utils/generic_pool.rb

Summary

Maintainability
A
1 hr
Test Coverage
module Volt
  # GenericPool is a base class you can inherit from to cache items
  # based on a lookup.
  #
  # GenericPool assumes either a block is passed to lookup, or a
  # #create method, that takes the path arguments and reutrns a new instance.
  #
  # GenericPool can handle as deep of paths as needed.  You can also lookup
  # all of the items at a sub-path with #lookup_all
  #
  # TODO: make the lookup/create threadsafe
  class GenericPoolDeleteException < StandardError; end

  class GenericPool
    attr_reader :pool

    def initialize
      @pool = {}
    end

    def clear
      @pool = {}
    end

    def lookup(*args, &block)
      section = @pool

      # TODO: This is to work around opal issue #500
      args.pop if args.last.nil? if RUBY_PLATFORM == 'opal'

      args.each_with_index do |arg, index|
        last = (args.size - 1) == index

        if last
          # return, creating if needed
          return (section[arg] ||= create_new_item(*args, &block))
        else
          next_section = section[arg]
          next_section ||= (section[arg] = {})
          section      = next_section
        end
      end
    end

    # Looks up the path without generating a new one
    def lookup_without_generate(*args)
      section = @pool

      args.each_with_index do |arg, index|
        section = section[arg]
        return nil unless section
      end

      section
    end

    # Does the actual creating, if a block is not passed in, it calls
    # #create on the class.
    def create_new_item(*args)
      if block_given?
        new_item = yield(*args)
      else
        new_item = create(*args)
      end

      transform_item(new_item)
    end

    # Allow other pools to override how the created item gets stored.
    def transform_item(item)
      item
    end

    # Make sure we call the pool one from lookup_all and not
    # an overridden one.
    alias_method :__lookup, :lookup

    def lookup_all(*args)
      result = __lookup(*args) { nil }

      if result
        result.values
      else
        []
      end
    end

    def remove(*args)
      stack   = []
      section = @pool

      args.each_with_index do |arg, index|
        stack << section

        if args.size - 1 == index
          unless section
            fail GenericPoolDeleteException, "An attempt was made to delete at #{arg}, full path: #{args.inspect} in #{inspect}"
          end

          section.delete(arg)
        else
          section = section[arg]
        end
      end

      (stack.size - 1).downto(1) do |index|
        node   = stack[index]
        parent = stack[index - 1]

        parent.delete(args[index - 1]) if node.size == 0
      end
    end

    def inspect
      "<#{self.class}:#{object_id} #{@pool.inspect}>"
    end

    def print
      puts '--- Running Queries ---'

      @pool.each_pair do |table, query_hash|
        query_hash.each_key do |query|
          puts "#{table.inspect}: #{query.inspect}"
        end
      end

      puts '---------------------'
    end
  end
end