Skip to content
This repository was archived by the owner on Feb 11, 2020. It is now read-only.

Commit 5d8806a

Browse files
author
Dr. Safi
committed
Created MultiplePrototypeChain module. Updated tests.
1 parent 5af95e3 commit 5d8806a

File tree

12 files changed

+218
-130
lines changed

12 files changed

+218
-130
lines changed

module/concept/concept.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import assert from 'assert'
22
import { ClassProducer } from './dynamicClassExtention.js'
33
// const ModuleContext = require('appscript/module/ModuleContext')
44

5-
describe('Testing different implementation concepts', () => {
5+
describe.skip('Testing different implementation concepts', () => {
66

77
describe('Creating static classes from function', () => {
88
let staticClass1 = ClassProducer({ Superclass: null /* semilar to extending Function.prototype (if it was possible as it is an object) */ })
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
let self = class MultiplePrototypeChain {
2+
3+
4+
static newChainOnInstanceCreation({
5+
Class, // wrap constructor of Class and create a new chain for each new instance.
6+
contextInstance // The instnace to be added to the chain.
7+
}) {
8+
return new Proxy(Class, {
9+
construct: (target, argumentsList, newConstructorFunc) => {
10+
let instance = Reflect.construct(target, argumentsList)
11+
instance = self.createUniqueProtoChain({ object: instance })
12+
instance = self.insertObjectToPrototypeChain({
13+
prototypeChain: instance,
14+
objectToAdd: contextInstance,
15+
beforePrototype: contextInstance.__proto__
16+
})
17+
return instance
18+
}
19+
})
20+
}
21+
22+
static createUniqueProtoChain({ object = null }) {
23+
if( object == null ||
24+
object.constructor == Object ||
25+
object.constructor == Function)
26+
return object
27+
28+
let delegatedPrototype = Object.getPrototypeOf(object)
29+
let nextPointerPrototype = self.createUniqueProtoChain({ object: delegatedPrototype })
30+
let pointerPrototype = Object.create(nextPointerPrototype)
31+
if(!pointerPrototype.hasOwnProperty('delegatedPrototype')) {
32+
Object.defineProperty(pointerPrototype, 'delegatedPrototype', { value: object, writable: true, enumerable: true, configurable: true })
33+
}
34+
let proxyHandler = self.handlerMultiplePrototypeChainPattern({ delegatedPrototype: object })
35+
pointerPrototype = new Proxy(pointerPrototype, proxyHandler)
36+
return pointerPrototype
37+
}
38+
39+
static handlerMultiplePrototypeChainPattern({ delegatedPrototype }) {
40+
return {
41+
get: function(target, property, receiver) {
42+
switch (property) {
43+
case 'delegatedPrototype':
44+
return Reflect.get(target, property)
45+
break;
46+
case '__proto__':
47+
return Object.getPrototypeOf(receiver)
48+
break;
49+
default:
50+
break;
51+
}
52+
if(delegatedPrototype.hasOwnProperty(property)) {
53+
return Reflect.get(delegatedPrototype, property)
54+
} else if(Object.getPrototypeOf(target)) {
55+
return Reflect.get(Object.getPrototypeOf(target), property)
56+
} else {
57+
return undefined
58+
}
59+
},
60+
set: function(target, property, value, receiver) {
61+
switch (property) {
62+
case '__proto__':
63+
return Object.setPrototypeOf(target, value)
64+
break;
65+
default:
66+
return Reflect.set(delegatedPrototype, property, value)
67+
break;
68+
}
69+
}
70+
}
71+
}
72+
73+
// Add an instance prototype to a prototype chain just before the instance's constructor prototype.
74+
static insertObjectToPrototypeChain({
75+
prototypeChain, // chain to add to
76+
objectToAdd, // object to add as prototype
77+
beforePrototype // location of insertion
78+
}) {
79+
// find position of beforePrototype with references to next and previous prototype
80+
let previousPrototype = prototypeChain // starting point
81+
let nextPrototype = prototypeChain.__proto__
82+
while (nextPrototype && beforePrototype !== nextPrototype.delegatedPrototype) {
83+
previousPrototype = nextPrototype
84+
nextPrototype = nextPrototype.__proto__
85+
}
86+
if(!nextPrototype) return false // if no appropriate insertion position was found.
87+
// create pointerPrototype to add between the previous and next prototypes.
88+
let pointerPrototype = Object.create(nextPrototype)
89+
if(!pointerPrototype.hasOwnProperty('delegatedPrototype')) {
90+
Object.defineProperty(pointerPrototype, 'delegatedPrototype', { value: objectToAdd, writable: true, enumerable: true, configurable: true })
91+
}
92+
pointerPrototype = new Proxy(pointerPrototype, self.handlerMultiplePrototypeChainPattern({
93+
delegatedPrototype: objectToAdd
94+
}))
95+
Object.setPrototypeOf(previousPrototype, pointerPrototype)
96+
return prototypeChain
97+
}
98+
99+
}
100+
101+
export { self as MultiplePrototypeChain }
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import assert from 'assert'
2+
import {MultiplePrototypeChain} from './entrypoint.js'
3+
4+
describe('Multiple Prototype Chain creation', () => {
5+
beforeEach(() => {
6+
})
7+
describe('Create new chain with proxied prototypes', () => {
8+
class Superclass {}
9+
Superclass.prototype.meta = { Class: 'Superclass'}
10+
class Class extends Superclass {}
11+
Class.prototype.meta = { Class: 'Class'}
12+
class Subclass extends Class {}
13+
Subclass.prototype.meta = { Class: 'Subclass'}
14+
15+
let oldInstance = new Subclass()
16+
oldInstance.x = 'x'
17+
let newInstance = MultiplePrototypeChain.createUniqueProtoChain({ object: oldInstance })
18+
19+
it('Get properties - Preserve properties of original instance and original prototypes', () => {
20+
assert.strictEqual(newInstance.x, oldInstance.x)
21+
assert.strictEqual(newInstance.meta.Class, 'Subclass')
22+
assert.strictEqual(newInstance.__proto__.meta.Class, 'Subclass')
23+
assert.strictEqual(newInstance.__proto__.__proto__.meta.Class, 'Class')
24+
assert.strictEqual(newInstance.__proto__.__proto__.__proto__.meta.Class, 'Superclass')
25+
})
26+
27+
it('Set properties - Should be able to set properties through the proxy', () => {
28+
newInstance.t = 't'
29+
assert.strictEqual(newInstance.t, 't')
30+
assert.strictEqual(newInstance.delegatedPrototype.t, 't')
31+
})
32+
33+
it('create new prototypes with corresponding delegatedPrototypes values equal to original proto chain', () => {
34+
// Subclass
35+
assert.strictEqual(newInstance.__proto__.delegatedPrototype, oldInstance.__proto__)
36+
// Class
37+
assert.strictEqual(newInstance.__proto__.__proto__.delegatedPrototype, oldInstance.__proto__.__proto__)
38+
// Superclass
39+
assert.strictEqual(newInstance.__proto__.__proto__.__proto__.delegatedPrototype, oldInstance.__proto__.__proto__.__proto__)
40+
// Object or Function or their prototypes ...
41+
assert.strictEqual(newInstance.__proto__.__proto__.__proto__.__proto__, oldInstance.__proto__.__proto__.__proto__.__proto__)
42+
// null
43+
assert.strictEqual(newInstance.__proto__.__proto__.__proto__.__proto__.__proto__, oldInstance.__proto__.__proto__.__proto__.__proto__.__proto__)
44+
})
45+
46+
})
47+
describe('Insert object to prototypechain', () => {
48+
class Superclass {}
49+
Superclass.prototype.meta = { Class: 'Superclass'}
50+
class Class extends Superclass {}
51+
Class.prototype.meta = { Class: 'Class'}
52+
class Subclass extends Class {}
53+
Subclass.prototype.meta = { Class: 'Subclass'}
54+
55+
let oldInstance = new Subclass()
56+
// prepare for multiplechain pattern format.
57+
58+
oldInstance.__proto__ = { delegatedPrototype: Subclass.prototype }
59+
oldInstance.__proto__.__proto__ = { delegatedPrototype: Class.prototype }
60+
oldInstance.__proto__.__proto__.__proto__ = { delegatedPrototype: Superclass.prototype }
61+
oldInstance.__proto__.__proto__.__proto__.__proto__ = Superclass.prototype.__proto__
62+
63+
let objectToAdd = { x: 'x' }
64+
let newInstance = MultiplePrototypeChain.insertObjectToPrototypeChain({
65+
prototypeChain: oldInstance,
66+
objectToAdd: objectToAdd,
67+
beforePrototype: Superclass.prototype
68+
})
69+
70+
it('Object should be added as delegatedPrototype inside a pointerPrototype in specified place preserving previous chain prototypes', () => {
71+
assert.strictEqual(newInstance.__proto__.delegatedPrototype, Subclass.prototype)
72+
assert.strictEqual(newInstance.__proto__.__proto__.delegatedPrototype, Class.prototype)
73+
assert.strictEqual(newInstance.__proto__.__proto__.__proto__.delegatedPrototype, objectToAdd)
74+
assert.strictEqual(newInstance.__proto__.__proto__.__proto__.__proto__.delegatedPrototype, Superclass.prototype)
75+
assert.strictEqual(newInstance.__proto__.__proto__.__proto__.__proto__.__proto__, Superclass.prototype.__proto__)
76+
})
77+
})
78+
79+
80+
})
81+
82+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "multiplePrototypeChain",
3+
"version": "0.1.0",
4+
"main": "entrypoint.js"
5+
}

module/populateInstancePropertyFromJson.method/entrypoint.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
**/
66
function usingGenericInstance(instance, jsonData) {
77
if(jsonData) {
8-
Object.entries(jsonData).forEach(
9-
([key, value]) => instance[key] = value
10-
)
8+
Object.entries(jsonData).forEach(([key, value]) => {
9+
instance[key] = value
10+
})
1111
}
1212
instance.jsonData = jsonData // to keep track of populated instances.
1313
}

module/reusableNestedUnit/Controller.class.js

Lines changed: 18 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import createInstance from 'appscript/module/createInstance.staticMethod'
55
import { usingGenericInstance as populateInstancePropertyFromJson, usingThis as populateInstancePropertyFromJson_this } from 'appscript/module/populateInstancePropertyFromJson.method'
66
import addStaticSubclassToClassArray from 'appscript/module/addStaticSubclassToClassArray.staticMethod'
77
import prototypeChainDebug from 'appscript/module/prototypeChainDebug'
8+
import { MultiplePrototypeChain } from 'appscript/module/multiplePrototypeChain'
89
import assert from 'assert'
910

1011
let debugExecuted = false
@@ -48,68 +49,19 @@ module.exports = ({
4849
let self = this // specific Controller that is a subclass of 'self' (ReusableController)
4950
let contextInstance = new self(false, ...args)
5051

51-
// create proxied refrence of subclasses
52+
// create a new list object for proxied refrence of subclasses
5253
contextInstance.instanceExtendedSubclass = Object.keys(self.extendedSubclass.static)
5354
.reduce((object, key) => {
54-
// add proxy to the subclass
55-
object[key] = self.createPrototypeChainOnConstructor(self.extendedSubclass.static[key]);
55+
// add proxied subclass to the list
56+
object[key] = MultiplePrototypeChain.newChainOnInstanceCreation({
57+
Class: self.extendedSubclass.static[key],
58+
contextInstance
59+
})
5660
return object
5761
}, {})
5862
return contextInstance
5963
}
6064

61-
static createPrototypeChainOnConstructor(Class) {
62-
return new Proxy(Class, {
63-
construct: (target, argumentsList, newConstructorFunc) => {
64-
let instance = Reflect.construct(target, argumentsList)
65-
Object.setPrototypeOf(instance, self.createUniqueProtoChain(Object.getPrototypeOf(instance)))
66-
console.log(instance.__proto__.__proto__)
67-
return instance
68-
},
69-
// getPrototypeOf(target) {
70-
71-
// }
72-
})
73-
}
74-
75-
static createUniqueProtoChain(currentPrototype) {
76-
if( currentPrototype == null ||
77-
currentPrototype.constructor == Object ||
78-
currentPrototype.constructor == Function
79-
) return currentPrototype
80-
81-
let delegatedPrototype = Object.getPrototypeOf(currentPrototype)
82-
let pointerPrototype = Object.create(self.createUniqueProtoChain(delegatedPrototype))
83-
pointerPrototype = new Proxy(pointerPrototype, {
84-
get: function(target, property, receiver) {
85-
Object.defineProperty(pointerPrototype, 'delegatedPrototype', {
86-
value: currentPrototype,
87-
writable: true,
88-
enumerable: true,
89-
configurable: true
90-
})
91-
switch (property) {
92-
case 'delegatedPrototype':
93-
return currentPrototype
94-
break;
95-
case '__proto__':
96-
return Object.getPrototypeOf(pointerPrototype)
97-
break;
98-
default:
99-
break;
100-
}
101-
if(currentPrototype.hasOwnProperty(property)) {
102-
return Reflect.get(currentPrototype, property)
103-
} else if(Object.getPrototypeOf(target)) {
104-
return Reflect.get(Object.getPrototypeOf(target), property)
105-
} else {
106-
return undefined
107-
}
108-
}
109-
})
110-
return pointerPrototype
111-
}
112-
11365
static initializeStaticClass() {
11466
if(methodInstanceName && superclass && superclass.eventEmitter) {
11567
superclass.eventEmitter.on('initializationEnd', () => {
@@ -128,82 +80,32 @@ module.exports = ({
12880
return Reflect.construct(contextInstance.instanceExtendedSubclass[name], args)
12981
}
13082

131-
async getNestedUnit({ nestedUnitKey, controllerInstance = this, additionalChildNestedUnit = [], pathPointerKey = null}) {
83+
async getNestedUnit({ nestedUnitKey, additionalChildNestedUnit = [], pathPointerKey = null}) {
13284
let nestedUnitInstance;
133-
if(!(nestedUnitKey in controllerInstance.instance.nestedUnit)) {
134-
if(this.instanceExtendedSubclass) {
135-
nestedUnitInstance = await this.callSubclass('NestedUnit', [nestedUnitKey])
136-
if(!debugExecuted) {
137-
controllerInstance = this.__proto__
138-
// console.log(controllerInstance)
139-
// console.log(nestedUnitInstance.__proto__.__proto__.__proto__)
140-
controllerInstance.AppInstance = this.AppInstance
141-
controllerInstance.instance = this.instance
142-
assert.strictEqual(controllerInstance, nestedUnitInstance.__proto__.__proto__.__proto__)
143-
debugExecuted = true
144-
}
145-
nestedUnitInstance.__proto__.__proto__.__proto__ = controllerInstance
146-
} else {
147-
nestedUnitInstance = await self.callSubclass('NestedUnit', [nestedUnitKey])
148-
if(!debugExecuted) {
149-
controllerInstance = this.__proto__
150-
// console.log(controllerInstance)
151-
// console.log(nestedUnitInstance.__proto__.__proto__.__proto__)
152-
controllerInstance.AppInstance = this.AppInstance
153-
controllerInstance.instance = this.instance
154-
assert.strictEqual(controllerInstance, nestedUnitInstance.__proto__.__proto__.__proto__)
155-
debugExecuted = true
156-
}
157-
nestedUnitInstance.__proto__.__proto__.__proto__ = controllerInstance
158-
}
85+
if(!(nestedUnitKey in this.instance.nestedUnit)) {
86+
nestedUnitInstance = await this.callSubclass('NestedUnit', [nestedUnitKey])
15987
await nestedUnitInstance.initializeInstance()
88+
console.log(nestedUnitInstance)
16089
// add children trees:
16190
nestedUnitInstance.additionalChildNestedUnit = additionalChildNestedUnit
16291
// add pathPointerKey to allow applying additional corresponding additional children.
16392
nestedUnitInstance.pathPointerKey = pathPointerKey
16493
// add to class cache
165-
controllerInstance.instance.nestedUnit[nestedUnitKey] = nestedUnitInstance
94+
this.instance.nestedUnit[nestedUnitKey] = nestedUnitInstance
16695
} else {
167-
nestedUnitInstance = controllerInstance.instance.nestedUnit[nestedUnitKey]
96+
nestedUnitInstance = this.instance.nestedUnit[nestedUnitKey]
16897
}
16998
return nestedUnitInstance
17099
}
171100

172-
async getUnit({unitKey, controllerInstance = this}) {
101+
async getUnit({unitKey}) {
173102
let unitInstance;
174-
if(!(unitKey in controllerInstance.instance.unit)) {
175-
if(this.instanceExtendedSubclass) {
176-
unitInstance = await this.callSubclass('Unit', [unitKey])
177-
if(!debugEx2) {
178-
controllerInstance = this.__proto__
179-
// console.log(controllerInstance)
180-
// console.log(nestedUnitInstance.__proto__.__proto__.__proto__)
181-
controllerInstance.AppInstance = this.AppInstance
182-
controllerInstance.instance = this.instance
183-
assert.strictEqual(controllerInstance, unitInstance.__proto__.__proto__.__proto__)
184-
debugEx2 = true
185-
}
186-
// assert.strictEqual(unitInstance.__proto__.__proto__.__proto__, controllerInstance)
187-
unitInstance.__proto__.__proto__.__proto__ = controllerInstance
188-
} else {
189-
unitInstance = await self.callSubclass('Unit', [unitKey])
190-
if(!debugEx2) {
191-
controllerInstance = this.__proto__
192-
// console.log(controllerInstance)
193-
// console.log(nestedUnitInstance.__proto__.__proto__.__proto__)
194-
controllerInstance.AppInstance = this.AppInstance
195-
controllerInstance.instance = this.instance
196-
assert.strictEqual(controllerInstance, unitInstance.__proto__.__proto__.__proto__)
197-
debugEx2 = true
198-
}
199-
// assert.strictEqual(unitInstance.__proto__.__proto__.__proto__, controllerInstance)
200-
unitInstance.__proto__.__proto__.__proto__ = controllerInstance
201-
}
202-
// console.log(unitInstance)
103+
if(!(unitKey in this.instance.unit)) {
104+
unitInstance = await this.callSubclass('Unit', [unitKey])
203105
await unitInstance.initializeInstance()
204-
controllerInstance.instance.unit[unitKey] = unitInstance
106+
this.instance.unit[unitKey] = unitInstance
205107
} else {
206-
unitInstance = controllerInstance.instance.unit[unitKey]
108+
unitInstance = this.instance.unit[unitKey]
207109
}
208110
return unitInstance
209111
}

0 commit comments

Comments
 (0)