# encoding: utf-8

module AttributesDSL

  # Describes a collection of attributes declaration with methods
  # to validate and extract instance attributes from a hash.
  #
  # @api private
  #
  # @author Andrew Kozin <Andrew.Kozin@gmail.com>
  #
  class Attributes
    # @!attribute [r] attributes
    #
    # Uses the set of attributes to ensure their uniqueness (by name)
    #
    # @return [Set] the set of registered attributes
    #
    def attributes
      @attributes ||= {}
    end

    # Initializes the attribute from given arguments
    # and returns new immutable collection with the attribute
    #
    # @param (see Attribute#initialize)
    #
    # @return [AttributesDSL::Attributes]
    #
    def add(name, options = {}, &coercer)
      name  = name.to_sym
      value = Attribute.new(name, options, &coercer)
      clone_with do
        @attributes  = attributes.merge(name => value)
        @transformer = nil
      end
    end

    # Returns the proc that converts a hash of attributes using current setting
    #
    # @return [Proc]
    #
    def transformer
      @transformer ||= transprocs.flatten.compact.reduce(:>>)
    end

    # Checks whether an attribute reader should be defined
    #
    # @param [#to_sym] name
    #
    # @return [Boolean]
    #
    def reader?(name)
      attributes[name.to_sym].reader
    end

    private

    def transprocs
      [
        Transprocs[:filter, keys],
        attributes.values.map(&:transformer),
        Transprocs[:update, keys]
      ]
    end

    def keys
      attributes.keys
    end

    def clone_with(&block)
      dup.tap { |instance| instance.instance_eval(&block) }
    end
  end
end