Skip to content

Commit

Permalink
Merge pull request #104 from ragboyjr/serviceFn
Browse files Browse the repository at this point in the history
Adding serviceFn
  • Loading branch information
young-steveo authored Jan 26, 2018
2 parents ed21639 + 143bfec commit a9d1964
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 8 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,37 @@ Param | Type | Details
**Constructor** | *Function* | A constructor function that will be instantiated as a singleton.
**dependency**<br />*(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**<br />*(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.
Expand Down
6 changes: 6 additions & 0 deletions dist/bottle.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ declare class Bottle {
service(name: string, Constructor: ((...any: any[]) => any), ...dependency: string[]): this;
service<T>(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<T>(name: string, factoryService: ((...any: any[]) => T), ...dependency: string[]): this;

/**
* Add an arbitrary value to the container.
*/
Expand Down
6 changes: 6 additions & 0 deletions src/Bottle/service-factory.js
Original file line number Diff line number Diff line change
@@ -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)));
};
27 changes: 19 additions & 8 deletions src/Bottle/service.js
Original file line number Diff line number Diff line change
@@ -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)))();
});
};
1 change: 1 addition & 0 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Bottle.prototype = {
register : register,
resolve : resolve,
service : service,
serviceFactory : serviceFactory,
value : value
};

Expand Down
24 changes: 24 additions & 0 deletions test/spec/service-factory.spec.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
}());

0 comments on commit a9d1964

Please sign in to comment.