dependable
Advanced tools
Comparing version 0.2.5 to 1.0.0
21
index.js
@@ -12,4 +12,5 @@ // Generated by CoffeeScript 1.3.3 | ||
exports.container = function() { | ||
var argList, container, factories, get, haveVisited, load, loaddir, loadfile, notEmpty, register, registerOne, resolve, toFactory; | ||
var argList, container, factories, get, haveVisited, list, load, loaddir, loadfile, modules, notEmpty, register, registerOne, resolve, toFactory; | ||
factories = {}; | ||
modules = {}; | ||
register = function(name, func) { | ||
@@ -35,2 +36,5 @@ var hash, _results; | ||
}; | ||
list = function() { | ||
return factories; | ||
}; | ||
load = function(file) { | ||
@@ -53,3 +57,3 @@ var exists, stats; | ||
}); | ||
return register(name, require(module)); | ||
return modules[name] = module; | ||
}; | ||
@@ -107,3 +111,3 @@ loaddir = function(dir) { | ||
get = function(name, overrides, visited) { | ||
var dependencies, factory, instance, isOverridden; | ||
var dependencies, factory, instance, isOverridden, module; | ||
if (visited == null) { | ||
@@ -119,3 +123,9 @@ visited = []; | ||
if (!(factory != null)) { | ||
throw new Error("dependency '" + name + "' was not registered"); | ||
module = modules[name]; | ||
if (module != null) { | ||
register(name, require(module)); | ||
factory = factories[name]; | ||
} else { | ||
throw new Error("dependency '" + name + "' was not registered"); | ||
} | ||
} | ||
@@ -157,3 +167,4 @@ if ((factory.instance != null) && !isOverridden) { | ||
register: register, | ||
load: load | ||
load: load, | ||
list: list | ||
}; | ||
@@ -160,0 +171,0 @@ container.register("_container", container); |
{ | ||
"name": "dependable", | ||
"version": "0.2.5", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"author": "Sean Hess", | ||
"contributors": ["Jamison Dance <jergason@gmail.com> (http://jamisondance.com)"], | ||
"contributors": [ | ||
"Jamison Dance <jergason@gmail.com> (http://jamisondance.com)" | ||
], | ||
"repository": { | ||
@@ -14,5 +16,4 @@ "url": "git@github.com:idottv/dependable.git" | ||
}, | ||
"license": "BSD", | ||
"dependencies": { | ||
}, | ||
"license": "MIT", | ||
"dependencies": {}, | ||
"devDependencies": { | ||
@@ -19,0 +20,0 @@ "mocha": "1.3.0", |
215
README.md
@@ -1,142 +0,165 @@ | ||
# Dependable | ||
# Dependable [![Build Status](https://travis-ci.org/idottv/dependable.png)](https://travis-ci.org/idottv/dependable) | ||
A minimalist dependency injection framework for javascript (fully tested for node.js) | ||
A minimalist dependency injection framework for node.js. | ||
## Goals | ||
## Example | ||
1. Allow creation of modules that can be passed dependencies instead of requiring them. | ||
2. Simple bootstrapping, minimal footprint | ||
3. Automatic | ||
### Create a container | ||
## Documentation | ||
Create a new container by calling `dependable.container`: | ||
### What is Dependency Injection | ||
In CommonJS, modules are normally defined like one of the following | ||
```js | ||
var dependable = require('dependable'), | ||
container = dependable.container(); | ||
``` | ||
exports.hello = -> "hello world" | ||
### Register some dependencies | ||
module.exports = | ||
hello: "hello world" | ||
Register a few dependencies for later use (a string and an object): | ||
```js | ||
container.register('occupation', 'tax attorney'); | ||
container.register('transport', { | ||
type: 'station wagon', | ||
material: 'wood-paneled' | ||
}); | ||
``` | ||
If you have a dependency, you normally just require it. The `greetings` module needs some config to work. | ||
### Register a dependency that has other dependencies | ||
Greetings = require "greetings" | ||
greetings = new Greetings("en") | ||
When the argument is a function, the function's arguments are automatically | ||
populated with the correct dependencies, and the return value of the function | ||
is registered as the dependency: | ||
module.exports = -> | ||
hello: -> greetings.hello("world") | ||
```js | ||
container.register('song', function (occupation, transport, legalStatus) { | ||
var song = {}; | ||
Instead of requiring it, and setting the config inside our module, we can require that a pre-configured greetings instance be passed in to our function. | ||
song.chorus = function chorus() { | ||
return [ | ||
'I\'m a ' + occupation, | ||
'On a ' + transport.material + ' ' + transport.type + ' I ride', | ||
'And I\'m ' + legalStatus.message | ||
].join('\n'); | ||
}; | ||
module.exports = (greetings) -> | ||
hello: -> greetings.hello("world") | ||
return song; | ||
}); | ||
``` | ||
This is "Dependency Injection". `greetings` is a dependency of our module. Its dependencies are "injected", meaning they are handed to us by whoever uses our module instead of us reaching out for them. | ||
### Register a dependency out-of-order | ||
### Why Dependency Injection? | ||
`song` depends on a `legalStatus`, which hasn't been registered yet. | ||
Dependable resolves dependencies lazily, so we can define this dependency | ||
after-the-fact: | ||
If you ever need to change your config options, such as when testing, or to hit different layers of a service, it is easier to have a pre-configured object passed in instead of creating it yourself. | ||
```js | ||
container.register('legalStatus', { | ||
warrants: [], | ||
message: 'without outstanding warrants' | ||
}); | ||
``` | ||
Your module is a little less coupled to its dependency | ||
### Resolve a dependency and use it | ||
You can pass in mock or alternate versions of a module if you want | ||
Like with container.register, the function arguments are automatically resolved, along | ||
with their dependencies: | ||
### This is Annoying | ||
```js | ||
container.resolve(function (song) { | ||
/* | ||
* I'm a tax attorney | ||
* On a wood-paneled station wagon I ride | ||
* And I'm without outstanding warrants | ||
*/ | ||
console.log(song.chorus()); | ||
}); | ||
``` | ||
It is hard to remember how to construct and configure the dependencies when you want to use your module. This should be automatic. Here is an example | ||
### Re-register dependencies | ||
robot.coffee | ||
As it stands, `song` returns boring, non-catchy lyrics. One way to change its behavior | ||
is to re-register its dependencies: | ||
module.exports = (greetings) -> | ||
hello: -> greetings.hello("world") | ||
```js | ||
container.register('occupation', 'cowboy'); | ||
container.register('legalStatus', { | ||
warrants: [ | ||
{ | ||
for: 'shooting the sheriff', | ||
notes: 'did not shoot the deputy' | ||
} | ||
], | ||
message: 'wanted: dead or alive' | ||
}); | ||
``` | ||
greetings.coffee | ||
This is really useful in a number of situations: | ||
module.exports = (language) -> | ||
... | ||
1. A container can register configuration parameters for an application---for example, a port---and allows them to be changed later | ||
2. Dependencies can be replaced with mock objects in order to test other dependencies | ||
app.coffee | ||
To resolve the updated dependencies, provide an empty override: | ||
# you have to do this in every file you want to use robot | ||
Greetings = require "greetings" | ||
greetings = Greetings("en") | ||
```js | ||
container.resolve({}, function (song) { | ||
/* | ||
* I'm a cowboy | ||
* On a wood-paneled station wagon I ride | ||
* And I'm wanted: dead or alive | ||
*/ | ||
console.log(song.chorus()); | ||
}); | ||
``` | ||
Robot = require "robot" | ||
robot = Robot greetings | ||
### Override dependencies at resolve time | ||
itsAliiiive = -> | ||
console.log robot.hello() | ||
It's also possible to override dependencies at resolve time: | ||
```js | ||
var horse = { | ||
type: 'horse', | ||
material: 'steel' | ||
}; | ||
*This gets much worse* when your dependencies have dependencies of their own. You have to remember in which order to configure them so you can pass them into each other. | ||
container.resolve({ transport: horse }, function (song) { | ||
/* | ||
* I'm a cowboy | ||
* On a steel horse I ride | ||
* And I'm wanted: dead or alive | ||
*/ | ||
console.log(song.chorus()); | ||
}); | ||
``` | ||
### Using Dependenable | ||
Sounds like a hit! | ||
Dependable automates this process. In the following example, you don't need to register the modules in order, or do it more than once (it will work for dependencies of dependencies) | ||
## API | ||
`container.register(name, function)` - Registers a dependency by name. `function` can be a function that takes dependencies and returns anything, or an object itself with no dependencies. | ||
robot.coffee | ||
`container.register(hash)` - Registers a hash of names and dependencies. This is useful for setting configuration constants. | ||
module.exports = (greetings) -> | ||
hello: -> greetings.hello("world") | ||
`container.load(fileOrFolder)` - Registers a file, using its file name as the name, or all files in a folder. Does not traverse subdirectories. | ||
greetings.coffee | ||
`container.get(name, overrides = {})` - Returns a dependency by name, with all dependencies injected. If you specify overrides, the dependency will be given those overrides instead of those registered. | ||
module.exports = (language) -> | ||
... | ||
`container.resolve(overrides={}, cb)` - Calls `cb` like a dependency function, injecting any dependencies found in the signature. Like `container.get`, this supports overrides. | ||
app.coffee | ||
`container.list()` - Return a list of registered dependencies. | ||
# create the container, you only have to do this once. | ||
container = require("dependable").container | ||
deps = container() | ||
deps.register "greetings", require("greetings") | ||
deps.register "robot", require("robot") | ||
## Development | ||
robot = deps.get "robot" | ||
Dependable is written in coffeescript. To generate javascript, run `npm run prepublish`. | ||
itsAliiiive = -> | ||
console.log robot.hello() | ||
Tests are written with mocha. To run the tests, run `npm test`. | ||
### Using Dependable's Load | ||
## License | ||
You can load files or directories instead of registering by hand. See [Reference](#reference) | ||
### Overriding Dependencies for Testing | ||
Copyright (c) 2013 i.TV LLC | ||
When testing, you usually want most dependencies loaded normally, but to mock others. You can use overrides for this. In the example below, `User` depends on `Friends.getInfo` for it's `getFriends` call. By setting `Friends` to `MockFriends` we can stub the dependency, but any other dependencies `User` has will be passed in normally. | ||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
# boostrap.coffee | ||
deps = container() | ||
deps.register "Friends", require('./Friends') | ||
deps.register "User", require('./User') | ||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
# test.coffee | ||
describe 'User', -> | ||
it 'should get friends plus info', (done) -> | ||
MockFriends = | ||
getInfo: (id, cb) -> cb null, {some:"info"} | ||
User = deps.get "User", {Friends: MockFriends} | ||
User.getFriends "userId", (err, friends) -> | ||
# assertions | ||
done() | ||
## Reference | ||
`container.register(name, function)` - registers a dependency by name. `function` can be a function that takes dependencies and returns anything, or an object itself with no dependencies. | ||
`container.register(hash)` - registers a hash of names and dependencies. Useful for config. | ||
`container.load(fileOrFolder)` - registers a file, using its file name as the name, or all files in a folder. Does not follow sub directories | ||
`container.get(name, overrides = {})` - returns a module by name, with all dependencies injected. If you specify overrides, the dependency will be given those overrides instead of those registerd. | ||
`container.resolve([overrides,] cb)` - calls cb like a dependency function, injecting any dependencies found in the signature | ||
deps.resolve (User) -> | ||
# do something with User | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Misc. License Issues
License(Experimental) A package's licensing information has fine-grained problems.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
24414
11
0
178
1
166
1