-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into greenkeeper/nyc-13.2.0
- Loading branch information
Showing
13 changed files
with
416 additions
and
237 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,96 +1,10 @@ | ||
import { foldl } from 'funcadelic'; | ||
import { promap, valueOf, pathOf, Meta, mount } from './meta'; | ||
import { methodsOf } from './reflection'; | ||
import { isArrayType } from './types/array'; | ||
import { create } from './microstates'; | ||
import { valueOf } from './meta'; | ||
|
||
//function composition should probably not be part of lens :) | ||
import { At, view, Path, compose, set } from './lens'; | ||
|
||
class Location { | ||
static id = At(Symbol('@id')); | ||
} | ||
import Storage from './storage'; | ||
import Pathmap from './pathmap'; | ||
|
||
export default function Identity(microstate, observe = x => x) { | ||
let current; | ||
let identity; | ||
let pathmap = {}; | ||
|
||
function tick(next) { | ||
update(next); | ||
observe(identity); | ||
return identity; | ||
} | ||
|
||
function update(microstate) { | ||
current = microstate; | ||
return identity = promap(proxify, persist, microstate); | ||
|
||
function proxify(microstate) { | ||
let path = pathOf(microstate); | ||
let Type = microstate.constructor.Type; | ||
let value = valueOf(microstate); | ||
|
||
let id = view(compose(Path(path), Location.id), pathmap); | ||
|
||
let Id = id != null && id.constructor.Type === Type ? id.constructor : IdType(Type, path); | ||
return new Id(value); | ||
} | ||
|
||
function persist(id) { | ||
let location = compose(Path(id.constructor.path), Location.id); | ||
let existing = view(location, pathmap); | ||
if (!equals(id, existing)) { | ||
pathmap = set(location, id, pathmap); | ||
return id; | ||
} else { | ||
return existing; | ||
} | ||
} | ||
} | ||
|
||
function IdType(Type, P) { | ||
class Id extends Type { | ||
static Type = Type; | ||
static path = P; | ||
static name = `Id<${Type.name}>`; | ||
|
||
constructor(value) { | ||
super(value); | ||
Object.defineProperty(this, Meta.symbol, { enumerable: false, configurable: true, value: new Meta(this, valueOf(value))}); | ||
} | ||
} | ||
|
||
let methods = Object.keys(methodsOf(Type)).concat(["set"]); | ||
|
||
Object.assign(Id.prototype, foldl((methods, name) => { | ||
methods[name] = function(...args) { | ||
let path = P; | ||
let microstate = path.reduce((microstate, key) => { | ||
if (isArrayType(microstate)) { | ||
let value = valueOf(microstate)[key]; | ||
return mount(microstate, create(microstate.constructor.T, value), key); | ||
} else { | ||
return microstate[key]; | ||
} | ||
}, current); | ||
let next = microstate[name](...args); | ||
|
||
return next === current ? identity : tick(next); | ||
}; | ||
return methods; | ||
}, {}, methods)); | ||
|
||
return Id; | ||
} | ||
update(microstate); | ||
return identity; | ||
} | ||
|
||
function equals(id, other) { | ||
if (other == null) { | ||
return false; | ||
} else { | ||
return other.constructor.Type === id.constructor.Type && valueOf(id) === valueOf(other); | ||
} | ||
let { Type } = microstate.constructor; | ||
let pathmap = Pathmap(Type, new Storage(valueOf(microstate), () => observe(pathmap.get()))); | ||
return pathmap.get(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { methodsOf } from './reflection'; | ||
import { create } from './microstates'; | ||
import { view, Path } from './lens'; | ||
import { valueOf, Meta } from './meta'; | ||
import { defineChildren } from './tree'; | ||
import { stable } from 'funcadelic'; | ||
|
||
import Storage from './storage'; | ||
// TODO: explore compacting non-existent locations (from removed arrays and objects). | ||
|
||
export default function Pathmap(Root, ref) { | ||
let paths = new Storage(); | ||
|
||
class Location { | ||
|
||
static symbol = Symbol('Location'); | ||
|
||
static allocate(path) { | ||
let existing = paths.getPath(path.concat(Location.symbol)); | ||
if (existing) { | ||
return existing; | ||
} else { | ||
let location = new Location(path); | ||
paths.setPath(path, { [Location.symbol]: location }); | ||
return location; | ||
} | ||
} | ||
|
||
get currentValue() { | ||
return view(this.lens, ref.get()); | ||
} | ||
|
||
get reference() { | ||
if (!this.currentReference || (this.currentValue !== valueOf(this.currentReference))) { | ||
return this.currentReference = this.createReference(); | ||
} else { | ||
return this.currentReference; | ||
} | ||
} | ||
|
||
get microstate() { | ||
return view(this.lens, create(Root, ref.get())); | ||
} | ||
|
||
constructor(path) { | ||
this.path = path; | ||
this.lens = Path(path); | ||
this.createReferenceType = stable(Type => { | ||
let location = this; | ||
let typeName = Type.name ? Type.name: 'Unknown'; | ||
|
||
class Reference extends Type { | ||
static name = `Ref<${typeName}>`; | ||
static location = location; | ||
|
||
constructor(value) { | ||
super(value); | ||
Object.defineProperty(this, Meta.symbol, { enumerable: false, configurable: true, value: new Meta(this, valueOf(value))}); | ||
defineChildren(key => Location.allocate(path.concat(key)).reference, this); | ||
} | ||
} | ||
|
||
for (let methodName of Object.keys(methodsOf(Type)).concat("set")) { | ||
Reference.prototype[methodName] = (...args) => { | ||
let microstate = location.microstate; | ||
let next = microstate[methodName](...args); | ||
ref.set(valueOf(next)); | ||
return location.reference; | ||
}; | ||
} | ||
|
||
return Reference; | ||
}); | ||
} | ||
|
||
createReference() { | ||
let { Type } = this.microstate.constructor; | ||
let Reference = this.createReferenceType(Type); | ||
|
||
return new Reference(this.currentValue); | ||
} | ||
|
||
get(reference = paths.getPath([Location.symbol, 'reference'])) { | ||
return reference.constructor.location.reference; | ||
} | ||
} | ||
|
||
return Location.allocate([]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { view, set, Path } from './lens'; | ||
|
||
export default class Storage { | ||
constructor(value, observe = x => x) { | ||
this.value = value; | ||
this.observe = observe; | ||
} | ||
|
||
get() { | ||
return this.value; | ||
} | ||
|
||
set(value) { | ||
if (value !== this.value) { | ||
this.value = value; | ||
this.observe(); | ||
} | ||
return this; | ||
} | ||
|
||
getPath(path) { | ||
return view(Path(path), this.value); | ||
} | ||
|
||
setPath(path, value) { | ||
return this.set(set(Path(path), value, this.value)); | ||
} | ||
} |
Oops, something went wrong.