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

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