diff --git a/data/service/data-service-reference.js b/data/service/data-service-reference.js
new file mode 100644
index 0000000000..d697655c8d
--- /dev/null
+++ b/data/service/data-service-reference.js
@@ -0,0 +1,118 @@
+var Montage = require("core/core").Montage,
+ ModuleReference = require("core/module-reference").ModuleReference;
+
+/**
+ * @class DataServiceReference
+ * @extends external:Montage
+ */
+exports.DataServiceReference = Montage.specialize(/** @lends DataServiceReference.prototype */ {
+
+
+
+ initWithIdTypesAndRequire: {
+ value: function (id, types, require) {
+ if (!id || !require) {
+ throw new Error("Module ID and require required");
+ }
+ this.module = new ModuleReference().initWithIdAndRequire(id, require);
+ this.types = types;
+
+ return this;
+ }
+ },
+
+ /**
+ * The identifier is the name of the service and is used to make the
+ * serialization of models more readable.
+ * @type {string}
+ * @default this.name
+ */
+ identifier: {
+ get: function () {
+ return [
+ "dataService",
+ (this.serviceName || this.prototypeName || "unnamed").toLowerCase(),
+ "reference"
+ ].join("_");
+ }
+ },
+
+ initWithModuleAndTypes: {
+ value: function (serviceModule, types) {
+ if (!serviceModule) {
+ throw new Error("Module is required");
+ }
+ this.module = serviceModule;
+ this.types = types;
+
+ return this;
+ }
+ },
+
+
+ deserializeSelf: {
+ value: function (deserializer) {
+ this.super(deserializer);
+ var value;
+
+ value = deserializer.getProperty("module");
+ this.module = value;
+
+ value = deserializer.getProperty("serviceName");
+ this.serviceName = value;
+
+ value = deserializer.getProperty("prototypeName");
+ this.prototypeName = value;
+
+ value = deserializer.getProperty("types") || [];
+ this.types = value;
+ }
+ },
+
+ serializeSelf: {
+ value: function (serializer) {
+ this.super(serializer);
+ }
+ },
+
+ promise: {
+ get: function () {
+ var prototypeName;
+ if (!this._promise) {
+ prototypeName = this.prototypeName;
+ this._promise = this.module ? this.module.exports.then(function (exports) {
+ var service = exports.montageObject;
+ if (!service && prototypeName) {
+ service = new exports[prototypeName];
+ //TODO Add Types To Service
+ }
+ return service;
+ }) : Promise.resolve(null);
+ }
+ return this._promise;
+ }
+ },
+
+ module: {
+ value: undefined
+ },
+
+ moduleId: {
+ get: function () {
+ return this.module && this.module.id || undefined;
+ }
+ },
+
+ prototypeName: {
+ value: undefined
+ },
+
+ serviceName: {
+ value: undefined
+ },
+
+ types: {
+ value: undefined
+ }
+
+});
\ No newline at end of file
diff --git a/data/service/data-service.js b/data/service/data-service.js
index 61a2137b57..077320814e 100644
--- a/data/service/data-service.js
+++ b/data/service/data-service.js
@@ -82,10 +82,20 @@ exports.DataService = Montage.specialize(/** @lends DataService.prototype */ {
Array.prototype.push.apply(this._childServiceMappings, value);
}
+ this.registerSelf();
+
value = deserializer.getProperty("delegate");
if (value) {
this.delegate = value;
}
+
+ if (this._childServiceRegistrationPromise) {
+ this._childServiceRegistrationPromise = this._childServiceRegistrationPromise.then(function () {
+ return self.registerSelf();
+ });
+ } else {
+ this._childServiceRegistrationPromise = this.registerSelf();
+ }
return result;
}
@@ -281,6 +291,15 @@ exports.DataService = Montage.specialize(/** @lends DataService.prototype */ {
// types or to the "all types" service array identified by the
// `null` type, and add each of the new child's types to the array
// of child types if they're not already there.
+ this._cacheServiceWithTypes(child, types);
+ // Set the new child service's parent.
+ child._parentService = this;
+ }
+ },
+
+ _cacheServiceWithTypes: {
+ value: function (child, types) {
+ var children, type, i, n, nIfEmpty = 1;
for (i = 0, n = types && types.length || nIfEmpty; i < n; i += 1) {
type = types && types.length && types[i] || null;
@@ -293,8 +312,6 @@ exports.DataService = Montage.specialize(/** @lends DataService.prototype */ {
}
}
}
- // Set the new child service's parent.
- child._parentService = this;
}
},
@@ -397,6 +414,48 @@ exports.DataService = Montage.specialize(/** @lends DataService.prototype */ {
}
},
+ registerSelf: {
+ value: function () {
+ var self = this,
+ mappings = this.mappings || [],
+ types;
+
+ // possible types
+ // -- types is passed in as an array or a single type.
+ // -- a model is set on the child.
+ // -- types is set on the child.
+ // any type can be asychronous or synchronous.
+ types = types && Array.isArray(types) && types ||
+ types && [types] ||
+ this.model && this.model.objectDescriptors ||
+ this.types && Array.isArray(this.types) && this.types ||
+ this.types && [this.types] ||
+ [];
+
+ return this._registerOwnTypesAndMappings(types, mappings).then(function () {
+ self._cacheServiceWithTypes(self, types);
+ return self;
+ });
+ }
+ },
+
+ _registerOwnTypesAndMappings: {
+ value: function (types, mappings) {
+ var self = this,
+ objectDescriptors;
+ return this._resolveAsynchronousTypes(types).then(function (descriptors) {
+ objectDescriptors = descriptors;
+ self._registerTypesByModuleId(objectDescriptors);
+ return self._registerChildServiceMappings(self, mappings);
+ }).then(function () {
+ return self._makePrototypesForTypes(self, objectDescriptors);
+ }).then(function () {
+ // self.addChildService(child, types);
+ return null;
+ });
+ }
+ },
+
_resolveAsynchronousTypes: {
value: function (types) {
var self = this;
@@ -1747,7 +1806,8 @@ exports.DataService = Montage.specialize(/** @lends DataService.prototype */ {
//have to go up to answer that question. The difference between
//.TYPE and Objectdescriptor still creeps-in when it comes to
//the service to answer that to itself
- if (self.parentService && self.parentService.childServiceForType(query.type) === self && typeof self.fetchRawData === "function") {
+ service = self.parentService ? self.parentService.childServiceForType(query.type) : self.childServiceForType(query.type);
+ if (service === self && typeof self.fetchRawData === "function") {
service = self;
service._fetchRawData(stream);
} else {
@@ -1760,6 +1820,7 @@ exports.DataService = Montage.specialize(/** @lends DataService.prototype */ {
stream = service.fetchData(query, stream) || stream;
self._dataServiceByDataStream.set(stream, service);
} else {
+ debugger;
throw new Error("Can't fetch data of unknown type - " + (query.type.typeName || query.type.name) + "/" + query.type.uuid);
}
} catch (e) {
diff --git a/data/service/raw-data-operation.js b/data/service/raw-data-operation.js
new file mode 100644
index 0000000000..52d6b11288
--- /dev/null
+++ b/data/service/raw-data-operation.js
@@ -0,0 +1,45 @@
+var Montage = require("core/core").Montage;
+
+/**
+ * Represents
+ *
+ * @class
+ * @extends external:Montage
+ */
+exports.RawDataOperation = Montage.specialize(/** @lends DataOperation.prototype */ {
+
+ /***************************************************************************
+ * Constructor
+ */
+
+ constructor: {
+ value: function RawDataOperation() {
+ this.time = Date.now();
+ this._index = exports.RawDataOperation.prototype._currentIndex + 1 || 0;
+ exports.RawDataOperation.prototype._currentIndex = this._index;
+ }
+ },
+
+ data: {
+ value: undefined,
+ serializable: "value"
+ },
+
+ objectDescriptorModule: {
+ value: undefined,
+ serializable: "value"
+ },
+
+ serviceModule: {
+ value: undefined,
+ serializable: "value"
+ },
+
+ type: {
+ value: undefined,
+ serializable: "value"
+ }
+
+
+});
+
diff --git a/data/service/raw-data-service.js b/data/service/raw-data-service.js
index 14df6020dd..92b3b1bd0a 100644
--- a/data/service/raw-data-service.js
+++ b/data/service/raw-data-service.js
@@ -202,7 +202,7 @@ exports.RawDataService = DataService.specialize(/** @lends RawDataService.protot
// if (childService && childService.identifier.indexOf("offline-service") === -1) {
// childService._fetchRawData(stream);
// } else
- if (childService) {
+ if (childService && childService !== this) {
childService._fetchRawData(stream);
} else if (query.authorization) {
stream.query = self.mapSelectorToRawDataQuery(query);
diff --git a/data/service/raw-data-worker.js b/data/service/raw-data-worker.js
new file mode 100644
index 0000000000..ee96d3d3fc
--- /dev/null
+++ b/data/service/raw-data-worker.js
@@ -0,0 +1,309 @@
+var Montage = require("montage").Montage,
+ Criteria = require("core/criteria").Criteria,
+ DataQuery = require("data/model/data-query").DataQuery,
+ Map = require("collections/map"),
+ ObjectDescriptor = require("core/meta/object-descriptor").ObjectDescriptor,
+ OperationType = require("data/service/data-operation").DataOperation.Type,
+ Promise = require("core/promise").Promise;
+
+
+exports.RawDataWorker = Montage.specialize({
+
+
+
+ /***************************************************************************
+ * Serialization
+ */
+
+ // serializeSelf: {
+ // value: function (serializer) {
+ // }
+ // },
+
+ deserializeSelf: {
+ value: function (deserializer) {
+ var references = deserializer.getProperty("childServices") || [];
+
+ console.log("RawDataWorker.deserializeSelf", references);
+ this.registerServiceReferences(references);
+ }
+ },
+
+
+ /***************************************************************************
+ * Service Tree
+ */
+
+
+ _serviceReferenceRegistrationPromise: {
+ get: function () {
+ if (!this.__serviceReferenceRegistrationPromise) {
+ this.__serviceReferenceRegistrationPromise = Promise.resolve(null);
+ }
+ return this.__serviceReferenceRegistrationPromise;
+ }
+ },
+
+ serviceReferences: {
+ get: function() {
+ if (!this._serviceReferences) {
+ this._serviceReferences = new Set();
+ }
+ return this._serviceReferences;
+ }
+ },
+
+ serviceReferenceByObjectDescriptor: {
+ get: function () {
+ if (!this._serviceReferenceByObjectDescriptor) {
+ this._serviceReferenceByObjectDescriptor = new Map();
+ }
+ return this._serviceReferenceByObjectDescriptor;
+ }
+ },
+
+ serviceReferenceForObjectDescriptor: {
+ value: function (type) {
+ var services;
+ type = type instanceof ObjectDescriptor ? type : this._objectDescriptorForType(type);
+ services = this._serviceReferenceByObjectDescriptor.get(type) || this._serviceReferenceByObjectDescriptor.get(null);
+ return services && services[0] || null;
+ }
+ },
+
+ registerServiceReferences: {
+ value: function (serviceReferences) {
+ var self;
+ if (!this.__serviceReferenceRegistrationPromise) {
+ self = this;
+ this.__serviceReferenceRegistrationPromise = Promise.all(serviceReferences.map(function (service) {
+ return self.registerServiceReference(service);
+ }));
+ }
+ }
+ },
+
+ registerServiceReference: {
+ value: function (service, types) {
+ var self = this;
+ // possible types
+ // -- types is passed in as an array or a single type.
+ // -- a model is set on the service.
+ // -- types is set on the service.
+ // any type can be asychronous or synchronous.
+ types = types && Array.isArray(types) && types ||
+ types && [types] ||
+ service.model && service.model.objectDescriptors ||
+ service.types && Array.isArray(service.types) && service.types ||
+ service.types && [service.types] ||
+ [];
+
+ return this._registerServiceReferenceObjectDescriptors(service, types);
+ }
+ },
+
+ _registerServiceReferenceObjectDescriptors: {
+ value: function (service, types) {
+ this._addServiceReference(service, types);
+ this._registerObjectDescriptorsByModuleId(types);
+ return this.nullPromise;
+ }
+ },
+
+ _addServiceReference: {
+ value: function (service, types) {
+ var serviceReference, type, i, n, nIfEmpty = 1;
+ types = types || service.model && service.model.objectDescriptors || service.types;
+
+ // Add the new service to this service's serviceren set.
+ this.serviceReferences.add(service);
+
+ // Add the new service service to the services array of each of its
+ // types or to the "all types" service array identified by the
+ // `null` type, and add each of the new service's types to the array
+ // of service types if they're not already there.
+ for (i = 0, n = types && types.length || nIfEmpty; i < n; i += 1) {
+ type = types && types.length && types[i] || null;
+ serviceReference = this.serviceReferenceByObjectDescriptor.get(type) || [];
+ serviceReference.push(service);
+ if (serviceReference.length === 1) {
+ this.serviceReferenceByObjectDescriptor.set(type, serviceReference);
+ }
+ }
+ }
+ },
+
+ _registerObjectDescriptorsByModuleId: {
+ value: function (types) {
+ var self = this;
+ types.forEach(function (objectDescriptor) {
+ var module = objectDescriptor.module,
+ moduleId = [module.id, objectDescriptor.exportName].join("/");
+ self.objectDescriptorsByModuleID.set(moduleId, objectDescriptor);
+ });
+ }
+ },
+
+ nullPromise: {
+ get: function () {
+ if (!exports.RawDataWorker._nullPromise) {
+ exports.RawDataWorker._nullPromise = Promise.resolve(null);
+ }
+ return exports.RawDataWorker._nullPromise;
+ }
+ },
+
+ /***************************************************************************
+ * Operation Handlers
+ */
+
+ handleOperation: {
+ value: function (rawOperation) {
+ var self = this,
+ objectDescriptor, service;
+
+ return this._serviceReferenceRegistrationPromise.then(function () {
+ return self._objectDescriptorForOperation(rawOperation);
+ }).then(function (descriptor) {
+ objectDescriptor = descriptor;
+ return self._serviceForObjectDescriptor(descriptor);
+ }).then(function (service) {
+ var handlerName = self._handlerNameForOperationType(rawOperation.type);
+ if (!service) {
+ console.log(rawOperation, self, objectDescriptor);
+ debugger;
+ throw new Error("No service available to handle operation with type (" + (objectDescriptor && objectDescriptor.name) + ")");
+ }
+ return self[handlerName](rawOperation, service, objectDescriptor);
+ });
+ }
+ },
+
+ _handlerNameForOperationType: {
+ value: function (operationType) {
+ var name = "_perform";
+ name += this._operationTypeNameByOperationType(operationType);
+ return name + "Operation";
+ }
+ },
+
+ _operationTypeNameByOperationType: {
+ value: function (operationType) {
+ return operationType.isCreate ? "Create" :
+ operationType.isRead ? "Read" :
+ operationType.isUpdate ? "Update" :
+ operationType.isDelete ? "Delete" :
+ null;
+ }
+ },
+
+ _performCreateOperation: {
+ value: function (rawOperation, service, objectDescriptor) {
+ return service.saveRawData(rawOperation.data);
+ }
+ },
+
+ _performDeleteOperation: {
+ value: function (rawOperation, service, objectDescriptor) {
+ return service.deleteRawData(rawOperation.data);
+ }
+ },
+
+ _performReadOperation: {
+ value: function (rawOperation, service, objectDescriptor) {
+ var criteria = rawOperation.criteria || rawOperation.data,
+ query, parameters, expression;
+
+ if (!(criteria instanceof Criteria)) {
+ parameters = criteria ? criteria.parameters : {};
+ expression = criteria ? criteria.expression : "";
+ criteria = new Criteria().initWithExpression(expression, parameters);
+ }
+ query = DataQuery.withTypeAndCriteria(objectDescriptor, criteria);
+
+ return service.fetchData(query);
+ }
+ },
+
+ _performUpdateOperation: {
+ value: function (rawOperation, service, objectDescriptor) {
+ return service.saveRawData(rawOperation.data);
+ }
+ },
+
+ /***************************************************************************
+ * Service & ObjectDescriptor Creation
+ */
+
+ objectDescriptorForType: {
+ value: function () {
+
+ }
+ },
+
+ _serviceReferenceForObjectDescriptor: {
+ value: function (objectDescriptor) {
+ var services = this.serviceReferenceByObjectDescriptor.get(objectDescriptor);
+ services = services || this.serviceReferenceByObjectDescriptor.get(null);
+ return services && services[0] || null;
+ }
+ },
+
+ _serviceForObjectDescriptor: {
+ value: function (objectDescriptor) {
+ var self = this,
+ serviceReference = this._serviceReferenceForObjectDescriptor(objectDescriptor),
+ service = serviceReference && this.servicesByModuleID.get(serviceReference.moduleId);
+
+
+
+ return !serviceReference ? Promise.resolve(null) :
+ service ? Promise.resolve(service) :
+ serviceReference.promise.then(function (service) {
+ self._servicesByModuleID.set(serviceReference.moduleId, service);
+ return service;
+ });
+ }
+ },
+
+ _objectDescriptorForOperation: {
+ value: function (rawOperation) {
+ var self = this,
+ module = rawOperation.objectDescriptorModule,
+ descriptor = this.objectDescriptorsByModuleID.get(module.id);
+
+
+ return descriptor ? Promise.resolve(descriptor) :
+ module.exports.then(function (exports) {
+ var instance = exports.montageObject,
+ moduleId = [module.id, instance.exportName].join("/");
+ self.objectDescriptorsByModuleID.set(module.id, instance);
+ self.objectDescriptorsByModuleID.set(moduleId, instance);
+ return instance;
+ });
+ }
+ },
+
+ /***************************************************************************
+ * Service/Type Caching
+ */
+
+ servicesByModuleID: {
+ get: function () {
+ if (!this._servicesByModuleID) {
+ this._servicesByModuleID = new Map();
+ }
+ return this._servicesByModuleID;
+ }
+ },
+
+ objectDescriptorsByModuleID: {
+ get: function () {
+ if (!this._objectDescriptorsByModuleID) {
+ this._objectDescriptorsByModuleID = new Map();
+ }
+ return this._objectDescriptorsByModuleID;
+ }
+ }
+
+});
\ No newline at end of file
diff --git a/package.json b/package.json
index 705a8dfb36..39bfb86074 100644
--- a/package.json
+++ b/package.json
@@ -52,14 +52,14 @@
"collections": "~5.0.x",
"frb": "~4.0.x",
"htmlparser2": "~3.0.5",
- "q-io": "^1.13.3",
- "mr": "^17.0.11",
- "weak-map": "^1.0.5",
- "lodash.kebabcase": "^4.1.1",
"lodash.camelcase": "^4.3.0",
- "lodash.trim": "^4.5.1",
+ "lodash.kebabcase": "^4.1.1",
"lodash.snakecase": "^4.1.1",
- "proxy-polyfill": "~0.1.7"
+ "lodash.trim": "^4.5.1",
+ "mr": "montagejs/mr#features/dirIndex",
+ "proxy-polyfill": "~0.1.7",
+ "q-io": "^1.13.3",
+ "weak-map": "^1.0.5"
},
"devDependencies": {
"concurrently": "^3.4.0",
diff --git a/test/all.js b/test/all.js
index 37607e0bd9..ce75f71b38 100644
--- a/test/all.js
+++ b/test/all.js
@@ -112,11 +112,11 @@ module.exports = require("montage-testing").run(require, [
{name: "spec/core/localizer-spec", node: false, karma: false},
{name: "spec/core/localizer/serialization-spec", node: false, karma: false},
// Data
- {name: "spec/data/data-query"},
{name: "spec/data/data-mapping"},
{name: "spec/data/data-object-descriptor"},
{name: "spec/data/data-property-descriptor"},
{name: "spec/data/data-provider"},
+ {name: "spec/data/data-query"},
{name: "spec/data/data-service"},
{name: "spec/data/data-stream"},
{name: "spec/data/expression-data-mapping"},
@@ -126,6 +126,7 @@ module.exports = require("montage-testing").run(require, [
{name: "spec/data/property-descriptor"},
{name: "spec/data/raw-data-service"},
{name: "spec/data/raw-data-type-mapping-spec"},
+ {name: "spec/data/raw-data-worker"},
{name: "spec/data/integration", node: false},
// Meta
diff --git a/test/raw-data-worker.js b/test/raw-data-worker.js
new file mode 100644
index 0000000000..366e87bf57
--- /dev/null
+++ b/test/raw-data-worker.js
@@ -0,0 +1,11 @@
+console.log('montage-testing', 'Start');
+
+module.exports = require("montage-testing").run(require, [
+
+ {name: "spec/data/raw-data-worker"}
+]).then(function () {
+ console.log('montage-testing', 'End');
+}, function (err) {
+ console.log('montage-testing', 'Fail', err, err.stack);
+ throw err;
+});
diff --git a/test/run.html b/test/run.html
index 6ba084031c..f2c7fc4ca3 100644
--- a/test/run.html
+++ b/test/run.html
@@ -9,6 +9,7 @@
-
+
+