module Di

Extended Modules

Defined in:

di.cr
di/errors.cr
di/key_parser.cr
di/provider.cr
di/registry.cr
di/scope.cr
di/version.cr

Constant Summary

VERSION = {{ (`shards version`).chomp.stringify }}

Full version string from shard.yml

VERSION_MAJOR = 0
VERSION_MINOR = 1
VERSION_PATCH = 3
VERSION_STATE = nil

Class Method Summary

Macro Summary

Class Method Detail

def self.get_all_providers(interface_type : String) : Array(Provider::Base) #

Get all providers for an interface type from the current scope chain.


[View source]
def self.get_named_provider(type_name : String, name : String) : Provider::Base #

Get a named provider by trying exact key (Type:name) then interface scan (Type:Impl:name). Raises AmbiguousServiceError if multiple interface impls share the same name.


[View source]
def self.get_named_provider?(type_name : String, name : String) : Provider::Base | Nil #

Get a named provider, returning nil if not found. Raises AmbiguousServiceError if multiple interface impls share the same name.


[View source]
def self.get_provider(key : String) : Provider::Base #

Get a provider from the current scope chain (or root registry). Tries exact key first, then interface prefix scan.


[View source]
def self.get_provider?(key : String) : Provider::Base | Nil #

Get a provider from the current scope chain, returning nil if not found.


[View source]
def self.healthy?(scope_name : Symbol) : Hash(String, Bool) #

Check health of all resolved singletons in a named scope.

Includes inherited services from parent scopes. Raises Di::ScopeNotFound if the scope is not active in the current fiber.


[View source]
def self.healthy? : Hash(String, Bool) #

Check health of all resolved singletons in the root registry.

Returns a hash mapping provider keys to health status. Only includes services that respond to .healthy? and have been resolved.


[View source]
def self.push_resolution(type_name : String, &) #

Track resolution chain to detect circular dependencies at runtime. Yields to block. Raises CircularDependency if type is already in chain.


[View source]
def self.register_provider(key : String, provider : Provider::Base) : Nil #

Register a provider in the current scope (or root registry). Sets the provider key for cycle detection before storing.


[View source]
def self.reset! : Nil #

Clear all providers and scopes (test helper).

Resets the container to a clean state. Primarily for use in specs. Raises Di::ScopeError if any scope is active in any fiber.


[View source]
def self.scope(name : Symbol, &) #

Create a named scope with parent inheritance.

Providers registered inside the block are scoped. The scope inherits all providers from the parent (or root if at top level). On block exit, shutdown is called on all scope-local singleton providers.

Example:

Di.scope(:request) do
  Di.provide { CurrentUser.from_token(token) }
  user = Di[CurrentUser]
end

[View source]
def self.shutdown! : Nil #

Shut down all singleton providers in reverse registration order.

Calls .shutdown on services that respond to it. Transient services and services without .shutdown are skipped. Raises Di::ScopeError if any scope is active in any fiber.


[View source]

Macro Detail

macro [](type, name = nil) #

Resolve a service by type.

Returns the instance as exactly T — fully typed, no casting. Singleton providers return the cached instance; transient providers create a new instance on every call.

Example:

db = Di[Database]
primary = Di[Database, :primary]

Raises Di::ServiceNotFound if the type is not registered.


[View source]
macro []?(type, name = nil) #

Resolve a service by type, returning nil if not registered.

Returns T? — the instance or nil. Does not raise.

Example:

db = Di[Database]?
replica = Di[Database, :replica]?

[View source]
macro invoke(type, name = nil) #

Resolve a service by type.

Deprecated: Use Di[Type] instead. This alias exists for backward compatibility but the bracket syntax is preferred for idiomatic Crystal.

Example:

db = Di.invoke(Database)                # deprecated
db = Di[Database]                       # preferred
primary = Di.invoke(Database, :primary) # deprecated
primary = Di[Database, :primary]        # preferred

Raises Di::ServiceNotFound if the type is not registered.


[View source]
macro invoke?(type, name = nil) #

Resolve a service by type, returning nil if not registered.

Deprecated: Use Di[Type]? instead. This alias exists for backward compatibility but the bracket syntax is preferred for idiomatic Crystal.

Example:

db = Di.invoke?(Database) # deprecated
db = Di[Database]?        # preferred

[View source]
macro provide(*deps, as _name = nil, transient _transient = false, &block) #

Register a service provider.

No block (auto-wire):

Interface binding (2 types, no block):

  • Di.provide Printable, Square # register Square under Printable key
  • Di.provide Printable, Square, as: :primary

Block forms:

  • Di.provide { Database.new(url) }
  • Di.provide(A) { |a| B.new(a) }
  • Di.provide(A, B) { |a, b| C.new(a, b) }
  • Di.provide({Database, :primary}) { |db| Repo.new(db) }

With dependency types, each is resolved and passed to block arguments in order. This works at top-level without macro-order issues.


[View source]