-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added kontainer namespace and initial implementation.
- Loading branch information
1 parent
416285a
commit f685daa
Showing
3 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
describe('kontainer', function () { | ||
'use strict'; | ||
|
||
describe('registerFactory', function () { | ||
it('should throw given non string name', function () { | ||
expect(function () { | ||
kontainer.registerFactory(true); | ||
}).toThrowError(/name/i); | ||
}); | ||
|
||
it('should throw given non array factory', function () { | ||
expect(function () { | ||
kontainer.registerFactory('name', {}); | ||
}).toThrowError(/array/i); | ||
}); | ||
|
||
it('should accept array factory', function () { | ||
kontainer.registerFactory('name', []); | ||
}); | ||
}); | ||
|
||
describe('registerValue', function () { | ||
it('should throw given non string name', function () { | ||
expect(function () { | ||
kontainer.registerValue(true); | ||
}).toThrowError(/name/i); | ||
}); | ||
|
||
it('should accept object value', function () { | ||
kontainer.registerValue('name', {}); | ||
}); | ||
|
||
it('should accept primitive value', function () { | ||
kontainer.registerValue('name', 'value'); | ||
}); | ||
}); | ||
|
||
describe('register', function () { | ||
it('should throw given non string name', function () { | ||
expect(function () { | ||
kontainer.register(false); | ||
}).toThrowError(/name/i); | ||
}); | ||
|
||
it('should accept any value', function () { | ||
kontainer.register('name', /regexp/); | ||
kontainer.register('name', []); | ||
kontainer.register('name', 12345); | ||
}); | ||
}); | ||
|
||
describe('loader', function () { | ||
var element; | ||
|
||
ko.components.loaders.unshift(kontainer.loader); | ||
|
||
function registerComponent(factory) { | ||
ko.components.register('fake-component', { | ||
viewModel: factory, | ||
template: '<p data-bind="text: name"></p>' | ||
}); | ||
} | ||
|
||
beforeEach(function () { | ||
jasmine.clock().install(); | ||
element = document.createElement('div'); | ||
element.setAttribute('data-bind', 'component: \'fake-component\''); | ||
document.body.appendChild(element); | ||
}); | ||
|
||
afterEach(function () { | ||
jasmine.clock().uninstall(); | ||
ko.components.unregister('fake-component'); | ||
document.body.removeChild(element); | ||
element = null; | ||
}); | ||
|
||
it('should inject value into template', function () { | ||
kontainer.registerValue('name', 'injected value'); | ||
|
||
registerComponent(['name', function (name) { | ||
return { | ||
name: ko.observable(name) | ||
}; | ||
}]); | ||
|
||
ko.applyBindings(null, element); | ||
jasmine.clock().tick(1); | ||
expect(element.firstChild.innerHTML).toBe('injected value'); | ||
}); | ||
|
||
it('should only invoke factories once', function () { | ||
var factory1 = jasmine.createSpy(), | ||
factory2 = jasmine.createSpy(); | ||
|
||
kontainer.register('service1', [factory1]); | ||
kontainer.register('service2', ['service1', factory2]); | ||
|
||
registerComponent(['service2', 'service1', function () { | ||
return { | ||
name: ko.observable() | ||
}; | ||
}]); | ||
|
||
ko.applyBindings(null, element); | ||
jasmine.clock().tick(1); | ||
expect(factory1.calls.count()).toBe(1); | ||
expect(factory2.calls.count()).toBe(1); | ||
}); | ||
|
||
it('should inject value from service into template', function () { | ||
kontainer.register('name', 'injected value'); | ||
kontainer.register('service', ['name', function (name) { | ||
var reverseName = name.split('').reverse().join(''); | ||
|
||
return { | ||
reverseName: reverseName | ||
}; | ||
}]); | ||
|
||
registerComponent(['service', function (service) { | ||
return { | ||
name: ko.observable(service.reverseName) | ||
}; | ||
}]); | ||
|
||
ko.applyBindings(null, element); | ||
jasmine.clock().tick(1); | ||
expect(element.firstChild.innerHTML).toBe('eulav detcejni'); | ||
}); | ||
|
||
it('should throw given cyclic dependency', function () { | ||
kontainer.registerFactory('service1', ['service2', function (name) { | ||
return; | ||
}]); | ||
kontainer.registerFactory('service2', ['service1', function (name) { | ||
return; | ||
}]); | ||
|
||
registerComponent(['service1', function (service) { | ||
return { | ||
name: ko.observable(service.reverseName) | ||
}; | ||
}]); | ||
|
||
ko.applyBindings(null, element); | ||
|
||
expect(function () { | ||
jasmine.clock().tick(1); | ||
}).toThrowError(/fake-component > service1 > service2 > service1/i); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
var kontainer; | ||
|
||
kontainer = (function () { | ||
'use strict'; | ||
|
||
var resolve, | ||
parse, | ||
registry = {}, | ||
states = { | ||
unparsed: 0, | ||
parsing: -1, | ||
parsed: 1 | ||
}, | ||
slice = Array.prototype.slice; | ||
|
||
resolve = function (name, path) { | ||
if (!registry.hasOwnProperty(name)) { | ||
throw new Error('Unknown dependency "' + name + '".'); | ||
} | ||
|
||
path.push(name); | ||
|
||
var dependency = registry[name]; | ||
|
||
if (dependency.state === states.parsing) { | ||
throw new Error('Cyclic dependency detected while resolving "' + name + '". ' + path.join(' > ')); | ||
} | ||
|
||
if (dependency.state === states.unparsed) { | ||
dependency.state = states.parsing; | ||
dependency.value = parse(dependency.factory, path); | ||
delete dependency.factory; | ||
dependency.state = states.parsed; | ||
} | ||
|
||
path.pop(); | ||
|
||
return dependency.value; | ||
}; | ||
|
||
parse = function (definition, path, custom) { | ||
var fn = definition.pop(), | ||
args = []; | ||
|
||
if (typeof fn !== 'function') { | ||
throw new Error('The last element in a dependency array must be a function.'); | ||
} | ||
|
||
custom = custom || {}; | ||
|
||
args = definition.map(function (key) { | ||
if (typeof key !== 'string') { | ||
throw new Error('Each element before a factory function must be a string.'); | ||
} | ||
|
||
return custom.hasOwnProperty(key) ? custom[key] : resolve(key, path); | ||
}); | ||
|
||
return fn.apply(undefined, args); | ||
}; | ||
|
||
function registerFactory(name, factory) { | ||
if (typeof name !== 'string') { | ||
throw new Error('The name parameter must be a string.'); | ||
} | ||
|
||
if (!Array.isArray(factory)) { | ||
throw new Error('Factories must always be arrays.'); | ||
} | ||
|
||
registry[name] = { | ||
state: states.unparsed, | ||
factory: slice.call(factory) | ||
}; | ||
} | ||
|
||
function registerValue(name, value) { | ||
if (typeof name !== 'string') { | ||
throw new Error('The name parameter must be a string.'); | ||
} | ||
|
||
registry[name] = { | ||
state: states.parsed, | ||
value: value | ||
}; | ||
} | ||
|
||
function register(name, value) { | ||
if (Array.isArray(value)) { | ||
registerFactory(name, value); | ||
|
||
return; | ||
} | ||
|
||
registerValue(name, value); | ||
} | ||
|
||
function createViewModel(name, params, componentInfo) { | ||
return parse( | ||
this, | ||
[name], | ||
{ | ||
params: params, | ||
componentInfo: componentInfo | ||
} | ||
); | ||
} | ||
|
||
function loadViewModel(componentName, viewModelConfig, callback) { | ||
if (Array.isArray(viewModelConfig)) { | ||
callback(createViewModel.bind(viewModelConfig, componentName)); | ||
|
||
return; | ||
} | ||
|
||
callback(null); | ||
} | ||
|
||
return { | ||
registerFactory: registerFactory, | ||
registerValue: registerValue, | ||
register: register, | ||
loader: { | ||
loadViewModel: loadViewModel | ||
} | ||
}; | ||
}()); |