aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Minar2011-12-02 19:38:39 -0500
committerIgor Minar2011-12-07 09:41:08 -0800
commit4e3c05b99e32dad4084637299b3a696f9579957f (patch)
tree46bab2c4b3af41090d9c747c6b78a814e06928a3
parent5e4d59adf0ba4eb7f993f66b868759c24ae94f4c (diff)
downloadangular.js-4e3c05b99e32dad4084637299b3a696f9579957f.tar.bz2
feat(injector): add $provide.decorator
-rw-r--r--src/Injector.js15
-rw-r--r--test/InjectorSpec.js141
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!");
+ }]);
+ });
+ });
});