Skip to content
A compile-time, non-intrusive dependency injection system for Crystal.
Crystal
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github/workflows intial commit Jan 22, 2020
spec add sample specs + regression spec Feb 5, 2020
src Merge branch 'master' into macro-verbatim Feb 5, 2020
.editorconfig intial commit Jan 22, 2020
.gitignore intial commit Jan 22, 2020
.travis.yml
CHANGELOG.md add changelog entry Feb 5, 2020
LICENSE update to v0.2.0, add changelog Jan 23, 2020
README.md fix typos, improve docs Feb 3, 2020
shard.yml

README.md

HardWire ⚡

A Compile-time Dependency Injection system for Crystal.

Installation

  1. Add the dependency to your shard.yml:
dependencies:
  hardwire:
    github: jerometwell/hardwire
  1. 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, simple identifiers (/\w+/) that allow you to resolve a different registration of the same class.

# [...]

# registering a transient dependency with tag "secret"
transient String, "secret" {
  "a secret string"
}

# registering a singleton
# When no tags are set, it is considered the "default" registration
singleton DbService

# registering a different singleton with a tag
singleton DbService, "primary"

# Resolving Dependencies
class Resolving
  @[Hardwire::Tags(input: "secret", primary_db: "primary")]
  def initialize(input : String, primary_db : DbService, default_db : DbService)
  end
end

Resolving Manually 🔨

You can resolve dependencies manually using the .resolve macro. This allows you to resolve dependencies manually with the tag string.

module Container
  include HardWire::Container

  transient SecretService, "primary"
  singleton DatabaseThing
end

service = Container.resolve SecretService, "primary"
db = Container.resolve DatabaseThing

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

  1. Fork it (https://github.com/jerometwell/hardwire/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

You can’t perform that action at this time.