diff options
| -rw-r--r-- | src/Injector.js | 15 | ||||
| -rw-r--r-- | test/InjectorSpec.js | 141 |
2 files changed, 155 insertions, 1 deletions
diff --git a/src/Injector.js b/src/Injector.js index 8df47306..517e3e00 100644 --- a/src/Injector.js +++ b/src/Injector.js @@ -251,7 +251,8 @@ function createInjector(modulesToLoad, moduleRegistry) { value('$provide', { service: service, factory: factory, - value: value + value: value, + decorator: decorator }); loadModule(modulesToLoad); @@ -274,6 +275,18 @@ function createInjector(modulesToLoad, moduleRegistry) { function value(name, value) { factory(name, valueFn(value)); } + function decorator(name, decorFn) { + var origProvider = cache['#' + name + providerSuffix]; + if (!origProvider) throw Error("Can't find provider for: " + name); + if (cache['#' + name]) throw Error("Service " + name + " already instantiated, can't decorate!"); + var orig$get = origProvider.$get; + origProvider.$get = function() { + var origInstance = $injector.invoke(origProvider, orig$get); + return $injector.invoke(null, decorFn, {$delegate: origInstance}); + }; + } + + function getService(value) { if (typeof value !== 'string') { throw Error('Service name expected'); diff --git a/test/InjectorSpec.js b/test/InjectorSpec.js index 24ae78fb..0bce5ffd 100644 --- a/test/InjectorSpec.js +++ b/test/InjectorSpec.js @@ -280,6 +280,147 @@ describe('injector', function() { }]).get('value')).toEqual('abc'); }); }); + + + describe('decorator', function() { + var log, injector; + + beforeEach(function() { + log = []; + }); + + + it('should be called with the original instance', function() { + injector = createInjector([function($provide) { + $provide.value('myService', function(val) { + log.push('myService:' + val); + return 'origReturn'; + }); + + $provide.decorator('myService', function($delegate) { + return function(val) { + log.push('myDecoratedService:' + val); + var origVal = $delegate('decInput'); + return 'dec+' + origVal; + }; + }); + }]); + + var out = injector.get('myService')('input'); + log.push(out); + expect(log.join('; ')). + toBe('myDecoratedService:input; myService:decInput; dec+origReturn'); + }); + + + it('should allow multiple decorators to be applied to a service', function() { + injector = createInjector([function($provide) { + $provide.value('myService', function(val) { + log.push('myService:' + val); + return 'origReturn'; + }); + + $provide.decorator('myService', function($delegate) { + return function(val) { + log.push('myDecoratedService1:' + val); + var origVal = $delegate('decInput1'); + return 'dec1+' + origVal; + }; + }); + + $provide.decorator('myService', function($delegate) { + return function(val) { + log.push('myDecoratedService2:' + val); + var origVal = $delegate('decInput2'); + return 'dec2+' + origVal; + }; + }); + }]); + + var out = injector.get('myService')('input'); + log.push(out); + expect(log).toEqual(['myDecoratedService2:input', + 'myDecoratedService1:decInput2', + 'myService:decInput1', + 'dec2+dec1+origReturn']); + }); + + + it('should decorate services with dependencies', function() { + injector = createInjector([function($provide) { + $provide.value('dep1', 'dependency1'); + + $provide.factory('myService', ['dep1', function(dep1) { + return function(val) { + log.push('myService:' + val + ',' + dep1); + return 'origReturn'; + } + }]); + + $provide.decorator('myService', function($delegate) { + return function(val) { + log.push('myDecoratedService:' + val); + var origVal = $delegate('decInput'); + return 'dec+' + origVal; + }; + }); + }]); + + var out = injector.get('myService')('input'); + log.push(out); + expect(log.join('; ')). + toBe('myDecoratedService:input; myService:decInput,dependency1; dec+origReturn'); + }); + + + it('should allow for decorators to be injectable', function() { + injector = createInjector([function($provide) { + $provide.value('dep1', 'dependency1'); + + $provide.factory('myService', function() { + return function(val) { + log.push('myService:' + val); + return 'origReturn'; + } + }); + + $provide.decorator('myService', function($delegate, dep1) { + return function(val) { + log.push('myDecoratedService:' + val + ',' + dep1); + var origVal = $delegate('decInput'); + return 'dec+' + origVal; + }; + }); + }]); + + var out = injector.get('myService')('input'); + log.push(out); + expect(log.join('; ')). + toBe('myDecoratedService:input,dependency1; myService:decInput; dec+origReturn'); + }); + + + it('should complain if the service to be decorated was already instantiated', function() { + injector = createInjector([function($provide, $injector) { + $provide.value('myService', function(val) { + log.push('myService:' + val); + return 'origReturn'; + }); + + $injector.get('myService'); + + expect(function() { + $provide.decorator('myService', function($delegate) { + return function(val) { + log.push('myDecoratedService:' + val); + var origVal = $delegate('decInput'); + return 'dec+' + origVal; + }; + }); + }).toThrow("Service myService already instantiated, can't decorate!"); + }]); + }); + }); }); |
