Permalink
Browse files

Added a faster path for transitionTo

  • Loading branch information...
wagenet committed Jun 25, 2012
1 parent 3d55878 commit 9d82473d7a543ea2e2b7b6d24dc2486aff7703c1
@@ -89,6 +89,7 @@ Ember.State = Ember.Object.extend(Ember.Evented,
}
set(this, 'pathsCache', {});
set(this, 'pathsCacheNoContext', {});
},
/** @private */
@@ -549,85 +549,93 @@ Ember.StateManager = Ember.State.extend(
currentState = get(this, 'currentState') || this,
resolveState = currentState,
exitStates = [],
matchedContexts = [],
cachedPath,
enterStates,
targetState,
state,
initialState;
initialState,
stateIdx,
useContext;
// Is the cache useful anymore?
if (!context && (cachedPath = currentState.pathsCacheNoContext[path])) {
// fast path
if (currentState.pathsCache[path]) {
// cache hit
var cachedPath = currentState.pathsCache[name];
exitStates = cachedPath.exitStates;
enterStates = cachedPath.enterStates;
resolveState = cachedPath.resolveState;
targetState = cachedPath.targetState;
} else {
// cache miss
// normal path
if ((cachedPath = currentState.pathsCache[path])) {
// cache hit
exitStates = cachedPath.exitStates;
enterStates = cachedPath.enterStates;
resolveState = cachedPath.resolveState;
} else {
// cache miss
enterStates = this.findStatesByPath(currentState, path);
enterStates = this.findStatesByPath(currentState, path);
while (resolveState && !enterStates) {
exitStates.unshift(resolveState);
while (resolveState && !enterStates) {
exitStates.unshift(resolveState);
resolveState = get(resolveState, 'parentState');
if (!resolveState) {
enterStates = this.findStatesByPath(this, path);
if (!enterStates) { return; }
resolveState = get(resolveState, 'parentState');
if (!resolveState) {
enterStates = this.findStatesByPath(this, path);
if (!enterStates) { return; }
}
enterStates = this.findStatesByPath(resolveState, path);
}
while (enterStates.length > 0 && enterStates[0] === exitStates[0]) {
resolveState = enterStates.shift();
exitStates.shift();
}
enterStates = this.findStatesByPath(resolveState, path);
}
while (enterStates.length > 0 && enterStates[0] === exitStates[0]) {
resolveState = enterStates.shift();
exitStates.shift();
currentState.pathsCache[name] = {

This comment has been minimized.

Show comment
Hide comment
@bradleypriest

bradleypriest Jun 28, 2012

Member

@wagenet This name variable doesn't seem to exist anywhere before here?

Edit: Looks like it should be 'path'

@bradleypriest

bradleypriest Jun 28, 2012

Member

@wagenet This name variable doesn't seem to exist anywhere before here?

Edit: Looks like it should be 'path'

exitStates: exitStates,
enterStates: enterStates,
resolveState: resolveState
};
}
currentState.pathsCache[name] = {
exitStates: exitStates,
enterStates: enterStates,
resolveState: resolveState
};
}
stateIdx = enterStates.length-1;
while (contexts.length > 0) {
if (stateIdx >= 0) {
state = enterStates[stateIdx--];
} else {
state = enterStates[0] ? get(enterStates[0], 'parentState') : resolveState;
if (!state) { throw "Cannot match all contexts to states"; }
enterStates.unshift(state);
exitStates.unshift(state);
}
var matchedContexts = [],
stateIdx = enterStates.length-1;
while (contexts.length > 0) {
if (stateIdx >= 0) {
state = enterStates[stateIdx--];
} else {
state = enterStates[0] ? get(enterStates[0], 'parentState') : resolveState;
if (!state) { throw "Cannot match all contexts to states"; }
enterStates.unshift(state);
exitStates.unshift(state);
useContext = context && (!get(state, 'isRoutable') || get(state, 'isDynamic'));
matchedContexts.unshift(useContext ? contexts.pop() : null);
}
var useContext = context && (!get(state, 'isRoutable') || get(state, 'isDynamic'));
matchedContexts.unshift(useContext ? contexts.pop() : null);
}
if (enterStates.length > 0) {
state = enterStates[enterStates.length - 1];
if (enterStates.length > 0) {
state = enterStates[enterStates.length - 1];
while(true) {
initialState = get(state, 'initialState') || 'start';
state = getPath(state, 'states.'+initialState);
if (!state) { break; }
enterStates.push(state);
}
while(true) {
initialState = get(state, 'initialState') || 'start';
state = getPath(state, 'states.'+initialState);
if (!state) { break; }
enterStates.push(state);
}
while (enterStates.length > 0) {
if (enterStates[0] !== exitStates[0]) { break; }
while (enterStates.length > 0) {
if (enterStates[0] !== exitStates[0]) { break; }
if (enterStates.length === matchedContexts.length) {
if (this.getStateMeta(enterStates[0], 'context') !== matchedContexts[0]) { break; }
matchedContexts.shift();
}
if (enterStates.length === matchedContexts.length) {
if (this.getStateMeta(enterStates[0], 'context') !== matchedContexts[0]) { break; }
matchedContexts.shift();
resolveState = enterStates.shift();
exitStates.shift();
}
resolveState = enterStates.shift();
exitStates.shift();
}
}
@@ -557,6 +557,48 @@ test("multiple contexts can be provided in a single transitionTo", function() {
});
test("transitionEvent is called for each nested state", function() {
expect(4);
var calledOnParent = false,
calledOnChild = true;
Ember.run(function() {
stateManager = Ember.StateManager.create({
start: Ember.State.create(),
planters: Ember.State.create({
setup: function(manager, context) {
calledOnParent = true;
},
nuts: Ember.State.create({
setup: function(manager, context) {
calledOnChild = true;
}
})
})
});
});
stateManager.transitionTo('planters.nuts');
ok(calledOnParent, 'called transitionEvent on parent');
ok(calledOnChild, 'called transitionEvent on child');
// repeat the test now that the path is cached
stateManager.transitionTo('start');
calledOnParent = false;
calledOnChild = false;
stateManager.transitionTo('planters.nuts');
ok(calledOnParent, 'called transitionEvent on parent');
ok(calledOnChild, 'called transitionEvent on child');
});
test("transitionEvent is called for each nested state with context", function() {
expect(8);
var calledOnParent = false,

0 comments on commit 9d82473

Please sign in to comment.