Skip to content

Commit

Permalink
fix: fix afterInstance advices not being fired
Browse files Browse the repository at this point in the history
fix #135
  • Loading branch information
k1r0s committed Dec 3, 2018
1 parent 5a12989 commit 6fb1ee7
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 71 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"node": ">=6.0.0"
},
"scripts": {
"lint": "tslint -t codeFrame src/**/*.ts test/**/*.ts",
"lint": "tslint -t codeFrame src/**/*.ts",
"prebuild": "rm -rf dist",
"build": "tsc && rollup -c && rm -rf compiled",
"test": "jest",
Expand Down
65 changes: 40 additions & 25 deletions src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,52 @@ function generateKey (scope, methodName) {
return `${scope}-${methodName}`
}

function wrapMethod (target, methodName, beforeKey, afterKey, caller?) {
const keyOriginalMethod = generateKey(KEY_ORIGINAL_METHOD, methodName)
return reflect.createProxyFn(target, methodName, [
...Reflect.getMetadata(beforeKey, target) || [],
Reflect.getMetadata(keyOriginalMethod, target),
...Reflect.getMetadata(afterKey, target) || []
], caller)
function wrapMethod (target, methodName, original, befores, afters, caller?) {
const adviceList = [
...(befores || []),
original,
...(afters || [])
]
return reflect.createProxyFn(target, methodName, adviceList, caller)
}

function applyReflect (target, advices, methodName, keyJoinPoint, original) {
const keyOriginalMethod = generateKey(KEY_ORIGINAL_METHOD, methodName)
const adviceArr = Reflect.getMetadata(keyJoinPoint, target) || []
function replace (target, metaContainer, advices, methodName, keyJoinPoint, original) {
const adviceArr = Reflect.getMetadata(keyJoinPoint, metaContainer) || []
adviceArr.unshift(...advices.map(reflect.advice))
Reflect.defineMetadata(keyJoinPoint, adviceArr, target)
if (!Reflect.getMetadata(keyOriginalMethod, target)) Reflect.defineMetadata(keyOriginalMethod, original, target)
Reflect.defineMetadata(keyJoinPoint, adviceArr, metaContainer)

const keyOriginalMethod = generateKey(KEY_ORIGINAL_METHOD, methodName)
if (!Reflect.getMetadata(keyOriginalMethod, metaContainer)) {
Reflect.defineMetadata(keyOriginalMethod, original, metaContainer)
}
const originalMethod = Reflect.getMetadata(keyOriginalMethod, metaContainer)

if (methodName === "constructor") {
const keyBeforeInstance = generateKey(KEY_BEFORE_INSTANCE, methodName)
const keyAfterInstance = generateKey(KEY_AFTER_INSTANCE, methodName)
const result = wrapMethod(target, methodName, keyBeforeInstance, keyAfterInstance, meta =>
Reflect.construct(meta.target, meta.args, meta.ES6newTarget))
result.prototype = target.prototype
return result
const keyBeforeMethod = generateKey(KEY_BEFORE_INSTANCE, methodName)
const keyAfterMethod = generateKey(KEY_AFTER_INSTANCE, methodName)
const beforeAdvices = Reflect.getMetadata(keyBeforeMethod, metaContainer)
const afterAdvices = Reflect.getMetadata(keyAfterMethod, metaContainer)

const nctor = wrapMethod(target, methodName, originalMethod, beforeAdvices, afterAdvices, meta =>
Reflect.construct(meta.target.prototype.constructor, meta.args, meta.ES6newTarget))

nctor.prototype = target.prototype
return nctor
} else {
const keyBeforeMethod = generateKey(KEY_BEFORE_METHOD, methodName)
const keyAfterMethod = generateKey(KEY_AFTER_METHOD, methodName)
return wrapMethod(target, methodName, keyBeforeMethod, keyAfterMethod)
const beforeAdvices = Reflect.getMetadata(keyBeforeMethod, metaContainer)
const afterAdvices = Reflect.getMetadata(keyAfterMethod, metaContainer)

return wrapMethod(target, methodName, originalMethod, beforeAdvices, afterAdvices)
}
}

export function beforeMethod<B = any, K extends keyof B = any> (...advices: AdviceRef<B>[]): MethodSignature<B, K> {
return Object.assign((target, methodName, descriptor) => {
const keyBeforeMethod = generateKey(KEY_BEFORE_METHOD, methodName)
descriptor.value = applyReflect(
descriptor.value = replace(
target,
target,
advices,
methodName,
Expand All @@ -54,7 +66,8 @@ export function beforeMethod<B = any, K extends keyof B = any> (...advices: Advi
export function afterMethod<B = any, K extends keyof B = any> (...advices: AdviceRef<B>[]): MethodSignature<B, K> {
return Object.assign((target, methodName, descriptor) => {
const keyAfterMethod = generateKey(KEY_AFTER_METHOD, methodName)
descriptor.value = applyReflect(
descriptor.value = replace(
target,
target,
advices,
methodName,
Expand All @@ -68,25 +81,27 @@ export function afterMethod<B = any, K extends keyof B = any> (...advices: Advic
export function beforeInstance<B = any> (...advices: AdviceRef<B>[]): ClassSignature<B> {
return Object.assign((target, methodName = "constructor") => {
const keyBeforeInstance = generateKey(KEY_BEFORE_INSTANCE, methodName)
return applyReflect(
return replace(
target,
target.prototype,
advices,
methodName,
keyBeforeInstance,
target
target.prototype.constructor
)
}, { advices: () => advices })
}

export function afterInstance<B = any> (...advices: AdviceRef<B>[]): ClassSignature<B> {
return Object.assign((target, methodName = "constructor") => {
const keyAfterInstance = generateKey(KEY_AFTER_INSTANCE, methodName)
return applyReflect(
return replace(
target,
target.prototype,
advices,
methodName,
keyAfterInstance,
target
target.prototype.constructor
)
}, { advices: () => advices })
}
82 changes: 37 additions & 45 deletions test/decorator-order.spec.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,90 @@
import { beforeMethod, afterMethod, afterInstance, beforeInstance } from "../src"

const op1 = meta => meta.args[0]++
const op2 = meta => meta.args[0] *= 2
const op3 = meta => meta.args[0] -= 5
const op4 = meta => meta.args[0] *= 10
const op5 = meta => meta.args[0] /= 2
const op1 = meta => meta.args[0].push("op1")
const op2 = meta => meta.args[0].push("op2")
const op3 = meta => meta.args[0].push("op3")
const op4 = meta => meta.args[0].push("op4")
const op5 = meta => meta.args[0].push("op5")

@beforeInstance(op2)
@beforeInstance(op1)
@beforeInstance(op5)
class Dummy1 {
output
constructor (arg) {
this.output = arg
}
constructor(a) {}
}

@beforeInstance(op2, op1, op5)
class Dummy2 {
output
constructor (arg) {
this.output = arg
}
constructor(a) {}
}

class Dummy3 {
@beforeMethod(op3, op5, op1)
static someMethod (arg) {
return arg
}
static someMethod (a) {}
}

class Dummy4 {
@beforeMethod(op3)
@beforeMethod(op5)
@beforeMethod(op1)
static someMethod (arg) {
return arg
}
static someMethod (a) {}
}

@afterInstance(op4)
@afterInstance(op5)
@afterInstance(op1)
class Dummy5 {
output
constructor (arg) {
this.output = arg
}
constructor(a) {}
}

@afterInstance(op4, op5, op1)
class Dummy6 {
output
constructor (arg) {
this.output = arg
}
constructor(a) {}
}

class Dummy7 {
@afterMethod(op2, op5, op3)
static someMethod (arg) {
return arg
}
static someMethod (a) {}
}

class Dummy8 {
@afterMethod(op2)
@afterMethod(op5)
@afterMethod(op3)
static someMethod (arg) {
return arg
}
static someMethod (a) {}
}

describe("obtain several product by doing calculation to ensure advice order", () => {
it("evaluate before instance order", () => {
const instance1 = new Dummy1(0)
const instance2 = new Dummy2(0)
expect(instance1.output).toEqual(instance2.output)
const r1 = []
const r2 = []
new Dummy1(r1)
new Dummy2(r2)
expect(r1.join()).toEqual(r2.join())
expect(r1.join()).toEqual("op2,op1,op5")
})
it("evaluate after instance order", () => {
const instance5 = new Dummy5(0)
const instance6 = new Dummy6(0)
expect(instance5.output).toEqual(instance6.output)
const r1 = []
const r2 = []
new Dummy5(r1)
new Dummy6(r2)
expect(r1.join()).toEqual(r2.join())
expect(r1.join()).toEqual("op4,op5,op1")
})
it("evaluate before method order", () => {
const res3 = Dummy3.someMethod(0)
const res4 = Dummy4.someMethod(0)
expect(res3).toEqual(res4)
const r1 = []
const r2 = []
Dummy3.someMethod(r1)
Dummy4.someMethod(r2)
expect(r1.join()).toEqual(r2.join())
expect(r1.join()).toEqual("op3,op5,op1")
})
it("evaluate after method order", () => {
const res7 = Dummy7.someMethod(0)
const res8 = Dummy8.someMethod(0)
expect(res7).toEqual(res8)
const r1 = []
const r2 = []
Dummy7.someMethod(r1)
Dummy8.someMethod(r2)
expect(r1.join()).toEqual(r2.join())
expect(r1.join()).toEqual("op2,op5,op3")
})
})

0 comments on commit 6fb1ee7

Please sign in to comment.