Simple and small DI Container.
-
Add the dependency to your
shard.yml:dependencies: radbas-container: github: radbas/container.cr version: ~> 1.0
-
Run
shards install
Extend the abstract Radbas::Container class and register/autowire your services:
require "radbas-container"
class MyService
def initialize(
@dep : DependencyService,
); end
end
class MyContainer < Radbas::Container
# autowire does let you register namespaces
# from which classes get resolved automatically
autowire(MyApp, CustomNamespace)
# register_subclasses registers all concrete subclasses of a given abstract class
register_subclasses(MyAbstractClass, public: false)
# register a single service
register(DependencyService)
# a factory can be used for setup
register(MyService, factory: -> {
# you can call get inside a factory to resolve dependencies
MyService.new(get(DependencyService))
}, public: true)
# or: params can be used to configure constructor params
# register(MyService, params: {dep: get(DependencyService)})
endBy default, all registered services are private. Normally you would register a single class as a public entrypoint and call get on the container:
class MyContainer < Radbas::Container
register(MyApplication, public: true)
end
container = MyContainer.new
app = container.get(MyApplication)
app.run
# ...- Services are created lazily on the first
getand then reused: every container instance holds one instance per service. Resolving is fiber-safe. - Registrations belong to the container class they are made in. A container inheriting from another container can use and override the registrations of its parent.
- Constructor arguments with a default value are left alone when their type is neither registered nor autowired. Nilable arguments (
dep : Foo? = nil) receiveFooif it is registered. - Generic services can be registered (
register(Repository(User))); type vars in their constructor resolve to the actual types. - Services with multiple
initializeoverloads, or with a typed splat argument (*deps : Foo), must be registered with a factory. - Structs can be registered, but
getreturns a copy of the cached value. - The container itself can be injected (
@container : MyContainer), andget(Radbas::Container)returns the container itself. - Configuration errors (unresolvable types, missing registrations, abstract types without factory) are reported at compile time, as is calling
getwith a type that is not registered or not public. - At runtime,
getraises a subclass ofRadbas::Container::Error:Radbas::Container::CircularDependencyErrorwhen a service depends on itself (directly or indirectly)Radbas::Container::SubclassAccessErrorwhen called with an unregistered subclass of a registered service
shards install
mkdir -p bin && crystal build -o bin/ameba lib/ameba/bin/ameba.cr
crystal spec
crystal tool format --check
bin/ameba- Fork it (https://github.com/radbas/container.cr/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
- Johannes Rabausch - creator and maintainer