Code coverage report for src/Container.js

Statements: 100% (104 / 104)      Branches: 100% (18 / 18)      Functions: 100% (17 / 17)      Lines: 100% (93 / 93)      Ignored: 1 branch     

All files » src/ » Container.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157    13 13 13 13 13 13       1 66   66 1     65 65 65 65 65 65 65   65 65 65 65 65 64     13 130 130 129   125 125 125   4 4   125         13 2   2 2   2 2   2 2 2 2           13 31 31   1 1       13 14 14   14 14     13 9 9 8 8     13 9 9   9   2 2 2         13 6     13 9 9   9   23 19 19   1 1   18         13 220 1   219 219 4       13 66 66     13 18 14       13 186 47         13
'use strict'
 
import { ACQUIRE, RELEASE, DISPOSE } from './signals'
import { CONTAINER_ALIAS, CONTEXT_ALIAS, CHILD_ALIAS } from './constants'
import { VALUE } from './options'
import { generateMask, noop, isEagerSingleton } from './utils'
import resolvers from './resolvers'
import createContext from './createContext';
 
class Container {
 
	constructor(conf, logger, mappings, signalRelease) {
		var context
 
		if (typeof conf !== 'function') {
			throw new Error('Invalid container creation, missing contribution function')
		}
 
		this._mappings = mappings || {}
		this._resolvers = resolvers
		this._logger = logger
		this._resolving = {}
		this._pending = []
		this._children = {}
		this._signalRelease = signalRelease || noop
		
		this._bind(CONTAINER_ALIAS, this, VALUE, [])
		context = this._context = createContext(this._bind.bind(this))
		this._bind(CONTEXT_ALIAS, context, VALUE, [])
		conf(context)
		context.flush()
		this._unbind(CONTEXT_ALIAS)
	}
 
	get(alias, transients) {
		return this._logger.log(`resolving '${alias}'`, () => {
			if (this._resolving[alias]) { throw new Error(`Circular dependency detected while resolving '${alias}'`) }
			if (!(alias in this._mappings)) { throw new Error(`'${alias}' is not available. Has it ever been registered?.`) }
 
			this._resolving[alias] = true
			try {
				return this._mappings[alias](ACQUIRE)
			} catch(err) {
				err.message = `Failed while resolving '${alias}' due to:\n\t${err.message}`
				throw err
			} finally {
				this._resolving[alias] = false	
			}
		})
	}
 
	using(transientsDeps) {
		return {
			get: (alias) => {
				var context = this._context
				var dep
 
				for (dep in transientsDeps) {
					context.map(dep).to(transientsDeps[dep]).as(VALUE)
				}
				context.flush()
				this.get(alias)
				for (dep in transientsDeps) {
					this._unbind(dep)
				}
			}
		}
	}
 
	release(alias) {
		try {
			this._mappings[alias](RELEASE)
		} catch(err) {
			err.message = `Failed while releasing '${alias}' due to:\n\t${err.message}`
			throw err
		}
	}
 
	createChild(conf) {
		var id = Object.keys(this._children).length + 1
		var child = new Container(conf, this._logger, Object.create(this._mappings), this._releaseChild.bind(this, id))
 
		this._children[id] = child
		return child
	}
 
	dispose() {
		this._disposeChildren()
		this._disposeInstances()
		this._signalRelease()
		this._signalRelease = noop
	}
 
	_disposeChildren() {
		var children = this._children
		var id
 
		for (id in children) {
			/* istanbul ignore else  */
			Eif (children.hasOwnProperty(id)) {
				children[id].dispose()
				this._releaseChild(id)
			}
		}
	}
 
	_releaseChild(id) {
		delete this._children[id]
	}
 
	_disposeInstances() {
		var mappings = this._mappings
		var alias
 
		for (alias in mappings) {
			/* istanbul ignore else  */
			if (mappings.hasOwnProperty(alias)) {
				try {
					mappings[alias](DISPOSE)
				} catch(err) {
					err.message = `Failed while disposing '${alias}' due to:\n\t${err.message}`
					throw err
				}
				delete mappings[alias]
			}
		}
	}
 
	_bind(alias, value, type, deps) {
		if ( !(type in this._resolvers) ) {
			throw new Error('Invalid flags combination. See documentation for valid flags combinations.')
		}
		this._mappings[alias] = this._resolvers[type].call(null, value, this._resolve.bind(this, deps), this._release.bind(this, deps))
		if (isEagerSingleton(type)) {
			this.get(alias)
		}
	}
 
	_unbind(alias) {
		this._mappings[alias](DISPOSE)
		delete this._mappings[alias]
	}
 
	_release(deps) {
		return deps.forEach((dep) => {
			this.release(dep)
		})
	}
 
	_resolve(deps) {
		return deps.map((dep) => {
			return this.get(dep)
		})
	}
}
 
export default Container