Last active
December 12, 2015 01:09
-
-
Save lancejpollard/4689178 to your computer and use it in GitHub Desktop.
Criteria for Generic Datastore API (tower internals)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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 | |
| }); |
Author
Author
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.
Author
More stuff: https://gist.github.com/4689443#comment-764567
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How crazy would this be lol, if the criteria could be handled by multiple adapters (where you register models with specific adapters):