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

bind factory: create partial function applications with injected arguments #167

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
22 changes: 20 additions & 2 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ are:
1. `module` - loads a module (AMD or CommonJS, depending on your environment)
2. `create` - creates objects using a constructor function, regular function, or by using Object.create.
3. `compose` - composes functions using a declarative syntax
4. `literal` - wire will not parse the right-hand side, but rather use it verbatim as a component
5. `wire` - recursively invokes wire on another wire spec
4. `partial` - creates a partial function application with the supplied arguments
5. `literal` - wire will not parse the right-hand side, but rather use it verbatim as a component
6. `wire` - recursively invokes wire on another wire spec

## Using factories

Expand Down Expand Up @@ -214,6 +215,23 @@ Functions are first-class citizens in wire.js. For example, you can [use them a

See [Composing Functions](functions.md#composing-functions) for more information on composing new functions with the compose factory.

## partial

The partial factory creates a partial function application with the supplied arguments.

### Syntax

```js
myComponent: {
partial: {
fn: { module: 'my/app/ModuleA' },

// Required: use these args to create a partially applied my/app/ModuleA
args: [arg1, arg2, arg3...]
}
}
```

## literal

It can be useful to have object literals in [wire specs](concepts.md#wire-specs) for various reasons, such as reference data or common configuration shared between several components. Most times, object literals can be declared directly:
Expand Down
17 changes: 17 additions & 0 deletions lib/plugin/basePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,22 @@ define(function(require) {
resolver.resolve(wire.loadModule(componentDef.options));
}

function partialFactory(resolver, componentDef, wire) {
var partial, fn, args, appliedFn;

partial = componentDef.options;
fn = wire(partial.fn);
args = partial.args ? wire(asArray(partial.args)) : [];

appliedFn = when.join(fn, args).spread(applyFunction);

resolver.resolve(appliedFn);

function applyFunction(fn, args) {
return functional.partial.apply(functional, [fn].concat(args));
}
}

/**
* Factory that uses an AMD module either directly, or as a
* constructor or plain function to create the resulting item.
Expand Down Expand Up @@ -265,6 +281,7 @@ define(function(require) {
pluginInstance = {
factories: {
module: moduleFactory,
partial: partialFactory,
create: instanceFactory,
literal: literalFactory,
prototype: protoFactory,
Expand Down
93 changes: 93 additions & 0 deletions test/node/lib/plugin/basePlugin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,99 @@ buster.testCase('lib/plugin/basePlugin', {
}
},

'partial factory': {
'should apply a single argument': function() {
var spy, add;

spy = this.spy();
add = function(x, y) { this(); return x + y; }.bind(spy);

return createContext({
increment: {
partial: {
fn: add,
args: 1
}
}
}).then(
function(context) {
var result = context.increment(4);
assert.calledOnce(spy);
assert.equals(result, 5);
},
fail
);
},

'should apply multiple arguments': function() {
var spy, add;

spy = this.spy();
add = function(x, y) { this(); return x + y; }.bind(spy);

return createContext({
three: {
partial: {
fn: add,
args: [1, 2]
}
}
}).then(
function(context) {
var result = context.three();
assert.calledOnce(spy);
assert.equals(result, 3);
},
fail
);
},

'should wire modules': function() {
return createContext({
increment: {
partial: {
fn: { module: '../../fixtures/function' },
args: 1
}
}
}).then(
function(context) {
assert.equals(context.increment(2), 3);
},
fail
);
},

'should wire references': function() {
var spy, add;

spy = this.spy();
add = function(x, y) { this(); return x + y; }.bind(spy);

return createContext({
increment: {
partial: {
fn: add,
args: 1
}
},
three: {
partial: {
fn: { $ref: 'increment' },
args: 2
}
}
}).then(
function(context) {
var result = context.three();
assert.calledOnce(spy);
assert.equals(result, 3);
},
fail
);
}
},

'create factory': {
'should call non-constructor functions': function() {
var spy = this.spy();
Expand Down