Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

break out core injector so it can be used without steal or JMVC

  • Loading branch information...
commit 9bd33be887a306f14a0f6eb1147cb6b608ead528 1 parent 53a5463
@iamnoah authored
View
6 README.md
@@ -1,12 +1,14 @@
[What is dependency injection?](https://github.com/cujojs/wire/wiki/IOC)
-# Functional Dependency Injection for JavaScriptMVC
+# Functional Dependency Injection for JavaScriptMVC (DoneJS/CanJS)
In a traditional dependency injection paradigm, the container manages objects that are connected to each other through the wiring spec. Injection is done by either setting properties or passing values to a constructor function (which typically then sets the properties on the object itself).
While that is a paradigm that works well in object oriented languages, it's more natural in a functional language like JavaScript to inject functions instead.
+**NOTE:** inject-core.js can be used independently of JavaScript MVC. You obviously wont be able to use the Class and Controller based functionality, but named functions and everything else should work.
+
## Advantages of injecting functions instead of objects
* No mutable state - no objects are modified, so it isn't possible for keys names to conflict or for injected values to be changed.
@@ -30,7 +32,7 @@ Injector definitions are simple objects with a `name` and a `factory` function.
In the examples, we name the injector variable `injector`, but you can call it whatever you want. e.g., `require`, `when`, `myInjector`, etc. The injector is also referred to as the context.
-**NOTE:** these docs are a work in progress, but for working examples of all functionality, you can refer to the qunit tests in the test directory.
+**NOTE:** these docs fairly complete, but you can always find working examples in the qunit tests in the test directory.
## Injecting plain functions
View
493 inject-core.coffee
@@ -1,259 +1,302 @@
-steal.plugins('jquery','jquery/class').then ($) ->
+###
+ Requirements:
+ jQuery or DoneJS/CanJS ($.when and $.extend)
+
+ Optional:
+ JavaScriptMVC/DoneJS/CanJS
+ $.String.getObject - required for parameterized factories
+###
+window = this
- exports = window
+exports = window
- factoryName = ///
- ^ # match the whole string
- ([^(]+) # everything up the ( or the end is the real name
- (\( # 2nd capture is the ()
- (.*?)? # 3rd capture is the arguments, unparsed
- \))?
- $
- ///
+factoryName = ///
+ ^ # match the whole string
+ ([^(]+) # everything up the ( or the end is the real name
+ (\( # 2nd capture is the ()
+ (.*?)? # 3rd capture is the arguments, unparsed
+ \))?
+ $
+///
- error = window.console && console.error || ->
+error = if window.console and console.error
+ (args...) -> console.error.apply(console,args)
+else
+ ->
- # the global stack of injectors
- CONTEXT = []
+bind = (obj,name) ->
+ fn = obj[name]
+ unless fn
+ throw new Error("#{name} is not defined.")
+ (args...) ->
+ fn.apply(obj,args)
+
+# D is for Dependencies
+D = if window.can
+ when: bind(can,'when')
+ extend: bind(can,'extend')
+ getObject: bind(can,'getObject') #optional, but we know it's there
+else
+ unless window.jQuery or window.$?.when and window.$?.extend
+ throw new Error("Either JavaScriptMVC, DoneJS, CanJS or jQuery is required.")
+ when: bind(jQuery,'when')
+ extend: bind(jQuery,'extend')
+ getObject: if jQuery.String?.getObject
+ bind(jQuery.String,'getObject')
+ else -> throw new Error("Cannot use controllers without JMVC")
- inject = (defs...)->
- factories = {}
- results = {}
- defs = groupBy(defs,'name')
- eager = []
- resolver = (name) ->
- def = {}
- # the factories are already built, so we just need to get the inject definitions
- # to create the mapping
- if(name && name.controller)
- controller = name.controller
- name = getName(controller)
- # find matching definitions and collapse them into def
- $.extend(true,def,d) for d in defs[name] || [] when \
- !controller || !d.controller || controller.element.is(d.controller)
- # def just tells us how to map the dependency names to global names
- mapping = mapper(def)
- resolve = (name) ->
- # TODO enable for non-controllers? would be accessing globals...
- sub = (name) ->
- controller && substitute(name,controller.options) || name
+# the global stack of injectors
+CONTEXT = []
- get = (path) ->
- unless controller
- throw new Error("parameterized factories can only be used on controllers. Cannot resolve '#{path}' for '#{name}' AKA '#{realName}'")
- $.String.getObject(path,[controller.options])
+inject = (defs...)->
+ factories = {}
+ results = {}
+ defs = groupBy(defs,'name')
+ eager = []
+ resolver = (name) ->
+ def = {}
- parts = factoryName.exec(mapping(sub(name)))
- realName = parts[1]
- args = (get(path) for path in parts[3]?.split(',') ? [] when path)
+ # the factories are already built, so we just need to get the inject definitions
+ # to create the mapping
+ if(name && name.controller)
+ controller = name.controller
+ name = getName(controller)
- unless factories[realName]
- throw new Error("Cannot resolve '#{realName}' AKA '#{name}'")
+ # find matching definitions and collapse them into def
+ ###
+ Important note: jQuery.is is required to use a controller selector.
+ ###
+ D.extend(true,def,d) for d in defs[name] || [] when \
+ !controller || !d.controller || controller.element.is(d.controller)
- factories[realName].apply(this,args)
+ # def just tells us how to map the dependency names to global names
+ mapping = mapper(def)
- injector = whenInjected(resolver)
+ resolve = (name) ->
+ # TODO enable for non-controllers? would be accessing globals...
+ sub = (name) ->
+ controller && substitute(name,controller.options) || name
- # pre-create factories
- for name, configs of defs
- def = {}
-
- $.extend(true,def,d) for d in configs
-
- [name,factory] = makeFactory(def)
-
- eager.push(factory) if def.eager
-
- factories[name] = factory
-
- # run the eager factories (presumably they cache themselves)
- # eager factories are built in to make it easier to resolve dependencies of the eager factory
- useInjector(injector, ->
- factory() for factory in eager
- ).call(this)
-
- injector
-
- # support for injecting using the current context
- injectUnbound = (name) ->
- require = (args...) ->
- injectCurrent = ->
- context = last(CONTEXT)
- unless context
- noContext()
- injected = context.named(name).apply(this,args) # create an injected function
- injected.apply(this,arguments) # and call it
- injectCurrent.andReturn = andReturn
- injectCurrent
-
- # require with no name
- inject.require = injectUnbound()
-
- inject.require.named = injectUnbound
-
-
- useInjector = (injector,fn) ->
- return ->
- try
- CONTEXT.push(injector)
- fn.apply(this,arguments)
- finally
- CONTEXT.pop()
-
- inject.useCurrent = (fn) ->
- context = last(CONTEXT);
- unless context
- noContext()
- useInjector(context,fn)
-
- noContext = ->
- throw new Error("""There is no current injector.
- You need to call this inside an injected function or an inject.useCurrent function.""")
-
- # cache offers a simple mechanism for creating (and clearing) singletons
- # without caching, the injected values are recreated/resolved each time
- cache = inject.cache = ->
- results = {}
+ get = (path) ->
+ unless controller
+ throw new Error("parameterized factories can only be used on controllers. Cannot resolve '#{path}' for '#{name}' AKA '#{realName}'")
+ D.getObject(path,[controller.options])
- singleton = (name,fn) ->
- cachedFactory = (args...) ->
- array = results[name] || (results[name] = [])
- result = matchArgs(array,args || [])
-
- unless result
- result = value: fn.apply(this,args), args: args
- array.push(result);
- result.value;
-
- singleton.def = (name,fn,eager) ->
- name: name
- eager: eager
- factory: this(name,fn)
+ parts = factoryName.exec(mapping(sub(name)))
+ realName = parts[1]
+ args = (get(path) for path in parts[3]?.split(',') ? [] when path)
- singleton.clear = (keys...)->
- if keys.length
- for key in keys
- if key.args
- matchArgs(results[key.name],key.args,true)
- else
- delete results[key]
- else
- results = {}
+ unless factories[realName]
+ throw new Error("Cannot resolve '#{realName}' AKA '#{name}'")
- singleton
+ factories[realName].apply(this,args)
- inject.setupControllerActions = ->
- for funcName, action of this.Class.actions
- this[funcName] = inject.useCurrent(this[funcName])
+ injector = whenInjected(resolver)
- @_super.apply(this,arguments)
-
- makeFactory = (def)->
- [fullName, name, params] = factoryName.exec(def.name)
- fn = def.factory
- [name, ->
- unless fn
- throw new Error("#{fullName} does not have a factory function so it cannot be injected into a function.");
- if arguments.length && !params
- throw new Error("#{fullName} is not a parameterized factory, it cannot take arguments. If you want to pass it arguments, the name must end with '()'.")
- fn.apply(this,arguments)
- ]
-
- substitute = (string,options) ->
- string.replace /\{(.+?)\}/g, (param,name) ->
- $.String.getObject(name,[options])
-
- whenInjected = (resolver) ->
- destroyed = false
- # injectorFor creates requires(), which is what the user sees as the injector
- injectorFor = (name) ->
- requires = (dependencies...,fn)->
- fn = useInjector injector, fn # make sure the function retains the right context
- # when takes a list of the dependencies and the function to inject
- # and returns a function that will resolve the dependencies and pipe them into the function
- injected = useInjector injector, (args...) -> # set the context when the injected function is called
- return if destroyed
- target = this
- resolve = resolver(name || nameOf(target))
- try
- deferreds = (resolve(d) for d in dependencies)
- catch e
- error('Error resolving for target:',target)
- throw e
- $.when.apply($,deferreds.concat(args)).pipe ->
- fn.apply(target,arguments) unless destroyed
- injected.andReturn = andReturn
- injected
-
- # XXX to support named functions, we have to expose injectorFor, which allows the name to be curried
- injector = injectorFor() # no name resolves by the target object
- injector.named = injectorFor
- injector.destroy = ->
- destroyed = true
- injector
-
- andReturn = (afterAdvice) ->
- fn = this
- unless afterAdvice.apply
- afterAdvice = ((value)-> -> value )(afterAdvice)
- (args...) ->
- def = fn.apply(this,args)
- afterAdvice.apply(this,[def].concat(args))
-
- nameOf = (target) ->
- if target.element && target.Class
- controller: target
- else
- getName(target)
+ # pre-create factories
+ for name, configs of defs
+ def = {}
+
+ D.extend(true,def,d) for d in configs
+
+ [name,factory] = makeFactory(def)
- getName = (target) ->
- target?.options?.inject?.name || target?.Class?.fullName
+ eager.push(factory) if def.eager
- mapper = (config) ->
- mapProperty = (property) ->
- config?.inject?[property] || property
+ factories[name] = factory
- matchArgs = (results,args,del) ->
- return unless results
+ # run the eager factories (presumably they cache themselves)
+ # eager factories are built in to make it easier to resolve dependencies of the eager factory
+ useInjector(injector, ->
+ factory() for factory in eager
+ ).call(this)
- for result, i in results
- miss = find result.args || [], (index,arg) ->
- args[index] isnt arg
+ injector
- unless miss
- if del
- delete result[i]
- return result
+# support for injecting using the current context
+injectUnbound = (name) ->
+ require = (args...) ->
+ injectCurrent = ->
+ context = last(CONTEXT)
+ unless context
+ noContext()
+ injected = context.named(name).apply(this,args) # create an injected function
+ injected.apply(this,arguments) # and call it
+ injectCurrent.andReturn = andReturn
+ injectCurrent
- exports.Inject = inject
+# require with no name
+inject.require = injectUnbound()
- ## Support Functions ##
+inject.require.named = injectUnbound
- groupBy = (array,fn) ->
- prop = fn;
- unless fn.call and fn.apply
- fn = (it) -> it?[prop]
- obj = {}
- for e in array
- key = fn(e)
- if obj[key]
- obj[key].push(e)
- else
- obj[key] = [e]
- obj
+useInjector = (injector,fn) ->
+ return ->
+ try
+ CONTEXT.push(injector)
+ fn.apply(this,arguments)
+ finally
+ CONTEXT.pop()
+
+inject.useCurrent = (fn) ->
+ context = last(CONTEXT);
+ unless context
+ noContext()
+ useInjector(context,fn)
+
+noContext = ->
+ throw new Error("""There is no current injector.
+ You need to call this inside an injected function or an inject.useCurrent function.""")
+
+# cache offers a simple mechanism for creating (and clearing) singletons
+# without caching, the injected values are recreated/resolved each time
+cache = inject.cache = ->
+ results = {}
+
+ singleton = (name,fn) ->
+ cachedFactory = (args...) ->
+ array = results[name] || (results[name] = [])
+ result = matchArgs(array,args || [])
+
+ unless result
+ result = value: fn.apply(this,args), args: args
+ array.push(result);
+
+ result.value;
+
+ singleton.def = (name,fn,eager) ->
+ name: name
+ eager: eager
+ factory: this(name,fn)
+
+ singleton.clear = (keys...)->
+ if keys.length
+ for key in keys
+ if key.args
+ matchArgs(results[key.name],key.args,true)
+ else
+ delete results[key]
+ else
+ results = {}
+
+ singleton
+
+inject.setupControllerActions = ->
+ for funcName, action of getClass(this).actions
+ this[funcName] = inject.useCurrent(this[funcName])
+
+ @_super.apply(this,arguments)
+
+makeFactory = (def)->
+ [fullName, name, params] = factoryName.exec(def.name)
+ fn = def.factory
+ [name, ->
+ unless fn
+ throw new Error("#{fullName} does not have a factory function so it cannot be injected into a function.");
+ if arguments.length && !params
+ throw new Error("#{fullName} is not a parameterized factory, it cannot take arguments. If you want to pass it arguments, the name must end with '()'.")
+ fn.apply(this,arguments)
+ ]
+
+substitute = (string,options) ->
+ string.replace /\{(.+?)\}/g, (param,name) ->
+ D.getObject(name,[options])
+
+whenInjected = (resolver) ->
+ destroyed = false
+ # injectorFor creates requires(), which is what the user sees as the injector
+ injectorFor = (name) ->
+ requires = (dependencies...,fn)->
+ fn = useInjector injector, fn # make sure the function retains the right context
+ # when takes a list of the dependencies and the function to inject
+ # and returns a function that will resolve the dependencies and pipe them into the function
+ injected = useInjector injector, (args...) -> # set the context when the injected function is called
+ return if destroyed
+ target = this
+ resolve = resolver(name || nameOf(target))
+ try
+ deferreds = (resolve(d) for d in dependencies)
+ catch e
+ error('Error resolving for target:',target)
+ throw e
+ D.when.apply(D,deferreds.concat(args)).pipe ->
+ fn.apply(target,arguments) unless destroyed
+ injected.andReturn = andReturn
+ injected
+
+ # XXX to support named functions, we have to expose injectorFor, which allows the name to be curried
+ injector = injectorFor() # no name resolves by the target object
+ injector.named = injectorFor
+ injector.destroy = ->
+ destroyed = true
+ injector
+
+andReturn = (afterAdvice) ->
+ fn = this
+ unless afterAdvice.apply
+ afterAdvice = ((value)-> -> value )(afterAdvice)
+ (args...) ->
+ def = fn.apply(this,args)
+ afterAdvice.apply(this,[def].concat(args))
+
+nameOf = (target) ->
+ if target.element && getClass(target)
+ controller: target
+ else
+ getName(target)
+
+getName = (target) ->
+ target?.options?.inject?.name || getClass(target)?.fullName
+
+getClass = (target) ->
+ target?.Class || target?.constructor
+
+mapper = (config) ->
+ mapProperty = (property) ->
+ config?.inject?[property] || property
+
+matchArgs = (results,args,del) ->
+ return unless results
+
+ for result, i in results
+ miss = find result.args || [], (index,arg) ->
+ args[index] isnt arg
+
+ unless miss
+ if del
+ delete result[i]
+ return result
+
+exports.Inject = inject
+
+## Support Functions ##
+
+groupBy = (array,fn) ->
+ prop = fn;
+ unless fn.call and fn.apply
+ fn = (it) -> it?[prop]
+
+ obj = {}
+ for e in array
+ key = fn(e)
+ if obj[key]
+ obj[key].push(e)
+ else
+ obj[key] = [e]
+ obj
- last = (array) ->
- array?[array?.length - 1]
+last = (array) ->
+ array?[array?.length - 1]
- find = (array,fn,context) ->
- fn ?= (it) -> it
- for value, index in array
- return value if fn.call(context,value,index)
+find = (array,fn,context) ->
+ fn ?= (it) -> it
+ for value, index in array
+ return value if fn.call(context,value,index)
View
670 inject-core.js
@@ -1,331 +1,409 @@
+
+/*
+ Requirements:
+ jQuery or DoneJS/CanJS ($.when and $.extend)
+
+ Optional:
+ JavaScriptMVC/DoneJS/CanJS
+ $.String.getObject - required for parameterized factories
+*/
+
(function() {
- var __slice = Array.prototype.slice;
-
- steal.plugins('jquery', 'jquery/class').then(function($) {
- var CONTEXT, andReturn, cache, error, exports, factoryName, find, getName, groupBy, inject, injectUnbound, last, makeFactory, mapper, matchArgs, nameOf, noContext, substitute, useInjector, whenInjected;
- exports = window;
- factoryName = /^([^(]+)(\((.*?)?\))?$/;
- error = window.console && console.error || function() {};
- CONTEXT = [];
- inject = function() {
- var configs, d, def, defs, eager, factories, factory, injector, name, resolver, results, _i, _len, _ref;
- defs = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- factories = {};
- results = {};
- defs = groupBy(defs, 'name');
- eager = [];
- resolver = function(name) {
- var controller, d, def, mapping, resolve, _i, _len, _ref;
- def = {};
- if (name && name.controller) {
- controller = name.controller;
- name = getName(controller);
- }
- _ref = defs[name] || [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- d = _ref[_i];
- if (!controller || !d.controller || controller.element.is(d.controller)) {
- $.extend(true, def, d);
- }
- }
- mapping = mapper(def);
- return resolve = function(name) {
- var args, get, parts, path, realName, sub;
- sub = function(name) {
- return controller && substitute(name, controller.options) || name;
- };
- get = function(path) {
- if (!controller) {
- throw new Error("parameterized factories can only be used on controllers. Cannot resolve '" + path + "' for '" + name + "' AKA '" + realName + "'");
- }
- return $.String.getObject(path, [controller.options]);
- };
- parts = factoryName.exec(mapping(sub(name)));
- realName = parts[1];
- args = (function() {
- var _j, _len2, _ref2, _ref3, _ref4, _results;
- _ref4 = (_ref2 = (_ref3 = parts[3]) != null ? _ref3.split(',') : void 0) != null ? _ref2 : [];
- _results = [];
- for (_j = 0, _len2 = _ref4.length; _j < _len2; _j++) {
- path = _ref4[_j];
- if (path) _results.push(get(path));
- }
- return _results;
- })();
- if (!factories[realName]) {
- throw new Error("Cannot resolve '" + realName + "' AKA '" + name + "'");
- }
- return factories[realName].apply(this, args);
- };
+ var CONTEXT, D, andReturn, bind, cache, error, exports, factoryName, find, getClass, getName, groupBy, inject, injectUnbound, last, makeFactory, mapper, matchArgs, nameOf, noContext, substitute, useInjector, whenInjected, window,
+ __slice = Array.prototype.slice;
+
+ window = this;
+
+ exports = window;
+
+ factoryName = /^([^(]+)(\((.*?)?\))?$/;
+
+ error = window.console && console.error ? function() {
+ var args;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ return console.error.apply(console, args);
+ } : function() {};
+
+ bind = function(obj, name) {
+ var fn;
+ fn = obj[name];
+ if (!fn) throw new Error("" + name + " is not defined.");
+ return function() {
+ var args;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ return fn.apply(obj, args);
+ };
+ };
+
+ D = (function() {
+ var _ref, _ref2, _ref3;
+ if (window.can) {
+ return {
+ when: bind(can, 'when'),
+ extend: bind(can, 'extend'),
+ getObject: bind(can, 'getObject')
};
- injector = whenInjected(resolver);
- for (name in defs) {
- configs = defs[name];
- def = {};
- for (_i = 0, _len = configs.length; _i < _len; _i++) {
- d = configs[_i];
- $.extend(true, def, d);
- }
- _ref = makeFactory(def), name = _ref[0], factory = _ref[1];
- if (def.eager) eager.push(factory);
- factories[name] = factory;
+ } else {
+ if (!(window.jQuery || ((_ref = window.$) != null ? _ref.when : void 0) && ((_ref2 = window.$) != null ? _ref2.extend : void 0))) {
+ throw new Error("Either JavaScriptMVC, DoneJS, CanJS or jQuery is required.");
}
- useInjector(injector, function() {
- var factory, _j, _len2, _results;
- _results = [];
- for (_j = 0, _len2 = eager.length; _j < _len2; _j++) {
- factory = eager[_j];
- _results.push(factory());
+ return {
+ when: bind(jQuery, 'when'),
+ extend: bind(jQuery, 'extend'),
+ getObject: ((_ref3 = jQuery.String) != null ? _ref3.getObject : void 0) ? bind(jQuery.String, 'getObject') : function() {
+ throw new Error("Cannot use controllers without JMVC");
}
- return _results;
- }).call(this);
- return injector;
- };
- injectUnbound = function(name) {
- var require;
- return require = function() {
- var args, injectCurrent;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- injectCurrent = function() {
- var context, injected;
- context = last(CONTEXT);
- if (!context) noContext();
- injected = context.named(name).apply(this, args);
- return injected.apply(this, arguments);
- };
- injectCurrent.andReturn = andReturn;
- return injectCurrent;
};
- };
- inject.require = injectUnbound();
- inject.require.named = injectUnbound;
- useInjector = function(injector, fn) {
- return function() {
- try {
- CONTEXT.push(injector);
- return fn.apply(this, arguments);
- } finally {
- CONTEXT.pop();
+ }
+ })();
+
+ CONTEXT = [];
+
+ inject = function() {
+ var configs, d, def, defs, eager, factories, factory, injector, name, resolver, results, _i, _len, _ref;
+ defs = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ factories = {};
+ results = {};
+ defs = groupBy(defs, 'name');
+ eager = [];
+ resolver = function(name) {
+ var controller, d, def, mapping, resolve, _i, _len, _ref;
+ def = {};
+ if (name && name.controller) {
+ controller = name.controller;
+ name = getName(controller);
+ }
+ /*
+ Important note: jQuery.is is required to use a controller selector.
+ */
+ _ref = defs[name] || [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ d = _ref[_i];
+ if (!controller || !d.controller || controller.element.is(d.controller)) {
+ D.extend(true, def, d);
}
- };
- };
- inject.useCurrent = function(fn) {
- var context;
- context = last(CONTEXT);
- if (!context) noContext();
- return useInjector(context, fn);
- };
- noContext = function() {
- throw new Error("There is no current injector.\nYou need to call this inside an injected function or an inject.useCurrent function.");
- };
- cache = inject.cache = function() {
- var results, singleton;
- results = {};
- singleton = function(name, fn) {
- var cachedFactory;
- return cachedFactory = function() {
- var args, array, result;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- array = results[name] || (results[name] = []);
- result = matchArgs(array, args || []);
- if (!result) {
- result = {
- value: fn.apply(this, args),
- args: args
- };
- array.push(result);
- }
- return result.value;
+ }
+ mapping = mapper(def);
+ return resolve = function(name) {
+ var args, get, parts, path, realName, sub;
+ sub = function(name) {
+ return controller && substitute(name, controller.options) || name;
};
- };
- singleton.def = function(name, fn, eager) {
- return {
- name: name,
- eager: eager,
- factory: this(name, fn)
+ get = function(path) {
+ if (!controller) {
+ throw new Error("parameterized factories can only be used on controllers. Cannot resolve '" + path + "' for '" + name + "' AKA '" + realName + "'");
+ }
+ return D.getObject(path, [controller.options]);
};
- };
- singleton.clear = function() {
- var key, keys, _i, _len, _results;
- keys = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- if (keys.length) {
+ parts = factoryName.exec(mapping(sub(name)));
+ realName = parts[1];
+ args = (function() {
+ var _j, _len2, _ref2, _ref3, _ref4, _results;
+ _ref4 = (_ref2 = (_ref3 = parts[3]) != null ? _ref3.split(',') : void 0) != null ? _ref2 : [];
_results = [];
- for (_i = 0, _len = keys.length; _i < _len; _i++) {
- key = keys[_i];
- if (key.args) {
- _results.push(matchArgs(results[key.name], key.args, true));
- } else {
- _results.push(delete results[key]);
- }
+ for (_j = 0, _len2 = _ref4.length; _j < _len2; _j++) {
+ path = _ref4[_j];
+ if (path) _results.push(get(path));
}
return _results;
- } else {
- return results = {};
+ })();
+ if (!factories[realName]) {
+ throw new Error("Cannot resolve '" + realName + "' AKA '" + name + "'");
}
+ return factories[realName].apply(this, args);
};
- return singleton;
};
- inject.setupControllerActions = function() {
- var action, funcName, _ref;
- _ref = this.Class.actions;
- for (funcName in _ref) {
- action = _ref[funcName];
- this[funcName] = inject.useCurrent(this[funcName]);
+ injector = whenInjected(resolver);
+ for (name in defs) {
+ configs = defs[name];
+ def = {};
+ for (_i = 0, _len = configs.length; _i < _len; _i++) {
+ d = configs[_i];
+ D.extend(true, def, d);
}
- return this._super.apply(this, arguments);
- };
- makeFactory = function(def) {
- var fn, fullName, name, params, _ref;
- _ref = factoryName.exec(def.name), fullName = _ref[0], name = _ref[1], params = _ref[2];
- fn = def.factory;
- return [
- name, function() {
- if (!fn) {
- throw new Error("" + fullName + " does not have a factory function so it cannot be injected into a function.");
- }
- if (arguments.length && !params) {
- throw new Error("" + fullName + " is not a parameterized factory, it cannot take arguments. If you want to pass it arguments, the name must end with '()'.");
- }
- return fn.apply(this, arguments);
- }
- ];
- };
- substitute = function(string, options) {
- return string.replace(/\{(.+?)\}/g, function(param, name) {
- return $.String.getObject(name, [options]);
- });
- };
- whenInjected = function(resolver) {
- var destroyed, injector, injectorFor;
- destroyed = false;
- injectorFor = function(name) {
- var requires;
- return requires = function() {
- var dependencies, fn, injected, _i;
- dependencies = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), fn = arguments[_i++];
- fn = useInjector(injector, fn);
- injected = useInjector(injector, function() {
- var args, d, deferreds, resolve, target;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- if (destroyed) return;
- target = this;
- resolve = resolver(name || nameOf(target));
- try {
- deferreds = (function() {
- var _j, _len, _results;
- _results = [];
- for (_j = 0, _len = dependencies.length; _j < _len; _j++) {
- d = dependencies[_j];
- _results.push(resolve(d));
- }
- return _results;
- })();
- } catch (e) {
- error('Error resolving for target:', target);
- throw e;
- }
- return $.when.apply($, deferreds.concat(args)).pipe(function() {
- if (!destroyed) return fn.apply(target, arguments);
- });
- });
- injected.andReturn = andReturn;
- return injected;
- };
- };
- injector = injectorFor();
- injector.named = injectorFor;
- injector.destroy = function() {
- return destroyed = true;
- };
- return injector;
- };
- andReturn = function(afterAdvice) {
- var fn;
- fn = this;
- if (!afterAdvice.apply) {
- afterAdvice = (function(value) {
- return function() {
- return value;
- };
- })(afterAdvice);
+ _ref = makeFactory(def), name = _ref[0], factory = _ref[1];
+ if (def.eager) eager.push(factory);
+ factories[name] = factory;
+ }
+ useInjector(injector, function() {
+ var factory, _j, _len2, _results;
+ _results = [];
+ for (_j = 0, _len2 = eager.length; _j < _len2; _j++) {
+ factory = eager[_j];
+ _results.push(factory());
}
- return function() {
- var args, def;
- args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
- def = fn.apply(this, args);
- return afterAdvice.apply(this, [def].concat(args));
+ return _results;
+ }).call(this);
+ return injector;
+ };
+
+ injectUnbound = function(name) {
+ var require;
+ return require = function() {
+ var args, injectCurrent;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ injectCurrent = function() {
+ var context, injected;
+ context = last(CONTEXT);
+ if (!context) noContext();
+ injected = context.named(name).apply(this, args);
+ return injected.apply(this, arguments);
};
+ injectCurrent.andReturn = andReturn;
+ return injectCurrent;
};
- nameOf = function(target) {
- if (target.element && target.Class) {
- return {
- controller: target
- };
- } else {
- return getName(target);
+ };
+
+ inject.require = injectUnbound();
+
+ inject.require.named = injectUnbound;
+
+ useInjector = function(injector, fn) {
+ return function() {
+ try {
+ CONTEXT.push(injector);
+ return fn.apply(this, arguments);
+ } finally {
+ CONTEXT.pop();
}
};
- getName = function(target) {
- var _ref, _ref2, _ref3;
- return (target != null ? (_ref = target.options) != null ? (_ref2 = _ref.inject) != null ? _ref2.name : void 0 : void 0 : void 0) || (target != null ? (_ref3 = target.Class) != null ? _ref3.fullName : void 0 : void 0);
+ };
+
+ inject.useCurrent = function(fn) {
+ var context;
+ context = last(CONTEXT);
+ if (!context) noContext();
+ return useInjector(context, fn);
+ };
+
+ noContext = function() {
+ throw new Error("There is no current injector.\nYou need to call this inside an injected function or an inject.useCurrent function.");
+ };
+
+ cache = inject.cache = function() {
+ var results, singleton;
+ results = {};
+ singleton = function(name, fn) {
+ var cachedFactory;
+ return cachedFactory = function() {
+ var args, array, result;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ array = results[name] || (results[name] = []);
+ result = matchArgs(array, args || []);
+ if (!result) {
+ result = {
+ value: fn.apply(this, args),
+ args: args
+ };
+ array.push(result);
+ }
+ return result.value;
+ };
};
- mapper = function(config) {
- var mapProperty;
- return mapProperty = function(property) {
- var _ref;
- return (config != null ? (_ref = config.inject) != null ? _ref[property] : void 0 : void 0) || property;
+ singleton.def = function(name, fn, eager) {
+ return {
+ name: name,
+ eager: eager,
+ factory: this(name, fn)
};
};
- matchArgs = function(results, args, del) {
- var i, miss, result, _len;
- if (!results) return;
- for (i = 0, _len = results.length; i < _len; i++) {
- result = results[i];
- miss = find(result.args || [], function(index, arg) {
- return args[index] !== arg;
- });
- if (!miss) {
- if (del) delete result[i];
- return result;
+ singleton.clear = function() {
+ var key, keys, _i, _len, _results;
+ keys = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ if (keys.length) {
+ _results = [];
+ for (_i = 0, _len = keys.length; _i < _len; _i++) {
+ key = keys[_i];
+ if (key.args) {
+ _results.push(matchArgs(results[key.name], key.args, true));
+ } else {
+ _results.push(delete results[key]);
+ }
}
+ return _results;
+ } else {
+ return results = {};
}
};
- exports.Inject = inject;
- groupBy = function(array, fn) {
- var e, key, obj, prop, _i, _len;
- prop = fn;
- if (!(fn.call && fn.apply)) {
- fn = function(it) {
- return it != null ? it[prop] : void 0;
- };
- }
- obj = {};
- for (_i = 0, _len = array.length; _i < _len; _i++) {
- e = array[_i];
- key = fn(e);
- if (obj[key]) {
- obj[key].push(e);
- } else {
- obj[key] = [e];
+ return singleton;
+ };
+
+ inject.setupControllerActions = function() {
+ var action, funcName, _ref;
+ _ref = getClass(this).actions;
+ for (funcName in _ref) {
+ action = _ref[funcName];
+ this[funcName] = inject.useCurrent(this[funcName]);
+ }
+ return this._super.apply(this, arguments);
+ };
+
+ makeFactory = function(def) {
+ var fn, fullName, name, params, _ref;
+ _ref = factoryName.exec(def.name), fullName = _ref[0], name = _ref[1], params = _ref[2];
+ fn = def.factory;
+ return [
+ name, function() {
+ if (!fn) {
+ throw new Error("" + fullName + " does not have a factory function so it cannot be injected into a function.");
+ }
+ if (arguments.length && !params) {
+ throw new Error("" + fullName + " is not a parameterized factory, it cannot take arguments. If you want to pass it arguments, the name must end with '()'.");
}
+ return fn.apply(this, arguments);
}
- return obj;
+ ];
+ };
+
+ substitute = function(string, options) {
+ return string.replace(/\{(.+?)\}/g, function(param, name) {
+ return D.getObject(name, [options]);
+ });
+ };
+
+ whenInjected = function(resolver) {
+ var destroyed, injector, injectorFor;
+ destroyed = false;
+ injectorFor = function(name) {
+ var requires;
+ return requires = function() {
+ var dependencies, fn, injected, _i;
+ dependencies = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), fn = arguments[_i++];
+ fn = useInjector(injector, fn);
+ injected = useInjector(injector, function() {
+ var args, d, deferreds, resolve, target;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ if (destroyed) return;
+ target = this;
+ resolve = resolver(name || nameOf(target));
+ try {
+ deferreds = (function() {
+ var _j, _len, _results;
+ _results = [];
+ for (_j = 0, _len = dependencies.length; _j < _len; _j++) {
+ d = dependencies[_j];
+ _results.push(resolve(d));
+ }
+ return _results;
+ })();
+ } catch (e) {
+ error('Error resolving for target:', target);
+ throw e;
+ }
+ return D.when.apply(D, deferreds.concat(args)).pipe(function() {
+ if (!destroyed) return fn.apply(target, arguments);
+ });
+ });
+ injected.andReturn = andReturn;
+ return injected;
+ };
};
- last = function(array) {
- return array != null ? array[(array != null ? array.length : void 0) - 1] : void 0;
+ injector = injectorFor();
+ injector.named = injectorFor;
+ injector.destroy = function() {
+ return destroyed = true;
};
- return find = function(array, fn, context) {
- var index, value, _len;
- if (fn == null) {
- fn = function(it) {
- return it;
+ return injector;
+ };
+
+ andReturn = function(afterAdvice) {
+ var fn;
+ fn = this;
+ if (!afterAdvice.apply) {
+ afterAdvice = (function(value) {
+ return function() {
+ return value;
};
+ })(afterAdvice);
+ }
+ return function() {
+ var args, def;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ def = fn.apply(this, args);
+ return afterAdvice.apply(this, [def].concat(args));
+ };
+ };
+
+ nameOf = function(target) {
+ if (target.element && getClass(target)) {
+ return {
+ controller: target
+ };
+ } else {
+ return getName(target);
+ }
+ };
+
+ getName = function(target) {
+ var _ref, _ref2, _ref3;
+ return (target != null ? (_ref = target.options) != null ? (_ref2 = _ref.inject) != null ? _ref2.name : void 0 : void 0 : void 0) || ((_ref3 = getClass(target)) != null ? _ref3.fullName : void 0);
+ };
+
+ getClass = function(target) {
+ return (target != null ? target.Class : void 0) || (target != null ? target.constructor : void 0);
+ };
+
+ mapper = function(config) {
+ var mapProperty;
+ return mapProperty = function(property) {
+ var _ref;
+ return (config != null ? (_ref = config.inject) != null ? _ref[property] : void 0 : void 0) || property;
+ };
+ };
+
+ matchArgs = function(results, args, del) {
+ var i, miss, result, _len;
+ if (!results) return;
+ for (i = 0, _len = results.length; i < _len; i++) {
+ result = results[i];
+ miss = find(result.args || [], function(index, arg) {
+ return args[index] !== arg;
+ });
+ if (!miss) {
+ if (del) delete result[i];
+ return result;
}
- for (index = 0, _len = array.length; index < _len; index++) {
- value = array[index];
- if (fn.call(context, value, index)) return value;
+ }
+ };
+
+ exports.Inject = inject;
+
+ groupBy = function(array, fn) {
+ var e, key, obj, prop, _i, _len;
+ prop = fn;
+ if (!(fn.call && fn.apply)) {
+ fn = function(it) {
+ return it != null ? it[prop] : void 0;
+ };
+ }
+ obj = {};
+ for (_i = 0, _len = array.length; _i < _len; _i++) {
+ e = array[_i];
+ key = fn(e);
+ if (obj[key]) {
+ obj[key].push(e);
+ } else {
+ obj[key] = [e];
}
- };
- });
+ }
+ return obj;
+ };
+
+ last = function(array) {
+ return array != null ? array[(array != null ? array.length : void 0) - 1] : void 0;
+ };
+
+ find = function(array, fn, context) {
+ var index, value, _len;
+ if (fn == null) {
+ fn = function(it) {
+ return it;
+ };
+ }
+ for (index = 0, _len = array.length; index < _len; index++) {
+ value = array[index];
+ if (fn.call(context, value, index)) return value;
+ }
+ };
}).call(this);
View
4 inject.coffee
@@ -0,0 +1,4 @@
+if steal.plugins
+ steal.plugins('jquery','jquery/lang')('inject-core.js')
+else
+ steal('jquery','jquery/lang','./inject-core.js')
View
9 inject.js
@@ -0,0 +1,9 @@
+(function() {
+
+ if (steal.plugins) {
+ steal.plugins('jquery', 'jquery/lang')('inject-core.js');
+ } else {
+ steal('jquery', 'jquery/lang', './inject-core.js');
+ }
+
+}).call(this);
Please sign in to comment.
Something went wrong with that request. Please try again.