diff --git a/.gitignore b/.gitignore index bb631a0971..1cc4e0a9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -13,24 +13,25 @@ log.txt ## jsdoc /doc - + ### SublimeText ### *.sublime-project *.sublime-workspace - + ### Intellij IDE ### .idea/ atlassian-ide-plugin.xml - + ### VisualStudioCode ### .vscode/* .vscode-upload.json - +.eslintrc.json + ### TextMate ### *.tmproj *.tmproject tmtags - + ### Node ### npm-debug.log* .npmignore diff --git a/.travis.yml b/.travis.yml index 01a610b3e2..c35104f8e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,8 @@ language: node_js node_js: - - 4 - - 6 - 8 - - 10 +services: + - xvfb script: npm run $COMMAND env: - COMMAND=test @@ -11,8 +10,7 @@ env: - COMMAND=integration MONTAGE_VERSION=. MOP_VERSION="#feature/npm3" before_install: - export CHROME_BIN=chromium-browser - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start + - export DISPLAY=:0 jobs: include: - stage: lint diff --git a/core/core.js b/core/core.js index fcd2f05df5..46a1a92dc8 100644 --- a/core/core.js +++ b/core/core.js @@ -308,8 +308,8 @@ Object.defineProperty(Montage.prototype, _serializableAttributeProperties, { var ObjectAttributeProperties = new Map(); function getAttributeProperties(proto, attributeName, privateAttributeName) { var attributePropertyName = privateAttributeName || ( - attributeName === SERIALIZABLE ? - _serializableAttributeProperties : + attributeName === SERIALIZABLE ? + _serializableAttributeProperties : (UNDERSCORE + attributeName + ATTRIBUTE_PROPERTIES)); if(proto !== Object.prototype) { @@ -422,7 +422,7 @@ valuePropertyDescriptor.value = function Montage_defineProperty(obj, prop, descr if (! (typeof obj === "object" || typeof obj === FUNCTION) || obj === null) { throw new TypeError("Object must be an object, not '" + obj + "'"); } - + var isValueDescriptor = (VALUE in descriptor); // reset defaults appropriately for framework. @@ -548,8 +548,8 @@ function __findSuperMethodImplementation( method, classFn, isFunctionSuper, meth if(!methodPropertyName) { //If methodPropertyNameArg is passed as an argument, we know what to look for, //But it may not be there... - propertyNames = methodPropertyNameArg ? - (_propertyNames[0] = methodPropertyNameArg) && _propertyNames : + propertyNames = methodPropertyNameArg ? + (_propertyNames[0] = methodPropertyNameArg) && _propertyNames : Object.getOwnPropertyNames(context); //As we start, we don't really know which property name points to method, we're going to find out: @@ -675,13 +675,13 @@ function __super(callerFn, methodPropertyName, isValue, isGetter, isSetter) { */ function _super() { // Figure out which function called us. - var callerFn = ( _super && _super.caller ) ? _super.caller : arguments.callee.caller, + var callerFn = ( _super && _super.caller ) ? _super.caller : arguments.callee.caller, superFn = __super.call(this,callerFn); return superFn ? superFn.apply(this, arguments) : undefined; } function _superForValue(methodName) { - var callerFn = ( _superForValue && _superForValue.caller ) ? _superForValue.caller : arguments.callee.caller, + var callerFn = ( _superForValue && _superForValue.caller ) ? _superForValue.caller : arguments.callee.caller, superFn = __super.call(this, callerFn, methodName, true, false, false); //We may need to cache that at some point if it gets called too often @@ -689,7 +689,7 @@ function _superForValue(methodName) { } function _superForGet(methodName) { - var callerFn = ( _superForGet && _superForGet.caller ) ? _superForGet.caller : arguments.callee.caller, + var callerFn = ( _superForGet && _superForGet.caller ) ? _superForGet.caller : arguments.callee.caller, superFn = __super.call(this, callerFn, methodName, false, true, false); //We may need to cache that at some point if it gets called too often @@ -697,7 +697,7 @@ function _superForGet(methodName) { } function _superForSet(methodName) { - var callerFn = ( _superForSet && _superForSet.caller ) ? _superForSet.caller : arguments.callee.caller, + var callerFn = ( _superForSet && _superForSet.caller ) ? _superForSet.caller : arguments.callee.caller, superFn = __super.call(this,callerFn,methodName,false,false,true); //We may need to cache that at some point if it gets called too often @@ -828,7 +828,7 @@ Montage.defineProperty(Montage, "getPropertyAttributes", {value: function (anObj for (var name in attributes) { if (hasProperty.call(attributes, name)) { attributeValues[name] = attributes[name]; - // should return the inherited defined attribute values + // should return the inherited defined attribute values } else { attributeValues[name] = attributes[name]; } @@ -979,7 +979,7 @@ Montage.defineProperty(Montage.prototype, "callDelegateMethod", { var delegateFunctionName = this.identifier + name.toCapitalized(); if ( - typeof this.identifier === "string" && + typeof this.identifier === "string" && typeof delegate[delegateFunctionName] === FUNCTION ) { delegateFunction = delegate[delegateFunctionName]; @@ -1666,7 +1666,7 @@ exports._objectDescriptorDescriptor = { get: function () { var info = Montage.getInfoForObject(this), self = info && !info.isInstance ? this : this.constructor; - + if (!Object.getOwnPropertyDescriptor(self, "_objectDescriptor") || !self._objectDescriptor ) { diff --git a/core/meta/module-object-descriptor.js b/core/meta/module-object-descriptor.js index 02a2ef7782..3318edb8af 100644 --- a/core/meta/module-object-descriptor.js +++ b/core/meta/module-object-descriptor.js @@ -60,6 +60,9 @@ var ModuleObjectDescriptor = exports.ModuleObjectDescriptor = ObjectDescriptor.s this.super(serializer); this._setPropertyWithDefaults(serializer, "module", this.module); this._setPropertyWithDefaults(serializer, "exportName", this.exportName); + if(this.object) { + this._setPropertyWithDefaults(serializer, "object", this.object); + } } }, @@ -82,6 +85,12 @@ var ModuleObjectDescriptor = exports.ModuleObjectDescriptor = ObjectDescriptor.s if (!this.exportName) { throw new Error("Cannot deserialize object descriptor without an exportName"); } + + value = deserializer.getProperty("object"); + if (value !== void 0) { + this.object = value; + } + } }, @@ -93,6 +102,15 @@ var ModuleObjectDescriptor = exports.ModuleObjectDescriptor = ObjectDescriptor.s value: null }, + /** + * A reference to the actual object that this object descriptor is for. + * @type {Object} + */ + object: { + value: null + }, + + /** * The name of the export this object descriptor is for. * @type {string} diff --git a/core/serialization/deserializer/montage-deserializer.js b/core/serialization/deserializer/montage-deserializer.js index 0679b2c04c..c1b5cc7b3d 100644 --- a/core/serialization/deserializer/montage-deserializer.js +++ b/core/serialization/deserializer/montage-deserializer.js @@ -25,24 +25,31 @@ var MontageDeserializer = exports.MontageDeserializer = Montage.specialize({ }, init: { - value: function (serialization, _require, objectRequires, locationId, isSync) { + value: function (serialization, _require, objectRequires, module, isSync) { if (typeof serialization === "string") { this._serializationString = serialization; } else { this._serializationString = JSON.stringify(serialization); } this._require = _require; - this._locationId = locationId ? locationId.indexOf(_require.location) === 0 ? locationId : _require.location + locationId : locationId; + this._module = module; + var locationId = module && _require.location + module.id; + this._locationId = locationId; this._reviver = new MontageReviver().init( - _require, objectRequires, this.constructor, isSync + _require, objectRequires, this, isSync, locationId ); this._isSync = isSync; return this; } }, - + _isSync: {value: false}, + isSync: { + get: function() { + return this._isSync; + } + }, /** * @param {Object} instances Map-like object of external user objects to @@ -55,7 +62,7 @@ var MontageDeserializer = exports.MontageDeserializer = Montage.specialize({ */ deserialize: { value: function (instances, element) { - var context = this._locationId && MontageDeserializer.moduleContexts.get(this._locationId), + var context = this._module && MontageDeserializer.moduleContexts.get(this._module), circularError; if (context) { if (context._objects.root) { @@ -79,7 +86,7 @@ var MontageDeserializer = exports.MontageDeserializer = Montage.specialize({ context = new MontageContext() .init(serialization, this._reviver, instances, element, this._require, this._isSync); if (this._locationId) { - MontageDeserializer.moduleContexts.set(this._locationId, context); + MontageDeserializer.moduleContexts.set(this._module, context); } try { return context.getObjects(); @@ -102,9 +109,10 @@ var MontageDeserializer = exports.MontageDeserializer = Montage.specialize({ deserializeObject: { value: function(objects) { - return this.deserialize(objects).then(function(objects) { - return objects.root; - }); + return (this._isSync ? this.deserialize(objects).root + : this.deserialize(objects).then(function(objects) { + return objects.root; + })); } }, diff --git a/core/serialization/deserializer/montage-interpreter.js b/core/serialization/deserializer/montage-interpreter.js index 760534c126..219f066d38 100644 --- a/core/serialization/deserializer/montage-interpreter.js +++ b/core/serialization/deserializer/montage-interpreter.js @@ -2,6 +2,7 @@ var Montage = require("../../core").Montage, MontageReviver = require("./montage-reviver").MontageReviver, Promise = require("../../promise").Promise, deprecate = require("../../deprecate"), + Set = require("collections/set"), ONE_ASSIGNMENT = "=", ONE_WAY = "<-", TWO_WAY = "<->"; @@ -77,9 +78,13 @@ var MontageInterpreter = Montage.specialize({ } }); +var idCount = 0; var MontageContext = Montage.specialize({ _ELEMENT_ID_ATTRIBUTE: {value: "data-montage-id"}, - _unitsToDeserialize: {value: null}, + _ELEMENT_ID_SELECTOR_PREFIX: {value: '*[data-montage-id="'}, + _ELEMENT_ID_SELECTOR_SUFFIX: {value: '"]'}, + unitsToDeserialize: {value: null}, + _mjsonDependencies: {value: null}, _element: {value: null}, _require: {value: null}, _objects: {value: null}, @@ -90,7 +95,7 @@ var MontageContext = Montage.specialize({ constructor: { value: function () { - this._unitsToDeserialize = []; + this.unitsToDeserialize = new Map(); } }, @@ -118,6 +123,13 @@ var MontageContext = Montage.specialize({ } }, + _isSync: {value: false}, + isSync: { + get: function() { + return this._isSync; + } + }, + setObjectLabel: { value: function(object, label) { this._objects[label] = object; @@ -158,22 +170,24 @@ var MontageContext = Montage.specialize({ value: function() { var self = this, serialization = this._serialization, - promises = [], - result; + promises, + result, + objectKeys; - for (var label in serialization) { - if (serialization.hasOwnProperty(label)) { + if(serialization) { + objectKeys = Object.keys(serialization); + for (var i=0, label;(label = objectKeys[i]); i++) { result = this.getObject(label); if (Promise.is(result)) { - promises.push(result); + (promises || (promises = [])).push(result); } } } - if (promises.length === 0) { + if (!promises || promises.length === 0) { result = this._invokeDidReviveObjects(); - return this._isSync ? result : Promise.resolve(result); + return this._isSync ? result : Promise.is(result) ? result : Promise.resolve(result); } else { // We shouldn't get here if this._isSync is true return Promise.all(promises).then(function() { @@ -208,11 +222,10 @@ var MontageContext = Montage.specialize({ _invokeDidReviveObjects: { value: function() { var self = this, - reviver = this._reviver, - result; + reviver = this._reviver; if (typeof reviver.didReviveObjects === "function") { - reviver.didReviveObjects(this._objects, this); + reviver.didReviveObjects(this); return self._objects; } @@ -240,31 +253,45 @@ var MontageContext = Montage.specialize({ getElementById: { value: function (id) { - var selector = '*[' + this._ELEMENT_ID_ATTRIBUTE + '="' + id + '"]'; - - return this._element.querySelector(selector); + return this._element.querySelector(this._ELEMENT_ID_SELECTOR_PREFIX + id + this._ELEMENT_ID_SELECTOR_SUFFIX); } }, - _extractBindingsToDeserialize: { - value: function (values, bindings) { - var value; - - for (var key in values) { - if (values.hasOwnProperty(key)) { - value = values[key]; - - if ((typeof value === "object" && value && - Object.keys(value).length === 1 && - (ONE_WAY in value || TWO_WAY in value || ONE_ASSIGNMENT in value)) || - key.indexOf('.') > -1 - ) { - bindings[key] = value; - delete values[key]; + _classifyValuesToDeserialize: { + value: function (object, objectDesc) { + var values, + value, + keys, + bindings; + + + //This is where we support backward compatib + if((values = objectDesc.properties)) { + objectDesc.values = values; + delete objectDesc.properties; + } + else { + if((values = objectDesc.values)) { + keys = Object.keys(values); + bindings = objectDesc.bindings || (objectDesc.bindings = {}); + for (var i=0, key;(key = keys[i]);i++) { + value = values[key]; + + //An expression based property + if ((typeof value === "object" && value && + Object.keys(value).length === 1 && + (ONE_WAY in value || TWO_WAY in value || ONE_ASSIGNMENT in value)) || + key.indexOf('.') > -1 + ) { + bindings[key] = value; + delete values[key]; + } } + } } + return bindings; } }, @@ -275,44 +302,83 @@ var MontageContext = Montage.specialize({ } }, + __propertyToReviveForObjectLiteralValue: { + value: undefined + }, + _propertyToReviveForObjectLiteralValue: { + get: function() { + return this.__propertyToReviveForObjectLiteralValue || (this.__propertyToReviveForObjectLiteralValue = new WeakMap()); + } + }, + propertyToReviveForObjectLiteralValue: { + value: function (objectLiteralValue) { + var propertyToRevive; + if(!(propertyToRevive = this._propertyToReviveForObjectLiteralValue.get(objectLiteralValue))) { + this._propertyToReviveForObjectLiteralValue.set(objectLiteralValue,(propertyToRevive = new Set(Object.keys(objectLiteralValue)))); + } + return propertyToRevive; + } + }, + + _objectDynamicValuesToDeserialize: { + value: undefined + }, + _objectValuesToDeserialize: { + value: undefined + }, + objectValuesToDeserialize: { + get: function() { + return this._objectValuesToDeserialize || (this._objectValuesToDeserialize = new Map()); + } + }, + + + setBindingsToDeserialize: { value: function (object, objectDesc) { - var bindings = Object.create(null); + this._classifyValuesToDeserialize(object, objectDesc); + } + }, + + setUnitsToDeserialize: { + value: function (object, objectDesc, unitNames) { + + var moduleId = objectDesc.prototype || objectDesc.object, + isMJSONDependency = moduleId && (moduleId.endsWith(".mjson") || moduleId.endsWith(".meta")), + unitsDesc = this.unitsToDeserialize.get(object); - if (objectDesc.values) { - this._extractBindingsToDeserialize(objectDesc.values, bindings); - } else if (objectDesc.properties) { // deprecated - this._extractBindingsToDeserialize(objectDesc.properties, bindings); + if(isMJSONDependency) { + (this._mjsonDependencies || (this._mjsonDependencies = new Set())).add(moduleId); } - if (Object.keys(bindings).length > 0) { - if (!this._bindingsToDeserialize) { - this._bindingsToDeserialize = []; + if(unitsDesc) { + if(unitNames !== unitsDesc.unitNames || unitsDesc.objectDesc !== objectDesc) { + var unitsDescObjectDesc = unitsDesc.objectDesc, + unitsDescUnitNames = unitsDesc.unitNames; + + for(var i=0, iUniteName, countI = unitNames.length;(i 0) { + unitsToDeserializeKeys = unitsToDeserialize.keys(); + unitDeserializer = new UnitDeserializer(); + + while((object = unitsToDeserializeKeys.next().value)) { + this._deserializeObjectUnit(context, object, unitsToDeserialize.get(object), unitDeserializer); + unitsToDeserialize.delete(object); } } } @@ -816,34 +891,40 @@ var MontageReviver = exports.MontageReviver = Montage.specialize(/** @lends Mont reviveObjectLiteral: { value: function(value, context, label, filterKeys) { var item, - promises = []; + promises, + propertyNames = context.propertyToReviveForObjectLiteralValue(value), + propertyName,iValue, + propertyNamesIterator = propertyNames.values(); + if (label) { context.setObjectLabel(value, label); } - for (var propertyName in value) { - if (value.hasOwnProperty(propertyName) && (!filterKeys || filterKeys.indexOf(propertyName) > -1)) { - if (value[propertyName] === value) { + while((propertyName = propertyNamesIterator.next().value)) { + if ((!filterKeys || filterKeys.indexOf(propertyName) > -1)) { + if ((iValue = value[propertyName]) === value) { // catch object property that point to its parent return value; } - item = this.reviveValue(value[propertyName], context); + item = this.reviveValue(iValue, context); if (Promise.is(item)) { - promises.push( + (promises || (promises = [])).push( item.then(this._createAssignValueFunction( value, propertyName) ) ); - } else { + } else if(iValue !== item) { value[propertyName] = item; } + + propertyNames.delete(propertyName); } } - if (promises.length === 0) { + if (!promises || (promises && promises.length === 0)) { return value; } else { return Promise.all(promises).then(function() { @@ -1049,6 +1130,53 @@ MontageReviver.findProxyForElement = function (element) { return PROXY_ELEMENT_MAP.get(element); }; + +MontageReviver.defineUnitReviver("values", function (unitDeserializer, object, values,_unitsDesc) { + var context = unitDeserializer.context, + montageObjectDesc = _unitsDesc.objectDesc, + substituteObject; + + if (typeof object.deserializeSelf === "function") { + + + var selfDeserializer = new SelfDeserializer(); + //If the context is sync, we carry the info to the selfDeserializer + selfDeserializer.isSync = context.isSync; + selfDeserializer.initWithObjectAndObjectDescriptorAndContextAndUnitNames(object, montageObjectDesc, context, MontageReviver._unitNames); + substituteObject = (substituteObject || object).deserializeSelf(selfDeserializer); + + if (typeof substituteObject !== "undefined" && substituteObject !== object) { + //The deserialization used to be inlined, so it was easy to substitute. + //setObjectLabel only replaces the object on the root dictionary. It's possible + //that properties pointing to the original object would do after the substitution + //even in the previous implementation. + //we got to find how to substitute.... We don't know which label it is + //This is rare so we're just going to walk objets. + var label, + context_objects = context._objects; + + for(label in context_objects) { + if(context_objects[label] === object) { + context.setObjectLabel(substituteObject, label); + break; + } + } + + } + } + else if(values) { + context._reviver.deserializeMontageObjectValues( + object, + montageObjectDesc.values || montageObjectDesc.properties, //deprecated + context + ); + } + + + +}); + + if (typeof exports !== "undefined") { exports.MontageReviver = MontageReviver; diff --git a/core/serialization/deserializer/self-deserializer.js b/core/serialization/deserializer/self-deserializer.js index 59bb87939b..4a4a0cfebe 100644 --- a/core/serialization/deserializer/self-deserializer.js +++ b/core/serialization/deserializer/self-deserializer.js @@ -2,6 +2,16 @@ var Montage = require("../../core").Montage, deprecate = require("../../deprecate"); var SelfDeserializer = Montage.specialize( { + _isSync: {value: false}, + isSync: { + get: function() { + return this._isSync; + }, + set: function(value) { + this._isSync = value; + } + }, + _object: {value: null}, _objectDescriptor: {value: null}, _context: {value: null}, @@ -18,6 +28,7 @@ var SelfDeserializer = Montage.specialize( { value: function (object, objectDescriptor, context, unitNames) { this._object = object; this._objectDescriptor = objectDescriptor; + this._objectDescriptorValues = objectDescriptor.values || objectDescriptor.properties || objectDescriptor; this._context = context; this._unitNames = unitNames; diff --git a/core/serialization/deserializer/unit-deserializer.js b/core/serialization/deserializer/unit-deserializer.js index 88cc1b6304..29377536c5 100644 --- a/core/serialization/deserializer/unit-deserializer.js +++ b/core/serialization/deserializer/unit-deserializer.js @@ -17,6 +17,12 @@ var UnitDeserializer = Montage.specialize(/** @lends UnitDeserializer# */ { } }, + context: { + get: function() { + return this._context; + } + }, + _templatePropertyRegExp: { value: /^([^:]+)(:.*)$/ }, diff --git a/data/converter/raw-embedded-value-to-object-converter.js b/data/converter/raw-embedded-value-to-object-converter.js new file mode 100644 index 0000000000..654277428e --- /dev/null +++ b/data/converter/raw-embedded-value-to-object-converter.js @@ -0,0 +1,106 @@ +var RawValueToObjectConverter = require("./raw-value-to-object-converter").RawValueToObjectConverter, +Promise = require("core/promise").Promise; +/** + * @class RawEmbeddedRelationshipValueToObjectConverter + * @classdesc Converts a property value of raw data to the referenced object. + * @extends RawValueToObjectConverter + */ +exports.RawEmbeddedValueToObjectConverter = RawValueToObjectConverter.specialize( /** @lends RawEmbeddedValueToObjectConverter# */ { + + /********************************************************************* + * Properties + */ + + /********************************************************************* + * Public API + */ + + /** + * Converts the fault for the relationship to an actual object that has an ObjectDescriptor. + * @function + * @param {Property} v The value to format. + * @returns {Promise} A promise for the referenced object. The promise is + * fulfilled after the object is successfully fetched. + */ + convert: { + value: function (v) { + var self = this, + convertedValue, + result; + + + return Promise.all([this._descriptorToFetch, this.service]).then(function (values) { + var typeToFetch = values[0], + service = values[1]; + + if(Array.isArray(v)) { + if(v.length) { + convertedValue = []; + for(var i=0, countI=v.length, promises;(i} */ addTriggers: { - value: function (service, type, prototype, requisitePropertyNames) { + value: function (service, type, prototype, requisitePropertyDescriptors) { // This function was split into two to provide backwards compatibility // to existing Montage data projects. Future montage data projects // should base their object descriptors on Montage's version of object // descriptor. var isMontageDataType = type instanceof DataObjectDescriptor || type instanceof ObjectDescriptor; return isMontageDataType ? this._addTriggersForMontageDataType(service, type, prototype, name) : - this._addTriggers(service, type, prototype, requisitePropertyNames); + this._addTriggers(service, type, prototype, requisitePropertyDescriptors); } }, _addTriggersForMontageDataType: { value: function (service, type, prototype) { var triggers = {}, - names = Object.keys(type.propertyDescriptors), - trigger, name, i; - for (i = 0; (name = names[i]); ++i) { - trigger = this.addTrigger(service, type, prototype, name); + propertyDescriptors = Object.keys(type.propertyDescriptors), + trigger, iPropertyDescriptor, i; + for (i = 0; (iPropertyDescriptor = propertyDescriptors[i]); ++i) { + trigger = this.addTrigger(service, type, prototype, iPropertyDescriptor); if (trigger) { - triggers[name] = trigger; + triggers[iPropertyDescriptor.name] = trigger; } } return triggers; @@ -403,14 +429,14 @@ Object.defineProperties(exports.DataTrigger, /** @lends DataTrigger */ { }, _addTriggers: { - value: function (service, objectDescriptor, prototype, requisitePropertyNames) { - var triggers = {}, + value: function (service, objectDescriptor, prototype, requisitePropertyDescriptors) { + var triggers = {}, propertyDescriptors = objectDescriptor.propertyDescriptors, propertyDescriptor, trigger, name, i; for (i = 0; (propertyDescriptor = propertyDescriptors[i]); i += 1) { name = propertyDescriptor.name; - trigger = this.addTrigger(service, objectDescriptor, prototype, name); + trigger = this.addTrigger(service, objectDescriptor, prototype, propertyDescriptor); if (trigger) { triggers[name] = trigger; } @@ -427,14 +453,14 @@ Object.defineProperties(exports.DataTrigger, /** @lends DataTrigger */ { * @returns {?DataTrigger} */ addTrigger: { - value: function (service, type, prototype, name) { + value: function (service, type, prototype, propertyDescriptor) { // This function was split into two to provide backwards compatibility // to existing Montage data projects. Future montage data projects // should base their object descriptors on Montage's version of object // descriptor. var isMontageDataType = type instanceof DataObjectDescriptor || type instanceof ObjectDescriptor; - return isMontageDataType ? this._addTriggerForMontageDataType(service, type, prototype, name) : - this._addTrigger(service, type, prototype, name); + return isMontageDataType ? this._addTriggerForMontageDataType(service, type, prototype, propertyDescriptor.name) : + this._addTrigger(service, type, prototype, propertyDescriptor); } }, @@ -445,7 +471,7 @@ Object.defineProperties(exports.DataTrigger, /** @lends DataTrigger */ { if (descriptor && descriptor.isRelationship) { trigger = Object.create(this._getTriggerPrototype(service)); trigger._objectPrototype = prototype; - trigger._propertyName = name; + trigger.propertyDescriptor = descriptor; trigger._isGlobal = descriptor.isGlobal; Montage.defineProperty(prototype, name, { get: function () { @@ -481,7 +507,7 @@ Object.defineProperties(exports.DataTrigger, /** @lends DataTrigger */ { var trigger = Object.create(this._getTriggerPrototype(service)), serviceTriggers = service._dataObjectTriggers.get(objectDescriptor); trigger._objectPrototype = prototype; - trigger._propertyName = name; + trigger.propertyDescriptor = propertyDescriptor; trigger._isGlobal = propertyDescriptor.isGlobal; if(!serviceTriggers) { serviceTriggers = {}; @@ -492,19 +518,19 @@ Object.defineProperties(exports.DataTrigger, /** @lends DataTrigger */ { } }, _addTrigger: { - value: function (service, objectDescriptor, prototype, name) { - var descriptor = objectDescriptor.propertyDescriptorForName(name), - trigger; + value: function (service, objectDescriptor, prototype, descriptor) { + // var descriptor = objectDescriptor.propertyDescriptorForName(name), + var trigger; if (descriptor) { trigger = Object.create(this._getTriggerPrototype(service)); trigger._objectPrototype = prototype; - trigger._propertyName = name; + trigger.propertyDescriptor = descriptor; trigger._isGlobal = descriptor.isGlobal; if (descriptor.definition) { - Montage.defineProperty(prototype, name, { + Montage.defineProperty(prototype, descriptor.name, { get: function () { - if (!this.getBinding(name)) { - this.defineBinding(name, {"<-": descriptor.definition}); + if (!this.getBinding(descriptor.name)) { + this.defineBinding(descriptor.name, {"<-": descriptor.definition}); } return trigger._getValue(this); // return (trigger||(trigger = DataTrigger._createTrigger(service, objectDescriptor, prototype, name,descriptor)))._getValue(this); @@ -515,7 +541,7 @@ Object.defineProperties(exports.DataTrigger, /** @lends DataTrigger */ { } }); } else { - Montage.defineProperty(prototype, name, { + Montage.defineProperty(prototype, descriptor.name, { get: function () { return trigger._getValue(this); // return (trigger||(trigger = DataTrigger._createTrigger(service, objectDescriptor, prototype, name,descriptor)))._getValue(this); diff --git a/data/service/expression-data-mapping.js b/data/service/expression-data-mapping.js index a86a9a96bd..35d1bf29b8 100644 --- a/data/service/expression-data-mapping.js +++ b/data/service/expression-data-mapping.js @@ -70,11 +70,13 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData self = this, hasReferences = false, result = this; - if (value instanceof ObjectDescriptorReference) { - this.objectDescriptorReference = value; - hasReferences = true; - } else { - this.objectDescriptor = value; + if (value) { + if (value instanceof ObjectDescriptorReference) { + this.objectDescriptorReference = value; + hasReferences = true; + } else { + this.objectDescriptor = value; + } } this.schemaReference = deserializer.getProperty("schema"); @@ -92,7 +94,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData this.rawDataPrimaryKeys = value; } - if (hasReferences) { + if (hasReferences && !deserializer.isSync) { result = this.resolveReferences().then(function () { value = deserializer.getProperty("objectMapping"); if (value) { @@ -105,6 +107,11 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData return self; }); } else { + + if(!this.objectDescriptor) { + this.objectDescriptor = deserializer._context._require(this._objectDescriptorReference._reference.objectDescriptorModule.id).montageObject; + } + value = deserializer.getProperty("objectMapping"); if (value) { self._rawOwnObjectMappingRules = value.rules; @@ -456,7 +463,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData isRelationship = propertyDescriptor && !propertyDescriptor.definition && propertyDescriptor.valueDescriptor, isDerived = propertyDescriptor && !!propertyDescriptor.definition, scope = this._scope, - debug = DataService.debugProperties.has(propertyName); + debug = DataService.debugProperties.has(propertyName) || (rule && rule.debug === true); // Check if property is included in the DataService.debugProperties collection. Intended for debugging. @@ -469,6 +476,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData this._prepareRawDataToObjectRule(rule, propertyDescriptor); + return isRelationship ? this._resolveRelationship(object, propertyDescriptor, rule, scope) : propertyDescriptor && !isDerived ? this._resolveProperty(object, propertyDescriptor, rule, scope) : null; @@ -480,10 +488,13 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData var self = this, hasInverse = !!propertyDescriptor.inversePropertyName || !!rule.inversePropertyName, data; + return rule.evaluate(scope).then(function (result) { data = result; + return hasInverse ? self._assignInversePropertyValue(data, object, propertyDescriptor, rule) : null; }).then(function () { + self._setObjectValueForPropertyDescriptor(object, data, propertyDescriptor); return null; }); @@ -497,7 +508,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData return propertyDescriptor.valueDescriptor.then(function (objectDescriptor) { var inversePropertyDescriptor = objectDescriptor.propertyDescriptorForName(inversePropertyName); - + if (data) { self._setObjectsValueForPropertyDescriptor(data, object, inversePropertyDescriptor); } @@ -607,7 +618,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData }, /** - * Maps the value of a single object property to raw data. Assumes that + * Maps the value of a single object property to raw data. Assumes that * the object property has been resolved * * @method @@ -640,7 +651,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData }, /** - * Prefetches any object properties required to map the rawData property + * Prefetches any object properties required to map the rawData property * and maps once the fetch is complete. * * @method @@ -669,7 +680,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData return result; } }, - + /** * Convert model object properties to the raw data properties present in the requirements * for a given propertyName @@ -702,7 +713,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData return promises ? Promise.all(promises) : null; } }, - + _prepareObjectToRawDataRule: { value: function (rule) { var converter = rule.converter, @@ -750,9 +761,7 @@ exports.ExpressionDataMapping = DataMapping.specialize(/** @lends ExpressionData if (Array.isArray(value)) { isToMany = propertyDescriptor.cardinality !== 1; - if (isToMany && Array.isArray(object[propertyName])) { - object[propertyName].splice.apply(object[propertyName], [0, Infinity].concat(value)); - } else if (isToMany) { + if (isToMany) { object[propertyName] = value; } else if (value.length) { //Cardinality is 1, if data contains more than 1 item, we throw diff --git a/data/service/raw-data-service.js b/data/service/raw-data-service.js index 696549f3c1..1f56b52573 100644 --- a/data/service/raw-data-service.js +++ b/data/service/raw-data-service.js @@ -435,6 +435,12 @@ exports.RawDataService = DataService.specialize(/** @lends RawDataService.protot } this._addMapDataPromiseForStream(result, stream); + + //TODO: #warning + //This method should evolve to use resolveObjectForTypeRawData instead, + //however resolveObjectForTypeRawData's promises resolves to object + //only after it's been mapped, so this delegate call should only be called then + //and not too early as it is now. Not sure if that may create a backward compatibility issue if (object) { this.callDelegateMethod("rawDataServiceDidAddOneRawData", this, stream, rawData, object); } @@ -461,10 +467,55 @@ exports.RawDataService = DataService.specialize(/** @lends RawDataService.protot } }, + /** + * Called by [addRawData()]{@link RawDataService#addRawData} to add an object + * for the passed record to the stream. This method both takes care of doing + * mapRawDataToObject and add the object to the stream. + * + * @method + * @argument {ObjectDescriptor} type + * - The type of the data object matching rawData. + * @argument {Object} rawData - An anonymnous object whose properties' + * values hold the raw data. + * @argument {?} context - An arbitrary value that will be passed to + * [getDataObject()]{@link RawDataService#getDataObject} + * and + * [mapRawDataToObject()]{@link RawDataService#mapRawDataToObject} + * if it is provided. + * + * @returns {Promise} - A promise resolving to the mapped object. + * + */ + + resolveObjectForTypeRawData: { + value:function(type, rawData, context) { + var dataIdentifier = this.dataIdentifierForTypeRawData(type,rawData), + //Retrieves an existing object is responsible data service is uniquing, or creates one + object, result; + + //Record snapshot before we may create an object + this.recordSnapshot(dataIdentifier, rawData); + + //Retrieves an existing object is responsible data service is uniquing, or creates one + object = this.getDataObject(type, rawData, context, dataIdentifier); + + result = this._mapRawDataToObject(rawData, object, context); + + if (Promise.is(result)) { + return result.then(function () { + return object; + }); + } else { + return Promise.resolve(object); + } + } + }, + + objectForTypeRawData: { value:function(type, rawData, context) { var dataIdentifier = this.dataIdentifierForTypeRawData(type,rawData); - //Record snapshot before we may create an object + //Record snapshot before we may create an object this.recordSnapshot(dataIdentifier, rawData); //iDataIdentifier argument should be all we need later on return this.getDataObject(type, rawData, context, dataIdentifier); diff --git a/karma.conf.js b/karma.conf.js index 1981ed82c0..b29fcdf58e 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -3,7 +3,7 @@ // Karma configuration // Generated on Tue Mar 07 2017 13:59:10 GMT-0800 (PST) module.exports = function(config) { - config.set({ + var cfg = { // base path, that will be used to resolve files and exclude basePath: '.', @@ -14,6 +14,8 @@ module.exports = function(config) { browserNoActivityTimeout: 30000, + browserSocketTimeout: 80000, + // list of files / patterns to load in the browser files: [ 'test/run-karma.js', @@ -221,5 +223,12 @@ module.exports = function(config) { 'karma-ie-launcher', 'karma-phantomjs-launcher' ] - }); + }; + +// if (process.env.TRAVIS) { +// cfg.browsers = ['Chrome_travis_ci']; +// } + + config.set(cfg); + }; diff --git a/montage.js b/montage.js index 1f2cf7a5a9..de5df1fe09 100644 --- a/montage.js +++ b/montage.js @@ -18,9 +18,11 @@ // reassigning causes eval to not use lexical scope. var globalEval = eval, - /*jshint evil:true */ - global = globalEval('this'); - /*jshint evil:false */ + /*jshint evil:true */ + global = globalEval('this'), + /*jshint evil:false */ + montageExports = exports; + // Here we expose global for legacy mop support. // TODO move to mr cause it's loader role to expose @@ -400,12 +402,137 @@ } }; - exports.compileMJSONFile = function (mjson, require, moduleId) { - var deserializer = new exports.MontageDeserializer(); - deserializer.init(mjson, require, void 0, require.location + moduleId); - return deserializer.deserializeObject(); + exports.MJSONCompilerFactory = function MJSONCompilerFactory(require, exports, module, global, moduleFilename, moduleDirectory) { + + //var root = Require.delegate.compileMJSONFile(module.text, require.config.requireForId(module.id), module.id, /*isSync*/ true); + + if(module.exports.hasOwnProperty("montageObject")) { + throw new Error( + 'using reserved word as property name, \'montageObject\' at: ' + + module.location + ); + } + + if(!module.deserializer) { + // var root = Require.delegate.compileMJSONFile(module.text, require.config.requireForId(module.id), module, /*isSync*/ true); + if(!montageExports.MontageDeserializer) { + montageExports.MontageDeserializer = require("montage/core/serialization/deserializer/montage-deserializer").MontageDeserializer; + } + + var deserializer = new montageExports.MontageDeserializer(), + deserializerRequire = require.config.requireForId(module.id), + root; + module.deserializer = deserializer; + deserializer.init(module.text, deserializerRequire, void 0, module, true); + root = deserializer.deserializeObject(); + + // console.log("********MJSONCompilerFactory END compileMJSONFile",module.id); + + if (module.exports.montageObject && module.exports.montageObject !== root) { + throw new Error( + 'Final deserialized object is different than one set on module ' + + module.location + ); + } + else if(!module.exports.montageObject) { + module.exports.montageObject = root; + } + + if(module.exports) { + Object.assign(module.exports, module.parsedText); + } + else { + module.exports = module.parsedText; + } + + module.deserializer = null; + module.text = null; + + } + + // console.log("********MJSONCompilerFactory END montageObject THERE",module.id); + + }; + exports.parseMJSONDependencies = function parseMJSONDependencies(jsonRoot) { + + var rootEntries = Object.keys(jsonRoot), + i=0, iLabel, dependencies = [], iLabelObject; + + while ((iLabel = rootEntries[i])) { + iLabelObject = jsonRoot[iLabel]; + if(iLabelObject.hasOwnProperty("prototype")) { + dependencies.push(iLabelObject["prototype"]); + + //This is to enable expression-data-mapping to deserialize itself synchronously + //despite the fact it may have been serialized using object-descriptor-reference. + //This allows us to add the objectDescriptorModule's id ("%") as a dependency upfront. + //A stronger version would analyze the whole file for the construct: {"%": "someModuleId"}. + //But this would impact performance for a use case that we don't need so far. + if(dependencies[dependencies.length-1] === "montage/core/meta/object-descriptor-reference") { + /* + We're adding the module of that referrence, typiacally serialized as: + "ObjectDescriptorReference": { + "prototype": "montage/core/meta/object-descriptor-reference", + "properties": { + "valueReference": { + "objectDescriptor": "Object_Descriptor_Name", + "prototypeName": "Object_Descriptor__Prototype_Name", + "objectDescriptorModule": {"%": "Object_Descriptor__module_id"} + } + } + }, + */ + dependencies.push(iLabelObject.properties.valueReference.objectDescriptorModule["%"]); + } + + } + else if(iLabelObject.hasOwnProperty("object")) { + dependencies.push(iLabelObject["object"]); + } + + i++; + } + return dependencies; + }; + + var dotMeta = ".meta", + dotMJSON = ".mjson", + dotMJSONLoadJs = ".mjson.load.js"; + + exports.Compiler = function (config, compile) { + return function(module) { + + if (module.exports || module.factory || (typeof module.text !== "string") || (typeof module.exports === "object")) { + return module; + } + + var location = module.location, + isMJSON = (location && (location.endsWith(dotMJSON) || location.endsWith(dotMJSONLoadJs) || location.endsWith(dotMeta))); + + if (isMJSON) { + if (typeof module.exports !== "object" && typeof module.text === "string") { + module.parsedText = JSON.parse(module.text); + if (module.parsedText.montageObject) { + throw new Error( + 'using reserved word as property name, \'montageObject\' at: ' + + location + ); + } + } + module.dependencies = montageExports.parseMJSONDependencies(module.parsedText); + module.factory = exports.MJSONCompilerFactory; + + return module; + } else { + var result = compile(module); + return result; + } + }; + }; + + exports.initMontageCustomElement = function () { if (typeof window.customElements === 'undefined' || typeof window.Reflect === 'undefined') { return void 0; diff --git a/node.js b/node.js index 1b93bc16dd..830239ec58 100644 --- a/node.js +++ b/node.js @@ -83,17 +83,7 @@ function getMontageMontageDeserializer() { })); } -exports.compileMJSONFile = function (mjson, require, moduleId) { - if (MontageBoot.MontageDeserializer) { - return MontageBoot.compileMJSONFile(mjson, require, moduleId); - } else { - return getMontageMontageDeserializer().then(function () { - return MontageBoot.compileMJSONFile(mjson, require, moduleId); - }); - } -}; - -Require.delegate = exports; +Require.delegate = MontageBoot; function parseHtml(html) { var dom, error; diff --git a/package.json b/package.json index 13fbf552ea..143058072a 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ }, "main": "montage", "engines": { - "node": ">=4", - "npm": ">=2" + "node": ">=10.16.2", + "npm": ">=6.9.0" }, "overlay": { "browser": { @@ -48,12 +48,12 @@ }, "production": true, "dependencies": { - "bluebird": "~3.5.0", + "bluebird": "~3.5.5", "collections": "~5.1.x", "frb": "~4.0.x", "htmlparser2": "~3.0.5", "q-io": "^1.13.3", - "mr": "18.0.0-rc2", + "mr": "montagejs/mr#master", "weak-map": "^1.0.5", "lodash.kebabcase": "^4.1.1", "lodash.camelcase": "^4.3.0", @@ -68,13 +68,13 @@ "jasmine-console-reporter": "^1.2.7", "jasmine-core": "^2.5.2", "jshint": "^2.9.5", - "karma": "^1.5.0", - "karma-chrome-launcher": "^2.0.0", - "karma-coverage": "^1.1.1", - "karma-firefox-launcher": "^1.0.1", + "karma": "^4.2.0", + "karma-chrome-launcher": "^3.0.0", + "karma-coverage": "^1.1.2", + "karma-firefox-launcher": "^1.2.0", "karma-ie-launcher": "^1.0.0", - "karma-jasmine": "^1.1.0", - "karma-phantomjs-launcher": "^1.0.2", + "karma-jasmine": "^2.0.1", + "karma-phantomjs-launcher": "^1.0.4", "karma-safari-launcher": "^1.0.0", "montage-testing": "git://github.com/montagejs/montage-testing.git#master", "mop-integration": "git://github.com/montagejs/mop-integration.git#master", diff --git a/test/spec/core/module-mjson/package.json b/test/spec/core/module-mjson/package.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/test/spec/core/module-mjson/package.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/spec/core/module-mjson/program.js b/test/spec/core/module-mjson/program.js new file mode 100644 index 0000000000..a9007131d5 --- /dev/null +++ b/test/spec/core/module-mjson/program.js @@ -0,0 +1,7 @@ +var data = require("spec/core/module-mjson/test.mjson"); + +describe("core/module-mjson", function () { + + expect(data.Hello === "World", 'parse string').toBe(true); + +}); diff --git a/test/spec/core/module-mjson/test.mjson b/test/spec/core/module-mjson/test.mjson new file mode 100644 index 0000000000..ac4c8c6364 --- /dev/null +++ b/test/spec/core/module-mjson/test.mjson @@ -0,0 +1,3 @@ +{ + "Hello": "World" +} \ No newline at end of file diff --git a/test/spec/data/expression-data-mapping.js b/test/spec/data/expression-data-mapping.js index 43f238a0cf..ac220b685d 100644 --- a/test/spec/data/expression-data-mapping.js +++ b/test/spec/data/expression-data-mapping.js @@ -11,7 +11,7 @@ var ExpressionDataMapping = require("montage/data/service/expression-data-mappin PropertyDescriptor = require("montage/core/meta/property-descriptor").PropertyDescriptor, RawDataService = require("montage/data/service/raw-data-service").RawDataService, RawDataTypeMapping = require("montage/data/service/raw-data-type-mapping").RawDataTypeMapping, - RawPropertyValueToObjectConverter = require("montage/data/converter/raw-property-value-to-object-converter").RawPropertyValueToObjectConverter; + RawForeignValueToObjectConverter = require("montage/data/converter/raw-foreign-value-to-object-converter").RawForeignValueToObjectConverter; var Movie = require("spec/data/logic/model/movie").Movie, @@ -60,7 +60,7 @@ describe("An Expression Data Mapping", function() { propsPropertyDescriptor, propConverter, propService, - + dateConverter = Object.create({}, { @@ -93,7 +93,7 @@ describe("An Expression Data Mapping", function() { movieObjectDescriptor.addPropertyDescriptor(new PropertyDescriptor().initWithNameObjectDescriptorAndCardinality("id", movieObjectDescriptor, 1)); movieSchemaModuleReference = new ModuleReference().initWithIdAndRequire("spec/data/schema/logic/movie", require); movieSchema = new ModuleObjectDescriptor().initWithModuleAndExportName(movieSchemaModuleReference, "MovieSchema"); - + actionMovieModuleReference = new ModuleReference().initWithIdAndRequire("spec/data/logic/model/action-movie", require); actionMovieObjectDescriptor = new ModuleObjectDescriptor().initWithModuleAndExportName(actionMovieModuleReference, "ActionMovie"); @@ -109,7 +109,7 @@ describe("An Expression Data Mapping", function() { categoryPropertyDescriptor = new PropertyDescriptor().initWithNameObjectDescriptorAndCardinality("category", movieObjectDescriptor, 1); categoryPropertyDescriptor.valueDescriptor = categoryObjectDescriptor; movieObjectDescriptor.addPropertyDescriptor(categoryPropertyDescriptor); - + countryService = new CountryService(); countryModuleReference = new ModuleReference().initWithIdAndRequire("spec/data/logic/model/country", require); @@ -161,24 +161,24 @@ describe("An Expression Data Mapping", function() { movieMapping = new ExpressionDataMapping().initWithServiceObjectDescriptorAndSchema(movieService, movieObjectDescriptor, movieSchema); movieMapping.addRequisitePropertyName( "title", "category", "budget", "isFeatured", "releaseDate", "id"); movieMapping.addObjectMappingRule("title", {"<->": "name"}); - + movieMapping.addObjectMappingRule("id", {"<->": "id"}); - categoryConverter = new RawPropertyValueToObjectConverter().initWithConvertExpression("category_id == $"); + categoryConverter = new RawForeignValueToObjectConverter().initWithConvertExpression("category_id == $"); categoryConverter.service = categoryService; movieMapping.addObjectMappingRule("category", { "<-": "{categoryID: category_id}", converter: categoryConverter }); - summaryConverter = new RawPropertyValueToObjectConverter().initWithConvertExpression("category_id == $"); + summaryConverter = new RawForeignValueToObjectConverter().initWithConvertExpression("category_id == $"); summaryConverter.service = plotSummaryService; movieMapping.addObjectMappingRule("plotSummary", { "<-": "{movie_id: id}", converter: summaryConverter, inversePropertyName: "movie" }); - propConverter = new RawPropertyValueToObjectConverter().initWithConvertExpression("category_id == $"); + propConverter = new RawForeignValueToObjectConverter().initWithConvertExpression("category_id == $"); propConverter.service = propService; movieMapping.addObjectMappingRule("props", { "<-": "{}", @@ -209,7 +209,7 @@ describe("An Expression Data Mapping", function() { actionMovieMapping.addRawDataMappingRule("mappedRating", {"<-": "rating"}); actionMovieMapping.addRawDataMappingRule("mappedScore", {"<-": "criticScore"}); actionMovieMapping.addRequisitePropertyName("country", "rating"); - countryConverter = new RawPropertyValueToObjectConverter().initWithConvertExpression("country_id"); + countryConverter = new RawForeignValueToObjectConverter().initWithConvertExpression("country_id"); countryConverter.revertExpression = "id"; countryConverter.owner = actionMovieMapping; actionMovieMapping.addObjectMappingRule("country", { @@ -287,14 +287,14 @@ describe("An Expression Data Mapping", function() { fcc_rating: "pg", country_id: 1 }; - + return actionMovieMapping.mapRawDataToObject(data, movie).then(function () { //Properties defined in parent descriptor - expect(movie.title).toBe("Star Wars"); + expect(movie.title).toBe("Star Wars"); expect(movie.budget).toEqual(14000000); //Properties defined in own descriptor - expect(movie.country).toBeDefined(); + expect(movie.country).toBeDefined(); done(); }); }); @@ -310,7 +310,7 @@ describe("An Expression Data Mapping", function() { fcc_rating: "pg", country_id: 1 }; - + return actionMovieMapping.mapRawDataToObject(data, movie).then(function () { //Properties defined in parent descriptor return mainService.updateObjectProperties(movie, "plotSummary"); @@ -332,7 +332,7 @@ describe("An Expression Data Mapping", function() { fcc_rating: "pg", country_id: 1 }; - + return movieMapping.mapRawDataToObject(data, movie).then(function () { //Properties defined in parent descriptor return mainService.updateObjectProperties(movie, "props"); @@ -351,7 +351,7 @@ describe("An Expression Data Mapping", function() { category_id: 1, budget: "14000000.00", is_featured: "true", - release_date: "05/25/1977" + release_date: "05/25/1977" }; return movieMapping.mapRawDataToObject(data, movie).then(function () { expect(typeof movie.budget === "number").toBeTruthy(); @@ -403,9 +403,9 @@ describe("An Expression Data Mapping", function() { movie = movieService.rootService.createDataObject(movieObjectDescriptor), category = new Category(), movieTitle = "The Social Network"; - - - var title = movie.title; //Trigger Title Getter + + + var title = movie.title; //Trigger Title Getter movie.title = movieTitle; movie.id = 2; category.name = "A Category"; @@ -433,12 +433,12 @@ describe("An Expression Data Mapping", function() { country.id = 1; actionMovieMapping.mapObjectToRawData(movie, data).then(function () { //Properties defined in parent descriptor - expect(data.name).toBe("Star Wars"); - expect(data.budget).toBe("14000000"); - expect(data.is_featured).toBe("true"); - expect(data.release_date).toBe("05/25/1977"); + expect(data.name).toBe("Star Wars"); + expect(data.budget).toBe("14000000"); + expect(data.is_featured).toBe("true"); + expect(data.release_date).toBe("05/25/1977"); //Properties defined in own descriptor - expect(data.fcc_rating).toBe("pg"); + expect(data.fcc_rating).toBe("pg"); expect(data.country_id).toEqual(1); done(); }); @@ -473,7 +473,7 @@ describe("An Expression Data Mapping", function() { }); }); - + }); diff --git a/test/spec/serialization/montage-deserializer-spec.js b/test/spec/serialization/montage-deserializer-spec.js index 408b5393a2..465a8a4039 100644 --- a/test/spec/serialization/montage-deserializer-spec.js +++ b/test/spec/serialization/montage-deserializer-spec.js @@ -756,7 +756,6 @@ describe("serialization/montage-deserializer-spec", function () { deserializer.init(serializationString, require); deserializer.deserializeObject().then(function (root) { - console.log("root", root); expect(root).toBeDefined(); }).catch(function(reason) { fail(reason); @@ -785,6 +784,8 @@ describe("serialization/montage-deserializer-spec", function () { }); it("should deserialize using prototype: module.mjson", function (done) { + //The fact that this is a string created in montage-deserializer-spec + //makes harder to assess what moduleId should really be expected. var serialization = { "root": { "prototype": "spec/serialization/testmjson.mjson", @@ -799,7 +800,7 @@ describe("serialization/montage-deserializer-spec", function () { deserializer.init(serializationString, require); deserializer.deserializeObject().then(function (root) { var info = Montage.getInfoForObject(root); - expect(info.moduleId).toBe("core/core"); + expect(info.moduleId).toBe("spec/serialization/testmjson.mjson"); expect(info.isInstance).toBe(true); expect(root.type).toBeUndefined(); expect(root.name).toBe("RootObjectDescriptor"); @@ -855,7 +856,7 @@ describe("serialization/montage-deserializer-spec", function () { deserializer.init(serializationString, require); deserializer.deserializeObject().then(function (root) { var info = Montage.getInfoForObject(root); - expect(info.moduleId).toBe("core/core"); + expect(info.moduleId).toBe("spec/serialization/testmjson.mjson"); expect(info.isInstance).toBe(true); expect(root.type).toBeUndefined(); expect(root.name).toBe("RootObjectDescriptor"); @@ -868,6 +869,40 @@ describe("serialization/montage-deserializer-spec", function () { }); }); + it("should deserialize using object: module.mjson, object created", function (done) { + var serialization = { + "root": { + "object": "spec/serialization/test-object-mjson.mjson", + "values": { + "number": 42, + "string": {"<-": "'a string'"} + } + } + }, + serializationString = JSON.stringify(serialization); + + deserializer.init(serializationString, require); + deserializer.deserializeObject().then(function (root) { + var info = Montage.getInfoForObject(root); + expect(info.moduleId).toBe("core/core"); + expect(info.isInstance).toBe(false); + expect(root.type).toBeUndefined(); + expect(root.blah).toBe("RootObjectDescriptor"); + expect(root.number).toBe(42); + expect(root.string).toBe("a string"); + }).catch(function (reason) { + fail(reason); + }).finally(function () { + done(); + }); + }); + + it("should deserialize mjson synchronously with require", function (done) { + var root = require("spec/serialization/test-object-mjson.mjson").montageObject; + expect(root.blah).toBe("RootObjectDescriptor"); + done(); + }); + it("should deserialize singleton using object: module.mjson", function (done) { var serialization = { "root": { @@ -1032,7 +1067,7 @@ describe("serialization/montage-deserializer-spec", function () { } }; - it("should only create the object", function (done) { + it("Empty deserializeSelf should only create the object and not set values", function (done) { var serializationString = JSON.stringify(serialization); deserializer.init( diff --git a/test/spec/serialization/test-object-mjson.mjson b/test/spec/serialization/test-object-mjson.mjson new file mode 100644 index 0000000000..19af4b28f3 --- /dev/null +++ b/test/spec/serialization/test-object-mjson.mjson @@ -0,0 +1,8 @@ +{ + "root": { + "object": "montage/core/core[Montage]", + "values": { + "blah": "RootObjectDescriptor" + } + } +}