Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding serviceFn #104

Merged
merged 1 commit into from
Jan 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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');
});
});
}());