Skip to content

Commit

Permalink
seclusion instance of
Browse files Browse the repository at this point in the history
  • Loading branch information
eddow committed Nov 2, 2024
1 parent b723a97 commit fe98620
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 9 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ As simple as that, methods (as well as accessors) of `Plane` and `DuckCourier` w

## But ... How ? And, how can I ...

When a secluded class is implemented, `this` (so, here, a `DuckCourier`) will be used as the prototype for a `Secluded<Plane>`. A Proxy is added between `Secluded` and `Plane` to manage who is `this` in method calls (either `DuckCourier` or `Secluded<Plane>`) - et voilà!
When a secluded class is implemented, the `Plane` instance prototype will be replaced by `this` (so, here, a `DuckCourier`). Some `Proxy` voodoo is juggled with to manage who is `this` in method calls (either `DuckCourier` or `Secluded<Plane>`) - et voilà!

Because of prototyping, `Secluded<Plane>` has access to all the functionalities of `DuckCourier` (and therefore of `Plane`) while never interfering with `DuckCourier::wingSpan`. Also, having several secluded class in the legacy list will only create several "heads" who will share a prototype.

Expand Down
18 changes: 11 additions & 7 deletions src/diamond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ export const diamondHandler: {
}
}

export function hasInstanceManager<Class extends Ctor>(cls: Class) {
return (obj: any) => {
if (!obj || typeof obj !== 'object') return false
const objBottom = bottomLeg(obj.constructor)
if (objBottom === cls) return true
const fLeg = allFLegs.get(objBottom)
return Boolean(fLeg && fLeg.some((base) => bottomLeg(base) === cls))
}
}

export default function Diamond<TBases extends Ctor[]>(
...baseClasses: TBases
): Newable<HasBases<TBases>> {
Expand Down Expand Up @@ -93,13 +103,7 @@ export default function Diamond<TBases extends Ctor[]>(
buildingDiamond = null
}
}
static [Symbol.hasInstance](obj: any) {
if (!obj || typeof obj !== 'object') return false
const objBottom = bottomLeg(obj.constructor)
if (objBottom === Diamond) return true
const fLeg = allFLegs.get(objBottom)
return fLeg && fLeg.some((base) => bottomLeg(base) === Diamond)
}
static [Symbol.hasInstance] = hasInstanceManager(Diamond)
}
allFLegs.set(Diamond, bases)
/**
Expand Down
7 changes: 6 additions & 1 deletion src/seclude.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Diamond, { diamondHandler } from './diamond'
import Diamond, { diamondHandler, hasInstanceManager } from './diamond'
import { constructedObject } from './helpers'
import { Ctor, KeySet, Newable } from './types'
import { allFLegs, bottomLeg, fLegs, nextInLine } from './utils'
Expand Down Expand Up @@ -49,6 +49,11 @@ export function Seclude<TBase extends Ctor, Keys extends (keyof InstanceType<TBa
diamondSecluded = !fLegs(base),
// any: abstract -> newable
diamond = diamondSecluded ? (Diamond(PropertyCollector) as any) : PropertyCollector
// We make sure `Secluded(X).secluded(x) instanceof X`
if (diamondSecluded) {
Object.defineProperty(base, Symbol.hasInstance, { value: hasInstanceManager(base) })
//base[Symbol.hasInstance] = hasInstanceManager(base)
}
class GateKeeper extends (diamond as any) {
static secluded(obj: TBase): TBase | undefined {
return privates.get(obj)
Expand Down
6 changes: 6 additions & 0 deletions test/seclude.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ test('leg-less', () => {
}

let t = testScenario(new Y(), P)
expect(t instanceof X).toBe(true)
expect(builtX! instanceof X).toBe(true)
expect(builtX).toBe(P.secluded(t))
})

Expand Down Expand Up @@ -106,8 +108,12 @@ test('leg-half', () => {
class E extends Diamond(Y, P) {}

let t = testScenario(new D(), P)
expect(t instanceof X).toBe(true)
expect(builtX! instanceof X).toBe(true)
expect(builtX).toBe(P.secluded(t))
t = testScenario(new E(), P)
expect(t instanceof X).toBe(true)
expect(builtX! instanceof X).toBe(true)
expect(builtX).toBe(P.secluded(t))
})

Expand Down

0 comments on commit fe98620

Please sign in to comment.