dry-types/lib/dry/types.rb

142 lines
3.5 KiB
Ruby

require 'bigdecimal'
require 'date'
require 'set'
require 'inflecto'
require 'concurrent'
require 'dry-container'
require 'dry-equalizer'
require 'dry/core/extensions'
require 'dry/core/constants'
require 'dry/types/version'
require 'dry/types/container'
require 'dry/types/type'
require 'dry/types/definition'
require 'dry/types/constructor'
require 'dry/types/fn_container'
require 'dry/types/errors'
module Dry
module Types
extend Dry::Configurable
extend Dry::Core::Extensions
include Dry::Core::Constants
# @!attribute [r] namespace
# @return [Container{String => Definition}]
setting :namespace, self
TYPE_SPEC_REGEX = %r[(.+)<(.+)>].freeze
# @return [Module]
def self.module
namespace = Module.new
define_constants(namespace, type_keys)
namespace
end
# @deprecated Include {Dry::Types.module} instead
def self.finalize
warn 'Dry::Types.finalize and configuring namespace is deprecated. Just'\
' do `include Dry::Types.module` in places where you want to have access'\
' to built-in types'
define_constants(config.namespace, type_keys)
end
# @return [Container{String => Definition}]
def self.container
@container ||= Container.new
end
# @api private
def self.registered?(class_or_identifier)
container.key?(identifier(class_or_identifier))
end
# @param [String] name
# @param [Type] type
# @param [#call,nil] block
# @return [Container{String => Definition}]
def self.register(name, type = nil, &block)
container.register(name, type || block.call)
end
# Registers given +klass+ in {#container} using +meth+ constructor
# @param [Class] klass
# @param [Symbol] meth
# @return [Container{String => Definition}]
def self.register_class(klass, meth = :new)
type = Definition.new(klass).constructor(klass.method(meth))
container.register(identifier(klass), type)
end
# @param [String,Class] name
# @return [Type,Class]
def self.[](name)
type_map.fetch_or_store(name) do
case name
when String
result = name.match(TYPE_SPEC_REGEX)
if result
type_id, member_id = result[1..2]
container[type_id].of(self[member_id])
else
container[name]
end
when Class
type_name = identifier(name)
if container.key?(type_name)
self[type_name]
else
name
end
end
end
end
# @param [Module] namespace
# @param [<String>] identifiers
# @return [<Definition>]
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
# @param [#to_s] klass
# @return [String]
def self.identifier(klass)
Inflecto.underscore(klass).tr('/', '.')
end
# @return [Concurrent::Map]
def self.type_map
@type_map ||= Concurrent::Map.new
end
# List of type keys defined in {Dry::Types.container}
# @return [<String>]
def self.type_keys
container._container.keys
end
end
end
require 'dry/types/core' # load built-in types
require 'dry/types/extensions'