Skip to content

Commit

Permalink
Merge branch 'master' of github.com:grob/ringo-sqlstore
Browse files Browse the repository at this point in the history
  • Loading branch information
grob committed Dec 18, 2014
2 parents c20d01f + 7c23e04 commit 8c39c7a
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 36 deletions.
3 changes: 2 additions & 1 deletion lib/sqlstore/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ AggressiveEntityCollector.prototype.collect = function(resultSet, ids, storables
}
}
}
storables[len - 1] = store.create(this.mapping.type, key, entity);
storables[len - 1] = store.entityRegistry.getConstructor(this.mapping.type)
.createInstance(key, entity);
if (hasEntityCache) {
store.entityCache.put(key.cacheKey, entity);
}
Expand Down
117 changes: 117 additions & 0 deletions lib/sqlstore/entityregistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
var log = require("ringo/logging").getLogger(module.id);

/**
* @fileoverview Entity registry instances are capable to both register
* defined entities and modules exporting entity constructors.
*/
var {Storable} = require("./storable");

/**
* Creates a new EntityRegistry instance
* @class EntityRegistry instances keep track of both defined entity constructors
* (as returned by `Store.prototype.defineEntity()`) and modules exporting
* entity constructors. The latter is necessary for stores to dynamically
* load entity constructors (eg. for querying).
* @constructor
*/
exports.EntityRegistry = function() {
var constructors = {};
var modules = [];
var reloadModules = false;

/**
* Registers the constructor function for the given entity type. Calling
* this method results in reloading all registered entity modules.
* @param {String} type The type of entity
* @param {Function} ctor The constructor function
* @returns {Function} The constructor function passed as argument
*/
this.registerConstructor = function(type, ctor) {
log.debug("Registering constructor", type);
if (typeof(type) !== "string") {
throw new Error("Missing type argument");
}
if (!ctor || !(ctor.prototype instanceof Storable)) {
throw new Error("Constructor argument must be an instance of Storable");
}
constructors[type] = ctor;
reloadModules = modules.length > 0;
return ctor;
};

/**
* Registers the module path for loading. The module is expected to either
* export a single entity constructor function (using `module.exports`) or
* export one or more entity constructors (using `exports.<Name>`).
* @param {String|Array} path The module path, or an array of module paths
*/
this.registerModule = function(path) {
log.debug("Registering module", path);
if (Array.isArray(path)) {
Array.prototype.push.apply(modules, path);
} else if (typeof(path) === "string") {
modules.push(path);
} else {
throw new Error("Invalid path argument " + path);
}
reloadModules = true;
};

/**
* Returns the constructor function for the given entity type. This method
* will reload all registered modules if necessary.
* @param {String} type The entity type
* @returns {Function} The entity constructor for the given type
*/
this.getConstructor = function(type) {
if (reloadModules === true) {
this.loadModules();
}
var ctor = constructors[type];
if (typeof(ctor) !== "function") {
throw new Error("Entity " + type + "is not defined");
}
return ctor;
};

/**
* Returns an array of entity constructors registered. This method will
* reload all registered modules if necessary.
* @returns {Array} An array containing constructor functions
*/
this.getConstructors = function() {
if (reloadModules === true) {
this.loadModules();
}
return [ctor for each (ctor in constructors)];
};

/**
* Loads all modules and registers the entity constructor function(s)
* exported by them.
*/
this.loadModules = function() {
log.debug("Loading", modules.length, "storable modules");
reloadModules = false;
for each (let path in modules) {
let exports = require(path);
if (typeof(exports) === "function") {
// register exports if it's an instance of Storable
if (exports.prototype instanceof Storable) {
constructors[exports.mapping.type] = exports;
continue;
}
throw new Error("Module " + path + " doesn't export a Storable");
}
// iterate over exports object and register all Storable constructors
for each (let value in exports) {
if (typeof(value) === "function" && value.prototype instanceof Storable) {
constructors[value.mapping.type] = value;
}
}
}
};

return this;
};

6 changes: 4 additions & 2 deletions lib/sqlstore/query/collectorgenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ EntityCollector.prototype.collect = function(resultSet, store) {
store.entityCache.put(key.cacheKey, entity);
}
}
return store.getEntityConstructor(this.mapping.type).createInstance(key, entity);
return store.entityRegistry.getConstructor(this.mapping.type)
.createInstance(key, entity);
};

/**
Expand Down Expand Up @@ -194,7 +195,8 @@ AggressiveEntityCollector.prototype.collect = function(resultSet, store) {
if (useCache === true) {
store.entityCache.put(key.cacheKey, entity);
}
return store.getEntityConstructor(this.mapping.type).createInstance(key, entity);
return store.entityRegistry.getConstructor(this.mapping.type)
.createInstance(key, entity);
};

/**
Expand Down
51 changes: 18 additions & 33 deletions lib/sqlstore/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* functionality for defining entities, manipulating and querying data.
*/
var {Storable} = require("./storable");
var {EntityRegistry} = require("./entityregistry");
var {Key} = require("./key");
var {Transaction} = require("./transaction");
var {Mapping} = require("./mapping");
Expand Down Expand Up @@ -93,7 +94,7 @@ var Store = exports.Store = function(connectionPool) {
* @ignore
*/
"entityRegistry": {
"value" : {}
"value" : new EntityRegistry()
}
});

Expand Down Expand Up @@ -160,12 +161,16 @@ Store.prototype.getConnection = function() {
* @see #Store.prototype.getEntityConstructor
*/
Store.prototype.defineEntity = function(type, mapping) {
if (this.entityRegistry.hasOwnProperty(type)) {
return this.entityRegistry[type];
}
log.debug("Defining entity", type);
return this.entityRegistry[type] =
Storable.defineEntity(this, type, new Mapping(this, type, mapping));
return this.entityRegistry.registerConstructor(type,
Storable.defineEntity(this, type, new Mapping(this, type, mapping)));
};

/**
* Registers the given module path(s) in the entity registry
* @param {String|Array} path The module path, or an array of module paths
*/
Store.prototype.registerEntityModule = function(path) {
this.entityRegistry.registerModule(path);
};

/**
Expand All @@ -177,9 +182,10 @@ Store.prototype.defineEntity = function(type, mapping) {
Store.prototype.syncTables = function() {
var conn = this.getConnection();
try {
for each (let ctor in this.entityRegistry) {
for each (let ctor in this.entityRegistry.getConstructors()) {
var schemaName = ctor.mapping.schemaName ||
this.dialect.getDefaultSchema(conn);
log.debug("Syncing database table", ctor.mapping.tableName);
if (!sqlUtils.tableExists(conn, ctor.mapping.tableName, schemaName)) {
this.createTable(conn, this.dialect, ctor.mapping);
if (ctor.mapping.id.hasSequence() && this.dialect.hasSequenceSupport()) {
Expand All @@ -201,26 +207,17 @@ Store.prototype.syncTables = function() {
* @returns {Function} The entity constructor function
*/
Store.prototype.getEntityConstructor = function(type) {
var ctor = this.entityRegistry[type];
if (ctor === null || ctor === undefined) {
throw new Error("Entity '" + type + "' is not defined");
}
return ctor;
return this.entityRegistry.getConstructor(type);
};

/**
* Returns the mapping for the given entity
* @param {String} type The name of the registered entity
* @returns {Mapping} The mapping of the entity
* @type Mapping
* @ignore
*/
Store.prototype.getEntityMapping = function(type) {
var ctor = this.entityRegistry[type];
if (ctor === null || ctor === undefined) {
throw new Error("Entity '" + type + "' is not defined");
}
return ctor.mapping;
return this.entityRegistry.getConstructor(type).mapping;
};

/**
Expand Down Expand Up @@ -357,18 +354,6 @@ Store.prototype.getEntity = function(storable) {
return entity;
};

/**
* Factory function for creating new entity instances
* @param {String} type The name of the registered entity type
* @param {Key} key The key to use
* @param {Object} entity The entity to use
* @returns {Object} A new instance of the defined entity
* @ignore
*/
Store.prototype.create = function(type, key, entity) {
return this.entityRegistry[type].createInstance(key, entity);
};

/**
* Removes the data with the given key from the database
* @param {Storable} storable The storable to remove from the database
Expand Down Expand Up @@ -744,7 +729,7 @@ Store.prototype.get = function(type, id, aggressive) {
(!transaction || !transaction.containsKey(key.cacheKey));
if (useCache && this.entityCache.containsKey(key.cacheKey)) {
entity = this.entityCache.get(key.cacheKey);
return this.create(type, key, entity);
return this.entityRegistry.getConstructor(type).createInstance(key, entity);
}
if (aggressive === true) {
entity = this.loadEntity(type, id);
Expand All @@ -755,7 +740,7 @@ Store.prototype.get = function(type, id, aggressive) {
if (useCache) {
this.entityCache.put(key.cacheKey, entity);
}
return this.create(type, key, entity);
return this.entityRegistry.getConstructor(type).createInstance(key, entity);
}
return null;
};
Expand Down

0 comments on commit 8c39c7a

Please sign in to comment.