-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of the Snapshot API
- Loading branch information
Showing
5 changed files
with
581 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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 file contains 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 file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
/** | ||
@module ember-data | ||
*/ | ||
|
||
var get = Ember.get; | ||
|
||
/** | ||
@class Snapshot | ||
@namespace DS | ||
@private | ||
*/ | ||
function Snapshot(record) { | ||
this._attributes = Ember.create(null); | ||
this._belongsToRelationships = Ember.create(null); | ||
this._hasManyRelationships = Ember.create(null); | ||
|
||
this.id = get(record, 'id'); | ||
|
||
record.eachAttribute(function(keyName) { | ||
this._attributes[keyName] = get(record, keyName); | ||
}, this); | ||
|
||
this.record = record; | ||
this.type = record.constructor; | ||
this.typeKey = record.constructor.typeKey; | ||
|
||
/** | ||
The following code is here to keep backwards compatibility when accessing | ||
`constructor` directly. | ||
With snapshots you should use `type` instead of `constructor`. | ||
Remove for Ember Data 1.0. | ||
*/ | ||
if (Ember.platform.hasPropertyAccessors) { | ||
var callDeprecate = true; | ||
|
||
Ember.defineProperty(this, 'constructor', { | ||
get: function() { | ||
/** | ||
Ugly hack since accessing error.stack (done in `Ember.deprecate()`) | ||
causes the internals of Chrome to access the constructor, which then | ||
causes an infinite loop if accessed and calls `Ember.deprecate()` | ||
again. | ||
*/ | ||
if (callDeprecate) { | ||
callDeprecate = false; | ||
Ember.deprecate('Usage of `snapshot.constructor` is deprecated, use `snapshot.type` instead.'); | ||
callDeprecate = true; | ||
} | ||
|
||
return this.type; | ||
} | ||
}); | ||
} else { | ||
this.constructor = this.type; | ||
} | ||
} | ||
|
||
Snapshot.prototype = { | ||
constructor: Snapshot, | ||
|
||
/** | ||
Returns the value of an attribute. | ||
Example | ||
```javascript | ||
var post = store.createRecord('post', { author: 'Tomster', title: 'Ember.js rocks' }); | ||
var snapshot = post._createSnapshot(); | ||
snapshot.attr('author'); // => 'Tomster' | ||
snapshot.attr('title'); // => 'Ember.js rocks' | ||
``` | ||
Note: Values are loaded eagerly and cached when the snapshot is created. | ||
@method attr | ||
@param {string} keyName | ||
@return {Object} The attribute value or undefined | ||
*/ | ||
attr: function(keyName) { | ||
if (keyName in this._attributes) { | ||
return this._attributes[keyName]; | ||
} | ||
throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no attribute named '" + keyName + "' defined."); | ||
}, | ||
|
||
/** | ||
Returns all attributes and their corresponding values. | ||
Example | ||
```javascript | ||
var post = store.createRecord('post', { author: 'Tomster', title: 'Ember.js rocks' }); | ||
var snapshot = post._createSnapshot(); | ||
snapshot.attributes(); // => { author: 'Tomster', title: 'Ember.js rocks' } | ||
``` | ||
@method attributes | ||
@return {Array} All attributes for the current snapshot | ||
*/ | ||
attributes: function() { | ||
return Ember.copy(this._attributes); | ||
}, | ||
|
||
/** | ||
Returns the current value of a belongsTo relationship. | ||
Example | ||
```javascript | ||
var post = store.createRecord('post', { title: 'Hello World' }); | ||
var comment = store.createRecord('comment', { body: 'Lorem ipsum', post: post }); | ||
var snapshot = comment._createSnapshot(); | ||
snapshot.belongsTo('post'); // => DS.Snapshot of post | ||
``` | ||
Calling `belongsTo` will return a new Snapshot as long as there's any | ||
data available, such as an ID. If there's no data available `belongsTo` will | ||
return undefined. | ||
Note: Relationships are loaded lazily and cached upon first access. | ||
@method belongsTo | ||
@param {string} keyName | ||
@return {DS.Snapshot|undefined} A snapshot of a belongsTo relationship or undefined | ||
*/ | ||
belongsTo: function(keyName) { | ||
var relationship, snapshot, inverseRecord; | ||
|
||
if (!(keyName in this._belongsToRelationships)) { | ||
relationship = this.record._relationships[keyName]; | ||
|
||
if (!(relationship && relationship.relationshipMeta.kind === 'belongsTo')) { | ||
throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no belongsTo relationship named '" + keyName + "' defined."); | ||
} | ||
|
||
inverseRecord = get(relationship, 'inverseRecord'); | ||
if (inverseRecord) { | ||
snapshot = inverseRecord._createSnapshot(); | ||
} | ||
|
||
this._belongsToRelationships[keyName] = snapshot; | ||
} | ||
|
||
return this._belongsToRelationships[keyName]; | ||
}, | ||
|
||
/** | ||
Returns the current value of a hasMany relationship. | ||
`hasMany` takes an optional hash of options as a second parameter, | ||
currently supported options are: | ||
- `ids`: set to `true` if you only want the IDs of the related items to be | ||
returned. | ||
Example | ||
```javascript | ||
var post = store.createRecord('post', { title: 'Hello World', comments: [2, 3] }); | ||
var snapshot = post._createSnapshot(); | ||
snapshot.hasMany('comments'); // => [DS.Snapshot, DS.Snapshot] | ||
snapshot.hasMany('comments', { ids: true }); // => [2, 3] | ||
``` | ||
Note: Relationships are loaded lazily and cached upon first access. | ||
@method hasMany | ||
@param {string} keyName | ||
@param {Object} [options] | ||
@return {Array} An array of snapshots of a hasMany relationship | ||
*/ | ||
hasMany: function(keyName, options) { | ||
var ids = options && options.ids; | ||
var snapshots = []; | ||
var relationship, members, hasMany; | ||
|
||
if (!(keyName in this._hasManyRelationships)) { | ||
relationship = this.record._relationships[keyName]; | ||
|
||
if (!(relationship && relationship.relationshipMeta.kind === 'hasMany')) { | ||
throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no hasMany relationship named '" + keyName + "' defined."); | ||
} | ||
|
||
members = get(relationship, 'members'); | ||
members.forEach(function(member) { | ||
snapshots.push(member._createSnapshot()); | ||
}); | ||
|
||
this._hasManyRelationships[keyName] = snapshots; | ||
} | ||
|
||
hasMany = this._hasManyRelationships[keyName]; | ||
|
||
return ids ? Ember.A(hasMany).mapBy('id') : hasMany; | ||
}, | ||
|
||
/** | ||
Iterates through all the attributes of the model, calling the passed | ||
function on each attribute. | ||
Example | ||
```javascript | ||
snapshot.eachAttribute(function(name, meta) { | ||
// ... | ||
}); | ||
``` | ||
@method eachAttribute | ||
@param {Function} callback the callback to execute | ||
@param {Object} [binding] the value to which the callback's `this` should be bound | ||
*/ | ||
eachAttribute: function(callback, binding) { | ||
this.record.eachAttribute(callback, binding); | ||
}, | ||
|
||
/** | ||
Iterates through all the relationships of the model, calling the passed | ||
function on each relationship. | ||
Example | ||
```javascript | ||
snapshot.eachRelationship(function(name, relationship) { | ||
// ... | ||
}); | ||
``` | ||
@method eachRelationship | ||
@param {Function} callback the callback to execute | ||
@param {Object} [binding] the value to which the callback's `this` should be bound | ||
*/ | ||
eachRelationship: function(callback, binding) { | ||
this.record.eachRelationship(callback, binding); | ||
}, | ||
|
||
/** | ||
@method get | ||
@param {string} keyName | ||
@return {Object} The property value | ||
@deprecated Use [attr](#method_attr), [belongsTo](#method_belongsTo) or [hasMany](#method_hasMany) instead | ||
*/ | ||
get: function(keyName) { | ||
Ember.deprecate('Using DS.Snapshot.get() is deprecated. Use .attr(), .belongsTo() or .hasMany() instead.'); | ||
|
||
if (keyName === 'id') { | ||
return this.id; | ||
} | ||
|
||
if (keyName in this._attributes) { | ||
return this.attr(keyName); | ||
} | ||
|
||
var relationship = this.record._relationships[keyName]; | ||
|
||
if (relationship && relationship.relationshipMeta.kind === 'belongsTo') { | ||
return this.belongsTo(keyName); | ||
} | ||
if (relationship && relationship.relationshipMeta.kind === 'hasMany') { | ||
return this.hasMany(keyName); | ||
} | ||
|
||
return get(this.record, keyName); | ||
}, | ||
|
||
/** | ||
@method unknownProperty | ||
@param {string} keyName | ||
@return {Object} The property value | ||
@deprecated Use [attr](#method_attr), [belongsTo](#method_belongsTo) or [hasMany](#method_hasMany) instead | ||
*/ | ||
unknownProperty: function(keyName) { | ||
return this.get(keyName); | ||
} | ||
}; | ||
|
||
export default Snapshot; |
This file contains 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
Oops, something went wrong.