Skip to content

Commit

Permalink
eliminated reference node
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed May 30, 2017
1 parent bec100e commit 0f6dcdf
Show file tree
Hide file tree
Showing 15 changed files with 113 additions and 135 deletions.
4 changes: 2 additions & 2 deletions src/core/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function createActionInvoker(name: string, fn: Function) {
// outer action, run middlewares and start the action!
const call: IRawActionCall = {
name,
object: adm.target,
object: adm.storedValue,
args: argsToArray(arguments)
}
const root = adm.root
Expand Down Expand Up @@ -107,7 +107,7 @@ function deserializeArgument(adm: ComplexNode, value: any): any {
if (typeof value === "object") {
const keys = Object.keys(value)
if (keys.length === 1 && keys[0] === "$ref")
return resolve(adm.target, value.$ref)
return resolve(adm.storedValue, value.$ref)
}
return value
}
Expand Down
1 change: 0 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export * from "./nodes/abstract-node"
export * from "./nodes/complex-node"
export * from "./nodes/immutable-node"
export * from "./nodes/reference-node"
export * from "./action"
export * from "./json-patch"
export * from "./mst-operations"
6 changes: 3 additions & 3 deletions src/core/mst-operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export function getParent<T>(target: IComplexValue, depth = 1): (T & IComplexVal
let parent: ComplexNode | null = getComplexNode(target).parent
while (parent) {
if (--d === 0)
return parent.target
return parent.storedValue
parent = parent.parent
}
return fail(`Failed to find the parent of ${getComplexNode(target)} at depth ${depth}`)
Expand All @@ -291,7 +291,7 @@ export function getParent<T>(target: IComplexValue, depth = 1): (T & IComplexVal
export function getRoot(target: IComplexValue): any & IComplexValue;
export function getRoot<T>(target: IComplexValue): T & IComplexValue;
export function getRoot(target: IComplexValue): IComplexValue {
return getComplexNode(target).root.target
return getComplexNode(target).root.storedValue
}

/**
Expand Down Expand Up @@ -427,7 +427,7 @@ export function walk(thing: IComplexValue, processor: (item: IComplexValue) => v
if (!child.isLeaf())
walk(child, processor)
})
processor(node.target)
processor(node.storedValue)
}

// TODO: remove
Expand Down
9 changes: 7 additions & 2 deletions src/core/nodes/abstract-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ let nextNodeId = 1
export abstract class AbstractNode {
readonly nodeId = ++nextNodeId
readonly type: IType<any, any>
readonly storedValue: any
@observable protected _parent: ComplexNode | null = null
@observable subpath: string = ""

// TODO: should have environment as well?
constructor(type: IType<any, any>, parent: ComplexNode | null, subpath: string) {
constructor(type: IType<any, any>, parent: ComplexNode | null, subpath: string, storedValue: any) {
this.type = type
this._parent = parent
this.subpath = subpath
this.storedValue = storedValue
}

/**
Expand Down Expand Up @@ -95,12 +97,15 @@ export abstract class AbstractNode {
return current!
}

getValue(): any {
return this.type.readValue(this.storedValue)
}

toString(): string {
return `${this.type.name}@${this.path || "<root>"}:${this.snapshot}`
}

abstract get snapshot(): any;
abstract getValue(): any
abstract isLeaf(): boolean
abstract getChildren(): AbstractNode[]
abstract getChildNode(name: string): AbstractNode | null
Expand Down
22 changes: 9 additions & 13 deletions src/core/nodes/complex-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { IType } from "../../types/type"

export class ComplexNode extends AbstractNode {
type: ComplexType<any, any>
readonly target: any
readonly storedValue: any
isProtectionEnabled = true
_environment: any = undefined
_isRunningAction = false // only relevant for root
Expand All @@ -21,10 +21,9 @@ export class ComplexNode extends AbstractNode {

// TODO: reorder argumetns
constructor(parent: ComplexNode | null, subpath: string, initialState: any, type: ComplexType<any, any>, environment: any) {
super(type, parent, subpath)
super(type, parent, subpath, initialState)
if (!(type instanceof ComplexType)) fail("Uh oh")
addHiddenFinalProp(initialState, "$treenode", this)
this.target = initialState
this._environment = environment

// optimization: don't keep the snapshot by default alive with a reaction by default
Expand All @@ -40,10 +39,6 @@ export class ComplexNode extends AbstractNode {
this.addDisposer(snapshotDisposer)
}

getValue() {
return this.target
}

isLeaf() {
return false
}
Expand All @@ -56,8 +51,8 @@ export class ComplexNode extends AbstractNode {
if (this._isDetaching)
return

walk(this.target, child => getComplexNode(child).aboutToDie())
walk(this.target, child => getComplexNode(child).finalizeDeath())
walk(this.storedValue, child => getComplexNode(child).aboutToDie())
walk(this.storedValue, child => getComplexNode(child).finalizeDeath())
}

public aboutToDie() {
Expand All @@ -80,7 +75,7 @@ export class ComplexNode extends AbstractNode {

// This is quite a hack, once interceptable objects / arrays / maps are extracted from mobx,
// we could express this in a much nicer way
Object.defineProperty(this.target, "$mobx", {
Object.defineProperty(this.storedValue, "$mobx", {
get() {
fail(`This object has died and is no longer part of a state tree. It cannot be used anymore. The object (of type '${self.type.name}') used to live at '${oldPath}'. It is possible to access the last snapshot of this object using 'getSnapshot', or to create a fresh copy using 'clone'. If you want to remove an object from the tree without killing it, use 'detach' instead.`)
}
Expand Down Expand Up @@ -164,6 +159,7 @@ export class ComplexNode extends AbstractNode {
}

reconcileChildren<T>(childType: IType<any, T>, oldNodes: AbstractNode[], newValues: T[], newPaths: (string|number)[]): T[] {
// TODO: pick identifiers based on actual type instead of declared type
// optimization: overload for a single old / new value to avoid all the array allocations
// optimization: skip reconciler for non-complex types
const res = new Array(newValues.length)
Expand Down Expand Up @@ -306,14 +302,14 @@ export class ComplexNode extends AbstractNode {
}

fireHook(name: string) {
const fn = this.target[name]
const fn = this.storedValue[name]
if (typeof fn === "function")
fn.apply(this.target)
fn.apply(this.storedValue)
}

toString(): string {
const identifierAttr = getIdentifierAttribute(this.type)
const identifier = identifierAttr ? `(${identifierAttr}: ${this.target[identifierAttr]})` : ""
const identifier = identifierAttr ? `(${identifierAttr}: ${this.storedValue[identifierAttr]})` : ""
return `${this.type.name}@${this.path || "<root>"}${identifier}${this.isAlive ? "" : "[dead]"}`
}
}
Expand Down
10 changes: 3 additions & 7 deletions src/core/nodes/immutable-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ export class ImmutableNode extends AbstractNode {
type: IType<any, any>,
parent: ComplexNode | null,
subpath: string,
protected readonly value: any
value: any
) {
super(type, parent, subpath)
}

getValue() {
return this.value
super(type, parent, subpath, value)
}

isLeaf() {
Expand All @@ -30,7 +26,7 @@ export class ImmutableNode extends AbstractNode {
}

get snapshot() {
return this.value
return this.type.toSnapshot(this.storedValue)
}

setParent(newParent: ComplexNode, subpath: string): void {
Expand Down
85 changes: 0 additions & 85 deletions src/core/nodes/reference-node.ts

This file was deleted.

12 changes: 6 additions & 6 deletions src/types/complex-types/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ export class ArrayType<S, T> extends ComplexType<S[], IObservableArray<T>> {
}

getChildren(node: ComplexNode): AbstractNode[] {
return node.target.$mobx.values // Shooting ourselves in the foot. JS, why do you so temptingly allow this?!
return node.storedValue.$mobx.values // Shooting ourselves in the foot. JS, why do you so temptingly allow this?!
}

getChildNode(node: ComplexNode, key: string): AbstractNode {
const index = parseInt(key, 10)
if (index < node.target.length)
return node.target.$mobx.values[index]
if (index < node.storedValue.length)
return node.storedValue.$mobx.values[index]
return fail("Not a child: " + key)
}

Expand Down Expand Up @@ -123,7 +123,7 @@ export class ArrayType<S, T> extends ComplexType<S[], IObservableArray<T>> {
}

applyPatchLocally(node: ComplexNode, subpath: string, patch: IJsonPatch): void {
const target = node.target as IObservableArray<any>
const target = node.storedValue as IObservableArray<any>
const index = subpath === "-" ? target.length : parseInt(subpath)
switch (patch.op) {
case "replace":
Expand All @@ -140,7 +140,7 @@ export class ArrayType<S, T> extends ComplexType<S[], IObservableArray<T>> {

@action applySnapshot(node: ComplexNode, snapshot: any[]): void {
node.pseudoAction(() => {
const target = node.target as IObservableArray<any>
const target = node.storedValue as IObservableArray<any>
target.replace(snapshot)
})
}
Expand All @@ -166,7 +166,7 @@ export class ArrayType<S, T> extends ComplexType<S[], IObservableArray<T>> {
}

removeChild(node: ComplexNode, subpath: string) {
node.target.splice(parseInt(subpath, 10), 1)
node.storedValue.splice(parseInt(subpath, 10), 1)
}

get identifierAttribute() {
Expand Down
12 changes: 6 additions & 6 deletions src/types/complex-types/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ export class MapType<S, T> extends ComplexType<{[key: string]: S}, IExtendedObse
getChildren(node: ComplexNode): AbstractNode[] {
const res: AbstractNode[] = []
// Ignore all alarm bells to be able to read this:...
Object.keys(node.target.$mobx.values).forEach(key => {
res.push(node.target.$mobx.values[key].value)
Object.keys(node.storedValue.$mobx.values).forEach(key => {
res.push(node.storedValue.$mobx.values[key].value)
})
return res
}

getChildNode(node: ComplexNode, key: string): AbstractNode {
const childNode = node.target.$mobx.values[key]
const childNode = node.storedValue.$mobx.values[key]
if (!childNode)
fail("Not a child" + key)
return childNode
Expand Down Expand Up @@ -132,7 +132,7 @@ export class MapType<S, T> extends ComplexType<{[key: string]: S}, IExtendedObse
}

applyPatchLocally(node: ComplexNode, subpath: string, patch: IJsonPatch): void {
const target = node.target as ObservableMap<any>
const target = node.storedValue as ObservableMap<any>
switch (patch.op) {
case "add":
case "replace":
Expand All @@ -146,7 +146,7 @@ export class MapType<S, T> extends ComplexType<{[key: string]: S}, IExtendedObse

@action applySnapshot(node: ComplexNode, snapshot: any): void {
node.pseudoAction(() => {
const target = node.target as ObservableMap<any>
const target = node.storedValue as ObservableMap<any>
target.replace(snapshot)
// const identifierAttr = getIdentifierAttribute(this.subType)
// // Try to update snapshot smartly, by reusing instances under the same key as much as possible
Expand Down Expand Up @@ -201,7 +201,7 @@ export class MapType<S, T> extends ComplexType<{[key: string]: S}, IExtendedObse
}

removeChild(node: ComplexNode, subpath: string) {
(node.target as ObservableMap<any>).delete(subpath)
(node.storedValue as ObservableMap<any>).delete(subpath)
}

get identifierAttribute() {
Expand Down

0 comments on commit 0f6dcdf

Please sign in to comment.