Skip to content

Commit b4297f8

Browse files
committed
Merge branch 'eager-instances'
Conflicts: README.md
2 parents 1c3fcb7 + 16df87f commit b4297f8

File tree

10 files changed

+145
-20
lines changed

10 files changed

+145
-20
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# ![Iniettore](https://github.com/cesarenaldi/iniettore/raw/master/logo.png) Iniettore
32

43
[![Build Status](https://travis-ci.org/cesarenaldi/iniettore.svg?branch=master)](https://travis-ci.org/cesarenaldi/iniettore)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "iniettore",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "A light and simple IoC container",
55
"main": "lib/iniettore.js",
66
"scripts": {

src/Container.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
'use strict'
22

3-
import { generateMask } from './utils'
43
import { ACQUIRE, RELEASE, DISPOSE } from './signals'
5-
import { PROVIDER } from './options'
6-
import resolvers from './resolvers'
4+
import { CONTAINER_ALIAS, CONTEXT_ALIAS } from './constants'
75
import { VALUE } from './options'
6+
import { generateMask, noop, isEagerSingleton } from './utils'
7+
import resolvers from './resolvers'
88
import createContext from './createContext';
99

10-
var CONTAINER_ALIAS = '$container'
11-
function noop() {}
12-
1310
class Container {
1411

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

30-
context = this._context = createContext(this._contribute.bind(this))
31-
context.map(CONTAINER_ALIAS).to(this).as(VALUE)
32-
context.flush()
27+
this._bind(CONTAINER_ALIAS, this, VALUE, [])
28+
context = this._context = createContext(this._bind.bind(this))
29+
this._bind(CONTEXT_ALIAS, context, VALUE, [])
3330
conf(context)
3431
context.flush()
32+
this._unbind(CONTEXT_ALIAS)
3533
}
3634

3735
get(alias, transients) {
@@ -93,6 +91,10 @@ class Container {
9391
this._signalRelease = noop
9492
}
9593

94+
_instanciateEagerSingleton(alias) {
95+
96+
}
97+
9698
_disposeChildren() {
9799
var children = this._children
98100
var id
@@ -128,12 +130,14 @@ class Container {
128130
}
129131
}
130132

131-
_contribute(alias, value, type, deps) {
133+
_bind(alias, value, type, deps) {
132134
if ( !(type in this._resolvers) ) {
133135
throw new Error('Invalid flags combination. See documentation for valid flags combinations.')
134136
}
135137
this._mappings[alias] = this._resolvers[type].call(null, value, this._resolve.bind(this, deps), this._release.bind(this, deps))
136-
138+
if (isEagerSingleton(type)) {
139+
this.get(alias)
140+
}
137141
}
138142

139143
_unbind(alias) {

src/constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict'
2+
3+
var CONTAINER_ALIAS = '$container'
4+
var CONTEXT_ALIAS = '$context'
5+
6+
export { CONTAINER_ALIAS, CONTEXT_ALIAS }

src/createContext.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { generateMask } from './utils'
44
import { BLUEPRINT, PROVIDER } from './options'
5+
import { CONTAINER_ALIAS, CONTEXT_ALIAS } from './constants'
56

67
var ALIAS_IDX = 0
78
var VALUE_IDX = 1
@@ -36,13 +37,14 @@ export default function createContext(contribute) {
3637

3738
return {
3839
as: (...flags) => {
40+
var mask = generateMask(flags)
3941

40-
if (flags.length === 1 && flags[0] === BLUEPRINT) {
42+
if (mask === BLUEPRINT) {
4143
// test if VALUE is a function
4244
flags = [PROVIDER]
4345
pending[VALUE_IDX] = createChildContainerFactory(pending[VALUE_IDX])
4446
pending.push(generateMask(flags))
45-
pending.push(['$container'])
47+
pending.push([CONTAINER_ALIAS])
4648

4749
return {
4850
exports: (alias) => {
@@ -55,7 +57,7 @@ export default function createContext(contribute) {
5557
}
5658
}
5759

58-
pending.push(generateMask(flags))
60+
pending.push(mask)
5961

6062
return {
6163
map: context.map,

src/options.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export default = [
99
'INSTANCE',
1010
'PERSISTENT',
1111
'TRANSIENT',
12-
'BLUEPRINT'
12+
'BLUEPRINT',
13+
'EAGER'
1314
].reduce(function (options, flag, idx) {
1415
options[flag] = Math.pow(2, idx)
1516
return options

src/resolvers.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import {
2323
FUNCTION,
2424
INSTANCE,
2525
TRANSIENT,
26-
PERSISTENT
26+
PERSISTENT,
27+
EAGER
2728
} from './options'
2829

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

41+
resolvers[ generateMask([EAGER, SINGLETON, PROVIDER])] = singletonify(invoke, true)
42+
resolvers[ generateMask([EAGER, CONSTRUCTOR, SINGLETON]) ] = singletonify(instanciate, true)
43+
4044
// aliases
4145
resolvers[ generateMask([INSTANCE]) ] = resolvers[ generateMask([VALUE]) ]
4246

src/utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import instanciate from './instanciate'
44
import singletonify from './singletonify'
5+
import { EAGER, SINGLETON, PROVIDER, CONSTRUCTOR } from './options'
6+
7+
var EAGER_SINGLETON_PROVIDER = generateMask([EAGER, SINGLETON, PROVIDER])
8+
var EAGER_SINGLETON_CONSTRUCTOR = generateMask([EAGER, SINGLETON, CONSTRUCTOR])
59

610
export function identity(value) { return value }
711

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

52+
export function isEagerSingleton(type) {
53+
return [EAGER_SINGLETON_PROVIDER, EAGER_SINGLETON_CONSTRUCTOR].indexOf(type) > -1
54+
}
55+
56+
export function noop() {}
57+
4858
export { instanciate, singletonify }

test/spec/childContainer.spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ describe('Given a child container', function () {
2323
})
2424
})
2525

26-
describe('when requesting a reference to the containers themself', function () {
27-
26+
describe('when requesting the reference to the containers', function () {
2827

2928
it('should return the respective containers', function () {
3029
var parent = iniettore.create(noop)

test/spec/eagerSingletons.spec.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
'use strict'
2+
3+
import iniettore from '../../src/iniettore'
4+
import { TRANSIENT, PERSISTENT, PROVIDER, SINGLETON, CONSTRUCTOR, EAGER } from '../../src/options'
5+
6+
describe('Given a provider and a contructor', function () {
7+
8+
var provider = sinon.spy()
9+
var constructorSpy = sinon.spy()
10+
11+
class Foo {
12+
constructor(...args) {
13+
constructorSpy.apply(null, args)
14+
}
15+
}
16+
17+
beforeEach(function () {
18+
provider.reset()
19+
constructorSpy.reset()
20+
})
21+
22+
describe('when registering them as EAGER, SINGLETONs', function () {
23+
24+
it('should create instances straightaway', function () {
25+
26+
iniettore.create(function (context) {
27+
context
28+
.map('bar')
29+
.to(provider)
30+
.as(EAGER, SINGLETON, PROVIDER)
31+
32+
.map('foo')
33+
.to(Foo)
34+
.as(EAGER, SINGLETON, CONSTRUCTOR)
35+
})
36+
expect(provider).to.be.calledOnce
37+
expect(constructorSpy).to.be.calledOnce
38+
})
39+
40+
describe('and $context as dependency', function () {
41+
42+
var context = sinon.match.object.and(sinon.match.has('map', sinon.match.func))
43+
44+
it('should provide a reference to the context', function () {
45+
iniettore.create(function (context) {
46+
context
47+
.map('bar')
48+
.to(provider)
49+
.as(EAGER, SINGLETON, PROVIDER)
50+
.injecting('$context')
51+
52+
.map('foo')
53+
.to(Foo)
54+
.as(EAGER, SINGLETON, CONSTRUCTOR)
55+
.injecting('$context')
56+
})
57+
expect(provider)
58+
.to.be.calledOnce
59+
.and.to.be.calledWith(context)
60+
expect(constructorSpy)
61+
.to.be.calledOnce
62+
.and.to.be.calledWith(context)
63+
})
64+
})
65+
})
66+
67+
describe('registered as simple CONSTRUCTOR and PROVIDER (not singleton)', function () {
68+
var container
69+
70+
describe('with $context as dependency', function () {
71+
72+
before(function () {
73+
container = iniettore.create(function (context) {
74+
context
75+
.map('bar')
76+
.to(provider)
77+
.as(TRANSIENT, SINGLETON, PROVIDER)
78+
.injecting('$context')
79+
80+
.map('foo')
81+
.to(Foo)
82+
.as(PERSISTENT, SINGLETON, CONSTRUCTOR)
83+
.injecting('$context')
84+
})
85+
})
86+
describe('when requesting those', function () {
87+
it('should throw an Error specifying that $context injection is available only for eager singletons', function () {
88+
function testCase1() {
89+
container.get('foo')
90+
}
91+
function testCase2() {
92+
container.get('bar')
93+
}
94+
expect(testCase1).to.throw(Error)
95+
expect(testCase2).to.throw(Error)
96+
})
97+
})
98+
})
99+
})
100+
})

0 commit comments

Comments
 (0)