Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into box-all-the-things
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed Jun 10, 2017
2 parents e6c1796 + e5f213c commit 2a3b309
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 18 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ store.removeTodo(0)
```
Despite all that, you will see that the [API](api.md) is pretty straight forward!
Despite all that, you will see that the [API](API.md) is pretty straight forward!
---
Expand Down Expand Up @@ -191,7 +191,7 @@ An example:
```javascript
const TodoStore = types.model("TodoStore", { // 1
loaded: type.boolean // 2
loaded: types.boolean // 2
endpoint: "http://localhost", // 3
todos: types.array(Todo), // 4
selectedTodo: types.reference(Todo, "todos"), // 5
Expand All @@ -211,7 +211,7 @@ const TodoStore = types.model("TodoStore", { // 1
})
```
When defining a model, it is adviced to give the model a name for debugging purposes (see `// 1`).
When defining a model, it is advised to give the model a name for debugging purposes (see `// 1`).
A model takes two objects arguments, first all the properties, then the actions.

The _properties_ argument is a key-value set where each key indicates the introduction of a property, and the value it's type. The following types are acceptable as type:
Expand Down Expand Up @@ -405,7 +405,7 @@ The above example: `selectedTodo: types.reference(Todo, "todos")` is namespaced,
Generic references can point to any element of the correct type in the current tree, and are stored behind the scenes as JSON path. The above example could also have been configured as `selectedTodo: types.reference(Todo)` to create a generic reference.
_Tip: It is recommended to use namespaced references; as those are more stable by since they always use immutable references and a preconfigured namespace._
_Tip: It is recommended to use namespaced references; as those are more stable, since they always use immutable references and a preconfigured namespace._
**Note: The exact semantics of references are still under investigation, and might change before MST 1.0. One of the two forms might be dropped_**
Expand Down
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.6.3

Fixed issue with array/maps of union types @abruzzihraig
Make types.extend support computed attributes @cpunion
Fixed issue with map of primitive types and applySnapshot @pioh
Better type declarations for union, up to 10 supported types

# 0.6.2

Fixed issue where arrays where not properly serialized as action argument
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mobx-state-tree",
"version": "0.6.2",
"version": "0.6.3",
"description": "Opinionated, transactional, MobX powered state container",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion src/types/complex-types/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class MapType<S, T> extends ComplexType<{[key: string]: S}, IExtendedObse
super(name)
this.subType = subType
}

instantiate(parent: Node | null, subpath: string, environment: any, snapshot: S): Node {
return createNode(this, parent, subpath, environment, snapshot, this.createNewInstance, this.finalizeNewInstance)
}
Expand Down
17 changes: 11 additions & 6 deletions src/types/complex-types/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "mobx"
import {
extend as extendObject,
extendKeepGetter,
fail,
isPrimitive,
hasOwnProperty,
Expand Down Expand Up @@ -61,7 +62,7 @@ export class ObjectType extends ComplexType<any, any> {
this.parseModelProps()
this.forAllProps(prop => prop.initializePrototype(this.modelConstructor.prototype))
}

instantiate(parent: Node | null, subpath: string, environment: any, snapshot: any): Node {
return createNode(this, parent, subpath, environment, snapshot, this.createNewInstance, this.finalizeNewInstance)
}
Expand Down Expand Up @@ -254,6 +255,12 @@ function getObjectFactoryBaseModel(item: any) {
return isObjectFactory(type) ? (type as ObjectType).baseModel : {}
}

function getObjectFactoryBaseActions(item: any) {
let type = isType(item) ? item : getType(item)

return isObjectFactory(type) ? (type as ObjectType).baseActions : {}
}

export function extend<A, B, AA, BA>(name: string, a: IModelType<A, AA>, b: IModelType<B, BA>): IModelType<A & B, AA & BA>
export function extend<A, B, C, AA, BA, CA>(name: string, a: IModelType<A, AA>, b: IModelType<B, BA>, c: IModelType<C, CA>): IModelType<A & B & C, AA & BA & CA>
export function extend<A, B, AA, BA>(a: IModelType<A, AA>, b: IModelType<B, BA>): IModelType<A & B, AA & BA>
Expand All @@ -262,11 +269,9 @@ export function extend(...args: any[]) {
console.warn("[mobx-state-tree] `extend` is an experimental feature and it's behavior will probably change in the future")
const baseFactories = typeof args[0] === "string" ? args.slice(1) : args
const factoryName = typeof args[0] === "string" ? args[0] : baseFactories.map(f => f.name).join("_")

return model(
factoryName,
extendObject.apply(null, [{}].concat(baseFactories.map(getObjectFactoryBaseModel)))
)
const properties = extendKeepGetter.apply(null, [{}].concat(baseFactories.map(getObjectFactoryBaseModel)))
const actions = extendObject.apply(null, [{}].concat(baseFactories.map(getObjectFactoryBaseActions)))
return model(factoryName, properties, actions)
}

export function isObjectFactory(type: any): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/types/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export abstract class Type<S, T> extends ComplexType<S, T> implements IType<S, T
}
}

import { EMPTY_ARRAY, fail, addReadOnlyProp, addHiddenFinalProp, isMutable } from "../utils"
import { EMPTY_ARRAY, fail, isMutable } from "../utils"
import { isStateTreeNode, getStateTreeNode } from "../core/node"
import { IContext, IValidationResult, typecheck, typeCheckFailure, typeCheckSuccess } from "./type-checker"
import { Node, IComplexValue, IJsonPatch } from "../core"
Expand Down
19 changes: 19 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ export function extend(a: any, ...b: any[]) {
return a
}

export function extendKeepGetter<A, B>(a: A, b: B): A & B
export function extendKeepGetter<A, B, C>(a: A, b: B, c: C): A & B & C
export function extendKeepGetter<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D
export function extendKeepGetter(a: any, ...b: any[]): any
export function extendKeepGetter(a: any, ...b: any[]) {
for (let i = 0; i < b.length; i++) {
const current = b[i]
for (let key in current) {
const descriptor = Object.getOwnPropertyDescriptor(current, key)
if ("get" in descriptor) {
Object.defineProperty(a, key, descriptor)
continue
}
a[key] = current[key]
}
}
return a
}

export function isPlainObject(value: any) {
if (value === null || typeof value !== "object")
return false
Expand Down
23 changes: 22 additions & 1 deletion test/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ const createTestFactories = () => {
ItemFactory
)

return {Factory, ItemFactory}
const PrimitiveMapFactory = types.model({
boolean: types.map(types.boolean),
string: types.map(types.string),
number: types.map(types.number)
}, {})

return {Factory, ItemFactory, PrimitiveMapFactory}
}

// === FACTORY TESTS ===
Expand Down Expand Up @@ -74,6 +80,21 @@ test("it should return a snapshot", (t) => {
t.deepEqual<any>(getSnapshot(doc), {hello: {to: "world"}})
})

test("it should be the same each time", (t) => {
const {PrimitiveMapFactory} = createTestFactories()
const data = {
string: {a: "a", b: ""},
boolean: {a: true, b: false},
number: {a: 0, b: 42, c: NaN}
}
const doc = PrimitiveMapFactory.create(data)
t.deepEqual<any>(getSnapshot(doc), data)
applySnapshot(doc, data)
t.deepEqual<any>(getSnapshot(doc), data)
applySnapshot(doc, data)
t.deepEqual<any>(getSnapshot(doc), data)
})

// === PATCHES TESTS ===
test("it should emit add patches", (t) => {
const {Factory, ItemFactory} = createTestFactories()
Expand Down
40 changes: 39 additions & 1 deletion test/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ const createTestFactories = () => {
}
})

const ComputedFactory2 = types.model({
props: types.map(types.number),
get area(){
return this.props.get('width') * this.props.get('height')
}
}, {
setWidth(value) {
this.props.set('width', value)
},
setHeight(value) {
this.props.set('height', value)
}
})

const BoxFactory = types.model({
width: 0,
height: 0
Expand All @@ -38,7 +52,7 @@ const createTestFactories = () => {
color: "#FFFFFF"
})

return {Factory, ComputedFactory, BoxFactory, ColorFactory}
return {Factory, ComputedFactory, ComputedFactory2, BoxFactory, ColorFactory}
}

// === FACTORY TESTS ===
Expand Down Expand Up @@ -79,6 +93,20 @@ test("it should apply snapshots", (t) => {
t.deepEqual(getSnapshot(doc), {to: 'universe'})
})

test("it should apply and accept null value for types.maybe(complexType)", (t) => {
const Item = types.model({
value: types.string
})
const Model = types.model({
item: types.maybe(Item)
})
const myModel = Model.create()
applySnapshot(myModel, {item: {value: "something"}})
applySnapshot(myModel, {item: null})

t.deepEqual(getSnapshot(myModel), {item: null})
})

test("it should return a snapshot", (t) => {
const {Factory} = createTestFactories()
const doc = Factory.create()
Expand Down Expand Up @@ -251,6 +279,16 @@ test("it should compose factories", (t) => {
t.deepEqual(getSnapshot(ComposedFactory.create()), {width: 0, height: 0, color: "#FFFFFF"})
})

test("it should compose factories with computed properties", (t) => {
const {ComputedFactory2, ColorFactory} = createTestFactories()
const ComposedFactory = types.extend(ColorFactory, ComputedFactory2)
const store = ComposedFactory.create({props: {width: 100, height: 200}})
t.deepEqual(getSnapshot(store), {props: {width: 100, height: 200}, color: "#FFFFFF"})
t.is(store.area, 20000)
t.is(typeof store.setWidth, 'function')
t.is(typeof store.setHeight, 'function')
})


// === TYPE CHECKS ===
test("it should check the type correctly", (t) => {
Expand Down
21 changes: 18 additions & 3 deletions test/union.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {types} from "../src"
import {types, hasParent, tryResolve} from "../src"
import {test} from "ava"

const createTestFactories = () => {
Expand All @@ -22,7 +22,11 @@ const createTestFactories = () => {

const DispatchPlane = types.union(snapshot => snapshot && "height" in snapshot ? Box : Square, Box, Square)

return {Box, Square, Cube, Plane, DispatchPlane, Heighed}
const Block = types.model("Block", {
list: types.array(Heighed)
})

return {Box, Square, Cube, Plane, DispatchPlane, Heighed, Block}
}

test("it should complain about no dispatch method", (t) => {
Expand All @@ -34,6 +38,17 @@ test("it should complain about no dispatch method", (t) => {
snapshot \`{"width":2,"height":2}\` is not assignable to type: \`Box | Square\` (Multiple types are applicable and no dispatch method is defined for the union), expected an instance of \`Box | Square\` or a snapshot like \`({ width: number; height: number } | { width: number })\` instead.`)
})

test("it should have parent whenever creating or applying from a complex data structure to a model which has Union typed children", (t) => {
const {Block, Heighed} = createTestFactories()
const block = Block.create({
list: [{width: 2, height: 2}]
})

const child = tryResolve(block, "./list/0")

hasParent(child) ? t.pass() : t.fail()
})

test("it should complain about no dispatch method and multiple applicable types", (t) => {
const {Heighed} = createTestFactories()

Expand Down Expand Up @@ -90,4 +105,4 @@ test("it should compute exact union types - 2", (t) => {

t.deepEqual(DispatchPlane.is(Box.create({ width: 3, height: 2})), true)
t.deepEqual(DispatchPlane.is(Square.create({ width: 3, height: 2} as any)), true)
})
})

0 comments on commit 2a3b309

Please sign in to comment.