<commit_before>require 'base64'
require 'openssl'
require 'time'
require 'uri'

module AWS
  # A Signed Amazon Web Services (AWS) URL.
  #
  # Currently supports Signature Version 2.
  #
  # Note to self: Do not touch this code unless you have a compelling reason.
  class URL
    # The SHA256 hash algorithm.
    SHA256 = OpenSSL::Digest::SHA256.new

    # Initializes a new URL.
    #
    # base_url - The String base URL, including scheme, host and path, of the
    #            AWS endpoint.
    # key      - The String AWS access key id.
    # secret   - The String AWS secret key.
    def initialize(base_url, key, secret)
      @base_url = URI base_url
      @key      = key
      @secret   = secret
      @params   = {}
    end

    # Builds a signed URL for specified HTTP method.
    #
    # method - A String-like HTTP method.
    #
    # Returns a String URL.
    def build(method)
      # Build an unsigned query string.
      query = params.sort.map { |k, v| "#{k}=#{ percent_encode v }" }.join '&'

      # Build the signature.
      string_to_sign = [
        method.to_s.upcase,
        @base_url.host,
        @base_url.path,
        query
      ].join "\n"
      signature = sign string_to_sign

      "#{@base_url}?#{query}&Signature=#{percent_encode signature}"
    end

    # Returns the Hash AWS parameters.
    def params
      default_params.merge @params
    end

    # Updates the AWS parameters.
    #
    # hash - A Hash.
    #
    # Returns self.
    def update(hash)
      hash.each do |key, val|
        # Syntactic sugar: Camelize symbol keys.
        if key.is_a? Symbol
          key = key.to_s.split('_').map(&:capitalize).join
        end
        @params[key] = val
      end

      self
    end

    private

    def percent_encode(value)
      value.to_s.gsub(/([^\w.~-]+)/) do
        '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
      end
    end

    def default_params
      {
        'AWSAccessKeyId'   => @key,
        'SignatureVersion' => '2',
        'SignatureMethod'  => 'HmacSHA256',
        'Timestamp'        => Time.now.utc.iso8601
      }
    end

    def sign(message)
      digest = OpenSSL::HMAC.digest SHA256, @secret, message
      Base64.encode64(digest).chomp
    end
  end
end<commit_msg>Extract signature builder to class<commit_after>require 'base64'
require 'openssl'
require 'time'
require 'uri'

module AWS
  # A Signed Amazon Web Services (AWS) URL.
  #
  # Currently supports Signature Version 2.
  #
  # Note to self: Do not touch this code unless you have a compelling reason.
  class URL
    # Internal: A signature builder.
    class Signature
      # The SHA256 hash algorithm.
      SHA256 = OpenSSL::Digest::SHA256.new

      # Builds a signature.
      #
      # secret  - A String AWS secret key.
      # message - The String to sign.
      #
      # Returns a String signature.
      def self.build(secret, message)
        new(secret, message).build
      end

      def initialize(secret, message)
        @secret  = secret
        @message = message
      end

      def build
        Base64.encode64(digest).chomp
      end

      def digest
        OpenSSL::HMAC.digest SHA256, @secret, @message
      end
    end

    # Initializes a new URL.
    #
    # base_url - The String base URL, including scheme, host and path, of the
    #            AWS endpoint.
    # key      - The String AWS access key id.
    # secret   - The String AWS secret key.
    def initialize(base_url, key, secret)
      @base_url = URI base_url
      @key      = key
      @secret   = secret
      @params   = {}
    end

    # Builds a signed URL for specified HTTP method.
    #
    # method - A String-like HTTP method.
    #
    # Returns a String URL.
    def build(method)
      # Build an unsigned query string.
      query = params.sort.map { |k, v| "#{k}=#{ percent_encode v }" }.join '&'

      # Build the signature.
      string_to_sign = [
        method.to_s.upcase,
        @base_url.host,
        @base_url.path,
        query
      ].join "\n"
      signature = Signature.build @secret, string_to_sign

      # Stitch together URL components.
      "#{@base_url}?#{query}&Signature=#{percent_encode signature}"
    end

    # Returns the Hash AWS parameters.
    def params
      default_params.merge @params
    end

    # Updates the AWS parameters.
    #
    # hash - A Hash.
    #
    # Returns self.
    def update(hash)
      hash.each do |key, val|
        # Syntactic sugar: Camelize symbol keys.
        if key.is_a? Symbol
          key = key.to_s.split('_').map(&:capitalize).join
        end
        @params[key] = val
      end

      self
    end

    private

    def percent_encode(value)
      value.to_s.gsub(/([^\w.~-]+)/) do
        '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
      end
    end

    def default_params
      {
        'AWSAccessKeyId'   => @key,
        'SignatureVersion' => '2',
        'SignatureMethod'  => 'HmacSHA256',
        'Timestamp'        => Time.now.utc.iso8601
      }
    end

  end
end
{'fraction_non_alphanumeric': 0.10583796664019063, 'fraction_numerical': 0.011914217633042097, 'mean_word_length': 1.92, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, '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': '176457', 'n_tokens_mistral': 1632, 'n_tokens_neox': 1572, 'n_words': 465}