<commit_before>require 'bigdecimal'
require 'date'
require 'set'

require 'dry-container'
require 'inflecto'
require 'thread_safe/cache'

require 'dry/data/version'
require 'dry/data/container'
require 'dry/data/type'
require 'dry/data/struct'
require 'dry/data/dsl'

module Dry
  module Data
    extend Dry::Configurable

    setting :namespace, self

    class SchemaError < TypeError
      def initialize(key, value)
        super("#{value.inspect} (#{value.class}) has invalid type for :#{key}")
      end
    end

    class SchemaKeyError < KeyError
      def initialize(key)
        super(":#{key} is missing in Hash input")
      end
    end

    StructError = Class.new(TypeError)
    ConstraintError = Class.new(TypeError)

    TYPE_SPEC_REGEX = %r[(.+)<(.+)>].freeze

    def self.finalize
      define_constants(config.namespace, container._container.keys)
    end

    def self.container
      @container ||= Container.new
    end

    def self.register(name, type = nil, &block)
      container.register(name, type || block.call)
    end

    def self.register_class(klass, constructor = :new)
      container.register(
        identifier(klass), Type.new(klass.method(constructor), klass)
      )
    end

    def self.identifier(klass)
      Inflecto.underscore(klass).gsub('/', '.')
    end

    def self.[](name)
      type_map.fetch_or_store(name) do
        result = name.match(TYPE_SPEC_REGEX)

        type =
          if result
            type_id, member_id = result[1..2]
            container[type_id].member(self[member_id])
          else
            container[name]
          end

        type_map[name] = type
      end
    end

    def self.define_constants(namespace, identifiers)
      names = identifiers.map do |id|
        parts = id.split('.')
        [Inflecto.classify(parts.pop), parts.map(&Inflecto.method(:classify))]
      end

      names.map do |(klass, parts)|
        mod = parts.reduce(namespace) do |a, e|
          a.constants.include?(e.to_sym) ? a.const_get(e) : a.const_set(e, Module.new)
        end

        mod.const_set(klass, self[identifier((parts + [klass]).join('::'))])
      end
    end

    def self.type(*args, &block)
      dsl = DSL.new(container)
      block ? yield(dsl) : registry[args.first]
    end

    def self.type_map
      @type_map ||= ThreadSafe::Cache.new
    end
  end
end

require 'dry/data/types' # load built-in types
<commit_msg>Use inflecto camelize rather than classify (no need to singularize)<commit_after>require 'bigdecimal'
require 'date'
require 'set'

require 'dry-container'
require 'inflecto'
require 'thread_safe/cache'

require 'dry/data/version'
require 'dry/data/container'
require 'dry/data/type'
require 'dry/data/struct'
require 'dry/data/dsl'

module Dry
  module Data
    extend Dry::Configurable

    setting :namespace, self

    class SchemaError < TypeError
      def initialize(key, value)
        super("#{value.inspect} (#{value.class}) has invalid type for :#{key}")
      end
    end

    class SchemaKeyError < KeyError
      def initialize(key)
        super(":#{key} is missing in Hash input")
      end
    end

    StructError = Class.new(TypeError)
    ConstraintError = Class.new(TypeError)

    TYPE_SPEC_REGEX = %r[(.+)<(.+)>].freeze

    def self.finalize
      define_constants(config.namespace, container._container.keys)
    end

    def self.container
      @container ||= Container.new
    end

    def self.register(name, type = nil, &block)
      container.register(name, type || block.call)
    end

    def self.register_class(klass, constructor = :new)
      container.register(
        identifier(klass), Type.new(klass.method(constructor), klass)
      )
    end

    def self.identifier(klass)
      Inflecto.underscore(klass).gsub('/', '.')
    end

    def self.[](name)
      type_map.fetch_or_store(name) do
        result = name.match(TYPE_SPEC_REGEX)

        type =
          if result
            type_id, member_id = result[1..2]
            container[type_id].member(self[member_id])
          else
            container[name]
          end

        type_map[name] = type
      end
    end

    def self.define_constants(namespace, identifiers)
      names = identifiers.map do |id|
        parts = id.split('.')
        [Inflecto.camelize(parts.pop), parts.map(&Inflecto.method(:camelize))]
      end

      names.map do |(klass, parts)|
        mod = parts.reduce(namespace) do |a, e|
          a.constants.include?(e.to_sym) ? a.const_get(e) : a.const_set(e, Module.new)
        end

        mod.const_set(klass, self[identifier((parts + [klass]).join('::'))])
      end
    end

    def self.type(*args, &block)
      dsl = DSL.new(container)
      block ? yield(dsl) : registry[args.first]
    end

    def self.type_map
      @type_map ||= ThreadSafe::Cache.new
    end
  end
end

require 'dry/data/types' # load built-in types

{'fraction_non_alphanumeric': 0.12336065573770492, 'fraction_numerical': 0.000819672131147541, 'mean_word_length': 2.5837004405286343, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 5, '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': '3462458', 'n_tokens_mistral': 1596, 'n_tokens_neox': 1558, 'n_words': 347}