<commit_before>module Attributor
  class Hash
    extend Forwardable

    include Container
    include Enumerable

    class << self
      attr_reader :key_type, :value_type, :options
      attr_reader :value_attribute
      attr_reader :key_attribute
    end

    @key_type = Object
    @value_type = Object
    

    @key_attribute = Attribute.new(@key_type)
    @value_attribute = Attribute.new(@value_type)

    @saved_blocks = []
    @options = {}
    @keys = {}


    def self.inherited(klass)
      k = self.key_type
      v = self.value_type

      klass.instance_eval do
        @saved_blocks = []
        @options = {}
        @keys = {}
        @key_type = k
        @value_type = v
        @key_attribute = Attribute.new(@key_type)
        @value_attribute = Attribute.new(@value_type)
      end
    end

    def self.keys(**options, &key_spec)
      if block_given?
        @saved_blocks << key_spec
        @options.merge!(options)
      elsif @saved_blocks.any?
        self.definition
      end
      @keys
    end

    def self.definition
      opts = {
        :key_type => @key_type,
        :value_type => @value_type
      }.merge(@options)

      blocks = @saved_blocks.shift(@saved_blocks.size)
      compiler = dsl_class.new(self, opts)
      compiler.parse(*blocks)
    end

    def self.dsl_class
      @options[:dsl_compiler] || DSLCompiler
    end

    def self.native_type
      self
    end

    def self.valid_type?(type)
      type.kind_of?(self) || type.kind_of?(::Hash)
    end

    # @example Hash.of(key: String, value: Integer)
    def self.of(key: @key_type, value: @value_type)
      if key
        resolved_key_type = Attributor.resolve_type(key)
        unless resolved_key_type.ancestors.include?(Attributor::Type)
          raise Attributor::AttributorException.new("Hashes only support key types that are Attributor::Types. Got #{resolved_key_type.name}")
        end
        
      end

      if value
        resolved_value_type = Attributor.resolve_type(value)
        unless resolved_value_type.ancestors.include?(Attributor::Type)
          raise Attributor::AttributorException.new("Hashes only support value types that are Attributor::Types. Got #{resolved_value_type.name}")
        end
      end


    

      Class.new(self) do
        @key_type = resolved_key_type
        @value_type = resolved_value_type
        @value_attribute = Attribute.new(@key_type)
        @key_attribute = Attribute.new(@value_type)
        @concrete = true
        @keys = {}
      end
    end


    def self.construct(constructor_block,  **options)
      return self if constructor_block.nil?

      unless @concrete
        return self.of(key:self.key_type, value: self.value_type)
                   .construct(constructor_block,**options)
      end

      self.keys(options, &constructor_block)
      self
    end


    def self.example(context=nil, options: {})
      return self.new if (key_type == Object && value_type == Object)
<commit_msg>Fix one more case of Hash not setting the key and value attributes properly.<commit_after>module Attributor
  class Hash
    extend Forwardable

    include Container
    include Enumerable

    class << self
      attr_reader :key_type, :value_type, :options
      attr_reader :value_attribute
      attr_reader :key_attribute
    end

    @key_type = Object
    @value_type = Object
    

    @key_attribute = Attribute.new(@key_type)
    @value_attribute = Attribute.new(@value_type)

    @saved_blocks = []
    @options = {}
    @keys = {}


    def self.inherited(klass)
      k = self.key_type
      v = self.value_type

      klass.instance_eval do
        @saved_blocks = []
        @options = {}
        @keys = {}
        @key_type = k
        @value_type = v
        @key_attribute = Attribute.new(@key_type)
        @value_attribute = Attribute.new(@value_type)
      end
    end

    def self.keys(**options, &key_spec)
      if block_given?
        @saved_blocks << key_spec
        @options.merge!(options)
      elsif @saved_blocks.any?
        self.definition
      end
      @keys
    end

    def self.definition
      opts = {
        :key_type => @key_type,
        :value_type => @value_type
      }.merge(@options)

      blocks = @saved_blocks.shift(@saved_blocks.size)
      compiler = dsl_class.new(self, opts)
      compiler.parse(*blocks)
    end

    def self.dsl_class
      @options[:dsl_compiler] || DSLCompiler
    end

    def self.native_type
      self
    end

    def self.valid_type?(type)
      type.kind_of?(self) || type.kind_of?(::Hash)
    end

    # @example Hash.of(key: String, value: Integer)
    def self.of(key: @key_type, value: @value_type)
      if key
        resolved_key_type = Attributor.resolve_type(key)
        unless resolved_key_type.ancestors.include?(Attributor::Type)
          raise Attributor::AttributorException.new("Hashes only support key types that are Attributor::Types. Got #{resolved_key_type.name}")
        end
        
      end

      if value
        resolved_value_type = Attributor.resolve_type(value)
        unless resolved_value_type.ancestors.include?(Attributor::Type)
          raise Attributor::AttributorException.new("Hashes only support value types that are Attributor::Types. Got #{resolved_value_type.name}")
        end
      end


    

      Class.new(self) do
        @key_type = resolved_key_type
        @value_type = resolved_value_type
        
        @key_attribute = Attribute.new(@key_type)
        @value_attribute = Attribute.new(@value_type)
        @concrete = true
        @keys = {}
      end
    end


    def self.construct(constructor_block,  **options)
      return self if constructor_block.nil?

      unless @concrete
        return self.of(key:self.key_type, value: self.value_type)
                   .construct(constructor_block,**options)
      end

      self.keys(options, &constructor_block)
      self
    end


    def self.example(context=nil, options: {})
      return self.new if (key_type == Object && value_type == Object)

{'fraction_non_alphanumeric': 0.09619205298013245, 'fraction_numerical': 0.0, 'mean_word_length': 2.4052987598647126, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 7, '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': '897793', 'n_tokens_mistral': 1868, 'n_tokens_neox': 1799, 'n_words': 389}