lib/hexp/node/normalize.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Hexp
  class Node
    # Normalize a node
    #
    class Normalize
      # Set a node to be normalized
      #
      # @param [Array] node A non-strict hexp
      #
      # @example
      #     Hexp::Node::Normalize.new([:p, {class:'foo'}])
      #
      # @api public
      def initialize(args)
        @raw = args
      end

      # Normalize to strict hexp nodes, cfr SPEC.md for details
      #
      # @return [Array] strict hexp node
      #
      # @api private
      def call
        [@raw.first, normalized_attributes, normalized_children]
      end

      private

      # Pulls the attributes hash out of a non-strict hexp
      #
      # @return [Hash] the attributes hash
      #
      # @api private
      def attributes
        attrs = @raw[1]
        return attrs if attrs.instance_of?(Hash)
        {}
      end

      # Returns the attributes hash with key and value converted to strings
      #
      # @return [Hash]
      #
      # @api private
      def normalized_attributes
        return attributes if attributes.all? {|k,v| k.instance_of?(String) && v.instance_of?(String) }
        Hash[*
          attributes.flat_map do |key, value|
            [key, value].map(&:to_s)
          end
        ]
      end

      # Pulls the children list out of a non-strict hexp
      #
      # @return [Array] the list of child hexps, non-strict
      #
      # @api private
      def children
        start = @raw[1].instance_of?(Hash) ? 2 : 1
        if @raw[start].respond_to?(:to_ary)
          @raw[start].to_ary
        else
          @raw.drop(start)
        end
      end

      # Normalize the third element of a hexp node, the list of children
      #
      # @return [Array] list of normalized hexps
      #
      # @api private
      def normalized_children
        if children.instance_of?(Hexp::List)
          children
        else
          Hexp::List.new( children )
        end
      end

      def self.coerce_node(node)
        case node
        when Hexp::Node, Hexp::TextNode
          node
        when String
          Hexp::TextNode.new(node)
        when ->(ch) { ch.respond_to? :to_hexp }
          response = node.to_hexp
          raise FormatError, "to_hexp must return a Hexp::Node, got #{response.inspect}" unless response.instance_of?(Hexp::Node) || response.instance_of?(Hexp::TextNode)
          response
        when Array
          Hexp::Node[*node]
        else
          raise FormatError, "Invalid value in Hexp literal : #{node.inspect} (#{node.class}) does not implement #to_hexp"
        end
      end
    end

  end
end