Commit 7e3db802 authored by Sindre Sorhus's avatar Sindre Sorhus

Merge pull request #886 from bantic/update-ember-1.5.0

Update to Ember 1.5.0, updates ember-data also
parents 32003552 6251179c
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
"todomvc-common": "~0.1.4", "todomvc-common": "~0.1.4",
"jquery": "~2.1.0", "jquery": "~2.1.0",
"handlebars": "~1.3.0", "handlebars": "~1.3.0",
"ember": "~1.3.1", "ember": "~1.5.0",
"ember-data": "1.0.0-beta.6", "ember-data": "1.0.0-beta.7",
"ember-localstorage-adapter": "latest" "ember-localstorage-adapter": "latest"
} }
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
/*global Ember*/ /*global Ember*/
/*global DS*/ /*global DS*/
(function () { 'use strict';
'use strict';
DS.LSSerializer = DS.JSONSerializer.extend({
DS.LSAdapter = DS.Adapter.extend(Ember.Evented, {
serializeHasMany: function(record, json, relationship) {
init: function () { var key = relationship.key,
this._loadData(); relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
},
if (relationshipType === 'manyToNone' ||
generateIdForRecord: function () { relationshipType === 'manyToMany' ||
return Math.random().toString(32).slice(2).substr(0, 5); relationshipType === 'manyToOne') {
}, json[key] = record.get(key).mapBy('id');
// TODO support for polymorphic manyToNone and manyToMany relationships
find: function (store, type, id) { }
var namespace = this._namespaceForType(type); },
return Ember.RSVP.resolve(Ember.copy(namespace.records[id]));
}, extractSingle: function(store, type, payload) {
if (payload && payload._embedded) {
findMany: function (store, type, ids) { for (var relation in payload._embedded) {
var namespace = this._namespaceForType(type); var typeName = Ember.String.singularize(relation),
var results = []; embeddedPayload = payload._embedded[relation];
for (var i = 0; i < ids.length; i++) {
results.push(Ember.copy(namespace.records[ids[i]])); if (embeddedPayload) {
} if (Ember.isArray(embeddedPayload)) {
return Ember.RSVP.resolve(results); store.pushMany(typeName, embeddedPayload);
}, } else {
store.push(typeName, embeddedPayload);
// Supports queries that look like this: }
// }
// { }
// <property to query>: <value or regex (for strings) to match>,
// ... delete payload._embedded;
// } }
//
// Every property added to the query is an "AND" query, not "OR" return this.normalize(type, payload);
// }
// Example:
// });
// match records with "complete: true" and the name "foo" or "bar"
// DS.LSAdapter = DS.Adapter.extend(Ember.Evented, {
// { complete: true, name: /foo|bar/ } /**
findQuery: function (store, type, query, recordArray) { This is the main entry point into finding records. The first parameter to
var namespace = this._namespaceForType(type); this method is the model's name as a string.
var results = this.query(namespace.records, query);
return Ember.RSVP.resolve(results); @method find
}, @param {DS.Model} type
@param {Object|String|Integer|null} id
query: function (records, query) { */
var results = []; find: function(store, type, id, opts) {
var id, record, property, test, push; var adapter = this;
for (id in records) { var allowRecursive = true;
record = records[id]; var namespace = this._namespaceForType(type);
for (property in query) {
test = query[property]; /**
push = false; * In the case where there are relationships, this method is called again
if (Object.prototype.toString.call(test) === '[object RegExp]') { * for each relation. Given the relations have references to the main
push = test.test(record[property]); * object, we use allowRecursive to avoid going further into infinite
} else { * recursiveness.
push = record[property] === test; *
} * Concept from ember-indexdb-adapter
} */
if (push) { if (opts && typeof opts.allowRecursive !== 'undefined') {
results.push(record); allowRecursive = opts.allowRecursive;
} }
}
return results; return new Ember.RSVP.Promise(function(resolve, reject) {
}, var record = Ember.A(namespace.records[id]);
findAll: function (store, type) { if (allowRecursive && record) {
var namespace = this._namespaceForType(type); adapter.loadRelationships(type, record).then(function(finalRecord) {
var results = []; resolve(finalRecord);
for (var id in namespace.records) { });
results.push(Ember.copy(namespace.records[id])); } else {
} if (!record) {
return Ember.RSVP.resolve(results); reject();
}, } else {
resolve(record);
createRecord: function (store, type, record) { }
var namespace = this._namespaceForType(type); }
this._addRecordToNamespace(namespace, record); });
this._saveData();
return Ember.RSVP.resolve(); resolve(record);
}, },
updateRecord: function (store, type, record) { findMany: function (store, type, ids) {
var namespace = this._namespaceForType(type); var adapter = this;
var id = record.get('id'); var namespace = this._namespaceForType(type);
namespace.records[id] = record.toJSON({ includeId: true });
this._saveData(); return new Ember.RSVP.Promise(function(resolve, reject) {
return Ember.RSVP.resolve(); var results = [];
},
for (var i = 0; i < ids.length; i++) {
deleteRecord: function (store, type, record) { results.push(Ember.copy(namespace.records[ids[i]]));
var namespace = this._namespaceForType(type); }
var id = record.get('id');
delete namespace.records[id]; resolve(results);
this._saveData(); });
return Ember.RSVP.resolve(); },
},
// Supports queries that look like this:
// private //
// {
_getNamespace: function () { // <property to query>: <value or regex (for strings) to match>,
return this.namespace || 'DS.LSAdapter'; // ...
}, // }
//
_loadData: function () { // Every property added to the query is an "AND" query, not "OR"
var storage = localStorage.getItem(this._getNamespace()); //
this._data = storage ? JSON.parse(storage) : {}; // Example:
}, //
// match records with "complete: true" and the name "foo" or "bar"
_saveData: function () { //
localStorage.setItem(this._getNamespace(), JSON.stringify(this._data)); // { complete: true, name: /foo|bar/ }
}, findQuery: function (store, type, query, recordArray) {
var namespace = this._namespaceForType(type),
_namespaceForType: function (type) { results = this.query(namespace.records, query);
var namespace = type.url || type.toString();
return this._data[namespace] || ( return Ember.RSVP.resolve(results);
this._data[namespace] = {records: {}} },
);
}, query: function (records, query) {
var results = [],
_addRecordToNamespace: function (namespace, record) { id, record, property, test, push;
var data = record.serialize({includeId: true}); for (id in records) {
namespace.records[data.id] = data; record = records[id];
} for (property in query) {
}); test = query[property];
})(); push = false;
if (Object.prototype.toString.call(test) === '[object RegExp]') {
push = test.test(record[property]);
} else {
push = record[property] === test;
}
}
if (push) {
results.push(record);
}
}
return results;
},
findAll: function (store, type) {
var namespace = this._namespaceForType(type),
results = [];
for (var id in namespace.records) {
results.push(Ember.copy(namespace.records[id]));
}
return Ember.RSVP.resolve(results);
},
createRecord: function (store, type, record) {
var namespaceRecords = this._namespaceForType(type),
recordHash = record.serialize({includeId: true});
namespaceRecords.records[recordHash.id] = recordHash;
this.persistData(type, namespaceRecords);
return Ember.RSVP.resolve();
},
updateRecord: function (store, type, record) {
var namespaceRecords = this._namespaceForType(type),
id = record.get('id');
namespaceRecords.records[id] = record.serialize({ includeId: true });
this.persistData(type, namespaceRecords);
return Ember.RSVP.resolve();
},
deleteRecord: function (store, type, record) {
var namespaceRecords = this._namespaceForType(type),
id = record.get('id');
delete namespaceRecords.records[id];
this.persistData(type, namespaceRecords);
return Ember.RSVP.resolve();
},
generateIdForRecord: function () {
return Math.random().toString(32).slice(2).substr(0, 5);
},
// private
adapterNamespace: function () {
return this.namespace || 'DS.LSAdapter';
},
loadData: function () {
var storage = localStorage.getItem(this.adapterNamespace());
return storage ? JSON.parse(storage) : {};
},
persistData: function(type, data) {
var modelNamespace = this.modelNamespace(type),
localStorageData = this.loadData();
localStorageData[modelNamespace] = data;
localStorage.setItem(this.adapterNamespace(), JSON.stringify(localStorageData));
},
_namespaceForType: function (type) {
var namespace = this.modelNamespace(type),
storage = localStorage.getItem(this.adapterNamespace());
return storage ? JSON.parse(storage)[namespace] || {records: {}} : {records: {}};
},
modelNamespace: function(type) {
return type.url || type.toString();
},
/**
* This takes a record, then analyzes the model relationships and replaces
* ids with the actual values.
*
* Stolen from ember-indexdb-adapter
*
* Consider the following JSON is entered:
*
* ```js
* {
* "id": 1,
* "title": "Rails Rambo",
* "comments": [1, 2]
* }
*
* This will return:
*
* ```js
* {
* "id": 1,
* "title": "Rails Rambo",
* "comments": [1, 2]
*
* "_embedded": {
* "comment": [{
* "_id": 1,
* "comment_title": "FIRST"
* }, {
* "_id": 2,
* "comment_title": "Rails is unagi"
* }]
* }
* }
*
* This way, whenever a resource returned, its relationships will be also
* returned.
*
* @method loadRelationships
* @private
* @param {DS.Model} type
* @param {Object} record
*/
loadRelationships: function(type, record) {
var adapter = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
var resultJSON = {},
typeKey = type.typeKey,
relationshipNames, relationships,
relationshipPromises = [];
relationshipNames = Ember.get(type, 'relationshipNames');
relationships = relationshipNames.belongsTo;
relationships = relationships.concat(relationshipNames.hasMany);
relationships.forEach(function(relationName) {
var relationModel = type.typeForRelationship(relationName),
relationEmbeddedId = record[relationName],
relationProp = adapter.relationshipProperties(type, relationName),
relationType = relationProp.kind,
/**
* This is the relationship field.
*/
promise, embedPromise;
var opts = {allowRecursive: false};
/**
* embeddedIds are ids of relations that are included in the main
* payload, such as:
*
* {
* cart: {
* id: "s85fb",
* customer: "rld9u"
* }
* }
*
* In this case, cart belongsTo customer and its id is present in the
* main payload. We find each of these records and add them to _embedded.
*/
if (relationEmbeddedId) {
if (relationType == 'belongsTo' || relationType == 'hasOne') {
promise = adapter.find(null, relationModel, relationEmbeddedId, opts)
} else if (relationType == 'hasMany') {
promise = adapter.findMany(null, relationModel, relationEmbeddedId, opts)
}
embedPromise = new Ember.RSVP.Promise(function(resolve, reject) {
promise.then(function(relationRecord) {
var finalPayload = adapter.addEmbeddedPayload(record, relationName, relationRecord)
resolve(finalPayload);
});
});
relationshipPromises.push(embedPromise);
}
});
Ember.RSVP.all(relationshipPromises).then(function() {
resolve(record);
});
});
},
/**
* Given the following payload,
*
* {
* cart: {
* id: "1",
* customer: "2"
* }
* }
*
* With `relationshipName` being `customer` and `relationshipRecord`
*
* {id: "2", name: "Rambo"}
*
* This method returns the following payload:
*
* {
* cart: {
* id: "1",
* customer: "2"
* },
* _embedded: {
* customer: {
* id: "2",
* name: "Rambo"
* }
* }
* }
*
* which is then treated by the serializer later.
*
* @method addEmbeddedPayload
* @private
* @param {Object} payload
* @param {String} relationshipName
* @param {Object} relationshipRecord
*/
addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) {
var objectHasId = (relationshipRecord && relationshipRecord.id),
arrayHasIds = (relationshipRecord.length && relationshipRecord.everyBy("id")),
isValidRelationship = (objectHasId || arrayHasIds);
if (isValidRelationship) {
if (!payload['_embedded']) {
payload['_embedded'] = {}
}
payload['_embedded'][relationshipName] = relationshipRecord;
if (relationshipRecord.length) {
payload[relationshipName] = relationshipRecord.mapBy('id');
} else {
payload[relationshipName] = relationshipRecord.id;
}
}
if (this.isArray(payload[relationshipName])) {
payload[relationshipName] = payload[relationshipName].filter(function(id) {
return id;
});
}
return payload;
},
isArray: function(value) {
return Object.prototype.toString.call(value) === '[object Array]';
},
/**
*
* @method relationshipProperties
* @private
* @param {DS.Model} type
* @param {String} relationName
*/
relationshipProperties: function(type, relationName) {
var relationships = Ember.get(type, 'relationshipsByName');
if (relationName) {
return relationships.get(relationName);
} else {
return relationships;
}
}
});
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -57,11 +57,15 @@ ...@@ -57,11 +57,15 @@
<a href="http://github.com/tomdale">Tom Dale</a>, <a href="http://github.com/tomdale">Tom Dale</a>,
<a href="http://github.com/addyosmani">Addy Osmani</a> <a href="http://github.com/addyosmani">Addy Osmani</a>
</p> </p>
<p>
Updated by
<a href="http://github.com/bantic">Cory Forsyth</a>
</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer> </footer>
</script> </script>
<script src="bower_components/todomvc-common/base.js"></script> <script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/jquery/jquery.js"></script> <script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/handlebars/handlebars.js"></script> <script src="bower_components/handlebars/handlebars.js"></script>
<script src="bower_components/ember/ember.js"></script> <script src="bower_components/ember/ember.js"></script>
<script src="bower_components/ember-data/ember-data.js"></script> <script src="bower_components/ember-data/ember-data.js"></script>
......
/*global Todos, Ember */ /*global Ember */
(function () { (function () {
'use strict'; 'use strict';
Ember.Handlebars.helper('pluralize', function (singular, count) { Ember.Handlebars.helper('pluralize', function (singular, count) {
/* From Ember-Data */ /* From Ember-Data */
var inflector = new Ember.Inflector(Ember.Inflector.defaultRules); var inflector = Ember.Inflector.inflector;
return count === 1 ? singular : inflector.pluralize(singular); return count === 1 ? singular : inflector.pluralize(singular);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment