Skip to content

Commit

Permalink
Merge branch 'eager-instances'
Browse files Browse the repository at this point in the history
Conflicts:
	README.md
  • Loading branch information
cesarenaldi committed Jul 28, 2014
2 parents 1c3fcb7 + 16df87f commit b4297f8
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 20 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# ![Iniettore](https://github.com/cesarenaldi/iniettore/raw/master/logo.png) Iniettore

[![Build Status](https://travis-ci.org/cesarenaldi/iniettore.svg?branch=master)](https://travis-ci.org/cesarenaldi/iniettore)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "iniettore",
"version": "1.0.0",
"version": "1.1.0",
"description": "A light and simple IoC container",
"main": "lib/iniettore.js",
"scripts": {
Expand Down
26 changes: 15 additions & 11 deletions src/Container.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
'use strict'

import { generateMask } from './utils'
import { ACQUIRE, RELEASE, DISPOSE } from './signals'
import { PROVIDER } from './options'
import resolvers from './resolvers'
import { CONTAINER_ALIAS, CONTEXT_ALIAS } from './constants'
import { VALUE } from './options'
import { generateMask, noop, isEagerSingleton } from './utils'
import resolvers from './resolvers'
import createContext from './createContext';

var CONTAINER_ALIAS = '$container'
function noop() {}

class Container {

constructor(conf, logger, mappings, signalRelease) {
Expand All @@ -27,11 +24,12 @@ class Container {
this._children = {}
this._signalRelease = signalRelease || noop

context = this._context = createContext(this._contribute.bind(this))
context.map(CONTAINER_ALIAS).to(this).as(VALUE)
context.flush()
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) {
Expand Down Expand Up @@ -93,6 +91,10 @@ class Container {
this._signalRelease = noop
}

_instanciateEagerSingleton(alias) {

}

_disposeChildren() {
var children = this._children
var id
Expand Down Expand Up @@ -128,12 +130,14 @@ class Container {
}
}

_contribute(alias, value, type, deps) {
_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) {
Expand Down
6 changes: 6 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict'

var CONTAINER_ALIAS = '$container'
var CONTEXT_ALIAS = '$context'

export { CONTAINER_ALIAS, CONTEXT_ALIAS }
8 changes: 5 additions & 3 deletions src/createContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { generateMask } from './utils'
import { BLUEPRINT, PROVIDER } from './options'
import { CONTAINER_ALIAS, CONTEXT_ALIAS } from './constants'

var ALIAS_IDX = 0
var VALUE_IDX = 1
Expand Down Expand Up @@ -36,13 +37,14 @@ export default function createContext(contribute) {

return {
as: (...flags) => {
var mask = generateMask(flags)

if (flags.length === 1 && flags[0] === BLUEPRINT) {
if (mask === BLUEPRINT) {
// test if VALUE is a function
flags = [PROVIDER]
pending[VALUE_IDX] = createChildContainerFactory(pending[VALUE_IDX])
pending.push(generateMask(flags))
pending.push(['$container'])
pending.push([CONTAINER_ALIAS])

return {
exports: (alias) => {
Expand All @@ -55,7 +57,7 @@ export default function createContext(contribute) {
}
}

pending.push(generateMask(flags))
pending.push(mask)

return {
map: context.map,
Expand Down
3 changes: 2 additions & 1 deletion src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export default = [
'INSTANCE',
'PERSISTENT',
'TRANSIENT',
'BLUEPRINT'
'BLUEPRINT',
'EAGER'
].reduce(function (options, flag, idx) {
options[flag] = Math.pow(2, idx)
return options
Expand Down
6 changes: 5 additions & 1 deletion src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
FUNCTION,
INSTANCE,
TRANSIENT,
PERSISTENT
PERSISTENT,
EAGER
} from './options'

var resolvers = {}
Expand All @@ -37,6 +38,9 @@ resolvers[ generateMask([PROVIDER]) ] = compose(leftCurryTwice, resolveDeps)(inv
resolvers[ generateMask([TRANSIENT, SINGLETON, PROVIDER]) ] = singletonify(invoke)
resolvers[ generateMask([PERSISTENT, SINGLETON, PROVIDER]) ] = singletonify(invoke, true)

resolvers[ generateMask([EAGER, SINGLETON, PROVIDER])] = singletonify(invoke, true)
resolvers[ generateMask([EAGER, CONSTRUCTOR, SINGLETON]) ] = singletonify(instanciate, true)

// aliases
resolvers[ generateMask([INSTANCE]) ] = resolvers[ generateMask([VALUE]) ]

Expand Down
10 changes: 10 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import instanciate from './instanciate'
import singletonify from './singletonify'
import { EAGER, SINGLETON, PROVIDER, CONSTRUCTOR } from './options'

var EAGER_SINGLETON_PROVIDER = generateMask([EAGER, SINGLETON, PROVIDER])
var EAGER_SINGLETON_CONSTRUCTOR = generateMask([EAGER, SINGLETON, CONSTRUCTOR])

export function identity(value) { return value }

Expand Down Expand Up @@ -45,4 +49,10 @@ export function generateMask(flags) {
return flags.reduce((prev, curr) => prev | curr, 0)
}

export function isEagerSingleton(type) {
return [EAGER_SINGLETON_PROVIDER, EAGER_SINGLETON_CONSTRUCTOR].indexOf(type) > -1
}

export function noop() {}

export { instanciate, singletonify }
3 changes: 1 addition & 2 deletions test/spec/childContainer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ describe('Given a child container', function () {
})
})

describe('when requesting a reference to the containers themself', function () {

describe('when requesting the reference to the containers', function () {

it('should return the respective containers', function () {
var parent = iniettore.create(noop)
Expand Down
100 changes: 100 additions & 0 deletions test/spec/eagerSingletons.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use strict'

import iniettore from '../../src/iniettore'
import { TRANSIENT, PERSISTENT, PROVIDER, SINGLETON, CONSTRUCTOR, EAGER } from '../../src/options'

describe('Given a provider and a contructor', function () {

var provider = sinon.spy()
var constructorSpy = sinon.spy()

class Foo {
constructor(...args) {
constructorSpy.apply(null, args)
}
}

beforeEach(function () {
provider.reset()
constructorSpy.reset()
})

describe('when registering them as EAGER, SINGLETONs', function () {

it('should create instances straightaway', function () {

iniettore.create(function (context) {
context
.map('bar')
.to(provider)
.as(EAGER, SINGLETON, PROVIDER)

.map('foo')
.to(Foo)
.as(EAGER, SINGLETON, CONSTRUCTOR)
})
expect(provider).to.be.calledOnce
expect(constructorSpy).to.be.calledOnce
})

describe('and $context as dependency', function () {

var context = sinon.match.object.and(sinon.match.has('map', sinon.match.func))

it('should provide a reference to the context', function () {
iniettore.create(function (context) {
context
.map('bar')
.to(provider)
.as(EAGER, SINGLETON, PROVIDER)
.injecting('$context')

.map('foo')
.to(Foo)
.as(EAGER, SINGLETON, CONSTRUCTOR)
.injecting('$context')
})
expect(provider)
.to.be.calledOnce
.and.to.be.calledWith(context)
expect(constructorSpy)
.to.be.calledOnce
.and.to.be.calledWith(context)
})
})
})

describe('registered as simple CONSTRUCTOR and PROVIDER (not singleton)', function () {
var container

describe('with $context as dependency', function () {

before(function () {
container = iniettore.create(function (context) {
context
.map('bar')
.to(provider)
.as(TRANSIENT, SINGLETON, PROVIDER)
.injecting('$context')

.map('foo')
.to(Foo)
.as(PERSISTENT, SINGLETON, CONSTRUCTOR)
.injecting('$context')
})
})
describe('when requesting those', function () {
it('should throw an Error specifying that $context injection is available only for eager singletons', function () {
function testCase1() {
container.get('foo')
}
function testCase2() {
container.get('bar')
}
expect(testCase1).to.throw(Error)
expect(testCase2).to.throw(Error)
})
})
})
})
})

0 comments on commit b4297f8

Please sign in to comment.