HardWire ⚡
A Compile-time Dependency Injection system for Crystal.
Installation
- Add the dependency to your
shard.yml
:
dependencies:
hardwire:
github: jerometwell/hardwire
- Run
shards install
Usage
require "hardwire"
Hardwire is designed to operate inside a container object. Since the resolution is compile-time (Using Macros), normally this will be a module.
Creating a container 📦
# To create a new container, include HardWire::Container
# This will add the macros you need to register and resolve wiring
module Container
include HardWire::Container
# use transient/singleton to wire different lifecycles
# singleton dependencies will be memoized
# dependencies for the constructor will be resolved from the constructor automatically
transient Dependency
singleton NeedsDependency
# you can also register dependencies with a block instead of inspecting the constructor
# Your block MUST return an instance of the class you are registering
singleton NeedsDependency {
NeedsDependency.new( self.resolve Dependency )
}
end
Hardwire tries to operate with minimal modifications to other classes (unless required). "simple" classes, e.g.
- Have a single constructor
- Have unique dependencies/do not require tags
If your classes match this signature, you can wire up in the container without adding anything to the classes.
For everything else, there's:
Multiple Constructors 🚧
Hardwire needs to know which constuctor function to use.
Annotate your "Injectable" constructor with the Hardwire::Inject annotation.
class MultipleInits
@[HardWire::Inject]
def initialize(input: String)
# register will inspect this method's arguments
# [...]
end
def initialize
# will not be used for injection
# [...]
end
end
Tags 🏷
To differentiate between registrations of the same type, use the HardWire::Tags annotation. Tags allow you to attach additional metadata to the signature. Tags themselves are string-based, delimited with commas, and all tags must match for injection.
# [...]
# registering a transient dependency with tag "secret"
transient String, "secret" {
"a secret string"
}
# registering a singleton with many tags
singleton DbService, "primary,magic,amazing"
# Resolving Dependencies
class Resolving
@[Hardwire::Tags(input: "secret", db: "primary,magic,amazing")]
def initialize(input : String, db : DbService)
end
end
Runtime Interrogation 👀
Hardwire can tell you information about the registrations at runtime, but the dependencies are HardWired(See what I did there?), so they can't be changed.
module Container
include HardWire::Container
singleton DbService
end
Container.registered?(DbService) # true
Container.registered?(DbService, "tagged") # false
Container.registered?(String) # false
Contributing
- Fork it (https://github.com/jerometwell/hardwire/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
Contributors
- Jerome Twell - creator and maintainer