mobx-decorated-models
Advanced tools
Comparing version 0.1.2 to 0.2.0
@@ -105,3 +105,12 @@ (function (global, factory) { | ||
belongsTo: function belongsTo(model) { | ||
return serializr.object(serializr.getDefaultModelSchema(model)); | ||
var defaultSerializer = serializr.object(serializr.getDefaultModelSchema(model)); | ||
return { | ||
deserializer: defaultSerializer.deserializer, | ||
serializer: function serializer(belongsTo, name, parent) { | ||
if (parent.$nonSerializable && -1 !== parent.$nonSerializable.indexOf(name)) { | ||
return undefined; | ||
} | ||
return defaultSerializer.serializer(belongsTo); | ||
} | ||
}; | ||
} | ||
@@ -126,3 +135,3 @@ }; | ||
var classDecorator = function (model) { | ||
function modelDecorator(model) { | ||
Object.assign(model.prototype, MixedInInstanceMethods); | ||
@@ -172,3 +181,3 @@ Object.assign(model, MixedInClassMethods); | ||
} | ||
}; | ||
} | ||
@@ -245,2 +254,54 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
var setupModel = mobx.action(function (attrs, modelClass, array, defaultAttributes, inverseOf, parentModel) { | ||
if (defaultAttributes) { | ||
if (typeof defaultAttributes === 'function') { | ||
defaultAttributes = defaultAttributes.call(parentModel, array, parentModel); | ||
} | ||
Object.keys(defaultAttributes).forEach(function (key) { | ||
if (!attrs[key]) { | ||
attrs[key] = defaultAttributes[key]; | ||
} | ||
}); | ||
} | ||
if (inverseOf) { | ||
attrs[inverseOf] = parentModel; | ||
} | ||
var model = modelClass && !(attrs instanceof modelClass) ? new modelClass(attrs) : attrs; | ||
model.$nonSerializable = model.$nonSerializable || []; | ||
model.$nonSerializable.push(inverseOf); | ||
return model; | ||
}); | ||
function buildInterceptor(_ref, parentModel) { | ||
var modelClass = _ref.modelClass, | ||
className = _ref.className, | ||
defaultAttributes = _ref.defaults, | ||
inverseOf = _ref.inverseOf; | ||
return function (change) { | ||
if (!change.newValue) { | ||
change.newValue = {}; | ||
} | ||
if (!modelClass) { | ||
modelClass = findModel(className); | ||
} | ||
if (change.type === 'splice') { | ||
for (var i = 0; i < change.added.length; i += 1) { | ||
change.added[i] = setupModel(change.added[i], modelClass, change.object, defaultAttributes, inverseOf, parentModel); | ||
} | ||
} else if (change.type === 'update') { | ||
change.newValue = setupModel(change.newValue, modelClass, change.object, defaultAttributes, inverseOf, parentModel); | ||
} | ||
return change; | ||
}; | ||
} | ||
function buildCollection(options, parentModel) { | ||
var ary = mobx.observable.array([]); | ||
if (options.className || options.modelClass) { | ||
ary.intercept(buildInterceptor(options, parentModel)); | ||
} | ||
return ary; | ||
} | ||
var Initializers = { | ||
@@ -250,5 +311,3 @@ object: function object$$1() { | ||
}, | ||
array: function array() { | ||
return mobx.observable.array([]); | ||
} | ||
array: buildCollection | ||
}; | ||
@@ -261,3 +320,10 @@ | ||
function getInitializer(type, options) { | ||
return TypeInitializers[type] || Initializers[options.type]; | ||
var fn = TypeInitializers[type] || Initializers[options.type]; | ||
if (!fn) { | ||
return undefined; | ||
} | ||
return function () { | ||
// eslint-disable-line func-names | ||
return fn(options, this); | ||
}; | ||
} | ||
@@ -273,3 +339,4 @@ | ||
} | ||
return mobx.observable(target, property, descriptor); | ||
var definition = mobx.observable(target, property, descriptor); | ||
return definition; | ||
} | ||
@@ -320,2 +387,3 @@ | ||
}; | ||
var hasMany = function hasMany() { | ||
@@ -329,4 +397,7 @@ for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { | ||
exports.model = classDecorator; | ||
exports.identifier = identifier$1; | ||
exports.registerModel = registerModel; | ||
exports.findModel = findModel; | ||
exports.rememberModelUsing = rememberModelUsing; | ||
exports.lookupModelUsing = lookupModelUsing; | ||
exports.modelDecorator = modelDecorator; | ||
exports.field = field; | ||
@@ -336,4 +407,4 @@ exports.session = session; | ||
exports.hasMany = hasMany; | ||
exports.lookupModelUsing = lookupModelUsing; | ||
exports.rememberModelUsing = rememberModelUsing; | ||
exports.identifier = identifier$1; | ||
exports.buildCollection = buildCollection; | ||
@@ -340,0 +411,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
import { createModelSchema, deserialize, getDefaultModelSchema, identifier, list, map, object, primitive, serialize, update } from 'serializr'; | ||
import { observable } from 'mobx'; | ||
import { action, observable } from 'mobx'; | ||
@@ -102,3 +102,12 @@ var ModelsMap = new Map(); | ||
belongsTo: function belongsTo(model) { | ||
return object(getDefaultModelSchema(model)); | ||
var defaultSerializer = object(getDefaultModelSchema(model)); | ||
return { | ||
deserializer: defaultSerializer.deserializer, | ||
serializer: function serializer(belongsTo, name, parent) { | ||
if (parent.$nonSerializable && -1 !== parent.$nonSerializable.indexOf(name)) { | ||
return undefined; | ||
} | ||
return defaultSerializer.serializer(belongsTo); | ||
} | ||
}; | ||
} | ||
@@ -123,3 +132,3 @@ }; | ||
var classDecorator = function (model) { | ||
function modelDecorator(model) { | ||
Object.assign(model.prototype, MixedInInstanceMethods); | ||
@@ -169,3 +178,3 @@ Object.assign(model, MixedInClassMethods); | ||
} | ||
}; | ||
} | ||
@@ -242,2 +251,54 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
var setupModel = action(function (attrs, modelClass, array, defaultAttributes, inverseOf, parentModel) { | ||
if (defaultAttributes) { | ||
if (typeof defaultAttributes === 'function') { | ||
defaultAttributes = defaultAttributes.call(parentModel, array, parentModel); | ||
} | ||
Object.keys(defaultAttributes).forEach(function (key) { | ||
if (!attrs[key]) { | ||
attrs[key] = defaultAttributes[key]; | ||
} | ||
}); | ||
} | ||
if (inverseOf) { | ||
attrs[inverseOf] = parentModel; | ||
} | ||
var model = modelClass && !(attrs instanceof modelClass) ? new modelClass(attrs) : attrs; | ||
model.$nonSerializable = model.$nonSerializable || []; | ||
model.$nonSerializable.push(inverseOf); | ||
return model; | ||
}); | ||
function buildInterceptor(_ref, parentModel) { | ||
var modelClass = _ref.modelClass, | ||
className = _ref.className, | ||
defaultAttributes = _ref.defaults, | ||
inverseOf = _ref.inverseOf; | ||
return function (change) { | ||
if (!change.newValue) { | ||
change.newValue = {}; | ||
} | ||
if (!modelClass) { | ||
modelClass = findModel(className); | ||
} | ||
if (change.type === 'splice') { | ||
for (var i = 0; i < change.added.length; i += 1) { | ||
change.added[i] = setupModel(change.added[i], modelClass, change.object, defaultAttributes, inverseOf, parentModel); | ||
} | ||
} else if (change.type === 'update') { | ||
change.newValue = setupModel(change.newValue, modelClass, change.object, defaultAttributes, inverseOf, parentModel); | ||
} | ||
return change; | ||
}; | ||
} | ||
function buildCollection(options, parentModel) { | ||
var ary = observable.array([]); | ||
if (options.className || options.modelClass) { | ||
ary.intercept(buildInterceptor(options, parentModel)); | ||
} | ||
return ary; | ||
} | ||
var Initializers = { | ||
@@ -247,5 +308,3 @@ object: function object$$1() { | ||
}, | ||
array: function array() { | ||
return observable.array([]); | ||
} | ||
array: buildCollection | ||
}; | ||
@@ -258,3 +317,10 @@ | ||
function getInitializer(type, options) { | ||
return TypeInitializers[type] || Initializers[options.type]; | ||
var fn = TypeInitializers[type] || Initializers[options.type]; | ||
if (!fn) { | ||
return undefined; | ||
} | ||
return function () { | ||
// eslint-disable-line func-names | ||
return fn(options, this); | ||
}; | ||
} | ||
@@ -270,3 +336,4 @@ | ||
} | ||
return observable(target, property, descriptor); | ||
var definition = observable(target, property, descriptor); | ||
return definition; | ||
} | ||
@@ -317,2 +384,3 @@ | ||
}; | ||
var hasMany = function hasMany() { | ||
@@ -326,3 +394,3 @@ for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { | ||
export { classDecorator as model, identifier$1 as identifier, field, session, belongsTo, hasMany, lookupModelUsing, rememberModelUsing }; | ||
export { registerModel, findModel, rememberModelUsing, lookupModelUsing, modelDecorator, field, session, belongsTo, hasMany, identifier$1 as identifier, buildCollection }; | ||
//# sourceMappingURL=build.module.js.map |
17
index.js
@@ -1,14 +0,3 @@ | ||
import model from './lib/class-decorator'; | ||
import { lookupModelUsing, rememberModelUsing } from './lib/model-lookup'; | ||
import { field, session, belongsTo, hasMany, identifier } from './lib/property-decorators'; | ||
export { | ||
model, | ||
identifier, | ||
field, | ||
session, | ||
belongsTo, | ||
hasMany, | ||
lookupModelUsing, | ||
rememberModelUsing, | ||
}; | ||
export * from './lib/model-lookup'; | ||
export * from './lib/class-decorator'; | ||
export * from './lib/property-decorators'; |
@@ -49,3 +49,14 @@ import { | ||
hasMany: modelRef => list(object(modelRef)), | ||
belongsTo: model => object(getDefaultModelSchema(model)), | ||
belongsTo: model => { | ||
const defaultSerializer = object(getDefaultModelSchema(model)); | ||
return { | ||
deserializer: defaultSerializer.deserializer, | ||
serializer: (belongsTo, name, parent) => { | ||
if (parent.$nonSerializable && -1 !== parent.$nonSerializable.indexOf(name)){ | ||
return undefined; | ||
} | ||
return defaultSerializer.serializer(belongsTo); | ||
} | ||
} | ||
} | ||
}; | ||
@@ -74,3 +85,3 @@ | ||
export default function (model) { | ||
export function modelDecorator(model) { | ||
Object.assign(model.prototype, MixedInInstanceMethods); | ||
@@ -77,0 +88,0 @@ Object.assign(model, MixedInClassMethods); |
import { | ||
observable, | ||
action, | ||
} from 'mobx'; | ||
import { findModel } from './model-lookup'; | ||
import getModelSchema from './schema'; | ||
const setupModel = action(function(attrs, modelClass, array, defaultAttributes, inverseOf, parentModel) { | ||
if (defaultAttributes) { | ||
if (typeof defaultAttributes === 'function') { | ||
defaultAttributes = defaultAttributes.call(parentModel, array, parentModel); | ||
} | ||
Object.keys(defaultAttributes).forEach((key) => { | ||
if (!attrs[key]) { | ||
attrs[key] = defaultAttributes[key]; | ||
} | ||
}); | ||
} | ||
if (inverseOf) { | ||
attrs[inverseOf] = parentModel; | ||
} | ||
const model = (modelClass && !(attrs instanceof modelClass)) ? new modelClass(attrs) : attrs; | ||
model.$nonSerializable = model.$nonSerializable || []; | ||
model.$nonSerializable.push(inverseOf); | ||
return model; | ||
}); | ||
function buildInterceptor({ modelClass, className, defaults: defaultAttributes, inverseOf }, parentModel) { | ||
return (change) => { | ||
if (!change.newValue) { | ||
change.newValue = {}; | ||
} | ||
if (!modelClass) { | ||
modelClass = findModel(className); | ||
} | ||
if (change.type === 'splice') { | ||
for (let i = 0; i < change.added.length; i += 1) { | ||
change.added[i] = setupModel(change.added[i], modelClass, change.object, | ||
defaultAttributes, inverseOf, parentModel); | ||
} | ||
} else if (change.type === 'update') { | ||
change.newValue = setupModel(change.newValue, modelClass, change.object, | ||
defaultAttributes, inverseOf, parentModel); | ||
} | ||
return change; | ||
}; | ||
} | ||
function buildCollection(options, parentModel) { | ||
const ary = observable.array([]); | ||
if (options.className || options.modelClass) { | ||
ary.intercept(buildInterceptor(options, parentModel)); | ||
} | ||
return ary; | ||
} | ||
const Initializers = { | ||
object: () => observable.map({}), | ||
array: () => observable.array([]), | ||
array: buildCollection, | ||
}; | ||
@@ -17,3 +69,7 @@ | ||
function getInitializer(type, options) { | ||
return TypeInitializers[type] || Initializers[options.type]; | ||
const fn = TypeInitializers[type] || Initializers[options.type]; | ||
if (!fn) { return undefined; } | ||
return function () { // eslint-disable-line func-names | ||
return fn(options, this); | ||
}; | ||
} | ||
@@ -27,3 +83,4 @@ | ||
} | ||
return observable(target, property, descriptor); | ||
const definition = observable(target, property, descriptor); | ||
return definition; | ||
} | ||
@@ -45,4 +102,5 @@ | ||
addAttribute(type, target, property, descriptor, options); | ||
const hasMany = (...args) => buildAttributeDecorator('hasMany', args, hasManyBuilder); | ||
export { field, session, belongsTo, hasMany, identifier }; | ||
export { field, session, belongsTo, hasMany, identifier, buildCollection }; |
{ | ||
"name": "mobx-decorated-models", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "Decorators to make using Mobx for model type structures easier", | ||
@@ -13,3 +13,3 @@ "main": "dist/build.full.js", | ||
"mobx": "^3.0.2", | ||
"serializr": "^1.1.9" | ||
"serializr": "https://github.com/nathanstitt/serializr#serializer-args" | ||
}, | ||
@@ -16,0 +16,0 @@ "scripts": { |
@@ -29,3 +29,3 @@ # Decorators for creating model type structures with mobx | ||
@model | ||
@modelDecorator | ||
export class Box { | ||
@@ -62,3 +62,3 @@ @identifier id; | ||
By default, the class `@model` decorator uses the `name` property of each class as a lookup key so | ||
By default, the class `@modelDecorator` uses the `name` property of each class as a lookup key so | ||
that `hasMany` and `belongsTo` relation ships can be established. | ||
@@ -72,5 +72,5 @@ | ||
import { model, belongsTo } from 'mobx-decorated-models'; | ||
import { modelDecorator, belongsTo } from 'mobx-decorated-models'; | ||
@model | ||
@modelDecorator | ||
class Chair { | ||
@@ -84,3 +84,3 @@ belongsTo 'table' | ||
@model | ||
@modelDecorator | ||
class Table { | ||
@@ -112,3 +112,3 @@ hasMany({ model: 'Chair' }) 'seats' | ||
@model | ||
@modelDecorator | ||
class ATestingModel { | ||
@@ -119,3 +119,3 @@ static identifiedBy = 'test'; | ||
@model | ||
@modelDecorator | ||
class Document { | ||
@@ -186,9 +186,41 @@ static identifiedBy = 'document'; | ||
Makes a property as belonging to an array of model. Sets the default value to an observable array | ||
Marks a property as belonging to an mobx observable array of models. | ||
Sets the default value to an empty observable array | ||
As in `belongsTo`, can be optionally given an option object with a `className` property to control the mapping. | ||
`hasMany` also accepts `inverseOf` and `defaults` properties. If an inverseOf is provided, | ||
when a model is added to the array, it will have the property named by `inverseOf` to the parent model | ||
If `defaults` are provided the new model's attributes will be defaulted to them. `defaults` may | ||
also be a function, which will be called and it's return values used. | ||
```javascript | ||
class Tire { | ||
@session numberInSet; | ||
@belongsTo vehicle; // will be autoset by the `inverseOf: auto` on Car | ||
} | ||
class Car { | ||
@belongsTo home; | ||
@session color; | ||
@hasMany({ className: 'Tire', inverseOf: 'vehicle', defaults: {numberInSet: 4} }) tires; | ||
} | ||
class Garage { | ||
@session owner; | ||
@hasMany({ | ||
className: 'Car', | ||
inverseOf: 'home', | ||
defaults(collection, parent) { | ||
return { color: this.owner.favoriteColor }; | ||
} | ||
}) cars; | ||
} | ||
``` | ||
# Future plans | ||
* Sessions: properties that will be set from JSON but won't be serialized. https://github.com/mobxjs/serializr/pull/32 is needed before this can be supported |
@@ -58,10 +58,12 @@ import { Container, Box } from './test-models'; | ||
const container = new Container({ id: 1, name: 'C23', location: 'z1' }); | ||
container.boxes.push(new Box({ id: 1, width: 8, depth: 12, height: 8 })); | ||
container.boxes.push(new Box({ id: 2, width: 3, depth: 12, height: 4 })); | ||
container.boxes[1].x = 4; | ||
container.boxes.push(new Box()); | ||
container.boxes.push(new Box()); | ||
container.boxes[1].width = 4; | ||
// hasMany sets the inverseOf, but it isn't serialized | ||
expect(container.boxes[0].container).toBe(container); | ||
expect(container.serialize()).toEqual({ | ||
id: undefined, location: undefined, name: undefined, tags: [], | ||
id: 1, location: 'z1', name: 'C23', tags: [], | ||
boxes: [ | ||
{ container: undefined, depth: 1, height: 1, id: undefined, metadata: {}, width: 1 }, | ||
{ container: undefined, depth: 1, height: 1, id: undefined, metadata: {}, width: 1 }, | ||
{ depth: 1, height: 1, metadata: {}, width: 1 }, | ||
{ depth: 1, height: 1, metadata: {}, width: 4 }, | ||
], | ||
@@ -68,0 +70,0 @@ }); |
import { Container, Box } from './test-models'; | ||
import { model as modelDecorator } from '../index'; | ||
import { modelDecorator } from '../index'; | ||
import * as ModelLookup from '../lib/model-lookup'; | ||
@@ -4,0 +4,0 @@ |
@@ -36,2 +36,15 @@ import { Box, Container } from './test-models'; | ||
it('sets defaults for hasMany', () => { | ||
const container = Container.deserialize({ color: 'blue' }); | ||
container.boxes.push({}); | ||
expect(container.boxes[0].color).toEqual('blue'); | ||
}); | ||
it('sets an inverse', () => { | ||
const container = Container.deserialize({ id: 1, name: 'Bob', location: 'water' }); | ||
container.boxes.push({}); | ||
expect(container.boxes[0]).toBeInstanceOf(Box); | ||
expect(container.boxes[0].container).toEqual(container); | ||
}); | ||
xit('merges both attributes and session props', () => { | ||
@@ -42,3 +55,3 @@ const box = Box.deserialize({ width: 3, isVisible: true }); | ||
it('can objserve associations', () => { | ||
it('can observe associations', () => { | ||
const container = Container.deserialize({ id: 1, name: 'Bob', location: 'water' }); | ||
@@ -50,4 +63,4 @@ const spy = jest.fn(); | ||
expect(container.areaInUse).toEqual(0); | ||
container.boxes.push(Box.deserialize({ id: 1, width: 8, depth: 12, height: 8 })); | ||
container.boxes.push(Box.deserialize({ id: 2, width: 3, depth: 12, height: 4 })); | ||
container.boxes.push({ id: 1, width: 8, depth: 12, height: 8 }); | ||
container.boxes.push({ id: 2, width: 3, depth: 12, height: 4 }); | ||
container.boxes[1].width = 4; | ||
@@ -54,0 +67,0 @@ expect(container.areaInUse).toEqual(960); |
import { observable, computed } from 'mobx'; | ||
import { model, field, session, belongsTo, hasMany, identifier } from '../index'; | ||
import { modelDecorator, field, session, belongsTo, hasMany, identifier } from '../index'; | ||
class RectangularCuboid { | ||
constructor() { | ||
constructor(attrs) { | ||
Object.assign(this, attrs); | ||
this.isCuboid = true; | ||
@@ -10,3 +11,3 @@ } | ||
@model | ||
@modelDecorator | ||
export class Box extends RectangularCuboid { | ||
@@ -18,5 +19,6 @@ @identifier id; | ||
@field depth = 1; | ||
@field({ type: 'object' }) metadata; | ||
@session color; | ||
@computed get volume() { | ||
@@ -29,3 +31,3 @@ return this.width * this.height * this.depth; | ||
@model | ||
@modelDecorator | ||
export class Container extends RectangularCuboid { | ||
@@ -36,5 +38,6 @@ @identifier id; | ||
@field location; | ||
@field({ type: 'array' }) tags = []; | ||
@session color; | ||
@computed get description() { | ||
@@ -44,3 +47,9 @@ return `${this.name} ${this.location}`; | ||
@hasMany({ className: 'Box', inverseOf: 'container' }) boxes; | ||
@hasMany({ | ||
className: 'Box', | ||
inverseOf: 'container', | ||
defaults() { | ||
return { color: this.color }; | ||
}, | ||
}) boxes; | ||
@@ -47,0 +56,0 @@ @computed get areaInUse() { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
HTTP dependency
Supply chain riskContains a dependency which resolves to a remote HTTP URL which could be used to inject untrusted code and reduce overall package reliability.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
176217
1092
220
1
1
- Removedserializr@1.5.4(transitive)
Updatedserializr@https://github.com/nathanstitt/serializr#serializer-args