Skip to content

Instantly share code, notes, and snippets.

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

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

Select an option

Save lancejpollard/4689443 to your computer and use it in GitHub Desktop.
Brain dumping on adapters.js
// associating properties from different apis with your model.
// say, by default if the associated fields are blank, it will
// fetch and save them from the service.
// `linkedIn.resume` has similar API meaning to `DS.attr('App.Company')`
adapter('linkedIn')
.resource('resume')
.mixin({
find: function(criteria, callback) {
// criteria ==
/* [
* ['resume', 'resume'] // only passed to linkedIn adapter
* , ['photoUrl', 'user', 'photoUrl'] // only passed to facebook adapter
* ]]
*/
this.get('http://api.linkedIn.com/resume', function(callback) {
});
}
})
adapter('twitter')
.resource('user')
.resource('tweet')
.mixin({
/*
* [
* ['user', 'profile_image_url']
* , ['user', 'email']
* ]
*/
find: function(criteria) {
}
})
adapter('facebook')
.resource('user')
.resource('status')
.resource('page')
.resource('app')
.mixin({
/*
* [['user', 'photoUrl']]
*/
find: function(criteria) {
}
})
model('user')
.field('resume', 'linkedIn.resume')
.field('photoUrl', 'facebook.user.photoUrl')
.field('photoUrl', {
// in this case it may try facebook first, then twitter if nothing found
sources: [
'facebook.user.photoUrl'
, 'twitter.user.profile_image_url'
]
})
.field('photoUrl', {
// or maybe they need to be, themselves, controllers (or, better, criteria)
sources: [
criteria('facebook.user').select('photoUrl')
, criteria('twitter.user').select('profile_image_url')
]
})
// since `twitter.user` is used more than once, the adapter will have the `twitter`
// object cached in there so it won't need to make another call.
.field('email', 'twitter.user.email')
.index('id', {in: 'redis'})
// somehow you'd have to know what to pass to the adapter
// (facebook/linkedin credentials, etc.)
controller('user').create(function(error, user) {
assert(user.get('resume'));
assert(user.get('photoUrl'));
});
@lancejpollard

Copy link
Copy Markdown
Author

Still figuring out the final end pieces. But I think the biggest thing is mentally breaking away from the way Rails used controllers (that's why I'm liking writing in lowercase like controller('users'), it makes the controllers feel lighter and I can step out of the Rails mindset). Instead, controllers are basically representations of the data; controllers or the ways you manipulate/query/compute/create/destroy data. In this new sense, controllers have arbitrary names, such as controller('unreadMessagesCount'), but there are also default controller classes that are created for (1) every route in the system (e.g. controller('users.index') for route('/users', 'users.index')), (2) every model in the system (e.g. controller('user')) which I'm thinking as of now will only really be used on the client for formatting the field values (that was the i18n stuff earlier today), and (3) every model plural (collection) in the system (e.g. controller('users')). There seems like there some overlap (i.e. controller('users.index') and controller('users') seem like they should probably do the same thing). But, this is where the convention for using controllers comes in. Since there is a controller for every route, you should choose controller('users.index') over controller('users') because the route-specific controller comes packed with extra stuff specific to the route. For example, you can specify specific parameters to be parsed on that route, and if they are found, to be appended to the route's controller's criteria. An example is:route('posts.index').param('title').setup(function(controller) { controller.all() })..., where that controller.all() call will be passing criteria to the store that contains whatever the query parameter value was for "title", such as ['field', 'title', 'match', 'the passed value']). In addition, the criteria passed to the store from controller.all() could also contain user-specific conditions, such as authorization rules for what the user can see. This is the reason you'd pick route-specific controllers over the model-plural controllers like controller('posts'). For each of these route-specific controllers like controller('posts.index') and controller('posts.create'), the first clause in the criteria is going to be ['start', modelName], which tells the adapter what table/collection to use. Now for controllers like controller('unreadMessagesCount'), which don't map to a specific model (at least we can't infer that based on the name), you have to specify the start criteria (start/extend), so realistically it would look more like controller('unreadMessagesCount').start('messages').where('read', false). This is how graph query languages work, specifying the "start node". Then that returns('count') tells the controller to return an integer rather than a record (or array of records). So, this controller doesn't map neatly to a model -- this is why removing the ideas of Rails controllers is helpful -- the controller, instead, is just a representation of the data, a controller for the data.

This also means the idea of a model is more flexible than Rails. Models are basically your database tables, but they're there more to just save your data in an easy-to-query and easy-to-represent way. The controllers are the interface to the models, and so, the only time you ever use model('user') is in defining the model -- creating the schema. To access the model, you use a controller. This is very different than Rails, where you access data from the model as in User.all, but it offers much much more power.

The center of the app, then, is the idea of "resources" more than it is "models". You need models to structure your data so you can persist/query it easily. But you define a set of "routes" for the user/client, and you use controllers to optimally feed different representations of the models to the client.

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