<commit_before>require_relative '../core/client'
module Cfer::Cfn

  class Client < Cfer::Core::Client
    attr_reader :name
    attr_reader :stack

    def initialize(options)
      @name = options[:stack_name]
      options.delete :stack_name
      @cfn = Aws::CloudFormation::Client.new(options)
      flush_cache
    end

    def create_stack(*args)
      begin
        @cfn.create_stack(*args)
      rescue Aws::CloudFormation::Errors::AlreadyExistsException
        raise Cfer::Util::StackExistsError
      end
    end

    def responds_to?(method)
      @cfn.responds_to? method
    end

    def method_missing(method, *args, &block)
      @cfn.send(method, *args, &block)
    end

    def converge(stack, options = {})
      Preconditions.check(@name).is_not_nil
      Preconditions.check(stack) { is_not_nil and has_type(Cfer::Core::Stack) }

      response = validate_template(template_body: stack.to_cfn)

      create_params = []
      update_params = []

      response.parameters.each do |tmpl_param|
        cfn_param = stack.input_parameters[tmpl_param.parameter_key]

        if cfn_param
          output_val = tmpl_param.no_echo ? '*****' : cfn_param
          Cfer::LOGGER.debug "Parameter #{tmpl_param.parameter_key}=#{output_val}"
          p = {
            parameter_key: tmpl_param.parameter_key,
            parameter_value: cfn_param,
            use_previous_value: false
          }

          create_params << p
          update_params << p
        else
          Cfer::LOGGER.debug "Parameter #{tmpl_param.parameter_key} is unspecified (unchanged or default)"
          update_params << {
            parameter_key: tmpl_param.parameter_key,
            use_previous_value: true
          }
        end
      end

      created = false
      cfn_stack =
        begin
          create_stack stack_name: name,
            template_body: stack.to_cfn,
            parameters: create_params,
            capabilities: response.capabilities
          created = true
        rescue Cfer::Util::StackExistsError
          update_stack stack_name: name,
            template_body: stack.to_cfn,
            parameters: update_params,
            capabilities: response.capabilities
        end

      flush_cache
      cfn_stack
    end

    # Yields to the given block for each CloudFormation event that qualifies, given the specified options.
    # @param options [Hash] The options hash
    # @option options [Fixnum] :number The maximum number of already-existing CloudFormation events to yield.
    # @option options [Boolean] :follow Set to true to wait until the stack enters a `COMPLETE` or `FAILED` state, yielding events as they occur.
    def tail(options = {}, &block)
      q = []
      event_id_highwater = nil
      counter = 0
      number = options[:number] || 0
      for_each_event name do |event|
        q.unshift event if counter < number
        counter = counter + 1
      end

      while q.size > 0
        event = q.shift
        yield event
        event_id_highwater = event.event_id
      end

      running = true
      if options[:follow]
        while running
          stack_status = describe_stacks(stack_name: name).stacks.first.stack_status
          running = running && (/.+_(COMPLETE|FAILED)$/.match(stack_status) == nil)

          yielding = true
          for_each_event name do |event|
            if event_id_highwater == event.event_id
              yielding = false
            end

            if yielding
              q.unshift event
            end
          end

          while q.size > 0
            event = q.shift
            yield event
            event_id_highwater = event.event_id
          end

          sleep 1 if running unless options[:no_sleep]
        end
      end
    end

    def fetch_stack(stack_name = @name)
      raise Cfer::Util::StackDoesNotExistError, 'Stack name must be specified' if stack_name == nil
      begin
        @stack_cache[stack_name] ||= describe_stacks(stack_name: stack_name).stacks.first.to_h
      rescue Aws::CloudFormation::Errors::ValidationError => e
        raise Cfer::Util::StackDoesNotExistError, e.message
      end
    end

    def fetch_parameters(stack_name = @name)
      @stack_parameters[stack_name] ||= cfn_list_to_hash('parameter', fetch_stack(stack_name)[:parameters])
    end

    def fetch_outputs(stack_name = @name)
      @stack_outputs[stack_name] ||= cfn_list_to_hash('output', fetch_stack(stack_name)[:outputs])
    end

    def fetch_output(stack_name, output_name)
      fetch_outputs(stack_name)[output_name] || raise(Cfer::Util::CferError, "Stack #{stack_name} has no output named `#{output_name}`")
    end

    def fetch_parameter(stack_name, param_name)
      fetch_parameters(stack_name)[param_name] || raise(Cfer::Util::CferError, "Stack #{stack_name} has no parameter named `#{param_name}`")
    end

    def to_h
      @stack.to_h
    end

    private

    def cfn_list_to_hash(attribute, list)
      pluralized = :"#{attribute}s"
      key = :"#{attribute}_key"
      value = :"#{attribute}_value"

      Hash[ *list.map { |kv| [ kv[key], kv[value] ] }.flatten ]
    end

    def flush_cache
      @stack_cache = {}
      @stack_parameters = {}
      @stack_outputs = {}
    end

    def for_each_event(stack_name)
      describe_stack_events(stack_name: stack_name).stack_events.each do |event|
        yield event
      end
    end
  end
end
<commit_msg>Cleanup<commit_after>require_relative '../core/client'
module Cfer::Cfn

  class Client < Cfer::Core::Client
    attr_reader :name
    attr_reader :stack

    def initialize(options)
      @name = options[:stack_name]
      options.delete :stack_name
      @cfn = Aws::CloudFormation::Client.new(options)
      flush_cache
    end

    def create_stack(*args)
      begin
        @cfn.create_stack(*args)
      rescue Aws::CloudFormation::Errors::AlreadyExistsException
        raise Cfer::Util::StackExistsError
      end
    end

    def responds_to?(method)
      @cfn.responds_to? method
    end

    def method_missing(method, *args, &block)
      @cfn.send(method, *args, &block)
    end

    def converge(stack, _options = {})
      Preconditions.check(@name).is_not_nil
      Preconditions.check(stack) { is_not_nil and has_type(Cfer::Core::Stack) }

      response = validate_template(template_body: stack.to_cfn)

      create_params = []
      update_params = []

      response.parameters.each do |tmpl_param|
        cfn_param = stack.input_parameters[tmpl_param.parameter_key]

        if cfn_param
          output_val = tmpl_param.no_echo ? '*****' : cfn_param
          Cfer::LOGGER.debug "Parameter #{tmpl_param.parameter_key}=#{output_val}"
          p = {
            parameter_key: tmpl_param.parameter_key,
            parameter_value: cfn_param,
            use_previous_value: false
          }

          create_params << p
          update_params << p
        else
          Cfer::LOGGER.debug "Parameter #{tmpl_param.parameter_key} is unspecified (unchanged or default)"
          update_params << {
            parameter_key: tmpl_param.parameter_key,
            use_previous_value: true
          }
        end
      end

      cfn_stack =
        begin
          create_stack stack_name: name,
            template_body: stack.to_cfn,
            parameters: create_params,
            capabilities: response.capabilities
        rescue Cfer::Util::StackExistsError
          update_stack stack_name: name,
            template_body: stack.to_cfn,
            parameters: update_params,
            capabilities: response.capabilities
        end

      flush_cache
      cfn_stack
    end

    # Yields to the given block for each CloudFormation event that qualifies, given the specified options.
    # @param options [Hash] The options hash
    # @option options [Fixnum] :number The maximum number of already-existing CloudFormation events to yield.
    # @option options [Boolean] :follow Set to true to wait until the stack enters a `COMPLETE` or `FAILED` state, yielding events as they occur.
    def tail(options = {})
      q = []
      event_id_highwater = nil
      counter = 0
      number = options[:number] || 0
      for_each_event name do |fetched_event|
        q.unshift fetched_event if counter < number
        counter = counter + 1
      end

      while q.size > 0
        event = q.shift
        yield event
        event_id_highwater = event.event_id
      end

      running = true
      if options[:follow]
        while running
          stack_status = describe_stacks(stack_name: name).stacks.first.stack_status
          running = running && (/.+_(COMPLETE|FAILED)$/.match(stack_status) == nil)

          yielding = true
          for_each_event name do |fetched_event|
            if event_id_highwater == fetched_event.event_id
              yielding = false
            end

            if yielding
              q.unshift fetched_event
            end
          end

          while q.size > 0
            event = q.shift
            yield event
            event_id_highwater = event.event_id
          end

          sleep 1 if running unless options[:no_sleep]
        end
      end
    end

    def fetch_stack(stack_name = @name)
      raise Cfer::Util::StackDoesNotExistError, 'Stack name must be specified' if stack_name == nil
      begin
        @stack_cache[stack_name] ||= describe_stacks(stack_name: stack_name).stacks.first.to_h
      rescue Aws::CloudFormation::Errors::ValidationError => e
        raise Cfer::Util::StackDoesNotExistError, e.message
      end
    end

    def fetch_parameters(stack_name = @name)
      @stack_parameters[stack_name] ||= cfn_list_to_hash('parameter', fetch_stack(stack_name)[:parameters])
    end

    def fetch_outputs(stack_name = @name)
      @stack_outputs[stack_name] ||= cfn_list_to_hash('output', fetch_stack(stack_name)[:outputs])
    end

    def fetch_output(stack_name, output_name)
      fetch_outputs(stack_name)[output_name] || raise(Cfer::Util::CferError, "Stack #{stack_name} has no output named `#{output_name}`")
    end

    def fetch_parameter(stack_name, param_name)
      fetch_parameters(stack_name)[param_name] || raise(Cfer::Util::CferError, "Stack #{stack_name} has no parameter named `#{param_name}`")
    end

    def to_h
      @stack.to_h
    end

    private

    def cfn_list_to_hash(attribute, list)
      key = :"#{attribute}_key"
      value = :"#{attribute}_value"

      Hash[ *list.map { |kv| [ kv[key], kv[value] ] }.flatten ]
    end

    def flush_cache
      @stack_cache = {}
      @stack_parameters = {}
      @stack_outputs = {}
    end

    def for_each_event(stack_name)
      describe_stack_events(stack_name: stack_name).stack_events.each do |event|
        yield event
      end
    end
  end
end

{'fraction_non_alphanumeric': 0.09031000556896232, 'fraction_numerical': 0.0011137924633376648, 'mean_word_length': 2.4391956591126718, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6114109', 'n_tokens_mistral': 3239, 'n_tokens_neox': 3189, 'n_words': 737}