lentildi
v0.3.0
Published
Lightweight Dependency Injection for JavaScript apps
Downloads
1,834
Maintainers
Readme
LentilDI
Lightweight + Simple ES6 Dependency Injection :)
LentilDI lets you build apps without the pain of having to instantiate, wire up, and manually manage your dependency tree. LentilDI emphasises:
- Ease of module testing
- Reduction of boilerplate dependency juggling
Check out the hello world example for a quick introduction.
Install
$ npm install --save lentildi
Example
With LentilDI, you can go from something like this:
const tuba = new Tuba();
const horn = new Horn();
const percussion = new Percussion();
const conductor = new Conductor('Snoop Dogg');
const brassSection = new BrassSection(conductor, tuba, horn, fs, os);
...
const orchestra = new Orchestra(conductor, brassSection, percussion);
To something like this:
const lentil = new Lentil();
lentil.setArgs(Conductor, ['Snoop Dogg']);
const orchestra = lentil.create(Orchestra);
Automatic Dependency Wiring
Typically, we might pass in our dependencies (including built-in objects) and bind them to this
in large constructors such as this:
class BrassSection extends LentilBase {
constructor (conductor, tuba, horn, fs, os) {
this.conductor = conductor;
this.tuba = tuba;
this.horn = horn;
this.fs = fs;
this.os = os;
}
loadSheetMusic () {
const sheetMusic = this.conductor.getScore();
this.fs.readFile(sheetMusic, ...
}
...
When we use LentilDI, we get that wiring done for us for free:
class BrassSection extends LentilBase {
static lentilDeps () {
return {
conductor: Conductor,
tuba: Tuba,
horn: Horn,
fs,
os,
}
}
loadSheetMusic () {
const sheetMusic = this.conductor.getScore();
this.fs.readFile(sheetMusic, ...
}
...
Testing
Testing modules is easy as pie - just create your module as normal!
If you want to override anything in lentilDeps
, just pass an object as the last argument to your constructor.
it('BrassSection should play some music', function () {
const dummyConductor = { ... };
const dummyTuba = { ... };
const dummyHorn = { ... };
// Note that we don't have to override fs or os
// We can let Lentil assign them as default values
const brassSection = new BrassSection({
conductor: dummyConductor,
tuba: dummyTuba,
horn: dummyHorn,
});
brassSection.playMusic();
...
});
More Examples
Check out some full example apps (with tests!) here.
LentilDep
You can specify different types of dependencies in your lentilDeps
declaration.
Currently, Lentil understands 3 types of dependencies:
LentilDep.Provided
LentilDep.Regular
LentilDep.Lentil
LentilDep.Provided
For cases where you might have an externally instantiated class (e.g. a logger) that you want to be available in any of your modules:
const logger = log4js.getLogger('My Logger');
const lentil = new Lentil();
lentil.provide('logger', logger);
const myApp = lentil.create(MyApp);
Your logger instance will now be available as normal through this.logger
inside a Lentil module:
class SomeModule extends LentilBase {
static lentilDeps () {
return {
logger: LentilDep.Provided('logger'),
}
}
doSomething() {
this.logger.info( ... );
}
}
LentilDep.Regular
This is the default type where values are simply passed along to your module.
Unless otherwise specified, this is how Lentil will treat a dependency.
class SomeModule extends LentilBase {
static lentilDeps () {
return {
whatever: 'Whatever',
}
}
}
'Whatever' would now be available through this.whatever
. (This is particularly useful for built in objects such as os
, console
etc.)
For the sake of clarity, note that this is functionally equivalent to the following:
class SomeModule extends LentilBase {
static lentilDeps () {
return {
// This is not recommended as Lentil can do this wrapping for us.
whatever: LentilDep.Regular('Whatever'),
}
}
}
LentilDep.Lentil
For sub-dependencies that you wish Lentil to also construct (i.e. other modules that extend from LentilBase.)
Similar to LentilDep.Regular, you do not need to explicitly wrap modules in this; Lentil will do this for you.
class SomeOtherModule extends LentilBase {
static lentilDeps () {
return {
someModule: SomeModule,
}
}
}
This is equivalent to the following:
class SomeOtherModule extends LentilBase {
static lentilDeps () {
return {
// This is not recommended as Lentil can do this wrapping for us.
someModule: LentilDep.Lentil(SomeModule),
}
}
}
Constructor Arguments
You can pass in arguments to your modules (useful for one-offs such as config values).
To do so, pass an array of arguments to lentil.setArgs
:
const lentil = new Lentil();
lentil.setArgs(Conductor, ['Snoop Dogg']);
...
Inside your module, your arguments are available as normal.
(Remember to call super
.)
class Conductor extends LentilBase {
constructor (conductorName, ...args) {
super(...args);
console.log(`Orchestra is being conducted by ${conductorName}`);
}
...
}
Full Documentation
Coming Soon
Contributing
Please do!
Why 'Lentil'?
I like lentils