Created
April 3, 2013 20:14
-
-
Save pzuraq/5304796 to your computer and use it in GitHub Desktop.
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
// *** WHY DOES THIS FILE EXIST *** | |
// | |
// For some godawful reason, ember-data's StateManager was written in a closure | |
// and I can't figure out a way to extend it, which is terrible because I really | |
// needed to. So, I copied over most of the closure here and reinstantiated | |
// the states var, which meant I needed a whole bunch of other stuff from the | |
// closure, and then I reopened DS.StateManager and set states to the new, | |
// modified states var. Replace this asap, please. | |
(function() { | |
var isEmptyObject = function(object) { | |
for (var name in object) { | |
if (object.hasOwnProperty(name)) { return false; } | |
} | |
return true; | |
}; | |
var hasDefinedProperties = function(object) { | |
for (var name in object) { | |
if (object.hasOwnProperty(name) && object[name]) { return true; } | |
} | |
return false; | |
}; | |
var didChangeData = function(manager) { | |
var record = get(manager, 'record'); | |
record.materializeData(); | |
}; | |
var willSetProperty = function(manager, context) { | |
context.oldValue = get(get(manager, 'record'), context.name); | |
var change = DS.AttributeChange.createChange(context); | |
get(manager, 'record')._changesToSync[context.attributeName] = change; | |
}; | |
var didSetProperty = function(manager, context) { | |
var change = get(manager, 'record')._changesToSync[context.attributeName]; | |
change.value = get(get(manager, 'record'), context.name); | |
change.sync(); | |
}; | |
// Whenever a property is set, recompute all dependent filters | |
var updateRecordArrays = function(manager) { | |
var record = manager.get('record'); | |
record.updateRecordArraysLater(); | |
}; | |
var DirtyState = DS.State.extend({ | |
initialState: 'uncommitted', | |
// FLAGS | |
isDirty: true, | |
// SUBSTATES | |
// When a record first becomes dirty, it is `uncommitted`. | |
// This means that there are local pending changes, but they | |
// have not yet begun to be saved, and are not invalid. | |
uncommitted: DS.State.extend({ | |
// TRANSITIONS | |
enter: function(manager) { | |
var dirtyType = get(this, 'dirtyType'), | |
record = get(manager, 'record'); | |
record.withTransaction(function (t) { | |
t.recordBecameDirty(dirtyType, record); | |
}); | |
}, | |
// EVENTS | |
willSetProperty: willSetProperty, | |
didSetProperty: didSetProperty, | |
becomeDirty: Ember.K, | |
willCommit: function(manager) { | |
manager.transitionTo('inFlight'); | |
}, | |
becameClean: function(manager) { | |
var record = get(manager, 'record'), | |
dirtyType = get(this, 'dirtyType'); | |
record.withTransaction(function(t) { | |
t.recordBecameClean(dirtyType, record); | |
}); | |
manager.transitionTo('loaded.materializing'); | |
}, | |
becameInvalid: function(manager) { | |
var dirtyType = get(this, 'dirtyType'), | |
record = get(manager, 'record'); | |
record.withTransaction(function (t) { | |
t.recordBecameInFlight(dirtyType, record); | |
}); | |
manager.transitionTo('invalid'); | |
}, | |
rollback: function(manager) { | |
get(manager, 'record').rollback(); | |
}, | |
// **************** | |
// THIS LINE DAMNIT | |
// **************** | |
loadedData: Ember.K | |
}), | |
// Once a record has been handed off to the adapter to be | |
// saved, it is in the 'in flight' state. Changes to the | |
// record cannot be made during this window. | |
inFlight: DS.State.extend({ | |
// FLAGS | |
isSaving: true, | |
// TRANSITIONS | |
enter: function(manager) { | |
var dirtyType = get(this, 'dirtyType'), | |
record = get(manager, 'record'); | |
record.becameInFlight(); | |
record.withTransaction(function (t) { | |
t.recordBecameInFlight(dirtyType, record); | |
}); | |
}, | |
// EVENTS | |
didCommit: function(manager) { | |
var dirtyType = get(this, 'dirtyType'), | |
record = get(manager, 'record'); | |
record.withTransaction(function(t) { | |
t.recordBecameClean('inflight', record); | |
}); | |
manager.transitionTo('saved'); | |
manager.send('invokeLifecycleCallbacks', dirtyType); | |
}, | |
becameInvalid: function(manager, errors) { | |
var record = get(manager, 'record'); | |
set(record, 'errors', errors); | |
manager.transitionTo('invalid'); | |
manager.send('invokeLifecycleCallbacks'); | |
}, | |
becameError: function(manager) { | |
manager.transitionTo('error'); | |
manager.send('invokeLifecycleCallbacks'); | |
} | |
}), | |
// A record is in the `invalid` state when its client-side | |
// invalidations have failed, or if the adapter has indicated | |
// the the record failed server-side invalidations. | |
invalid: DS.State.extend({ | |
// FLAGS | |
isValid: false, | |
exit: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function (t) { | |
t.recordBecameClean('inflight', record); | |
}); | |
}, | |
// EVENTS | |
deleteRecord: function(manager) { | |
manager.transitionTo('deleted'); | |
get(manager, 'record').clearRelationships(); | |
}, | |
willSetProperty: willSetProperty, | |
didSetProperty: function(manager, context) { | |
var record = get(manager, 'record'), | |
errors = get(record, 'errors'), | |
key = context.name; | |
set(errors, key, null); | |
if (!hasDefinedProperties(errors)) { | |
manager.send('becameValid'); | |
} | |
didSetProperty(manager, context); | |
}, | |
becomeDirty: Ember.K, | |
rollback: function(manager) { | |
manager.send('becameValid'); | |
manager.send('rollback'); | |
}, | |
becameValid: function(manager) { | |
manager.transitionTo('uncommitted'); | |
}, | |
invokeLifecycleCallbacks: function(manager) { | |
var record = get(manager, 'record'); | |
record.trigger('becameInvalid', record); | |
} | |
}) | |
}); | |
// The created and updated states are created outside the state | |
// chart so we can reopen their substates and add mixins as | |
// necessary. | |
var createdState = DirtyState.create({ | |
dirtyType: 'created', | |
// FLAGS | |
isNew: true | |
}); | |
var updatedState = DirtyState.create({ | |
dirtyType: 'updated' | |
}); | |
createdState.states.uncommitted.reopen({ | |
deleteRecord: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function(t) { | |
t.recordIsMoving('created', record); | |
}); | |
record.clearRelationships(); | |
manager.transitionTo('deleted.saved'); | |
} | |
}); | |
createdState.states.uncommitted.reopen({ | |
rollback: function(manager) { | |
this._super(manager); | |
manager.transitionTo('deleted.saved'); | |
} | |
}); | |
updatedState.states.uncommitted.reopen({ | |
deleteRecord: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function(t) { | |
t.recordIsMoving('updated', record); | |
}); | |
manager.transitionTo('deleted'); | |
get(manager, 'record').clearRelationships(); | |
} | |
}); | |
var states = { | |
rootState: Ember.State.create({ | |
// FLAGS | |
isLoaded: false, | |
isReloading: false, | |
isDirty: false, | |
isSaving: false, | |
isDeleted: false, | |
isError: false, | |
isNew: false, | |
isValid: true, | |
// SUBSTATES | |
// A record begins its lifecycle in the `empty` state. | |
// If its data will come from the adapter, it will | |
// transition into the `loading` state. Otherwise, if | |
// the record is being created on the client, it will | |
// transition into the `created` state. | |
empty: DS.State.create({ | |
// EVENTS | |
loadingData: function(manager) { | |
manager.transitionTo('loading'); | |
}, | |
loadedData: function(manager) { | |
manager.transitionTo('loaded.created'); | |
} | |
}), | |
// A record enters this state when the store askes | |
// the adapter for its data. It remains in this state | |
// until the adapter provides the requested data. | |
// | |
// Usually, this process is asynchronous, using an | |
// XHR to retrieve the data. | |
loading: DS.State.create({ | |
// EVENTS | |
loadedData: didChangeData, | |
materializingData: function(manager) { | |
manager.transitionTo('loaded.materializing.firstTime'); | |
} | |
}), | |
// A record enters this state when its data is populated. | |
// Most of a record's lifecycle is spent inside substates | |
// of the `loaded` state. | |
loaded: DS.State.create({ | |
initialState: 'saved', | |
// FLAGS | |
isLoaded: true, | |
// SUBSTATES | |
materializing: DS.State.create({ | |
// FLAGS | |
isLoaded: false, | |
// EVENTS | |
willSetProperty: Ember.K, | |
didSetProperty: Ember.K, | |
didChangeData: didChangeData, | |
finishedMaterializing: function(manager) { | |
manager.transitionTo('loaded.saved'); | |
}, | |
// SUBSTATES | |
firstTime: DS.State.create({ | |
exit: function(manager) { | |
var record = get(manager, 'record'); | |
Ember.run.once(function() { | |
record.trigger('didLoad'); | |
}); | |
} | |
}) | |
}), | |
reloading: DS.State.create({ | |
// FLAGS | |
isReloading: true, | |
// TRANSITIONS | |
enter: function(manager) { | |
var record = get(manager, 'record'), | |
store = get(record, 'store'); | |
store.reloadRecord(record); | |
}, | |
exit: function(manager) { | |
var record = get(manager, 'record'); | |
once(record, 'trigger', 'didReload'); | |
}, | |
// EVENTS | |
loadedData: didChangeData, | |
materializingData: function(manager) { | |
manager.transitionTo('loaded.materializing'); | |
} | |
}), | |
// If there are no local changes to a record, it remains | |
// in the `saved` state. | |
saved: DS.State.create({ | |
// EVENTS | |
willSetProperty: willSetProperty, | |
didSetProperty: didSetProperty, | |
didChangeData: didChangeData, | |
loadedData: didChangeData, | |
reloadRecord: function(manager) { | |
manager.transitionTo('loaded.reloading'); | |
}, | |
materializingData: function(manager) { | |
manager.transitionTo('loaded.materializing'); | |
}, | |
becomeDirty: function(manager) { | |
manager.transitionTo('updated'); | |
}, | |
deleteRecord: function(manager) { | |
manager.transitionTo('deleted'); | |
get(manager, 'record').clearRelationships(); | |
}, | |
unloadRecord: function(manager) { | |
manager.transitionTo('deleted.saved'); | |
get(manager, 'record').clearRelationships(); | |
}, | |
willCommit: function(manager) { | |
manager.transitionTo('relationshipsInFlight'); | |
}, | |
invokeLifecycleCallbacks: function(manager, dirtyType) { | |
var record = get(manager, 'record'); | |
if (dirtyType === 'created') { | |
record.trigger('didCreate', record); | |
} else { | |
record.trigger('didUpdate', record); | |
} | |
} | |
}), | |
relationshipsInFlight: Ember.State.create({ | |
// TRANSITIONS | |
enter: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function (t) { | |
t.recordBecameInFlight('clean', record); | |
}); | |
}, | |
// EVENTS | |
didCommit: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function(t) { | |
t.recordBecameClean('inflight', record); | |
}); | |
manager.transitionTo('saved'); | |
manager.send('invokeLifecycleCallbacks'); | |
} | |
}), | |
// A record is in this state after it has been locally | |
// created but before the adapter has indicated that | |
// it has been saved. | |
created: createdState, | |
// A record is in this state if it has already been | |
// saved to the server, but there are new local changes | |
// that have not yet been saved. | |
updated: updatedState | |
}), | |
// A record is in this state if it was deleted from the store. | |
deleted: DS.State.create({ | |
initialState: 'uncommitted', | |
dirtyType: 'deleted', | |
// FLAGS | |
isDeleted: true, | |
isLoaded: true, | |
isDirty: true, | |
// TRANSITIONS | |
setup: function(manager) { | |
var record = get(manager, 'record'), | |
store = get(record, 'store'); | |
store.removeFromRecordArrays(record); | |
}, | |
// SUBSTATES | |
// When a record is deleted, it enters the `start` | |
// state. It will exit this state when the record's | |
// transaction starts to commit. | |
uncommitted: DS.State.create({ | |
// TRANSITIONS | |
enter: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function(t) { | |
t.recordBecameDirty('deleted', record); | |
}); | |
}, | |
// EVENTS | |
willCommit: function(manager) { | |
manager.transitionTo('inFlight'); | |
}, | |
rollback: function(manager) { | |
get(manager, 'record').rollback(); | |
}, | |
becomeDirty: Ember.K, | |
becameClean: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function(t) { | |
t.recordBecameClean('deleted', record); | |
}); | |
manager.transitionTo('loaded.materializing'); | |
} | |
}), | |
// After a record's transaction is committing, but | |
// before the adapter indicates that the deletion | |
// has saved to the server, a record is in the | |
// `inFlight` substate of `deleted`. | |
inFlight: DS.State.create({ | |
// FLAGS | |
isSaving: true, | |
// TRANSITIONS | |
enter: function(manager) { | |
var record = get(manager, 'record'); | |
record.becameInFlight(); | |
record.withTransaction(function (t) { | |
t.recordBecameInFlight('deleted', record); | |
}); | |
}, | |
// EVENTS | |
didCommit: function(manager) { | |
var record = get(manager, 'record'); | |
record.withTransaction(function(t) { | |
t.recordBecameClean('inflight', record); | |
}); | |
manager.transitionTo('saved'); | |
manager.send('invokeLifecycleCallbacks'); | |
} | |
}), | |
// Once the adapter indicates that the deletion has | |
// been saved, the record enters the `saved` substate | |
// of `deleted`. | |
saved: DS.State.create({ | |
// FLAGS | |
isDirty: false, | |
setup: function(manager) { | |
var record = get(manager, 'record'), | |
store = get(record, 'store'); | |
store.dematerializeRecord(record); | |
}, | |
invokeLifecycleCallbacks: function(manager) { | |
var record = get(manager, 'record'); | |
record.trigger('didDelete', record); | |
} | |
}) | |
}), | |
// If the adapter indicates that there was an unknown | |
// error saving a record, the record enters the `error` | |
// state. | |
error: DS.State.create({ | |
isError: true, | |
// EVENTS | |
invokeLifecycleCallbacks: function(manager) { | |
var record = get(manager, 'record'); | |
record.trigger('becameError', record); | |
} | |
}) | |
}) | |
}; | |
DS.StateManager.reopen({ | |
states: states | |
}) | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment