Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Move lib to top level

  • Loading branch information...
commit 400f19e0d24ba6d4a6d79c8066e84cedc9cd7b7c 1 parent 5336f3f
@kriszyp authored
View
0  lib/coerce.js → coerce.js
File renamed without changes
View
0  lib/errors.js → errors.js
File renamed without changes
View
0  lib/facet.js → facet.js
File renamed without changes
View
0  lib/jsgi/transactional.js → jsgi/transactional.js
File renamed without changes
View
0  lib/json-rpc.js → json-rpc.js
File renamed without changes
View
0  lib/model.js → model.js
File renamed without changes
View
0  lib/package.js → package.js
File renamed without changes
View
9 package.json
@@ -3,7 +3,6 @@
"version": "0.2.3",
"author": "Kris Zyp",
"email": "kriszyp@gmail.com",
- "dependencies": ["narwhal", "commonjs-utils"],
"contributors": ["Vladimir Dronnikov <dronnikov@gmail.com>"],
"keywords": [
"persistence",
@@ -14,12 +13,12 @@
"engines": {"node":">=0.1.30", "rhino": true},
"mappings": {
"tunguska": "http://github.com/kriszyp/tunguska/zipball/v0.2.2",
- "rql": "http://github.com/kriszyp/rql/zipball/v0.2.2",
- "patr": "http://github.com/kriszyp/patr/zipball/v0.2.2",
- "promised-io": "http://github.com/kriszyp/promised-io/zipball/v0.2.2",
+ "rql": "jar:http://github.com/kriszyp/rql/zipball/v0.2.2!/",
+ "patr": "jar:http://github.com/kriszyp/patr/zipball/v0.2.2!/",
+ "promised-io": "jar:http://github.com/kriszyp/promised-io/zipball/v0.2.2!/",
"commonjs-utils": "http://github.com/kriszyp/commonjs-utils/zipball/v0.2.2",
"mysql-native": "jar:http://github.com/sidorares/nodejs-mysql-native/zipball/master!/lib/mysql-native/",
- "mongodb": "jar:http://github.com/christkv/node-mongodb-native/zipball/master!/lib/mongodb/"
+ "mongodb": "jar:http://github.com/christkv/node-mongodb-native/zipball/V0.9.4.4!/lib/mongodb/"
},
"overlay": {
"narwhal": {
View
0  lib/path.js → path.js
File renamed without changes
View
4 seed.yml
@@ -1,4 +0,0 @@
----
- name: perstore
- description: Persistence data modeling system
- version: 0.0.1
View
0  lib/store/README.md → store/README.md
File renamed without changes
View
0  lib/store/adaptive-index.js → store/adaptive-index.js
File renamed without changes
View
0  lib/store/aggregate.js → store/aggregate.js
File renamed without changes
View
0  lib/store/cache.js → store/cache.js
File renamed without changes
View
0  lib/store/converter.js → store/converter.js
File renamed without changes
View
211 store/couch-backup.js
@@ -0,0 +1,211 @@
+/*
+* CouchDB store
+*/
+
+var request = require("jsgi-client").request,
+ when = require("promised-io/promise").when,
+ error = require("jsgi/error");
+
+
+var decode = exports.decode = function(source) {
+ var object = JSON.parse(source); // use JSONExt?
+ object.id = object._id; // TODO use jsonschema's "self"
+ delete object._id;
+ delete object._rev;
+ // TODO translate other _ properties?
+ return object;
+};
+
+var encode = exports.encode = function(object) {
+ if (object.id) {
+ object._id = object.id;
+ delete object.id;
+ }
+ return JSON.stringify(object);
+};
+
+
+exports.Server = function(config) {
+ var server = {};
+ server.url = config.url;
+ server.getConfig = function() {
+ return when(
+ request({
+ method: "GET",
+ uri: url + "_config"
+ }),
+ function(response) {
+ error.handle(response);
+ return JSON.parse(response.body.join(""));
+ }
+ );
+ };
+ return server;
+};
+
+// TODO get from settings
+var defaultServer = exports.Server({uri: "http://127.0.0.1:5984/"});
+
+exports.Database = function(name, config) {
+ config = config || {}
+ var db = {};
+ db.server = config.server || defaultServer;
+ db.url = db.server.url + name + "/";
+
+ db.get = function(id) {
+ return when(
+ request({
+ method: "GET",
+ uri: db.url + id
+ }),
+ function(response) {
+ error.handle(response);
+ return decode(response.body.join(""));
+ }
+ );
+ };
+ /* TODO
+db.query = function(query, options) {
+var headers = {};
+if(options.start || options.end){
+headers.range = "items=" + options.start + '-' + options.end;
+}
+return when(
+request({
+method: "GET",
+queryString: query,
+headers: headers
+}),
+function(response){
+return decode(response.body.join(""))
+}
+);
+};
+*/
+ db.put = function(object, id) {
+ var etag = object.getMetadata().etag;
+ if (etag) object._rev = etag;
+ return when(
+ request({
+ method: "PUT",
+ uri: db.url + id,
+ body: encode(object)
+ }),
+ function(response) {
+ if (response.status != 201) throw new Error("PUT failed");
+ return decode(response.body.join(""));
+ }
+ );
+ };
+
+ db.post = function(object) {
+ return when(
+ request({
+ method: "POST",
+ uri: db.url,
+ body: encode(object)
+ }),
+ function(response) {
+ if (response.status != 201) throw new Error("POST failed");
+ return decode(response.body.join(""));
+ }
+ );
+ };
+
+ db["delete"] = function(object) {
+ return when(
+ request({
+ method: "DELETE",
+ uri: db.url + id,
+ headers: {"if-match": object.getMetadata()["if-match"]}
+ }),
+ function(response) {
+ // TODO try to get error messages from json response
+ if (response.status != 200) throw new Error("DELETE failed");
+ var info = JSON.parse(response.body.join(""));
+ return {
+ getMetadata: function() {
+ return {etag: info.rev};
+ }
+ }
+ }
+ );
+ };
+
+ /*
+* CouchDB-specific API extensions
+*/
+
+ db.copy = function(object) {
+ var convertDestination = function(destination) {
+ /* Convert parameterized Destination header into Couch ?rev= form
+* source: some_other_doc; etag=rev_id
+* target: some_other_doc?rev=rev_id
+*/
+ var parsed = destination.split(";");
+ if (parsed.length > 1) {
+ if (parsed[1].trim().toLowerCase().indexOf("etag=") === 0) {
+ parsed[1] = "?rev=" + parsed[1].trim().substring(5);
+ destination = parsed[0] + parsed.slice(1).join(";");
+ }
+ }
+ return destination;
+ };
+ var headers = object.getMetadata();
+ headers.destination = convertDestination(headers.destination);
+ return when(
+ request({
+ method: "COPY",
+ uri: db.url + id,
+ headers: headers
+ }),
+ function(response) {
+ if (response.status != 201) throw new Error("COPY failed");
+ return {
+ getMetadata: function() {
+ return {etag: response.headers.etag};
+ }
+ }
+ }
+ );
+ }
+
+ db.getDesigns = function() {
+ return when(
+ request({
+ method: "GET",
+ uri: db.url + "_all_docs?_all_docs?startkey=%22_design%2F%22&endkey=%22_design0%22&include_docs=true"
+ }),
+ function(response) {
+ error.handle(response);
+ var view = JSON.parse(response.body.join(""));
+ var docs = {};
+ view.rows && view.rows.forEach(function(row) {
+ var key = row.id.split("/")[1]; // TODO confirm there can't be more than one slash
+ delete row.doc._id;
+ delete row.doc._rev;
+ docs[key] = row.doc;
+ });
+ print(docs.toSource());
+ return docs;
+ }
+ );
+ };
+
+ db.getDesign = function(name) {
+ return when(
+ db.get("_design/" + name),
+ function(response) {
+ response.name = response.id.split("/")[1];
+ delete response.id;
+ return response;
+ }
+ );
+ };
+
+ // TODO get design, if not there or not up to date, put design
+ //var designUrl = db.url + "_design/" + (config.design || "perstore") + "/";
+
+ return db;
+};
+
View
1  lib/store/couchdb.js → store/couchdb.js
@@ -7,7 +7,6 @@ var http = require("promised-io/http-client"),
when = require("promised-io/promise").when,
LazyArray = require("promised-io/lazy-array").LazyArray,
settings = require("commonjs-utils/settings");
- //getIdentityProperty = require("../util/schema").getIdentityProperty;
function bodyToString(body) {
View
2  lib/store/filesystem.js → store/filesystem.js
@@ -2,7 +2,7 @@
* A very simple filesystem based storage
*/
var fs = require("promised-io/fs"),
- MIME_TYPES = require("jack/mime").MIME_TYPES,
+ MIME_TYPES = require("pintura/jsgi/mime").MIME_TYPES,
when = require("promised-io/promise").when,
AutoTransaction = require("../transaction").AutoTransaction;
View
0  lib/store/inherited.js → store/inherited.js
File renamed without changes
View
61 store/map-index.js
@@ -0,0 +1,61 @@
+/**
+ * This is a wrapper store that adds indexing through map functions
+ */
+var when = require("promised-io/promise").when;
+
+module.exports = function(store, options){
+ options = options || {};
+ var IndexConstructor = options.IndexConstructor || require("../stores").DefaultStore;
+ store.deriveView = function deriveView(map){
+ var index = new IndexConstructor();
+ store.setPath(map.toString());
+ var getRevision = index.getRevision || function(){
+ return index.get("__revision__");
+ };
+ var getRevision = index.setRevision || function(revision){
+ return index.put("__revision__", revision);
+ };
+ var getRevisions = derivedFrom.getRevisions || store.getRevisions || function(from){
+ return store.query("revisions(" + from + ")");
+ };
+ var revision;
+ return {
+ cursor: function(){
+ var storeRevision = store.getRevision();
+ // TODO: Might use a vclock type of strategy to determine if we really need to update
+ if(storeRevision > getRevision()){
+ var transaction = index.transaction(true);
+ // make sure we still need to update after getting the lock
+ if(storeRevision > getRevision()){
+ getRevisions(getRevisition()).forEach(function(object){
+ var old = store.getPrevisionVersion(object);
+ if(old){
+ map(old, function(key, value){
+ index.remove(key + '/' + old.id);
+ });
+ }
+ if(object){
+ if(maybeAlreadyApplied){
+ map(object, function(key, value){
+ index.remove(key + '/' + object.id);
+ });
+ }
+ map(object, function(key, value){
+ index.put(key + '/' + object.id, value);
+ });
+ }
+ });
+ }
+ setRevision(storeRevision);
+ transaction.commit();
+ }
+ return index.cursor();
+ },
+ deriveView: function(){
+
+ }
+
+ }
+ };
+ return store;
+};
View
0  lib/store/memory.js → store/memory.js
File renamed without changes
View
0  lib/store/mirrored.js → store/mirrored.js
File renamed without changes
View
6 lib/store/mongodb.js → store/mongodb.js
@@ -12,9 +12,10 @@
//
var convertNodeAsyncFunction = require('promised-io/promise').convertNodeAsyncFunction,
+ Connection = require("mongodb/connection").Connection,
mongo = require('mongodb/db'),
BSON = require('mongodb/bson/bson'),
- Server = require("mongodb/connection").Server,
+ Server = require("mongodb/connections/server").Server,
sys = require('util'),
defer = require("promised-io/promise").defer,
when = require("promised-io/promise").when,
@@ -213,7 +214,8 @@ exports.MongoDB = function(options){
});
}
else {
- var database = options.database || new mongo.Db(dbOptions.name, new Server(dbOptions.host, dbOptions.port, {}), {});
+ var database = options.database || new mongo.Db(dbOptions.name,
+ new Server(dbOptions.host, dbOptions.port, {}), {});
database.open(function(err, db){
if(err){
sys.puts("Failed to load mongo database " + dbOptions.name + " error " + err.message);
View
0  lib/store/notifying.js → store/notifying.js
File renamed without changes
View
299 store/patched_sql.js
@@ -0,0 +1,299 @@
+/**
+ * This is an SQL store that (partially) implements:
+ * http://www.w3.org/TR/WebSimpleDB/
+ * and wraps an SQL database engine based on:
+ * based on http://www.w3.org/TR/webdatabase/
+ */
+var SQLDatabase = require("./sql-engine").SQLDatabase,
+ first = require("lazy-array").first,
+ AutoTransaction = require("../transaction").AutoTransaction,
+ parseQuery = require("../resource-query").parseQuery,
+ print = require("system").print,
+ settings = require("commonjs-utils/settings"),
+ defineProperty = require("es5-helper").defineProperty;
+
+exports.SQLStore = function(config){
+ var database = config.database || exports.defaultDatabase();
+ var idColumn = config.idColumn || "id";
+ config.indexPrefix = config.indexPrefix || "idx_";
+ var store = {
+ indexedProperties: {id: true},
+ selectColumns: ["*"],
+ get: function(id){
+ var object = first(store.executeSql("SELECT * FROM " + config.table + " WHERE " + idColumn + "=?", [id]).rows);
+ if(object){
+ defineProperty(object.__proto__ = {
+ getId: function(object){
+ return this[idColumn];
+ }
+ }, "getId", {enumerable: false});
+ }
+ return object;
+ },
+ "delete": function(id){
+ store.executeSql("DELETE FROM " + config.table + " WHERE " + idColumn + "=?", [id]);
+ },
+ put: function(object, directives){
+ id = directives.id || object[config.idColumn];
+ var overwrite = directives.overwrite;
+ if(overwrite === undefined){
+ overwrite = this.get(id);
+ }
+ var params = [];
+ var valuesPlacement = "";
+ var columnsString = "";
+ if(!overwrite){
+ var first = true;
+ for(var i in object){
+ if(object.hasOwnProperty(i)){
+ params.push(object[i]);
+ valuesPlacement += first ? "?" : ",?";
+ columnsString += (first ? "" : ",") + i;
+ first = false;
+ }
+ }
+ params.idColumn = config.idColumn;
+ var results = store.executeSql("INSERT INTO " + config.table + " (" + columnsString + ") values (" + valuesPlacement + ")", params);
+ id = results.insertId;
+ object[idColumn] = id;
+ return id;
+ }
+ var sql = "UPDATE " + config.table + " SET ";
+ var first = true;
+ for(var i in object){
+ if(object.hasOwnProperty(i)){
+ if(first){
+ first = false;
+ }
+ else{
+ sql += ",";
+ }
+ sql += i + "=?";
+ params.push(object[i]);
+ }
+ }
+ sql += " WHERE " + idColumn + "=?";
+ params.push(object[idColumn]);
+ store.executeSql(sql, params);
+ },
+ query: function(query, options){
+ options = options || {};
+ var selectColumns = this.selectColumns;
+ var indexedProperties = this.indexedProperties;
+ if(typeof query === "string"){
+ query = parseQuery(query);
+ }
+ var filter = this.getWhereClause(query, options);
+ var order = [];
+ query.forEach(function(term) {
+ if (term.type == "call") {
+ if(term.name == "sort") {
+ if(term.parameters.length === 0)
+ throw new URIError("Must specify a sort criteria");
+ term.parameters.forEach(function(sortAttribute) {
+ var firstChar = sortAttribute.charAt(0);
+ var orderDir = "ASC";
+ if(firstChar == "-" || firstChar == "+") {
+ if(firstChar == "-") {
+ orderDir = "DESC";
+ }
+ sortAttribute = sortAttribute.substring(1);
+ }
+ if(!indexedProperties[sortAttribute]) {
+ throw new URIError("Can only sort by " + Object.keys(indexedProperties));
+ }
+ order.push(config.table + "." + sortAttribute + " " + orderDir);
+ });
+ }
+ }
+ });
+ var slice = [];
+ if (options && typeof options.start === "number") {
+ slice.push(options.start);
+ if (typeof options.end === "number") {
+ slice.push(options.end);
+ }
+ }
+ var selectObject = {
+ select: selectColumns,
+ from: config.table,
+ where: filter,
+ order: order,
+ slice: slice,
+ dialect: settings.database.type,
+ toString: function(count) {
+ var sql,
+ start = this.slice[0],
+ end = this.slice[1];
+
+ if (this.dialect == "mssql" && !count) {
+ sql = "SELECT " + this.select;
+ sql += " FROM (SELECT ROW_NUMBER() OVER (ORDER BY ";
+ if (this.order.length) {
+ sql += this.order.join(", ");
+ }
+ else {
+ sql += this.from + "." + idColumn;
+ }
+ sql += ") AS __rownum__, " + this.select;
+ sql += " FROM " + this.from;
+ sql += " WHERE " + this.where;
+ sql += ") AS " + this.from;
+ if (start)
+ sql += " WHERE __rownum__ >= " + start;
+ if (end)
+ sql += (start && " AND" || " WHERE") + " __rownum__ <= " + (end + 1);
+ return sql;
+ }
+
+ sql = " FROM " + this.from;
+ sql += " WHERE " + this.where;
+ if (count) {
+ return "SELECT COUNT(*) AS count" + sql;
+ }
+
+ sql = "SELECT " + this.select + sql;
+ if (this.order.length) sql += " ORDER BY " + this.order.join(", ");
+ if (end) sql += " LIMIT " + (options.end - options.start + 1);
+ if (start) sql += " OFFSET " + options.start;
+ return sql;
+ }
+ };
+ return this.executeQuery(selectObject, options);
+ },
+ getWhereClause: function(query, options){
+ var sql = "";
+ if(!options){
+ throw new Error("Values must be set as parameters on the options argument, which was not provided");
+ }
+ var indexedProperties = this.indexedProperties;
+ var params = (options.parameters = options.parameters || []);
+ query.forEach(function(term){
+ if(term.type == "comparison"){
+ addClause(term.name, config.table + '.' + term.name + term.comparator + "?");
+ params.push(term.value);
+ }
+ else if(term.type == "call") {
+ if (term.name == "sort") {
+ // handling somewhere else
+ }
+ else if (term.name instanceof Array && term.name[1] === "in") {
+ var name = term.name[0];
+ if(term.parameters.length == 0){
+ // an empty IN clause is considered invalid SQL
+ if(sql){
+ sql += term.logic == "&" ? " AND " : " OR ";
+ }
+ sql += "0=1";
+ }
+ else{
+ addClause(name, name + " IN (" + term.parameters.map(function(param){
+ params.push(param);
+ return "?";
+ }).join(",") + ")");
+ }
+ }
+ else{
+ throw new URIError("Invalid query syntax, " + term.method + " not implemented");
+ }
+ }
+ else{
+ throw new URIError("Invalid query syntax, unknown type");
+ }
+ function addClause(name, sqlClause){
+ if(!indexedProperties[name]){
+ throw new URIError("Can only query by " + Object.keys(indexedProperties));
+ }
+ if(sql){
+ sql += term.logic == "&" ? " AND " : " OR ";
+ }
+ sql += sqlClause;
+ }
+ });
+ return sql || "1=1";
+ },
+ executeQuery: function(selectObject, options) {
+ // executes a query with provide start and end parameters, calculating the total number of rows
+ if (selectObject.slice.length) {
+ var results = this.executeSql(selectObject+"", options.parameters).rows;
+ var lengthObject = first(this.executeSql(selectObject.toString(true), options.parameters).rows);
+ results.totalCount = lengthObject.count;
+ return results;
+ }
+ var results = this.executeSql(selectObject+"", options.parameters).rows;
+ results.totalCount = results.length;
+ return results;
+ },
+ executeSql: function(sql, parameters){
+ return database.executeSql(sql, parameters);
+ },
+ setSchema: function(schema) {
+ for(var i in schema.properties) {
+ if (schema.properties[i].index) {
+ this.indexedProperties[i] = schema.properties[i].index;
+ }
+ }
+ },
+ getSchema: function(){
+ if(config.type == "mysql"){
+ store.startTransaction();
+ var results = store.executeSql("DESCRIBE " + config.table, {});
+ store.commitTransaction();
+ var schema = {properties:{}};
+ results.some(function(column){
+ schema.properties[column.Field] = {
+ "default": column.Default,
+ type: [column.Type.match(/(char)|(text)/) ? "string" :
+ column.Type.match(/tinyint/) ? "boolean" :
+ column.Type.match(/(int)|(number)/) ? "number" :
+ "any", "null"]
+ };
+ if(column.Key == "PRI"){
+ schema.links = [{
+ rel: "full",
+ hrefProperty: column.Field
+ }];
+ }
+ });
+ return schema;
+ }
+ return {properties:{}};
+ },
+ setIndex: function(column) {
+ var sql = "CREATE INDEX " + config.indexPrefix + column + " ON " + config.table + " (" + column + ")";
+ print(sql);
+ //print( first(this.executeSql(sql).rows) );
+
+ }
+ };
+ for(var i in config){
+ store[i] = config[i];
+ }
+ return AutoTransaction(store, database);
+}
+
+try{
+ var DATABASE = require("settings").database;
+}catch(e){
+ print("No settings file defined");
+}
+
+var defaultDatabase;
+exports.defaultDatabase = function(parameters){
+ parameters = parameters || {};
+ for(var i in DATABASE){
+ if(!(i in parameters)){
+ parameters[i] = DATABASE[i];
+ }
+ }
+
+ if(defaultDatabase){
+ return defaultDatabase;
+ }
+ defaultDatabase = SQLDatabase(parameters);
+ require("stores").registerDatabase(defaultDatabase);
+ return defaultDatabase;
+};
+exports.openDatabase = function(name){
+ throw new Error("not implemented yet");
+};
View
0  lib/store/redis.js → store/redis.js
File renamed without changes
View
0  lib/store/remote.js → store/remote.js
File renamed without changes
View
0  lib/store/replicated.js → store/replicated.js
File renamed without changes
View
0  lib/store/slow.js → store/slow.js
File renamed without changes
View
0  lib/store/sql.js → store/sql.js
File renamed without changes
View
0  lib/stores.js → stores.js
File renamed without changes
View
0  lib/transaction.js → transaction.js
File renamed without changes
Please sign in to comment.
Something went wrong with that request. Please try again.