Skip to content

Commit

Permalink
feat(all): initial work for paramless decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
steelsojka committed Mar 14, 2018
1 parent 3b8ac5d commit 5300a2e
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 35 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ node_modules
/test
/buildDocs
src/**/*.js
src/**/*.map
src/**/*.map
.history
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
21 changes: 20 additions & 1 deletion src/attempt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,23 @@ describe('attempt', () => {
expect(myClass.fn()).to.be.an.instanceOf(Error);
expect(myClass.fn2()).to.equal(10);
});
});

it('should catch the error and return it (paramless)', () => {
class MyClass {
@Attempt
fn() {
throw new Error();
}

@Attempt
fn2() {
return 10;
}
}

const myClass = new MyClass();

expect(myClass.fn()).to.be.an.instanceOf(Error);
expect(myClass.fn2()).to.equal(10);
});
});
15 changes: 7 additions & 8 deletions src/attempt.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import attempt = require('lodash/attempt');
import partial = require('lodash/partial');

import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
import { DecoratorConfig, DecoratorFactory, TypedMethodDecorator } from './factory';
import { PreValueApplicator } from './applicators';

const attemptFn = (fn: () => void) => partial(attempt, fn);
const decorator = DecoratorFactory.createDecorator(
new DecoratorConfig(attemptFn, new PreValueApplicator())
);

/**
* Attempts to invoke func, returning either the result or the caught error object. Any additional arguments are provided to func when it's invoked.
Expand All @@ -30,8 +27,10 @@ const decorator = DecoratorFactory.createDecorator(
* myClass.fn(10); // => 10;
* myClass.fn(null); // => Error
*/
export function Attempt(...partials: any[]): LodashMethodDecorator {
return decorator(...partials);
}
export const Attempt = DecoratorFactory.createDecorator(
new DecoratorConfig(attemptFn, new PreValueApplicator(), {
optionalParams: true
})
) as TypedMethodDecorator;
export { Attempt as attempt };
export default decorator;
export default Attempt;
22 changes: 21 additions & 1 deletion src/bind.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,24 @@ describe('bind', () => {
myClass.fn.call(null);
expect(context).to.equal(myClass);
});
});

it('should bind without params', () => {
let context;

class MyClass {
@Bind
fn() {
context = this;
}
}

const myClass = new MyClass();
const myClass2 = new MyClass();

myClass.fn.call(null);
expect(context).to.equal(myClass);

myClass2.fn.call(null);
expect(context).to.equal(myClass2);
});
});
18 changes: 8 additions & 10 deletions src/bind.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import bind = require('lodash/bind');

import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
import { DecoratorConfig, DecoratorFactory, TypedMethodDecorator1 } from './factory';
import { BindApplicator } from './applicators';

const decorator = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(bind, new BindApplicator())
);

/**
* Creates a function that invokes func with the this binding of thisArg and partials prepended to the arguments it receives.
*
Expand All @@ -32,8 +28,10 @@ const decorator = DecoratorFactory.createInstanceDecorator(
* myClass.bound.call(null); // => myClass {}
* myClass.unbound.call(null); // => null
*/
export function Bind(...partials: any[]): LodashMethodDecorator {
return decorator(...partials);
}
export { Bind as bind };
export default decorator;
export const Bind = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(bind, new BindApplicator(), {
optionalParams: true
})
) as TypedMethodDecorator1<any>;
export { Bind as bind, };
export default Bind;
15 changes: 15 additions & 0 deletions src/curry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ describe('curry', () => {
expect(add5(10)).to.equal(15);
});

it('should curry the method with default arity (paramless)', () => {
class MyClass {
@Curry
add(a: any, b?: any) {
return a + b;
}
}

const myClass = new MyClass();
const add5 = myClass.add(5);

expect(add5).to.be.an.instanceOf(Function);
expect(add5(10)).to.equal(15);
});

it('should curry the method with fixed arity', () => {
class MyClass {
@Curry(2)
Expand Down
14 changes: 5 additions & 9 deletions src/curry.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import curry = require('lodash/curry');

import { DecoratorConfig, DecoratorFactory, LodashMethodDecorator } from './factory';
import { DecoratorConfig, DecoratorFactory, TypedMethodDecorator1 } from './factory';
import { PreValueApplicator } from './applicators';

const decorator = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(curry, new PreValueApplicator(), { bound: true })
);

/**
* Creates a function that accepts arguments of func and either invokes func returning its result, if at least arity number of arguments have been provided, or returns a function that accepts the remaining func arguments, and so on.
* The arity of func may be specified if func.length is not sufficient.
Expand All @@ -33,8 +29,8 @@ const decorator = DecoratorFactory.createInstanceDecorator(
*
* add5AndMultiply(10); // => 30
*/
export function Curry(arity?: number): LodashMethodDecorator {
return decorator(arity);
}
export const Curry = DecoratorFactory.createInstanceDecorator(
new DecoratorConfig(curry, new PreValueApplicator(), { bound: true, optionalParams: true })
) as TypedMethodDecorator1<number>;
export { Curry as curry };
export default decorator;
export default Curry;
7 changes: 6 additions & 1 deletion src/factory/DecoratorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface DecoratorConfigOptions {
getter?: boolean;
property?: boolean;
method?: boolean;
optionalParams?: boolean;
}

export class DecoratorConfig {
Expand Down Expand Up @@ -34,4 +35,8 @@ export class DecoratorConfig {
get method(): boolean {
return this.options.method != null ? this.options.method : true;
}
}

get optionalParams(): boolean {
return this.options.optionalParams != null ? this.options.optionalParams : false;
}
}
12 changes: 8 additions & 4 deletions src/factory/DecoratorFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ export type GenericDecorator = (...args: any[]) => LodashDecorator;

export class InternalDecoratorFactory {
createDecorator(config: DecoratorConfig): GenericDecorator {
const { applicator } = config;
const { applicator, optionalParams } = config;

return (...args: any[]): LodashDecorator => {
return (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
const decorator = (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
const descriptor = this._resolveDescriptor(target, name, _descriptor);
const { value, get, set } = descriptor;

Expand All @@ -33,14 +33,16 @@ export class InternalDecoratorFactory {

return descriptor;
};

return optionalParams && args.length >= 2 ? decorator(args[0], args[1], args[2]) as any : decorator;
};
}

createInstanceDecorator(config: DecoratorConfig): GenericDecorator {
const { applicator, bound } = config;
const { applicator, bound, optionalParams } = config;

return (...args: any[]): LodashDecorator => {
return (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
const decorator = (target: Object, name: string, _descriptor?: PropertyDescriptor): PropertyDescriptor => {
const descriptor = this._resolveDescriptor(target, name, _descriptor);
const { value, writable, enumerable, configurable, get, set } = descriptor;
const isFirstInstance = !InstanceChainMap.has([ target, name ]);
Expand Down Expand Up @@ -146,6 +148,8 @@ export class InternalDecoratorFactory {

return descriptor;
};

return optionalParams && args.length >= 2 ? decorator(args[0], args[1], args[2]) as any: decorator;
};
}

Expand Down
8 changes: 8 additions & 0 deletions src/factory/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ export type LodashMethodDecorator = MethodDecorator;
export type LodashDecorator = MethodDecorator & PropertyDecorator;
export type ResolvableFunction = string|Function;

export type TypedMethodDecorator = (() => LodashMethodDecorator) & LodashMethodDecorator;
export type TypedMethodDecorator1<T> = ((arg?: T) => LodashMethodDecorator) & LodashMethodDecorator;
export type TypedMethodDecorator2<T, T2> = ((arg1?: T, arg2?: T2) => LodashMethodDecorator) & LodashMethodDecorator;
export type TypedMethodDecorator3<T, T2, T3> = ((arg1?: T, arg2?: T2, arg3?: T3) => LodashMethodDecorator) & LodashMethodDecorator;
export type TypedDecorator1<T> = ((arg?: T) => LodashDecorator) & LodashDecorator;
export type TypedDecorator2<T, T2> = ((arg1?: T, arg2?: T2) => LodashDecorator) & LodashDecorator;
export type TypedDecorator3<T, T2, T3> = ((arg1?: T, arg2?: T2, arg3?: T3) => LodashDecorator) & LodashDecorator;

export interface InstanceChainContext {
getter?: boolean;
setter?: boolean;
Expand Down

0 comments on commit 5300a2e

Please sign in to comment.