require 'active_support/core_ext/string/inflections'
require 'json'
require 'memoist'
require 'stacker/differ'
require 'stacker/stack/component'

module Stacker
  class Stack
    class Template < Component

      FORMAT_VERSION = '2010-09-09'

      extend Memoist

      def exists?
        File.exists? path
      end

      def local
        @local ||= begin
          if exists?
            template = JSON.parse File.read path
            template['AWSTemplateFormatVersion'] ||= FORMAT_VERSION
            template
          else
            {}
          end
        end
      end

      def remote
        @remote ||= JSON.parse client.template
      rescue AWS::CloudFormation::Errors::ValidationError => err
        if err.message =~ /does not exist/
          raise DoesNotExistError.new err.message
        else
          raise Error.new err.message
        end
      end

      def diff *args
        Differ.json_diff local, remote, *args
      end
      memoize :diff

      def write value = local
        File.write path, JSON.pretty_generate(value) + "\n"
      end

      def dump
        write remote
      end

      private

      def path
        @path ||= File.join(
          stack.region.templates_path,
          "#{stack.options.fetch('template_name', stack.name)}.json"
        )
      end

    end
  end
end

require 'active_support/core_ext/string/inflections'
require 'json'
require 'memoist'
require 'stacker/differ'
require 'stacker/stack/component'

module Stacker
  class Stack
    class Template < Component

      FORMAT_VERSION = '2010-09-09'

      extend Memoist

      def self.format object
        formatted = JSON.pretty_generate object

        # put empty arrays on a single line
        formatted.gsub! /: \[\s*\]/m, ': []'

        # put { "Ref": ... } on a single line
        formatted.gsub! /\{\s+\"Ref\"\:\s+(?<ref>\"[^\"]+\")\s+\}/m, '{ "Ref": \\k<ref> }'

        formatted + "\n"
      end

      def exists?
        File.exists? path
      end

      def local
        @local ||= begin
          if exists?
            template = JSON.parse File.read path
            template['AWSTemplateFormatVersion'] ||= FORMAT_VERSION
            template
          else
            {}
          end
        end
      end

      def remote
        @remote ||= JSON.parse client.template
      rescue AWS::CloudFormation::Errors::ValidationError => err
        if err.message =~ /does not exist/
          raise DoesNotExistError.new err.message
        else
          raise Error.new err.message
        end
      end

      def diff *args
        Differ.json_diff local, remote, *args
      end
      memoize :diff

      def write value = local
        File.write path, self.class.format(value)
      end

      def dump
        write remote
      end

      private

      def path
        @path ||= File.join(
          stack.region.templates_path,
          "#{stack.options.fetch('template_name', stack.name)}.json"
        )
      end

    end
  end
end

Add a bit of more intelligent formatting to JSON output
Add a bit of more intelligent formatting to JSON output

There's no way to make everyone happy, but some things can be
standardized a bit.

e.g. having Refs on a single line makes a lot of sense.

Signed-off-by: Evan Owen <a31932a4150a6c43f7c4879727da9a731c3d5877@gmail.com>
