Skip to content

Instantly share code, notes, and snippets.

@lancejpollard
Last active December 12, 2015 01:09
Show Gist options
  • Select an option

  • Save lancejpollard/4689178 to your computer and use it in GitHub Desktop.

Select an option

Save lancejpollard/4689178 to your computer and use it in GitHub Desktop.
Criteria for Generic Datastore API (tower internals)
function adapter(name) {
return adapter.instances[name] || (adapter.instances[name] = new AdapterClass(name));
}
adapter.instances = {};
function AdapterClass(name) {
this.name = name;
this.types = {};
this.mixins = [];
}
var AdapterClassPrototype = AdapterClass.prototype;
AdapterClassPrototype.type = function(key, value) {
this.__lastKey__ = key;
this.types[key] = value || {};
return this;
}
AdapterClassPrototype.to = function(block) {
this.types[this.__lastKey__].to = block;
return this;
}
AdapterClassPrototype.from = function(block) {
this.types[this.__lastKey__].from = block;
return this;
}
AdapterClassPrototype.mixin = function(mixin) {
this.mixins.push(mixin);
return this;
}
AdapterClassPrototype.create = function() {
for (var key in mixin)
this[key] = mixin[key];
return Object.create(this);
}
var adapter = require('./adapter');
adapter(key)
.type('string')
.to(function(value) { return value; })
.from(function(value) { return value; })
.type('boolean')
.type('date')
.type('datetime')
.type('time')
.type('number')
.type('integer')
.type('float')
.mixin({
/*
* ['field', 'status', 'published'] // 3 values
* ['field', 'title', 'match', /[aA]/] // 4 values
* ['field', 'createdAt', 'lte', new Date] // 4 values
*/
find: function(criteria, callback) {
var collections = this._compileCriteriaForFind(criteria);
if (collections.length > 1) {
// async iterate
var i = 0
, recordsByType = {}
, collection;
function iterate() {
// on second time around, you would append post ids to comments query,
// stuff like that.
collection = collections[i];
// collection[0] is the collection name
// collection[1] are the query conditions
this.db.collectionFor(collection[0]).find(collection[1]).toArray(function(error, records) {
i++;
if (i >= collections.length) {
callback(null, records);
} else {
// save post records so you can use them in comments record query
recordsByType[collection[0]] = records;
process.nextTick(iterate);
}
});
}
iterate();
} else {
this.db.collectionFor(collections[0][0]).find(collections[0][1]).toArray(callback);
}
callback.call(this, undefined, collections);
return this;
},
create: function(criteria, callback) {
},
update: function(criteria, callback) {
},
destroy: function(criteria, callback) {
},
save: function(criteria, callback) {
},
findOrCreate: function(criteria, callback) {
},
stream: function(criteria, callback) {
},
connect: function(callback) {
callback.call(this);
return this;
},
disconnect: function(callback) {
},
_compileCriteriaForFind: function(criteria) {
var i = 0
, n = criteria.length
, criterion
, collections = []
, collection
, conditions;
while (i < n) {
criterion = criteria[i];
switch (criterion[0]) {
case 'start':
// this is hasMany, but is ready for graph queries.
case 'incoming':
conditions = {};
collection = [criterion[1], conditions];
collections.push(collection);
break;
case 'field':
if (criterion.length === 3) {
conditions[criterion[1]] = criterion[2];
} else {
conditions[criterion[1]] || (conditions[criterion[1]] = {});
conditions[criterion[1]][criterion[2]] = criterion[3];
}
break;
}
i++;
}
return collections;
}
});
var adapter = require('./adapter');
function store(key) {
return store.instances[key] || (store.instances[key] = new Store(key));
}
store.instances = {};
function Store(key) {
var adapterReference = adapter(key)
, i = adapterReference.mixins.length
, mixin
, key;
while (i--) {
mixin = adapterReference.mixins[i];
for (key in mixin)
this[key] = mixin[key];
}
this.adapter = adapterReference;
}
// this is how you'd use it
controller('post')
.where('status', 'published')
.match('title', /[aA]/)
.lte('createdAt', new Date)
.incoming('comments')
.match('message', 'node.js')
.all(function(error, comments) {
// comments is a controller, which is an array w/ extra functionality (Ember.ArrayProxy)
});
// you could accomplish directly on a "store"
// by manually passing a criteria array.
store('mongodb').connect(function() {
// the params in the routes could also be parsed into this,
// so there is the property `route.criteria`
var criteria = [
['start', 'posts']
, ['field', 'status', 'published']
, ['field', 'title', 'match', '[aA]']
, ['field', 'createdAt', 'lte', new Date]
, ['incoming', 'comments']
, ['field', 'message', 'match', 'node.js'] // where someone commented on post explicitly saying "node.js"
];
this.find(criteria, function(error, records) {
console.log(JSON.stringify(records, null, 2))
});
});
// and here is how you would start doing graph queries:
controller('user').outgoing('friends').outgoing('friends').all(function(error, fof) {
fof // is a controller
});
@lancejpollard

Copy link
Copy Markdown
Author

The store would also be unique to the client connection, so the store could theoretically serve as an identity map for the current user making the request to the server.

@lancejpollard

Copy link
Copy Markdown
Author

How crazy would this be lol, if the criteria could be handled by multiple adapters (where you register models with specific adapters):

// users are stored in mongodb
// friendships are fetched from facebook via a FacebookFriendAdapter or something
// friends is defined on the friendship object
// gists are defined from github, via a GistAdapter or GitHubAdapter
controller('user').outgoing('friendships').incoming('friends').outgoing('gists').all(function(error, gists) {

});

@lancejpollard

Copy link
Copy Markdown
Author

@thehydroimpulse

Copy link
Copy Markdown

Wow, I really love that 3rd party adapter concept. Working with any other service won't become a pain anymore. I'm thinking of working with the criteria format for one of my projects, I really like the idea of conforming to a set of standards.

@lancejpollard

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment