Skip to content

Instantly share code, notes, and snippets.

@JemiloII
Last active August 29, 2015 14:03
Show Gist options
  • Save JemiloII/b419fddd1b9b1052a8f9 to your computer and use it in GitHub Desktop.
Save JemiloII/b419fddd1b9b1052a8f9 to your computer and use it in GitHub Desktop.
Sails Adaptor for Ember Data
App = Ember.Application.create();
//var posts = [{id: '1', title: "Rails is Omakase", author: { name: "d2h" }, date: new Date('12-27-2012'), excerpt: "There are lots of à la carte software environments in this world. Places where in order to eat, you must first carefully look over the menu of options to order exactly what you want.", body: "I want this for my ORM, I want that for my template language, and let's finish it off with this routing library. Of course, you're going to have to know what you want, and you'll rarely have your horizon expanded if you always order the same thing, but there it is. It's a very popular way of consuming software.\n\nRails is not that. Rails is omakase."}, { id: '2', title: "The Parley Letter", author: { name: "d2h" }, date: new Date('12-24-2012'), excerpt: "My [appearance on the Ruby Rogues podcast](http://rubyrogues.com/056-rr-david-heinemeier-hansson/) recently came up for discussion again on the private Parley mailing list.", body: "A long list of topics were raised and I took a time to ramble at large about all of them at once. Apologies for not taking the time to be more succinct, but at least each topic has a header so you can skip stuff you don't care about.\n\n### Maintainability\n\nIt's simply not true to say that I don't care about maintainability. I still work on the oldest Rails app in the world." }];
var showdown = new Showdown.converter();
Ember.Handlebars.helper('markdown', function (input) {
return new Handlebars.SafeString(showdown.makeHtml(input));
});
Ember.Handlebars.helper('format-date', function (date) {
return moment(date).format('lll');
});
App.Router.map(function(){
this.resource('about');
this.resource('posts', function(){
this.resource('post', { path: ':post_id' });
});
});
App.ApplicationAdapter = DS.SailsAdapter;
App.Post = DS.Model.extend({
title : DS.attr('string'),
author : DS.attr('json'),
date : DS.attr('date'),
excerpt : DS.attr('text'),
body : DS.attr('text')
});
App.PostRoute = Ember.Route.extend({
index: function (){
return App.Post.store.find('posts');
},
model: function(params){
return this.store.find('post', params.post_id);
},
// deserialize: function(data){
// return data;
// }
});
App.PostController = Ember.ObjectController.extend({
//index: function () {},
isEditing: false,
actions: {
edit: function(){
this.set('isEditing', true);
},
doneEditing: function(){
this.set('isEditing', false);
}
}
});
(function() {
/*global Ember*/
/*global DS*/
/*global io*/
'use strict';
var RSVP = Ember.RSVP;
var get = Ember.get;
DS.SailsRESTAdapter = DS.RESTAdapter.extend({
defaultSerializer: '-default',
ajaxError: function(jqXHR) {
var error = this._super(jqXHR);
var data = Ember.$.parseJSON(jqXHR.responseText);
if (data.errors) {
return new DS.InvalidError(this.formatError(data));
} else {
return error;
}
},
/*
Sails error objects look something like this:
error: "E_VALIDATION",
model: "User",
summary: "1 attribute is invalid",
status: 400,
invalidAttributes: {
name: [
{
rule: "minLength",
message: "Validation error: "bar" Rule "minLength(10)" failed."
}
]
}
*/
formatError: function(error) {
return Object.keys(error.invalidAttributes).reduce(function(memo, property) {
memo[property] = error.invalidAttributes[property].map(function(err) {
return err.message;
});
return memo;
}, {});
},
pathForType: function(type) {
var camelized = Ember.String.camelize(type);
return Ember.String.singularize(camelized);
}
});
DS.SailsSocketAdapter = DS.SailsAdapter = DS.SailsRESTAdapter.extend({
useCSRF: false,
CSRFToken: '',
listeningModels: {},
init: function () {
this._super();
if(this.useCSRF) {
io.socket.get('/csrfToken', function response(tokenObject) {
this.CSRFToken = tokenObject._csrf;
}.bind(this));
}
},
isErrorObject: function(data) {
return !!(data.error && data.model && data.summary && data.status);
},
ajax: function(url, method, data) {
return this.socket(url, method, data);
},
socket: function(url, method, data ) {
var isErrorObject = this.isErrorObject.bind(this);
method = method.toLowerCase();
var adapter = this;
adapter._log(method, url, data);
if(method !== 'get')
this.checkCSRF(data);
return new RSVP.Promise(function(resolve, reject) {
io.socket[method](url, data, function (data) {
if (isErrorObject(data)) {
adapter._log('error:', data);
if (data.errors) {
reject(new DS.InvalidError(adapter.formatError(data)));
} else {
reject(data);
}
} else {
resolve(data);
}
});
});
},
buildURL: function(type, id) {
this._listenToSocket(type);
return this._super.apply(this, arguments);
},
_listenToSocket: function(model) {
if(model in this.listeningModels) {
return;
}
var self = this;
var store = this.container.lookup('store:main');
var socketModel = model;
function findModelName(model) {
var mappedName = self.modelNameMap[model];
return mappedName || model;
}
function pushMessage(message) {
var type = store.modelFor(socketModel);
var serializer = store.serializerFor(type.typeKey);
// Messages from 'created' don't seem to be wrapped correctly,
// however messages from 'updated' are, so need to double check here.
if(!(model in message.data)) {
var obj = {};
obj[model] = message.data;
message.data = obj;
}
var record = serializer.extractSingle(store, type, message.data);
store.push(socketModel, record);
}
function destroy(message) {
var type = store.modelFor(socketModel);
var record = store.getById(type, message.id);
if ( record && typeof record.get('dirtyType') === 'undefined' ) {
record.unloadRecord();
}
}
var eventName = Ember.String.camelize(model).toLowerCase();
io.socket.on(eventName, function (message) {
// Left here to help further debugging.
//console.log("Got message on Socket : " + JSON.stringify(message));
if (message.verb === 'created') {
// Run later to prevent creating duplicate records when calling store.createRecord
Ember.run.later(null, pushMessage, message, 50);
}
if (message.verb === 'updated') {
pushMessage(message);
}
if (message.verb === 'destroyed') {
destroy(message);
}
});
// We add an emtpy property instead of using an array
// ao we can utilize the 'in' keyword in first test in this function.
this.listeningModels[model] = 0;
},
_log: function() {
if (this.log) {
console.log.apply(console, arguments);
}
},
checkCSRF: function(data) {
if(!this.useCSRF) return data;
if(this.CSRFToken.length === 0) {
throw new Error("CSRF Token not fetched yet.");
}
data._csrf = this.CSRFToken;
return data;
},
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment