diff options
Diffstat (limited to 'src/Injector.js')
| -rw-r--r-- | src/Injector.js | 171 | 
1 files changed, 100 insertions, 71 deletions
| diff --git a/src/Injector.js b/src/Injector.js index 77d25a77..9e1464e9 100644 --- a/src/Injector.js +++ b/src/Injector.js @@ -4,81 +4,114 @@   * @function   *   * @description - * Creates an inject function that can be used for dependency injection. - * (See {@link guide/dev_guide.di dependency injection}) + * Creates an injector function that can be used for retrieving services as well as for + * dependency injection (see {@link guide/dev_guide.di dependency injection}).   * - * The inject function can be used for retrieving service instances or for calling any function - * which has the $inject property so that the services can be automatically provided. Angular - * creates an injection function automatically for the root scope and it is available as - * {@link angular.scope.$service $service}. + * Angular creates an injector automatically for the root scope and it is available as the + * {@link angular.scope.$service $service} property. Creation of the injector automatically creates + * all of the `$eager` {@link angular.service services}.   * - * @param {Object=} [providerScope={}] provider's `this` - * @param {Object.<string, function()>=} [providers=angular.service] Map of provider (factory) - *     function. - * @param {Object.<string, function()>=} [cache={}] Place where instances are saved for reuse. Can - *     also be used to override services speciafied by `providers` (useful in tests). - * @returns - *   {function()} Injector function: `function(value, scope, args...)`: + * @param {Object=} [factoryScope={}] `this` for the service factory function. + * @param {Object.<string, function()>=} [factories=angular.service] Map of service factory + *     functions. + * @param {Object.<string, function()>=} [instanceCache={}] Place where instances of services are + *     saved for reuse. Can also be used to override services specified by `serviceFactory` + *     (useful in tests). + * @returns {function()} Injector function:   * - *     * `value` - `{string|array|function}` - *     * `scope(optional=rootScope)` -  optional function "`this`" when `value` is type `function`. - *     * `args(optional)` - optional set of arguments to pass to function after injection arguments. - *        (also known as curry arguments or currying). + *   * `injector(serviceName)`: + *     * `serviceName` - `{string=}` - name of the service to retrieve.   * - *   #Return value of `function(value, scope, args...)` - *   The injector function return value depended on the type of `value` argument: - * - *     * `string`: return an instance for the injection key. - *     * `array` of keys: returns an array of instances for those keys. (see `string` above.) - *     * `function`: look at `$inject` property of function to determine instances to inject - *       and then call the function with instances and `scope`. Any additional arguments - *       (`args`) are appended to the function arguments. - *     * `none`: initialize eager providers. + * The injector function also has these properties:   * + *   * an `invoke` property which can be used to invoke methods with dependency-injected arguments. + *    `injector.invoke(self, fn, curryArgs)` + *     * `self` -  "`this`" to be used when invoking the function. + *     * `fn` - the function to be invoked. The function may have the `$inject` property which + *        lists the set of arguments which should be auto injected + *        (see {@link guide.di dependency injection}). + *     * `curryArgs(array)` - optional array of arguments to pass to function invocation after the + *        injection arguments (also known as curry arguments or currying). + *   * an `eager` property which is used to initialize the eager services. + *     `injector.eager()`   */ -function createInjector(providerScope, providers, cache) { -  providers = providers || angularService; -  cache = cache || {}; -  providerScope = providerScope || {}; -  return function inject(value, scope, args){ -    var returnValue, provider; -    if (isString(value)) { -      if (!(value in cache)) { -        provider = providers[value]; -        if (!provider) throw "Unknown provider for '"+value+"'."; -        cache[value] = inject(provider, providerScope); -      } -      returnValue = cache[value]; -    } else if (isArray(value)) { -      returnValue = []; -      forEach(value, function(name) { -        returnValue.push(inject(name)); -      }); -    } else if (isFunction(value)) { -      returnValue = inject(injectionArgs(value)); -      returnValue = value.apply(scope, concat(returnValue, arguments, 2)); -    } else if (isObject(value)) { -      forEach(providers, function(provider, name){ -        if (provider.$eager) -          inject(name); +function createInjector(factoryScope, factories, instanceCache) { +  factories = factories || angularService; +  instanceCache = instanceCache || {}; +  factoryScope = factoryScope || {}; +  injector.invoke = invoke; -        if (provider.$creation) -          throw new Error("Failed to register service '" + name + -              "': $creation property is unsupported. Use $eager:true or see release notes."); -      }); -    } else { -      returnValue = inject(providerScope); +  injector.eager = function(){ +    forEach(factories, function(factory, name){ +      if (factory.$eager) +        injector(name); + +      if (factory.$creation) +        throw new Error("Failed to register service '" + name + +        "': $creation property is unsupported. Use $eager:true or see release notes."); +    }); +  }; +  return injector; + +  function injector(value){ +    if (!(value in instanceCache)) { +      var factory = factories[value]; +      if (!factory) throw Error("Unknown provider for '"+value+"'."); +      instanceCache[value] = invoke(factoryScope, factory);      } -    return returnValue; +    return instanceCache[value];    }; -} -function injectService(services, fn) { -  return extend(fn, {$inject:services}); +  function invoke(self, fn, args){ +    args = args || []; +    var injectNames = injectionArgs(fn); +    var i = injectNames.length; +    while(i--) { +      args.unshift(injector(injectNames[i])); +    } +    return fn.apply(self, args); +  }  } -function injectUpdateView(fn) { -  return injectService(['$updateView'], fn); +/*NOT_PUBLIC_YET + * @ngdoc function + * @name angular.annotate + * @function + * + * @description + * Annotate the function with injection arguments. This is equivalent to setting the `$inject` + * property as described in {@link guide.di dependency injection}. + * + * <pre> + * var MyController = angular.annotate('$location', function($location){ ... }); + * </pre> + * + * is the same as + * + * <pre> + * var MyController = function($location){ ... }; + * MyController.$inject = ['$location']; + * </pre> + * + * @param {String|Array} serviceName... zero or more service names to inject into the + *     `annotatedFunction`. + * @param {function} annotatedFunction function to annotate with `$inject` + *     functions. + * @returns {function} `annotatedFunction` + */ +function annotate(services, fn) { +  if (services instanceof Array) { +    fn.$inject = services; +    return fn; +  } else { +    var i = 0, +        length = arguments.length - 1, // last one is the destination function +        $inject = arguments[length].$inject = []; +    for (; i < length; i++) { +      $inject.push(arguments[i]); +    } +    return arguments[length]; // return the last one +  }  }  function angularServiceInject(name, fn, inject, eager) { @@ -89,12 +122,12 @@ function angularServiceInject(name, fn, inject, eager) {  /**   * @returns the $inject property of function. If not found the   * the $inject is computed by looking at the toString of function and - * extracting all arguments which start with $ or end with _ as the + * extracting all arguments which and assuming that they are the   * injection names.   */  var FN_ARGS = /^function\s*[^\(]*\(([^\)]*)\)/;  var FN_ARG_SPLIT = /,/; -var FN_ARG = /^\s*(((\$?).+?)(_?))\s*$/; +var FN_ARG = /^\s*(.+?)\s*$/;  var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;  function injectionArgs(fn) {    assertArgFn(fn); @@ -103,12 +136,8 @@ function injectionArgs(fn) {      var fnText = fn.toString().replace(STRIP_COMMENTS, '');      var argDecl = fnText.match(FN_ARGS);      forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ -      arg.replace(FN_ARG, function(all, name, injectName, $, _){ -        assertArg(args, name, 'after non-injectable arg'); -        if ($ || _) -          args.push(injectName); -        else -          args = null; // once we reach an argument which is not injectable then ignore +      arg.replace(FN_ARG, function(all, name){ +        args.push(name);        });      });    } | 
