SpontaneousCMS/spontaneous

View on GitHub
lib/spontaneous/collections/prototype_set.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# encoding: UTF-8

module Spontaneous::Collections
  class PrototypeSet
    include Enumerable

    attr_reader :store, :local_order

    def initialize(superobject = nil, superset_name = nil, &default_proc)
      @superobject, @superset_name = superobject, superset_name
      @has_superset = !!(@superobject && @superobject.respond_to?(@superset_name))
      @store = Hash.new
      @default_proc = default_proc
      @local_order = []
    end

    def []=(name, value)
      key = name.to_sym
      @store[key] = value
      add_key(key)
    end

    def [](key)
      case key
      when Fixnum
        indexed(key)
      else
        named(key)
      end
    end

    def add_key(key)
      local_order << key unless order.include?(key)
    end

    def key?(key, inherited = true)
      keys(inherited).include?(key.to_sym)
    end

    alias_method :has_key?, :key?

    def empty?
      order.empty?
    end

    def length
      order.length
    end

    alias_method :count, :length

    def last
      named(order.last)
    end

    def local_first
      if (key = local_order.first)
        named(key)
      else
        superset? ? superset.local_first : nil
      end
    end

    def hierarchy_detect(&block)
      if (found = local_detect(&block))
        found
      else
        superset? ? superset.hierarchy_detect(&block) : nil
      end
    end

    def local_detect(&block)
      local_values.detect(&block)
    end

    def local_values
      local_order.map { |name| named(name) }
    end

    def sid(schema_id)
      values.detect { |e| e.schema_id == schema_id }
    end

    def each
      order.each do |name|
        yield(named(name)) if block_given?
      end
    end

    def keys(inherited = true)
      order(inherited).map { |name| name }
    end

    def values
      map { | value | value }
    end

    # overwrites the existing ordering with a custom one
    # any current keys not explicitly included in the new ordering
    # will be tacked on the end in their current arrangement
    #
    # e.g.
    # set.order #=> [:a, :b, :c, :d]
    # set.order = [:b, :d]
    # set.order #=> [:b, :d, :a, :c]
    #
    def order=(newlocal_order)
      # clear any previous custom order
      @custom_order = nil
      oldlocal_order = order.dup
      order = []
      newlocal_order.each do |name|
        key = name.to_sym
        if oldlocal_order.include?(key)
          oldlocal_order.delete(key)
          order << key
        end
      end
      order += oldlocal_order
      @custom_order = order
    end


    def order(inherited = true)
      return @custom_order if @custom_order
      return local_order unless inherited
      superset? ? (superset.order + local_order) : local_order
    end

    alias_method :names, :order

    def indexed(index)
      named(order[index])
    end

    def named(name)
      key = name.to_sym
      return @store[key] if @store.key?(key)
      value = (superset? ? superset.named(name) : nil)
      return value unless value.nil?
      if @default_proc
        @default_proc.call(self, key)
      else
        nil
      end
    end

    def index(entry)
      order.index(entry.name)
    end

    def superset?
      @has_superset
    end

    # returns the superset for this set, if it exists
    # initialised lazily to avoid potential loops
    def superset
      @superset ||= @superobject.send(@superset_name) if superset?
    end



    def method_missing(method, *args)
      if key?(method)
        named(method)
      else
        super
      end
    end

    protected :local_order, :method_missing

  end # PrototypeSet
end # Spontaneous