diff --git a/README.md b/README.md
index 0a80507..4447ece 100644
--- a/README.md
+++ b/README.md
@@ -415,6 +415,37 @@ Param | Type | Details
**Constructor** | *Function* | A constructor function that will be instantiated as a singleton.
**dependency**
*(optional)* | *String* | An optional name for a dependency to be passed to the constructor. A dependency will be passed to the constructor for each name passed to `Bottle#service` in the order they are listed.
+#### serviceFactory(name, factoryService [, dependency [, ...]])
+
+Used to register a service factory function. Works exactly like `factory` except the factory arguments will be injected instead of receiving the `container`. This is useful when implementing the Module Pattern or adding dependencies to your Higher Order Functions.
+
+```js
+function createApiActions(axios) {
+ return {
+ createUser: function(user) {
+ return axios.post('/users', user);
+ }
+ };
+}
+
+var bottle = new Bottle();
+
+bottle.serviceFactory('api', createApiActions, 'axios');
+bottle.factory('axios', function() {
+ return axios.create({baseURL: 'http://api.mydomain'});
+});
+
+bottle.container.api.createUser({name: "BottleJS"});
+```
+
+If `Bottle.config.strict` is set to `true`, this method will throw an error if an injected dependency is `undefined`.
+
+Param | Type | Details
+:--------------------------------|:-----------|:--------
+**name** | *String* | The name of the service. Must be unique to each Bottle instance.
+**serviceFactory** | *Function* | A function that will be invoked to create the service object/value.
+**dependency**
*(optional)* | *String* | An optional name for a dependency to be passed to the service function. A dependency will be passed to the service function for each name passed to `Bottle#serviceFn` in the order they are listed.
+
#### value(name, val)
Used to add an arbitrary value to the container.
diff --git a/dist/bottle.d.ts b/dist/bottle.d.ts
index 36905c1..29f8b40 100644
--- a/dist/bottle.d.ts
+++ b/dist/bottle.d.ts
@@ -80,6 +80,12 @@ declare class Bottle {
service(name: string, Constructor: ((...any: any[]) => any), ...dependency: string[]): this;
service(name: string, Constructor: new (...any: any[]) => T, ...dependency: string[]): this;
+ /**
+ * Register a service function. If Bottle.config.strict is set to true, this method will throw an error if an injected dependency is undefined.
+ */
+ serviceFactory(name: string, factoryService: ((...any: any[]) => any), ...dependency: string[]): this;
+ serviceFactory(name: string, factoryService: ((...any: any[]) => T), ...dependency: string[]): this;
+
/**
* Add an arbitrary value to the container.
*/
diff --git a/src/Bottle/service-factory.js b/src/Bottle/service-factory.js
new file mode 100644
index 0000000..6a9e410
--- /dev/null
+++ b/src/Bottle/service-factory.js
@@ -0,0 +1,6 @@
+/**
+ * Register a function service
+ */
+var serviceFactory = function serviceFactory(name, factoryService) {
+ return createService.apply(this, [name, factoryService, false].concat(slice.call(arguments, 2)));
+};
diff --git a/src/Bottle/service.js b/src/Bottle/service.js
index c7cd56b..2df7513 100644
--- a/src/Bottle/service.js
+++ b/src/Bottle/service.js
@@ -1,20 +1,31 @@
/**
- * Register a service inside a generic factory.
+ * Register a class service
*
* @param String name
* @param Function Service
* @return Bottle
*/
var service = function service(name, Service) {
- var deps = arguments.length > 2 ? slice.call(arguments, 2) : null;
+ return createService.apply(this, [name, Service, true].concat(slice.call(arguments, 2)));
+};
+
+/**
+ * Private helper for creating service and service factories.
+ *
+ * @param String name
+ * @param Function Service
+ * @return Bottle
+ */
+var createService = function createService(name, Service, isClass) {
+ var deps = arguments.length > 3 ? slice.call(arguments, 3) : [];
var bottle = this;
return factory.call(this, name, function GenericFactory() {
- var ServiceCopy = Service;
- if (deps) {
- var args = deps.map(getNestedService, bottle.container);
- args.unshift(Service);
- ServiceCopy = Service.bind.apply(Service, args);
+ var serviceFactory = Service; // alias for jshint
+ var args = deps.map(getNestedService, bottle.container);
+
+ if (!isClass) {
+ return serviceFactory.apply(null, args);
}
- return new ServiceCopy();
+ return new (Service.bind.apply(Service, [null].concat(args)))();
});
};
diff --git a/src/api.js b/src/api.js
index 0cd2923..dd47b10 100644
--- a/src/api.js
+++ b/src/api.js
@@ -41,6 +41,7 @@ Bottle.prototype = {
register : register,
resolve : resolve,
service : service,
+ serviceFactory : serviceFactory,
value : value
};
diff --git a/test/spec/service-factory.spec.js b/test/spec/service-factory.spec.js
new file mode 100644
index 0000000..941fd80
--- /dev/null
+++ b/test/spec/service-factory.spec.js
@@ -0,0 +1,24 @@
+/* globals Bottle */
+;(function() {
+ 'use strict';
+
+ /**
+ * Bottle Factory test suite
+ */
+ describe('Bottle#serviceFactory', function() {
+ it('injects dependencies to a service factory', function() {
+ var b = new Bottle();
+ var createThing = function(foo, bar) {
+ return {foo: foo, bar: bar};
+ };
+ b.serviceFactory('Thing', createThing, 'foo', 'bar');
+ b.service('foo', function() { this.name = 'foo'; });
+ b.value('bar', 'bippity');
+
+ expect(b.container.Thing).toBeDefined();
+ expect(b.container.Thing.foo).toBeDefined();
+ expect(b.container.Thing.foo.name).toBe('foo');
+ expect(b.container.Thing.bar).toBe('bippity');
+ });
+ });
+}());