mobx-decorated-models
Advanced tools
Comparing version 0.3.3 to 0.4.0
@@ -13,3 +13,14 @@ module.exports = { | ||
"import/extensions": 0, | ||
"import/no-extraneous-dependencies": [0, { devDependencies: true }] | ||
"import/no-extraneous-dependencies": [0, { devDependencies: true }], | ||
"key-spacing": [2, { | ||
"singleLine": { | ||
"beforeColon": false, | ||
"afterColon": true | ||
}, | ||
"multiLine": { | ||
"beforeColon": false, | ||
"afterColon": true, | ||
"mode": "minimum" | ||
} | ||
}], | ||
}, | ||
@@ -16,0 +27,0 @@ "globals": { |
@@ -22,3 +22,3 @@ (function (global, factory) { | ||
var defaultRememberModel = function defaultRememberModel(model) { | ||
ModelsMap[model.name] = model; | ||
ModelsMap[model.identifiedBy || model.name] = model; | ||
}; | ||
@@ -44,8 +44,8 @@ var rememberModel = defaultRememberModel; | ||
function isSerializable(model, property) { | ||
return !!(!model.$nonSerializable || -1 === model.$nonSerializable.indexOf(property)); | ||
return !!(!model.$nonSerializable || !model.$nonSerializable[property]); | ||
} | ||
function markNonserializable(model, property) { | ||
model.$nonSerializable = model.$nonSerializable || []; | ||
model.$nonSerializable.push(property); | ||
model.$nonSerializable = model.$nonSerializable || {}; | ||
model.$nonSerializable[property] = true; | ||
} | ||
@@ -69,2 +69,13 @@ | ||
function objectSerializer() { | ||
return { | ||
serializer: function serializer(obj, prop, parent) { | ||
return isSerializable(parent, prop) ? obj : undefined; | ||
}, | ||
deserializer: function deserializer(json, cb) { | ||
cb(null, json); | ||
} | ||
}; | ||
} | ||
function addReference(parentModel, propName, options, cb) { | ||
@@ -75,2 +86,3 @@ var model = findModel(propName, options); | ||
} else { | ||
serializr.getDefaultModelSchema(parentModel).props[propName] = objectSerializer(); | ||
PendingLookups.push({ parentModel: parentModel, propName: propName, options: options, cb: cb }); | ||
@@ -80,13 +92,2 @@ } | ||
function objectSerializer() { | ||
return { | ||
serializer: function serializer(obj) { | ||
return obj; | ||
}, | ||
deserializer: function deserializer(json, cb) { | ||
cb(null, json); | ||
} | ||
}; | ||
} | ||
function getSerializer(options, defaultSerializer) { | ||
@@ -126,11 +127,11 @@ var serializer = void 0; | ||
var AsyncHandlers = { | ||
hasMany: function hasMany(modelKlass) { | ||
hasMany: function hasMany(modelKlass, options) { | ||
var defaultSerializer = serializr.list(serializr.object(modelKlass)); | ||
return Object.assign(defaultSerializer, { | ||
model: modelKlass | ||
}); | ||
}, options); | ||
}, | ||
belongsTo: function belongsTo(modelKlass, options, propName) { | ||
var defaultSerializer = serializr.object(serializr.getDefaultModelSchema(modelKlass)); | ||
return { | ||
return Object.assign({ | ||
model: modelKlass, | ||
@@ -153,3 +154,3 @@ deserializer: function deserializer(value, cb, context) { | ||
} | ||
}; | ||
}, options); | ||
} | ||
@@ -174,3 +175,3 @@ }; | ||
function modelDecorator(model) { | ||
function decorateModel(model, identifier$$1) { | ||
Object.assign(model.prototype, MixedInInstanceMethods); | ||
@@ -180,7 +181,4 @@ Object.assign(model, MixedInClassMethods); | ||
var schema = getModelSchema(model); | ||
registerModel(model); | ||
var serializeSchema = {}; | ||
schema.forEach(function (_ref, name) { | ||
@@ -194,5 +192,3 @@ var type = _ref.type, | ||
}); | ||
serializr.createModelSchema(model, serializeSchema); | ||
schema.forEach(function (_ref2, name) { | ||
@@ -206,3 +202,2 @@ var type = _ref2.type, | ||
}); | ||
for (var i = PendingLookups.length - 1; i >= 0; i -= 1) { | ||
@@ -216,3 +211,2 @@ var _PendingLookups$i = PendingLookups[i], | ||
var referencedModel = findModel(propName, options); | ||
if (referencedModel) { | ||
@@ -226,2 +220,21 @@ var parentModelSchema = serializr.getDefaultModelSchema(parentModel); | ||
function modelDecorator(modelOrIdentifier) { | ||
if (typeof modelOrIdentifier === 'function') { | ||
modelOrIdentifier.identifiedBy = modelOrIdentifier.name; | ||
return decorateModel(modelOrIdentifier); | ||
} | ||
return function (model) { | ||
model.identifiedBy = modelOrIdentifier; | ||
return decorateModel(model); | ||
}; | ||
} | ||
function unresolvedAssociations() { | ||
return PendingLookups.map(function (_ref3) { | ||
var model = _ref3.parentModel, | ||
property = _ref3.propName; | ||
return { model: model, property: property }; | ||
}); | ||
} | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
@@ -297,3 +310,7 @@ return typeof obj; | ||
var setupModel = mobx.action(function (_ref) { | ||
function addLazyInitializer(target, fn) { | ||
target.__mobxLazyInitializers.push(fn); | ||
} | ||
function setupModel(_ref) { | ||
var attrs = _ref.attrs, | ||
@@ -324,12 +341,50 @@ Klass = _ref.modelClass, | ||
var model = Klass && !(attrs instanceof Klass) ? new Klass(attrs) : attrs; | ||
markNonserializable(model, inverseOf); | ||
if (inverseOf) { | ||
markNonserializable(model, inverseOf); | ||
} | ||
return model; | ||
}); | ||
} | ||
function buildInterceptor(_ref2, parentModel, parentModelProp) { | ||
function onBelongsToSet(change, _ref2) { | ||
var modelClass = _ref2.modelClass, | ||
className = _ref2.className, | ||
defaultAttributes = _ref2.defaults, | ||
inverseOf = _ref2.inverseOf; | ||
defaultAttributes = _ref2.defaultAttributes, | ||
inverseOf = _ref2.inverseOf, | ||
parentModel = _ref2.parentModel, | ||
parentModelProp = _ref2.parentModelProp; | ||
change.newValue = setupModel({ | ||
attrs: change.newValue, modelClass: modelClass, | ||
defaultAttributes: defaultAttributes, inverseOf: inverseOf, parentModel: parentModel, parentModelProp: parentModelProp | ||
}); | ||
return change; | ||
} | ||
function onHasManySet(change, _ref3) { | ||
var modelClass = _ref3.modelClass, | ||
defaultAttributes = _ref3.defaultAttributes, | ||
inverseOf = _ref3.inverseOf, | ||
parentModel = _ref3.parentModel, | ||
parentModelProp = _ref3.parentModelProp; | ||
if (change.type !== 'update') { | ||
return change; | ||
} | ||
var array = change.newValue; | ||
for (var i = 0; i < array.length; i += 1) { | ||
array[i] = setupModel({ | ||
attrs: array[i], array: array, modelClass: modelClass, | ||
defaultAttributes: defaultAttributes, inverseOf: inverseOf, parentModel: parentModel, parentModelProp: parentModelProp | ||
}); | ||
} | ||
parentModel[parentModelProp].replace(array); | ||
return null; | ||
} | ||
function onCollectionChangeInterceptor(_ref4, parentModel, parentModelProp) { | ||
var modelClass = _ref4.modelClass, | ||
className = _ref4.className, | ||
defaultAttributes = _ref4.defaults, | ||
inverseOf = _ref4.inverseOf; | ||
return function (change) { | ||
@@ -360,3 +415,3 @@ if (!change.newValue) { | ||
if (options.className || options.modelClass) { | ||
ary.intercept(buildInterceptor(options, parentModel, parentModelProp)); | ||
ary.intercept(onCollectionChangeInterceptor(options, parentModel, parentModelProp)); | ||
} | ||
@@ -411,3 +466,29 @@ return ary; | ||
var identifier$1 = function identifier$$1() { | ||
function interceptingDecorator(interceptingFn) { | ||
return function (type, target, property, descriptor) { | ||
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; | ||
var decorator = addAttribute(type, target, property, descriptor, options); | ||
addLazyInitializer(target, function (model) { | ||
var schema = serializr.getDefaultModelSchema(target); | ||
if (schema && schema.props[property]) { | ||
(function () { | ||
var schemaProps = schema.props[property]; | ||
mobx.intercept(model, property, function (change) { | ||
return interceptingFn(change, { | ||
inverseOf: schemaProps.inverseOf, | ||
modelClass: schemaProps.model, | ||
parentModel: model, | ||
parentModelProp: property, | ||
defaultAttributes: schemaProps.defaults | ||
}); | ||
}); | ||
})(); | ||
} | ||
}); | ||
return decorator; | ||
}; | ||
} | ||
var hasMany = function hasMany() { | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
@@ -417,5 +498,6 @@ args[_key] = arguments[_key]; | ||
return buildAttributeDecorator('identifier', args); | ||
return buildAttributeDecorator('hasMany', args, interceptingDecorator(onHasManySet)); | ||
}; | ||
var field = function field() { | ||
var belongsTo = function belongsTo() { | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
@@ -425,5 +507,6 @@ args[_key2] = arguments[_key2]; | ||
return buildAttributeDecorator('field', args); | ||
return buildAttributeDecorator('belongsTo', args, interceptingDecorator(onBelongsToSet)); | ||
}; | ||
var session = function session() { | ||
var identifier$1 = function identifier$$1() { | ||
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
@@ -433,5 +516,5 @@ args[_key3] = arguments[_key3]; | ||
return buildAttributeDecorator('session', args); | ||
return buildAttributeDecorator('identifier', args); | ||
}; | ||
var belongsTo = function belongsTo() { | ||
var field = function field() { | ||
for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { | ||
@@ -441,10 +524,5 @@ args[_key4] = arguments[_key4]; | ||
return buildAttributeDecorator('belongsTo', args); | ||
return buildAttributeDecorator('field', args); | ||
}; | ||
var hasManyBuilder = function hasManyBuilder(type, target, property, descriptor) { | ||
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; | ||
return addAttribute(type, target, property, descriptor, options); | ||
}; | ||
var hasMany = function hasMany() { | ||
var session = function session() { | ||
for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { | ||
@@ -454,3 +532,3 @@ args[_key5] = arguments[_key5]; | ||
return buildAttributeDecorator('hasMany', args, hasManyBuilder); | ||
return buildAttributeDecorator('session', args); | ||
}; | ||
@@ -463,2 +541,3 @@ | ||
exports.modelDecorator = modelDecorator; | ||
exports.unresolvedAssociations = unresolvedAssociations; | ||
exports.field = field; | ||
@@ -465,0 +544,0 @@ exports.session = session; |
import { createModelSchema, deserialize, getDefaultModelSchema, identifier, list, object, primitive, serialize, update } from 'serializr'; | ||
import { action, observable } from 'mobx'; | ||
import { intercept, observable } from 'mobx'; | ||
@@ -19,3 +19,3 @@ var ModelsMap = new Map(); | ||
var defaultRememberModel = function defaultRememberModel(model) { | ||
ModelsMap[model.name] = model; | ||
ModelsMap[model.identifiedBy || model.name] = model; | ||
}; | ||
@@ -41,8 +41,8 @@ var rememberModel = defaultRememberModel; | ||
function isSerializable(model, property) { | ||
return !!(!model.$nonSerializable || -1 === model.$nonSerializable.indexOf(property)); | ||
return !!(!model.$nonSerializable || !model.$nonSerializable[property]); | ||
} | ||
function markNonserializable(model, property) { | ||
model.$nonSerializable = model.$nonSerializable || []; | ||
model.$nonSerializable.push(property); | ||
model.$nonSerializable = model.$nonSerializable || {}; | ||
model.$nonSerializable[property] = true; | ||
} | ||
@@ -66,2 +66,13 @@ | ||
function objectSerializer() { | ||
return { | ||
serializer: function serializer(obj, prop, parent) { | ||
return isSerializable(parent, prop) ? obj : undefined; | ||
}, | ||
deserializer: function deserializer(json, cb) { | ||
cb(null, json); | ||
} | ||
}; | ||
} | ||
function addReference(parentModel, propName, options, cb) { | ||
@@ -72,2 +83,3 @@ var model = findModel(propName, options); | ||
} else { | ||
getDefaultModelSchema(parentModel).props[propName] = objectSerializer(); | ||
PendingLookups.push({ parentModel: parentModel, propName: propName, options: options, cb: cb }); | ||
@@ -77,13 +89,2 @@ } | ||
function objectSerializer() { | ||
return { | ||
serializer: function serializer(obj) { | ||
return obj; | ||
}, | ||
deserializer: function deserializer(json, cb) { | ||
cb(null, json); | ||
} | ||
}; | ||
} | ||
function getSerializer(options, defaultSerializer) { | ||
@@ -123,11 +124,11 @@ var serializer = void 0; | ||
var AsyncHandlers = { | ||
hasMany: function hasMany(modelKlass) { | ||
hasMany: function hasMany(modelKlass, options) { | ||
var defaultSerializer = list(object(modelKlass)); | ||
return Object.assign(defaultSerializer, { | ||
model: modelKlass | ||
}); | ||
}, options); | ||
}, | ||
belongsTo: function belongsTo(modelKlass, options, propName) { | ||
var defaultSerializer = object(getDefaultModelSchema(modelKlass)); | ||
return { | ||
return Object.assign({ | ||
model: modelKlass, | ||
@@ -150,3 +151,3 @@ deserializer: function deserializer(value, cb, context) { | ||
} | ||
}; | ||
}, options); | ||
} | ||
@@ -171,3 +172,3 @@ }; | ||
function modelDecorator(model) { | ||
function decorateModel(model, identifier$$1) { | ||
Object.assign(model.prototype, MixedInInstanceMethods); | ||
@@ -177,7 +178,4 @@ Object.assign(model, MixedInClassMethods); | ||
var schema = getModelSchema(model); | ||
registerModel(model); | ||
var serializeSchema = {}; | ||
schema.forEach(function (_ref, name) { | ||
@@ -191,5 +189,3 @@ var type = _ref.type, | ||
}); | ||
createModelSchema(model, serializeSchema); | ||
schema.forEach(function (_ref2, name) { | ||
@@ -203,3 +199,2 @@ var type = _ref2.type, | ||
}); | ||
for (var i = PendingLookups.length - 1; i >= 0; i -= 1) { | ||
@@ -213,3 +208,2 @@ var _PendingLookups$i = PendingLookups[i], | ||
var referencedModel = findModel(propName, options); | ||
if (referencedModel) { | ||
@@ -223,2 +217,21 @@ var parentModelSchema = getDefaultModelSchema(parentModel); | ||
function modelDecorator(modelOrIdentifier) { | ||
if (typeof modelOrIdentifier === 'function') { | ||
modelOrIdentifier.identifiedBy = modelOrIdentifier.name; | ||
return decorateModel(modelOrIdentifier); | ||
} | ||
return function (model) { | ||
model.identifiedBy = modelOrIdentifier; | ||
return decorateModel(model); | ||
}; | ||
} | ||
function unresolvedAssociations() { | ||
return PendingLookups.map(function (_ref3) { | ||
var model = _ref3.parentModel, | ||
property = _ref3.propName; | ||
return { model: model, property: property }; | ||
}); | ||
} | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | ||
@@ -294,3 +307,7 @@ return typeof obj; | ||
var setupModel = action(function (_ref) { | ||
function addLazyInitializer(target, fn) { | ||
target.__mobxLazyInitializers.push(fn); | ||
} | ||
function setupModel(_ref) { | ||
var attrs = _ref.attrs, | ||
@@ -321,12 +338,50 @@ Klass = _ref.modelClass, | ||
var model = Klass && !(attrs instanceof Klass) ? new Klass(attrs) : attrs; | ||
markNonserializable(model, inverseOf); | ||
if (inverseOf) { | ||
markNonserializable(model, inverseOf); | ||
} | ||
return model; | ||
}); | ||
} | ||
function buildInterceptor(_ref2, parentModel, parentModelProp) { | ||
function onBelongsToSet(change, _ref2) { | ||
var modelClass = _ref2.modelClass, | ||
className = _ref2.className, | ||
defaultAttributes = _ref2.defaults, | ||
inverseOf = _ref2.inverseOf; | ||
defaultAttributes = _ref2.defaultAttributes, | ||
inverseOf = _ref2.inverseOf, | ||
parentModel = _ref2.parentModel, | ||
parentModelProp = _ref2.parentModelProp; | ||
change.newValue = setupModel({ | ||
attrs: change.newValue, modelClass: modelClass, | ||
defaultAttributes: defaultAttributes, inverseOf: inverseOf, parentModel: parentModel, parentModelProp: parentModelProp | ||
}); | ||
return change; | ||
} | ||
function onHasManySet(change, _ref3) { | ||
var modelClass = _ref3.modelClass, | ||
defaultAttributes = _ref3.defaultAttributes, | ||
inverseOf = _ref3.inverseOf, | ||
parentModel = _ref3.parentModel, | ||
parentModelProp = _ref3.parentModelProp; | ||
if (change.type !== 'update') { | ||
return change; | ||
} | ||
var array = change.newValue; | ||
for (var i = 0; i < array.length; i += 1) { | ||
array[i] = setupModel({ | ||
attrs: array[i], array: array, modelClass: modelClass, | ||
defaultAttributes: defaultAttributes, inverseOf: inverseOf, parentModel: parentModel, parentModelProp: parentModelProp | ||
}); | ||
} | ||
parentModel[parentModelProp].replace(array); | ||
return null; | ||
} | ||
function onCollectionChangeInterceptor(_ref4, parentModel, parentModelProp) { | ||
var modelClass = _ref4.modelClass, | ||
className = _ref4.className, | ||
defaultAttributes = _ref4.defaults, | ||
inverseOf = _ref4.inverseOf; | ||
return function (change) { | ||
@@ -357,3 +412,3 @@ if (!change.newValue) { | ||
if (options.className || options.modelClass) { | ||
ary.intercept(buildInterceptor(options, parentModel, parentModelProp)); | ||
ary.intercept(onCollectionChangeInterceptor(options, parentModel, parentModelProp)); | ||
} | ||
@@ -408,3 +463,29 @@ return ary; | ||
var identifier$1 = function identifier$$1() { | ||
function interceptingDecorator(interceptingFn) { | ||
return function (type, target, property, descriptor) { | ||
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; | ||
var decorator = addAttribute(type, target, property, descriptor, options); | ||
addLazyInitializer(target, function (model) { | ||
var schema = getDefaultModelSchema(target); | ||
if (schema && schema.props[property]) { | ||
(function () { | ||
var schemaProps = schema.props[property]; | ||
intercept(model, property, function (change) { | ||
return interceptingFn(change, { | ||
inverseOf: schemaProps.inverseOf, | ||
modelClass: schemaProps.model, | ||
parentModel: model, | ||
parentModelProp: property, | ||
defaultAttributes: schemaProps.defaults | ||
}); | ||
}); | ||
})(); | ||
} | ||
}); | ||
return decorator; | ||
}; | ||
} | ||
var hasMany = function hasMany() { | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
@@ -414,5 +495,6 @@ args[_key] = arguments[_key]; | ||
return buildAttributeDecorator('identifier', args); | ||
return buildAttributeDecorator('hasMany', args, interceptingDecorator(onHasManySet)); | ||
}; | ||
var field = function field() { | ||
var belongsTo = function belongsTo() { | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
@@ -422,5 +504,6 @@ args[_key2] = arguments[_key2]; | ||
return buildAttributeDecorator('field', args); | ||
return buildAttributeDecorator('belongsTo', args, interceptingDecorator(onBelongsToSet)); | ||
}; | ||
var session = function session() { | ||
var identifier$1 = function identifier$$1() { | ||
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
@@ -430,5 +513,5 @@ args[_key3] = arguments[_key3]; | ||
return buildAttributeDecorator('session', args); | ||
return buildAttributeDecorator('identifier', args); | ||
}; | ||
var belongsTo = function belongsTo() { | ||
var field = function field() { | ||
for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { | ||
@@ -438,10 +521,5 @@ args[_key4] = arguments[_key4]; | ||
return buildAttributeDecorator('belongsTo', args); | ||
return buildAttributeDecorator('field', args); | ||
}; | ||
var hasManyBuilder = function hasManyBuilder(type, target, property, descriptor) { | ||
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; | ||
return addAttribute(type, target, property, descriptor, options); | ||
}; | ||
var hasMany = function hasMany() { | ||
var session = function session() { | ||
for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { | ||
@@ -451,6 +529,6 @@ args[_key5] = arguments[_key5]; | ||
return buildAttributeDecorator('hasMany', args, hasManyBuilder); | ||
return buildAttributeDecorator('session', args); | ||
}; | ||
export { registerModel, findModel, rememberModelUsing, lookupModelUsing, modelDecorator, field, session, belongsTo, hasMany, identifier$1 as identifier, buildCollection }; | ||
export { registerModel, findModel, rememberModelUsing, lookupModelUsing, modelDecorator, unresolvedAssociations, field, session, belongsTo, hasMany, identifier$1 as identifier, buildCollection }; | ||
//# sourceMappingURL=build.module.js.map |
@@ -5,5 +5,2 @@ import { | ||
} from 'serializr'; | ||
import { | ||
observable, | ||
} from 'mobx'; | ||
@@ -16,2 +13,13 @@ import { registerModel, findModel } from './model-lookup'; | ||
function objectSerializer() { | ||
return { | ||
serializer(obj, prop, parent) { | ||
return isSerializable(parent, prop) ? obj : undefined; | ||
}, | ||
deserializer(json, cb) { | ||
cb(null, json); | ||
}, | ||
}; | ||
} | ||
function addReference(parentModel, propName, options, cb) { | ||
@@ -22,2 +30,3 @@ const model = findModel(propName, options); | ||
} else { | ||
getDefaultModelSchema(parentModel).props[propName] = objectSerializer(); | ||
PendingLookups.push({ parentModel, propName, options, cb }); | ||
@@ -27,9 +36,2 @@ } | ||
function objectSerializer() { | ||
return { | ||
serializer(obj) { return obj; }, | ||
deserializer(json, cb) { cb(null, json); }, | ||
}; | ||
} | ||
function getSerializer(options, defaultSerializer) { | ||
@@ -62,12 +64,11 @@ let serializer; | ||
const AsyncHandlers = { | ||
hasMany(modelKlass) { | ||
hasMany(modelKlass, options) { | ||
const defaultSerializer = list(object(modelKlass)); | ||
return Object.assign(defaultSerializer, { | ||
model: modelKlass, | ||
}); | ||
}, options); | ||
}, | ||
belongsTo(modelKlass, options, propName) { | ||
const defaultSerializer = object(getDefaultModelSchema(modelKlass)); | ||
return { | ||
return Object.assign({ | ||
model: modelKlass, | ||
@@ -88,3 +89,3 @@ deserializer(value, cb, context) { | ||
}, | ||
}; | ||
}, options); | ||
}, | ||
@@ -95,3 +96,2 @@ }; | ||
const MixedInInstanceMethods = { | ||
serialize() { | ||
@@ -101,18 +101,14 @@ const schema = getDefaultModelSchema(this.constructor); | ||
}, | ||
update(json, callback) { | ||
return update(getDefaultModelSchema(this.constructor), this, json, callback); | ||
}, | ||
}; | ||
const MixedInClassMethods = { | ||
deserialize(json, callback) { | ||
return deserialize(getDefaultModelSchema(this), json, callback); | ||
}, | ||
}; | ||
export function modelDecorator(model) { | ||
function decorateModel(model, identifier) { | ||
Object.assign(model.prototype, MixedInInstanceMethods); | ||
@@ -122,7 +118,4 @@ Object.assign(model, MixedInClassMethods); | ||
const schema = getSchema(model); | ||
registerModel(model); | ||
const serializeSchema = {}; | ||
schema.forEach(({ type, options }, name) => { | ||
@@ -133,5 +126,3 @@ if (SimpleHandlers[type]) { | ||
}); | ||
createModelSchema(model, serializeSchema); | ||
schema.forEach(({ type, options }, name) => { | ||
@@ -142,7 +133,5 @@ if (AsyncHandlers[type]) { | ||
}); | ||
for (let i = PendingLookups.length - 1; i >= 0; i -= 1) { | ||
const { parentModel, propName, options, cb } = PendingLookups[i]; | ||
const referencedModel = findModel(propName, options); | ||
if (referencedModel) { | ||
@@ -155,1 +144,18 @@ const parentModelSchema = getDefaultModelSchema(parentModel); | ||
} | ||
export function modelDecorator(modelOrIdentifier) { | ||
if (typeof modelOrIdentifier === 'function') { | ||
modelOrIdentifier.identifiedBy = modelOrIdentifier.name; | ||
return decorateModel(modelOrIdentifier); | ||
} | ||
return (model) => { | ||
model.identifiedBy = modelOrIdentifier; | ||
return decorateModel(model); | ||
}; | ||
} | ||
export function unresolvedAssociations() { | ||
return PendingLookups.map(({ parentModel: model, propName: property }) => ( | ||
{ model, property } | ||
)); | ||
} |
@@ -14,3 +14,3 @@ const ModelsMap = new Map(); | ||
const defaultRememberModel = (model) => { | ||
ModelsMap[model.name] = model; | ||
ModelsMap[model.identifiedBy || model.name] = model; | ||
}; | ||
@@ -17,0 +17,0 @@ let rememberModel = defaultRememberModel; |
@@ -1,5 +0,3 @@ | ||
import { | ||
observable, | ||
action, | ||
} from 'mobx'; | ||
import { observable, intercept } from 'mobx'; | ||
import { getDefaultModelSchema } from 'serializr'; | ||
@@ -9,4 +7,10 @@ import { findModel } from './model-lookup'; | ||
import { markNonserializable } from './serializable' | ||
const setupModel = action(function({ | ||
attrs, modelClass: Klass, array, defaultAttributes, inverseOf, parentModel, parentModelProp, | ||
function addLazyInitializer(target, fn) { | ||
target.__mobxLazyInitializers.push(fn); | ||
} | ||
function setupModel({ | ||
attrs, modelClass: Klass, array, defaultAttributes, | ||
inverseOf, parentModel, parentModelProp, | ||
}) { | ||
@@ -30,8 +34,37 @@ if (defaultAttributes) { | ||
const model = (Klass && !(attrs instanceof Klass)) ? new Klass(attrs) : attrs; | ||
markNonserializable(model, inverseOf); | ||
if (inverseOf) { | ||
markNonserializable(model, inverseOf); | ||
} | ||
return model; | ||
}); | ||
} | ||
function buildInterceptor({ modelClass, className, defaults: defaultAttributes, inverseOf }, | ||
parentModel, parentModelProp) { | ||
function onBelongsToSet(change, | ||
{ modelClass, defaultAttributes, inverseOf, | ||
parentModel, parentModelProp }) { | ||
change.newValue = setupModel({ | ||
attrs: change.newValue, modelClass, | ||
defaultAttributes, inverseOf, parentModel, parentModelProp, | ||
}); | ||
return change; | ||
} | ||
function onHasManySet(change, | ||
{ modelClass, defaultAttributes, inverseOf, | ||
parentModel, parentModelProp }) { | ||
if (change.type !== 'update') { return change; } | ||
const { newValue: array } = change; | ||
for (let i = 0; i < array.length; i += 1) { | ||
array[i] = setupModel({ | ||
attrs: array[i], array, modelClass, | ||
defaultAttributes, inverseOf, parentModel, parentModelProp, | ||
}); | ||
} | ||
parentModel[parentModelProp].replace(array); | ||
return null; | ||
} | ||
function onCollectionChangeInterceptor( | ||
{ modelClass, className, defaults: defaultAttributes, inverseOf }, | ||
parentModel, parentModelProp, | ||
) { | ||
return (change) => { | ||
@@ -59,7 +92,6 @@ if (!change.newValue) { | ||
function buildCollection(options, parentModel, parentModelProp) { | ||
const ary = observable.array([]); | ||
if (options.className || options.modelClass) { | ||
ary.intercept(buildInterceptor(options, parentModel, parentModelProp)); | ||
ary.intercept(onCollectionChangeInterceptor(options, parentModel, parentModelProp)); | ||
} | ||
@@ -104,11 +136,35 @@ return ary; | ||
function interceptingDecorator(interceptingFn) { | ||
return (type, target, property, descriptor, options = {}) => { | ||
const decorator = addAttribute(type, target, property, descriptor, options); | ||
addLazyInitializer(target, (model) => { | ||
const schema = getDefaultModelSchema(target); | ||
if (schema && schema.props[property]) { | ||
const schemaProps = schema.props[property]; | ||
intercept(model, property, (change => interceptingFn(change, { | ||
inverseOf: schemaProps.inverseOf, | ||
modelClass: schemaProps.model, | ||
parentModel: model, | ||
parentModelProp: property, | ||
defaultAttributes: schemaProps.defaults, | ||
}))); | ||
} | ||
}); | ||
return decorator; | ||
}; | ||
} | ||
const hasMany = (...args) => buildAttributeDecorator( | ||
'hasMany', args, interceptingDecorator(onHasManySet), | ||
); | ||
const belongsTo = (...args) => buildAttributeDecorator( | ||
'belongsTo', args, interceptingDecorator(onBelongsToSet), | ||
); | ||
const identifier = (...args) => buildAttributeDecorator('identifier', args); | ||
const field = (...args) => buildAttributeDecorator('field', args); | ||
const session = (...args) => buildAttributeDecorator('session', args); | ||
const belongsTo = (...args) => buildAttributeDecorator('belongsTo', args); | ||
const hasManyBuilder = (type, target, property, descriptor, options = {}) => | ||
addAttribute(type, target, property, descriptor, options); | ||
const hasMany = (...args) => buildAttributeDecorator('hasMany', args, hasManyBuilder); | ||
export { field, session, belongsTo, hasMany, identifier, buildCollection }; |
export function isSerializable(model, property) { | ||
return !!(!model.$nonSerializable || -1 === model.$nonSerializable.indexOf(property)); | ||
return !!(!model.$nonSerializable || !model.$nonSerializable[property]); | ||
} | ||
export function markNonserializable(model, property) { | ||
model.$nonSerializable = model.$nonSerializable || []; | ||
model.$nonSerializable.push(property); | ||
model.$nonSerializable = model.$nonSerializable || {}; | ||
model.$nonSerializable[property] = true; | ||
} |
{ | ||
"name": "mobx-decorated-models", | ||
"version": "0.3.3", | ||
"version": "0.4.0", | ||
"description": "Decorators to make using Mobx for model type structures easier", | ||
@@ -5,0 +5,0 @@ "main": "dist/build.full.js", |
@@ -29,3 +29,3 @@ # Decorators for creating model type structures with mobx | ||
@modelDecorator | ||
@modelDecorator('box') | ||
export class Box { | ||
@@ -43,3 +43,3 @@ @identifier id; | ||
@belongsTo container; | ||
@belongsTo({ model: 'Address' }) warehouse; | ||
@belongsTo({ model: 'address' }) warehouse; | ||
} | ||
@@ -63,3 +63,3 @@ ``` | ||
By default, the class `@modelDecorator` uses the `name` property of each class as a lookup key so | ||
The class `@modelDecorator` uses either the `name` property of each class, or can be supplied with a unique string that should be used as a lookup key so | ||
that `hasMany` and `belongsTo` relation ships can be established. | ||
@@ -75,3 +75,3 @@ | ||
@modelDecorator | ||
@modelDecorator('chair') | ||
class Chair { | ||
@@ -85,5 +85,5 @@ belongsTo 'table' | ||
@modelDecorator | ||
@modelDecorator('table') | ||
class Table { | ||
hasMany({ model: 'Chair' }) 'seats' | ||
hasMany({ className: 'chair' }) 'seats' | ||
} | ||
@@ -154,2 +154,3 @@ ``` | ||
```javascript | ||
@modelDecorator | ||
class Foo { | ||
@@ -179,2 +180,3 @@ @field({ type: 'object' }) options; // will default to an observable map | ||
```javascript | ||
@modelDecorator | ||
class Person({ | ||
@@ -186,2 +188,3 @@ @identifier id; | ||
@modelDecorator | ||
class Pants { | ||
@@ -221,2 +224,3 @@ @session color; | ||
```javascript | ||
@modelDecorator | ||
class Tire { | ||
@@ -227,2 +231,3 @@ @session numberInSet; | ||
@modelDecorator | ||
class Car { | ||
@@ -234,2 +239,3 @@ @belongsTo home; | ||
@modelDecorator | ||
class Garage { | ||
@@ -247,4 +253,32 @@ @session owner; | ||
## unresolvedAssociations | ||
mobx-decorated-models attempts to do lazy lookups for the model that **hasMany** and **belongsTo** should use. In order to do so, it keeps track of associations that are not immediatly resolved in the hope that the model for them will be decorated with **@modelDecorator** later. | ||
However if the model is never decorated the association will continue to be set to a plain observable.object. | ||
Properties that are not resolved can be listed using the `unresolvedAssociations` method, which will return an array of object with model and property keys. | ||
**Example:** | ||
import { model, field, session, belongsTo, hasMany, identifier } from 'mobx-decorated-models'; | ||
@modelDecorator | ||
class Parallelogram { | ||
} | ||
@modelDecorator('box') | ||
class Box { | ||
hasMany sides; | ||
} | ||
unresolvedAssociations().forEach(({ model, property }) => { | ||
console.log(`The model for ${model.identifiedBy}(${property}) cannot be found`); | ||
}); | ||
// outputs: The model for box(sides) cannot be found | ||
# 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 |
@@ -1,3 +0,5 @@ | ||
import { Container, Box } from './test-models'; | ||
import { Container, Box, Ship } from './test-models'; | ||
import { unresolvedAssociations } from '../lib/class-decorator'; | ||
describe('Class Decorators', () => { | ||
@@ -92,2 +94,13 @@ it('adds static deserialize method and serialize to prototype', () => { | ||
}); | ||
it('reports on associations that are not resolved', () => { | ||
const box = Box.deserialize({ vessel: { id: 1 } }); | ||
expect(box.vessel).toEqual({ id: 1 }); | ||
expect(box.vessel).not.toBeInstanceOf(Ship); | ||
const pending = unresolvedAssociations(); | ||
expect(pending).toHaveLength(1); | ||
expect(pending[0].model).toBe(Box); | ||
expect(pending[0].property).toEqual('vessel'); | ||
}); | ||
}); |
@@ -0,4 +1,4 @@ | ||
import { autorun } from 'mobx'; | ||
import { Box, Container, Ship } from './test-models'; | ||
import { autorun, observable } from 'mobx'; | ||
import { update } from 'serializr'; | ||
import { isSerializable } from '../lib/serializable'; | ||
@@ -52,3 +52,5 @@ describe('Property Decorators', () => { | ||
const ship = Ship.deserialize({ name: 'HMS Mobx', box: { width: 42 } }); | ||
expect(ship.box.container).toBe(ship); | ||
expect(ship.box.depth).toEqual(1); | ||
expect(isSerializable(ship, 'box')).toBe(true); | ||
expect(isSerializable(ship.box, 'vessel')).toBe(false); | ||
expect(ship.serialize()).toEqual({ | ||
@@ -58,3 +60,3 @@ name: 'HMS Mobx', | ||
}); | ||
expect(ship.box.container_association_name).toEqual('box'); | ||
expect(ship.box.vessel_association_name).toEqual('box'); | ||
}); | ||
@@ -103,2 +105,22 @@ | ||
it('guards against setting a belongsTo', () => { | ||
const ship = Ship.deserialize({ name: 'HMS Glory' }); | ||
ship.box = { width: 1, height: 1, depth: 1 }; | ||
expect(ship.box).toBeInstanceOf(Box); | ||
expect(ship.box.vessel).toBe(ship); | ||
}); | ||
it('guards against setting a hasMany', () => { | ||
const container = Container.deserialize({ id: 1 }); | ||
const originalBoxes = container.boxes; | ||
container.boxes = [ | ||
{ width: 1, height: 1, depth: 1 }, | ||
{ width: 2, height: 2, depth: 2 }, | ||
]; | ||
expect(container.boxes).toBe(originalBoxes); | ||
expect(container.boxes[0]).toBeInstanceOf(Box); | ||
expect(container.boxes[1].container).toBe(container); | ||
}); | ||
}); |
@@ -11,10 +11,10 @@ import { observable, computed } from 'mobx'; | ||
@modelDecorator | ||
@modelDecorator('boat') | ||
export class Ship { | ||
@identifier name; | ||
@belongsTo({ inverseOf: 'container' }) box; | ||
@belongsTo({ inverseOf: 'vessel' }) box; | ||
} | ||
@modelDecorator | ||
@modelDecorator('box') | ||
export class Box extends RectangularCuboid { | ||
@@ -29,2 +29,3 @@ @identifier id; | ||
@session color; | ||
@session vessel_association_name; | ||
@@ -34,7 +35,7 @@ @computed get volume() { | ||
} | ||
@belongsTo vessel; | ||
@belongsTo container; | ||
} | ||
@modelDecorator | ||
@modelDecorator('container') | ||
export class Container extends RectangularCuboid { | ||
@@ -54,3 +55,3 @@ @identifier id; | ||
@hasMany({ | ||
className: 'Box', | ||
className: 'box', | ||
inverseOf: 'container', | ||
@@ -57,0 +58,0 @@ defaults() { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
208938
1499
273