Skip to content

Commit

Permalink
feat(decorators): negate, flip, rearg can now be used on properties
Browse files Browse the repository at this point in the history
  • Loading branch information
steelsojka committed May 6, 2017
1 parent 59b5485 commit 6f951dc
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 21 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ decorators are applied at the instance level.
- `Once`
- `Flow`
- `FlowRight`
- `Rearg`
- `Negate`
- `Flip`
- `Bind`
- `Partial`
- `PartialRight`

### Mixin

Expand Down Expand Up @@ -388,4 +394,4 @@ If a prototype decorator comes after an instance decorator it will be ignored si
- More and better unit tests.
- Better performance with instance decorators.
- Single imports with `import { Debounce } from 'lodash-decorators/debounce'`;
- Composition decorators can be used on properties. These will generate the composed function.
- Composition decorators can be used on properties. These will generate the composed function.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"readme": "doctoc ./README.md",
"release": "npm run build && npm run docs && npm run readme",
"lint": "tslint src/**",
"lint:fix": "tslint src/** --fix"
"lint:fix": "tslint src/** --fix",
"version": "standard-version"
},
"main": "index.js",
"keywords": [
Expand Down Expand Up @@ -59,6 +60,7 @@
"http-server": "^0.9.0",
"mocha": "^3.3.0",
"sinon": "~1.14.1",
"standard-version": "^4.0.0",
"ts-node": "^3.0.2",
"tslint": "^5.1.0",
"tslint-language-service": "^0.9.2",
Expand Down
20 changes: 20 additions & 0 deletions src/applicators/PartialValueApplicator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import isFunction = require('lodash/isFunction');

import { Applicator, ApplicateOptions } from './Applicator';
import { resolveFunction } from '../utils';

export class PartialValueApplicator extends Applicator {
apply({ args, target, value, config: { execute } }: ApplicateOptions): any {
return function(...invokeArgs: any[]): any {
let fn = value;
let argIndex = 0;

if (!isFunction(fn)) {
fn = resolveFunction(args[0], this, target);
argIndex = 1;
}

return execute(fn, ...args.slice(argIndex)).apply(this, invokeArgs);
}
}
}
3 changes: 2 additions & 1 deletion src/applicators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ export * from './Applicator';
export * from './ComposeApplicator';
export * from './PartialApplicator';
export * from './PartialedApplicator';
export * from './PartialValueApplicator';
export * from './PostValueApplicator';
export * from './PreValueApplicator';
export * from './WrapApplicator';
export * from './BindApplicator';
export * from './InvokeApplicator';
export * from './MemoizeApplicator';
export * from './MemoizeApplicator';
15 changes: 15 additions & 0 deletions src/flip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,19 @@ describe('flip', () => {

expect(myClass.fn(10, 20)).to.eql([ 20, 10 ]);
});

it('should flip the arguments of the resolved function', () => {
class MyClass {
@Flip('fn')
fn2: (b: number, a: number) => [ number, number ];

fn(a: number, b: number) {
return [ a, b ];
}
}

const myClass = new MyClass();

expect(myClass.fn2(10, 20)).to.eql([ 20, 10 ]);
});
});
21 changes: 14 additions & 7 deletions src/flip.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import flip = require('lodash/flip');

import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
import { PreValueApplicator } from './applicators';
import {
DecoratorConfig,
DecoratorFactory,
LodashDecorator,
ResolvableFunction
} from './factory';
import { PartialValueApplicator } from './applicators';
/**
* Creates a function that invokes func with arguments reversed. Honestly, there is probably not much
* use for this decorator but maybe you will find one?
Expand All @@ -11,18 +16,20 @@ import { PreValueApplicator } from './applicators';
* class MyClass {
* value = 100;
*
* @Flip()
* fn(a, b) {
* @Flip('fn')
* fn2: (b: number, a: string) => [ number, string ];
*
* fn(a: string, b: number): [ string, number ] {
* return [ a, b ];
* }
* }
*
* const myClass = new MyClass();
*
* myClass.fn(10, 20); // => [ 20, 10 ]
* myClass.fn2(10, '20'); // => [ '20', 10 ]
*/
export const Flip: () => LodashMethodDecorator = DecoratorFactory.createDecorator(
new DecoratorConfig(flip, new PreValueApplicator())
export const Flip: (fn?: ResolvableFunction) => LodashDecorator = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(flip, new PartialValueApplicator(), { property: true })
);
export { Flip as flip };
export default Flip;
25 changes: 24 additions & 1 deletion src/memoize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,30 @@ import {
} from './factory';
import { MemoizeApplicator } from './applicators';
import { MemoizeConfig } from './shared';

/**
* Creates a function that memoizes the result of func. If resolver is provided,
* it determines the cache key for storing the result based on the arguments provided to the memoized function.
* By default, the first argument provided to the memoized function is used as the map cache key.
* The func is invoked with the this binding of the memoized function.
*
* You can use a Function or a string that references a method on the class as the resolver.
* You can also use a configuration object that lets provide a prexisting cache or specify
* the map type to use.
*
* @example
*
* class MyClass {
* @Memoize({ type: WeakMap })
* getName(item) {
* return item.name;
* }
*
* @Memoize('getName')
* getLastName(item) {
* return item.lastName;
* }
* }
*/
export const Memoize: (resolver?: ResolvableFunction|MemoizeConfig<any, any>) => LodashMethodDecorator = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(memoize, new MemoizeApplicator())
);
Expand Down
18 changes: 18 additions & 0 deletions src/mixin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
import assign = require('lodash/assign');

/**
* Mixins an object into the classes prototype.
* @export
* @param {...Object[]} srcs
* @returns {ClassDecorator}
* @example
*
* const myMixin = {
* blorg: () => 'blorg!'
* }
*
* @Mixin(myMixin)
* class MyClass {}
*
* const myClass = new MyClass();
*
* myClass.blorg(); // => 'blorg!'
*/
export function Mixin(...srcs: Object[]): ClassDecorator {
return (target: Function) => {
assign(target.prototype, ...srcs);
Expand Down
17 changes: 16 additions & 1 deletion src/negate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,19 @@ describe('negate', () => {

expect(myClass.fn()).to.be.false;
});
});

it('should inverse the result of the resolved function', () => {
class MyClass {
@Negate('fn')
fn2: () => boolean;

fn() {
return true;
}
}

const myClass = new MyClass();

expect(myClass.fn2()).to.be.false;
});
});
13 changes: 9 additions & 4 deletions src/negate.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import negate = require('lodash/negate');

import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
import { PreValueApplicator } from './applicators';
import {
DecoratorConfig,
DecoratorFactory,
LodashDecorator,
ResolvableFunction
} from './factory';
import { PartialValueApplicator } from './applicators';

export const Negate: () => LodashMethodDecorator = DecoratorFactory.createDecorator(
new DecoratorConfig(negate, new PreValueApplicator())
export const Negate: (fn?: ResolvableFunction) => LodashDecorator = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(negate, new PartialValueApplicator(), { property: true })
);
export { Negate as negate };
export default Negate;
17 changes: 16 additions & 1 deletion src/rearg.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,19 @@ describe('rearg', () => {

expect(myClass.fn(1, 2, 3)).to.eql([ 3, 2, 1 ]);
});
});

it('should change the order of the resolved function', () => {
class MyClass {
@Rearg('fn', 2, 1, 0)
fn2: Function;

fn(a: any, b: any, c: any): any[] {
return [ a, b, c ];
}
}

const myClass = new MyClass();

expect(myClass.fn2(1, 2, 3)).to.eql([ 3, 2, 1 ]);
});
});
13 changes: 9 additions & 4 deletions src/rearg.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import rearg = require('lodash/rearg');

import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
import { PreValueApplicator } from './applicators';
import {
DecoratorConfig,
DecoratorFactory,
LodashDecorator,
ResolvableFunction
} from './factory';
import { PartialValueApplicator } from './applicators';

export const Rearg: (indexes: number|number[], ...args: number[]) => LodashMethodDecorator = DecoratorFactory.createDecorator(
new DecoratorConfig(rearg, new PreValueApplicator())
export const Rearg: (indexes: ResolvableFunction|number|number[], ...args: (number|number[])[]) => LodashDecorator = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(rearg, new PartialValueApplicator(), { property: true })
);
export { Rearg as rearg };
export default Rearg;

0 comments on commit 6f951dc

Please sign in to comment.