injection

2016-05-31

Dependency injection for Common Lisp

Upstream URL

github.com/ahungry/injection

Author

Matthew Carter <m@ahungry.com>

License

GPLv3
README

1Injection

Dependency Injection for Common Lisp.

2Example calls

Set up a config file similar to the one found in examples/config.yml(modeled after the PHP Symfony framework config):
parameters:
  foo: bar
  an_array: [1, 2, 3]
  extra_config_file: "../example/config_only_params.yml"

services:
  file_loader:
    # 'factory:' is a function that will return an instance of the class we require,
    # since Common Lisp doesn't have position based class
    # constructors, a factory function is needed to map them to the class.
    factory: File-Loader-Factory
    arguments: ["%extra_config_file%"]

  service_container:
    factory: Container-Factory
    arguments: ["@file_loader"]

Note that the factory functions are assumed to do something equivalent to the following (return a class instance as the return value):

(defun File-Loader-Factory (your-input-arg)
  "Create an instance of a File-Loader or whatever class."
  (make-instance 'File-Loader :file-name your-input-arg))

Then load up your config file with:

(injection:Container-Factory "/full/path/to/examples/config.yml" :singleton t)

This will create a global singleton instance of the last loaded config that is stored in

*container-singleton*
that you can access parameters and services from with these calls.

Access a parameter within it by using:

(get-parameter "foo") -> "bar"
(get-parameter "an_array") -> '(1 2 3)

Access a service within it by using:

(get-service "file_loader") -> #<FILE-LOADER {CB8A941}>
(get-service "service_container") -> #<CONTAINER {CB7FEE9}>

In case you didn't notice - yes, this is a sample config that is chaining one container to another different config (you can do that no problem!) - just be careful to avoid a circular loop or you may have an issue.

3I will not use global scope Pam I am, I will not in a can

Ok, so, not a fan of global state, I understand.

You can avoid any global state by not specifying the singleton keyword up above, and instead using the package as follows:

(use-package :injection)

(let ((my-container (Container-Factory "example/config.yml")))
           (Container-Get-Parameter my-container "foo")) -> "bar"

Or even simplified to (assuming you only need one value out of there:

(injection:Container-Get-Parameter (injection:Container-Factory "example/config.yml") "foo") -> "bar"

4Still on the agenda/caveats :TODO:

At the moment, the YAML files are read from top to bottom, and thismeans that you have to write them procedurally if injectingdependencies (which you're likely to do if you wanted to use this tobegin with).

I might also add an optional directory keyword that would allow specifying the location of a package to load (so you could actually load packages or files on your machine through asdf/quicklisp through the YAML configuration, instead of having to do it on your top level asd yourself).

Essentially it would look something like this:

services:
  service_container:
    factory: Container-Factory
    arguments: ["@file_loader"]
    directory: "/path/to/your-project"
    package: "package-name"

and would add the specified directory to your local quicklisp projects directory, followed by a (ql:quickload :package-name), although that may add needless complication.

5Installation

Clone the repository (you do know how to git clone by now right?)

Load the system with:

(ql:quickload :injection)
(use-package :injection)

6License

GPLv3

Dependencies (2)

  • cl-yaml
  • fiveam

Dependents (0)

    • GitHub
    • Quicklisp