port all packages to new Deps (tests pass)

This commit is contained in:
David Greenspan
2013-02-25 17:52:51 -08:00
parent 58fde14bea
commit f6aabffab3
22 changed files with 506 additions and 512 deletions

View File

@@ -6,18 +6,18 @@
};
var loggingIn = false;
var loggingInListeners = new Meteor.deps._ContextSet;
var loggingInVar = new Deps.Variable;
// This is mostly just called within this file, but Meteor.loginWithPassword
// also uses it to make loggingIn() be true during the beginPasswordExchange
// method call too.
Accounts._setLoggingIn = function (x) {
if (loggingIn !== x) {
loggingIn = x;
loggingInListeners.invalidateAll();
loggingInVar.changed();
}
};
Meteor.loggingIn = function () {
loggingInListeners.addCurrentContext();
loggingInVar.changed();
return loggingIn;
};
@@ -181,10 +181,10 @@
// XXX this can be simplified if we merge in
// https://github.com/meteor/meteor/pull/273
var loginServicesConfigured = false;
var loginServicesConfiguredListeners = new Meteor.deps._ContextSet;
var loginServicesConfiguredVar = new Deps.Variable;
Meteor.subscribe("meteor.loginServiceConfiguration", function () {
loginServicesConfigured = true;
loginServicesConfiguredListeners.invalidateAll();
loginServicesConfiguredVar.changed();
});
// A reactive function returning whether the
@@ -196,7 +196,7 @@
return true;
// not yet complete, save the context for invalidation once we are.
loginServicesConfiguredListeners.addCurrentContext();
Deps.depend(loginServicesConfiguredVar);
return false;
};
})();

View File

@@ -54,7 +54,7 @@ if (Meteor.isClient) (function () {
// Set up a reactive context that only refreshes when Meteor.user() is
// invalidated.
var loaded = false;
var handle = Meteor.autorun(function () {
var handle = Deps.autorun(function () {
if (Meteor.user() && Meteor.user().emails)
loaded = true;
});
@@ -66,7 +66,7 @@ if (Meteor.isClient) (function () {
// By the time of the login callback, the user should be loaded.
test.isTrue(Meteor.user().emails);
// Flushing should get us the autorun as well.
Meteor.flush();
Deps.flush();
test.isTrue(loaded);
handle.stop();
}));

View File

@@ -7,7 +7,7 @@
Template._loginButtons.events({
'click #login-name-link, click #login-sign-in-link': function () {
loginButtonsSession.set('dropdownVisible', true);
Meteor.flush();
Deps.flush();
correctDropdownZIndexes();
},
'click .login-close-text': function () {
@@ -85,7 +85,7 @@
loginButtonsSession.set('inSignupFlow', true);
loginButtonsSession.set('inForgotPasswordFlow', false);
// force the ui to update so that we have the approprate fields to fill in
Meteor.flush();
Deps.flush();
// update new fields with appropriate defaults
if (username !== null)
@@ -121,7 +121,7 @@
loginButtonsSession.set('inSignupFlow', false);
loginButtonsSession.set('inForgotPasswordFlow', true);
// force the ui to update so that we have the approprate fields to fill in
Meteor.flush();
Deps.flush();
// update new fields with appropriate defaults
if (email !== null)
@@ -141,7 +141,7 @@
loginButtonsSession.set('inSignupFlow', false);
loginButtonsSession.set('inForgotPasswordFlow', false);
// force the ui to update so that we have the approprate fields to fill in
Meteor.flush();
Deps.flush();
if (document.getElementById('login-username'))
document.getElementById('login-username').value = username;

View File

@@ -27,7 +27,7 @@
this.invalidated = false;
};
_.extend(Computation.prototype, {
_.extend(Deps.Computation.prototype, {
run: function (f) {
var previous = Deps.currentComputation;
Deps.currentComputation = this;
@@ -60,8 +60,10 @@
this._callbacks.push(f);
},
// Make this computation depend on v. Return true
// if this is a new dependency.
depend: function (v) {
v._addDependent(this);
return v._addDependent(this);
}
});
@@ -69,7 +71,7 @@
this._dependentsById = {};
};
_.extend(Variable.prototype, {
_.extend(Deps.Variable.prototype, {
// Adds `computation` to this set if it is not already
// present. Returns true if `computation` is a new member of the set.
_addDependent: function (computation) {
@@ -98,9 +100,13 @@
});
_.extend(Deps, {
// Make the current computation depend on v. Returns true
// if this is a new dependency. If there is no current
// computation, does nothing and returns false.
depend: function (v) {
if (Deps.active)
v._addDependent(Deps.currentComputation);
return v._addDependent(Deps.currentComputation);
return false;
},
flush: function () {

View File

@@ -1,46 +1,46 @@
Tinytest.add('deps - autorun', function (test) {
var listeners = new Meteor.deps._ContextSet;
var v = new Deps.Variable;
var x = 0;
var handle = Meteor.autorun(function (handle) {
listeners.addCurrentContext();
var handle = Deps.autorun(function (handle) {
Deps.depend(v);
++x;
});
test.equal(x, 1);
Meteor.flush();
Deps.flush();
test.equal(x, 1);
listeners.invalidateAll();
v.changed();
test.equal(x, 1);
Meteor.flush();
Deps.flush();
test.equal(x, 2);
listeners.invalidateAll();
v.changed();
test.equal(x, 2);
Meteor.flush();
Deps.flush();
test.equal(x, 3);
listeners.invalidateAll();
v.changed();
// Prevent the function from running further.
handle.stop();
Meteor.flush();
Deps.flush();
test.equal(x, 3);
listeners.invalidateAll();
Meteor.flush();
v.changed();
Deps.flush();
test.equal(x, 3);
Meteor.autorun(function (internalHandle) {
listeners.addCurrentContext();
Deps.autorun(function (internalHandle) {
Deps.depend(v);
++x;
if (x == 6)
internalHandle.stop();
});
test.equal(x, 4);
listeners.invalidateAll();
Meteor.flush();
v.changed();
Deps.flush();
test.equal(x, 5);
listeners.invalidateAll();
v.changed();
// Increment to 6 and stop.
Meteor.flush();
Deps.flush();
test.equal(x, 6);
listeners.invalidateAll();
Meteor.flush();
v.changed();
Deps.flush();
// Still 6!
test.equal(x, 6);
});

View File

@@ -9,7 +9,7 @@ Package.on_use(function (api, where) {
where = where || ['client', 'server'];
api.use('underscore', where);
api.add_files(['deps.js', 'deps-utils.js'], where);
api.add_files(['deps.js'], where);
});
Package.on_test(function (api) {

View File

@@ -145,7 +145,8 @@ Meteor._LivedataConnection = function (url, options) {
// - id
// - name
// - params
// - context (the Context in which Meteor.subscribe was called, if any)
// - computation (the Deps.Computation in which Meteor.subscribe was
// called, if any)
// - ready (has the 'ready' message been received?)
// - readyCallback (an optional callback to call when ready)
// - errorCallback (an optional callback to call if the sub terminates with
@@ -158,7 +159,7 @@ Meteor._LivedataConnection = function (url, options) {
// Reactive userId.
self._userId = null;
self._userIdListeners = Meteor.deps && new Meteor.deps._ContextSet;
self._userIdVar = (typeof Deps !== "undefined") && new Deps.Variable;
// Block auto-reload while we're waiting for method responses.
if (!options.reloadWithOutstanding) {
@@ -426,10 +427,10 @@ _.extend(Meteor._LivedataConnection.prototype, {
}
// Is there an existing sub with the same name and param, run in an
// invalidated Context? This can only happen if the context just got
// invalidated and we haven't fully finished a round of Meteor.flush()
// invalidated Computation? This can only happen if the computation just got
// invalidated and we haven't fully finished a round of Deps.flush()
// yet. For example, this will happen with the pattern of:
// Meteor.autorun(function () {
// Deps.autorun(function () {
// Meteor.subscribe("foo", Session.get("foo"));
// Meteor.subscribe("bar", Session.get("bar"));
// });
@@ -440,16 +441,16 @@ _.extend(Meteor._LivedataConnection.prototype, {
// being invalidated, we will require N matching subscribe calls to keep
// them all active.
var existing = _.find(self._subscriptions, function (sub) {
return sub.context && sub.context.invalidated && sub.name === name &&
EJSON.equals(sub.params, params);
return sub.computation && sub.computation.invalidated &&
sub.name === name && EJSON.equals(sub.params, params);
});
var currentContext = Meteor.deps && Meteor.deps.Context.current;
var currentComputation = Deps.currentComputation;
var id;
if (existing) {
id = existing.id;
// Substitute our current context (if any) for the one on the sub.
existing.context = currentContext;
// Substitute our current computation (if any) for the one on the sub.
existing.computation = currentComputation;
if (callbacks.onReady) {
// If the sub is not already ready, replace any ready callback with the
@@ -472,9 +473,9 @@ _.extend(Meteor._LivedataConnection.prototype, {
id: id,
name: name,
params: params,
context: currentContext,
computation: currentComputation,
ready: false,
readyListeners: Meteor.deps && new Meteor.deps._ContextSet,
readyVar: (typeof Deps !== "undefined") && new Deps.Variable,
readyCallback: callbacks.onReady,
errorCallback: callbacks.onError
};
@@ -494,37 +495,34 @@ _.extend(Meteor._LivedataConnection.prototype, {
if (!_.has(self._subscriptions, id))
return false;
var record = self._subscriptions[id];
record.readyListeners && record.readyListeners.addCurrentContext();
record.readyVar && Deps.depend(record.readyVar);
return record.ready;
}
};
if (currentContext) {
// We're in a reactive context, so we'd like to unsubscribe when the
// context is invalidated... but not if some *OTHER* onInvalidate callback
// on currentContext re-subscribes to the same subscription (eg, as part
// of an autorun). Meteor.flush guarantees that it won't interleave calls
// to currentContext's callbacks and unsubscribeContext's callbacks, so
// this ensures that by the time unsubscribeContext's onInvalidate
// callback is called, we've already re-run the autorun function (if this
// was an autorun context).
var unsubscribeContext = new Meteor.deps.Context;
unsubscribeContext.onInvalidate(function () {
// Did we already unsubscribe from this? Do nothing.
if (!_.has(self._subscriptions, id))
return;
// Did we substitute a new context in for this constitute in the
// "Substitute" block above? (eg, are we in an autorun and the re-run of
// the function subscribed to this again?)
if (self._subscriptions[id].context !== currentContext)
return;
// Nope, the only reason we are currently subscribed to this
// subscription is that *THIS* subscribe call wanted it to be so, and
// its context is invalidated, so it's time to unsubscribe.
handle.stop();
});
currentContext.onInvalidate(function () {
unsubscribeContext.invalidate();
if (currentComputation) {
// We're in a reactive computation, so we'd like to unsubscribe when the
// computation is invalidated... but not if some *OTHER* onInvalidate
// callback on currentComputation re-subscribes to the same subscription
// (eg, as part of a Deps.autorun). Use Deps.afterFlush to schedule
// this check to happen later in the flush cycle, after all of
// currentComputation's callbacks have been called, and therefore after
// the current Deps.autorun (if any) has been re-run.
currentComputation.onInvalidate(function () {
Deps.afterFlush(function () {
// Did we already unsubscribe from this? Do nothing.
if (!_.has(self._subscriptions, id))
return;
// Did we substitute a new computation in for this constitute in the
// "Substitute" block above? (eg, are we in an autorun and the re-run of
// the function subscribed to this again?)
if (self._subscriptions[id].computation !== currentComputation)
return;
// Nope, the only reason we are currently subscribed to this
// subscription is that *THIS* subscribe call wanted it to be so, and
// its computation is invalidated, so it's time to unsubscribe.
handle.stop();
});
});
}
@@ -785,19 +783,19 @@ _.extend(Meteor._LivedataConnection.prototype, {
///
userId: function () {
var self = this;
if (self._userIdListeners)
self._userIdListeners.addCurrentContext();
if (self._userIdVar)
Deps.depend(self._userIdVar);
return self._userId;
},
setUserId: function (userId) {
var self = this;
// Avoid invalidating listeners if setUserId is called with current value.
// Avoid invalidating dependents if setUserId is called with current value.
if (self._userId === userId)
return;
self._userId = userId;
if (self._userIdListeners)
self._userIdListeners.invalidateAll();
if (self._userIdVar)
self._userIdVar.changed();
},
// Returns true if we are in a state after reconnect of waiting for subs to be
@@ -1116,7 +1114,7 @@ _.extend(Meteor._LivedataConnection.prototype, {
return;
subRecord.readyCallback && subRecord.readyCallback();
subRecord.ready = true;
subRecord.readyListeners && subRecord.readyListeners.invalidateAll();
subRecord.readyVar && subRecord.readyVar.changed();
});
});
},

View File

@@ -115,7 +115,7 @@ Tinytest.add("livedata stub - subscribe", function (test) {
test.equal(message, {msg: 'sub', name: 'my_data', params: []});
var reactivelyReady = false;
var autorunHandle = Meteor.autorun(function () {
var autorunHandle = Deps.autorun(function () {
reactivelyReady = sub.ready();
});
test.isFalse(reactivelyReady);
@@ -123,7 +123,7 @@ Tinytest.add("livedata stub - subscribe", function (test) {
// get the sub satisfied. callback fires.
stream.receive({msg: 'ready', 'subs': [id]});
test.isTrue(callback_fired);
Meteor.flush();
Deps.flush();
test.isTrue(reactivelyReady);
autorunHandle.stop();
@@ -165,7 +165,7 @@ Tinytest.add("livedata stub - reactive subscribe", function (test) {
// Subscribe to some subs.
var stopperHandle;
var autorunHandle = Meteor.autorun(function () {
var autorunHandle = Deps.autorun(function () {
conn.subscribe("foo", rFoo.get(), onReady(rFoo.get()));
conn.subscribe("bar", rBar.get(), onReady(rBar.get()));
conn.subscribe("completer", onReady("completer"));
@@ -216,7 +216,7 @@ Tinytest.add("livedata stub - reactive subscribe", function (test) {
// subscription should *NOT* call its new onReady callback, because we only
// call at most one onReady for a given reactively-saved subscription.
rFoo.set("foo2");
Meteor.flush();
Deps.flush();
test.length(stream.sent, 3);
message = JSON.parse(stream.sent.shift());
@@ -246,7 +246,7 @@ Tinytest.add("livedata stub - reactive subscribe", function (test) {
// Shut down the autorun. This should unsub us from all current subs at flush
// time.
autorunHandle.stop();
Meteor.flush();
Deps.flush();
test.length(stream.sent, 4);
// The order of unsubs here is not important.

View File

@@ -44,7 +44,7 @@ _.extend(Meteor, {
// won't be necessary once we clobber the global setTimeout
//
// XXX consider making this guarantee ordering of defer'd callbacks, like
// Meteor._atFlush or Node's nextTick (in practice). Then tests can do:
// Deps.afterFlush or Node's nextTick (in practice). Then tests can do:
// callSomethingThatDefersSomeWork();
// Meteor.defer(expect(somethingThatValidatesThatTheWorkHappened));
defer: function (f) {

View File

@@ -140,11 +140,11 @@ LocalCollection.Cursor.prototype.forEach = function (callback) {
self.db_objects = self._getRawObjects(true);
if (self.reactive)
self._markAsReactive({
addedBefore: true,
removed: true,
changed: true,
movedBefore: true});
self._depend({
addedBefore: true,
removed: true,
changed: true,
movedBefore: true});
while (self.cursor_pos < self.db_objects.length)
callback(EJSON.clone(self.db_objects[self.cursor_pos++]));
@@ -172,7 +172,7 @@ LocalCollection.Cursor.prototype.count = function () {
var self = this;
if (self.reactive)
self._markAsReactive({added: true, removed: true});
self._depend({added: true, removed: true});
if (self.db_objects === null)
self.db_objects = self._getRawObjects(true);
@@ -339,27 +339,28 @@ LocalCollection.Cursor.prototype._getRawObjects = function (ordered) {
// XXX Maybe we need a version of observe that just calls a callback if
// anything changed.
LocalCollection.Cursor.prototype._markAsReactive = function (options) {
LocalCollection.Cursor.prototype._depend = function (changers) {
var self = this;
var context = Meteor.deps.Context.current;
if (Deps.active) {
var v = new Deps.Variable;
Deps.depend(v);
var notifyChange = _.bind(v.change, v);
if (context) {
var invalidate = _.bind(context.invalidate, context);
var handle;
var newOptions = {_suppress_initial: true};
var options = {_suppress_initial: true};
_.each(['added', 'changed', 'removed', 'addedBefore', 'movedBefore'],
function (fnName) {
if (options[fnName])
newOptions[fnName] = invalidate;
});
handle = self.observeChanges(newOptions);
if (changers[fnName])
options[fnName] = notifyChange;
});
var handle = self.observeChanges(options);
// XXX in many cases, the query will be immediately
// recreated. so we might want to let it linger for a little
// while and repurpose it if it comes back. this will save us
// work because we won't have to redo the initial find.
context.onInvalidate(handle.stop);
Deps.currentComputation.onInvalidate(handle.stop);
}
};

View File

@@ -1,8 +1,8 @@
// Old under_score version of camelCase public API names.
Meteor.is_client = Meteor.isClient;
Meteor.is_server = Meteor.isServer;
Meteor.deps.Context.prototype.on_invalidate =
Meteor.deps.Context.prototype.onInvalidate;
//Meteor.deps.Context.prototype.on_invalidate =
// Meteor.deps.Context.prototype.onInvalidate;
// See also the "this.is_simulation" assignment in livedata/livedata_common.js
// and the retry_count and retry_time fields of self.current_status in
// stream/stream_client.js.
@@ -10,7 +10,7 @@ Meteor.deps.Context.prototype.on_invalidate =
// We used to require a special "autosubscribe" call to reactively subscribe to
// things. Now, it works with autorun.
Meteor.autosubscribe = Meteor.autorun;
Meteor.autosubscribe = Deps.autorun;
// Instead of the "random" package with Random.id(), we used to have this
// Meteor.uuid() implementing the RFC 4122 v4 UUID.

View File

@@ -15,8 +15,8 @@
Session = _.extend({}, {
keys: {}, // key -> value
keyDeps: {}, // key -> _ContextSet
keyValueDeps: {}, // key -> value -> _ContextSet
keyVars: {}, // key -> Variable
keyValueVars: {}, // key -> value -> Variable
set: function (key, value) {
var self = this;
@@ -29,14 +29,14 @@
return;
self.keys[key] = value;
var invalidateAll = function (cset) {
cset && cset.invalidateAll();
var changed = function (v) {
v && v.changed();
};
invalidateAll(self.keyDeps[key]);
if (self.keyValueDeps[key]) {
invalidateAll(self.keyValueDeps[key][oldSerializedValue]);
invalidateAll(self.keyValueDeps[key][value]);
changed(self.keyVars[key]);
if (self.keyValueVars[key]) {
changed(self.keyValueVars[key][oldSerializedValue]);
changed(self.keyValueVars[key][value]);
}
},
@@ -53,19 +53,18 @@
get: function (key) {
var self = this;
self._ensureKey(key);
self.keyDeps[key].addCurrentContext();
Deps.depend(self.keyVars[key]);
return parse(self.keys[key]);
},
equals: function (key, value) {
var self = this;
var context = Meteor.deps.Context.current;
// We don't allow objects (or arrays that might include objects) for
// .equals, because JSON.stringify doesn't canonicalize object key
// order. (We can make equals have the right return value by parsing the
// current value and using EJSON.equals, but we won't have a canonical
// element of keyValueDeps[key] to store the context.) You can still use
// element of keyValueVars[key] to store the dependency.) You can still use
// "EJSON.equals(Session.get(key), value)".
//
// XXX we could allow arrays as long as we recursively check that there
@@ -80,19 +79,19 @@
throw new Error("Session.equals: value must be scalar");
var serializedValue = stringify(value);
if (context) {
if (Deps.active) {
self._ensureKey(key);
if (! _.has(self.keyValueDeps[key], serializedValue))
self.keyValueDeps[key][serializedValue] = new Meteor.deps._ContextSet;
if (! _.has(self.keyValueVars[key], serializedValue))
self.keyValueVars[key][serializedValue] = new Deps.Variable;
var isNew = self.keyValueDeps[key][serializedValue].add(context);
var isNew = Deps.depend(self.keyValueVars[key][serializedValue]);
if (isNew) {
context.onInvalidate(function () {
Deps.currentComputation.onInvalidate(function () {
// clean up [key][serializedValue] if it's now empty, so we don't
// use O(n) memory for n = values seen ever
if (self.keyValueDeps[key][serializedValue].isEmpty())
delete self.keyValueDeps[key][serializedValue];
if (! self.keyValueVars[key][serializedValue].hasDependents())
delete self.keyValueVars[key][serializedValue];
});
}
}
@@ -104,9 +103,9 @@
_ensureKey: function (key) {
var self = this;
if (!(key in self.keyDeps)) {
self.keyDeps[key] = new Meteor.deps._ContextSet;
self.keyValueDeps[key] = {};
if (!(key in self.keyVars)) {
self.keyVars[key] = new Deps.Variable;
self.keyValueVars[key] = {};
}
}
});

View File

@@ -105,7 +105,7 @@
Tinytest.add('session - context invalidation for get', function (test) {
var xGetExecutions = 0;
Meteor.autorun(function () {
Deps.autorun(function () {
++xGetExecutions;
Session.get('x');
});
@@ -113,44 +113,44 @@
Session.set('x', 1);
// Invalidation shouldn't happen until flush time.
test.equal(xGetExecutions, 1);
Meteor.flush();
Deps.flush();
test.equal(xGetExecutions, 2);
// Setting to the same value doesn't re-run.
Session.set('x', 1);
Meteor.flush();
Deps.flush();
test.equal(xGetExecutions, 2);
Session.set('x', '1');
Meteor.flush();
Deps.flush();
test.equal(xGetExecutions, 3);
});
Tinytest.add('session - context invalidation for equals', function (test) {
var xEqualsExecutions = 0;
Meteor.autorun(function () {
Deps.autorun(function () {
++xEqualsExecutions;
Session.equals('x', 5);
});
test.equal(xEqualsExecutions, 1);
Session.set('x', 1);
Meteor.flush();
Deps.flush();
// Changing undefined -> 1 shouldn't affect equals(5).
test.equal(xEqualsExecutions, 1);
Session.set('x', 5);
// Invalidation shouldn't happen until flush time.
test.equal(xEqualsExecutions, 1);
Meteor.flush();
Deps.flush();
test.equal(xEqualsExecutions, 2);
Session.set('x', 5);
Meteor.flush();
Deps.flush();
// Setting to the same value doesn't re-run.
test.equal(xEqualsExecutions, 2);
Session.set('x', '5');
test.equal(xEqualsExecutions, 2);
Meteor.flush();
Deps.flush();
test.equal(xEqualsExecutions, 3);
Session.set('x', 5);
test.equal(xEqualsExecutions, 3);
Meteor.flush();
Deps.flush();
test.equal(xEqualsExecutions, 4);
});
@@ -159,27 +159,27 @@
function (test) {
// Make sure the special casing for equals undefined works.
var yEqualsExecutions = 0;
Meteor.autorun(function () {
Deps.autorun(function () {
++yEqualsExecutions;
Session.equals('y', undefined);
});
test.equal(yEqualsExecutions, 1);
Session.set('y', undefined);
Meteor.flush();
Deps.flush();
test.equal(yEqualsExecutions, 1);
Session.set('y', 5);
test.equal(yEqualsExecutions, 1);
Meteor.flush();
Deps.flush();
test.equal(yEqualsExecutions, 2);
Session.set('y', 3);
Meteor.flush();
Deps.flush();
test.equal(yEqualsExecutions, 2);
Session.set('y', 'undefined');
Meteor.flush();
Deps.flush();
test.equal(yEqualsExecutions, 2);
Session.set('y', undefined);
test.equal(yEqualsExecutions, 2);
Meteor.flush();
Deps.flush();
test.equal(yEqualsExecutions, 3);
});
}());

View File

@@ -352,7 +352,7 @@ var scheduleOnscreenSetup = function (frag, landmarkRanges) {
finalized = true;
};
Meteor._atFlush(function () {
Deps.afterFlush(function () {
if (finalized)
return;
@@ -807,7 +807,7 @@ Spark.attachEvents = withRenderer(function (eventMap, html, _renderer) {
var landmark = (landmarkRange && landmarkRange.landmark);
// Note that the handler can do arbitrary things, like call
// Meteor.flush() or otherwise remove and finalize parts of
// Deps.flush() or otherwise remove and finalize parts of
// the DOM. We can't assume `range` is valid past this point,
// and we'll check the `finalized` flag at the top of the loop.
var returnValue = callback.call(eventData, event, landmark);
@@ -837,20 +837,20 @@ Spark.isolate = function (htmlFunc) {
var range;
var firstRun = true;
var retHtml;
Meteor.autorun(function (handle) {
Deps.autorun(function (handle) {
if (firstRun) {
retHtml = renderer.annotate(
htmlFunc(), Spark._ANNOTATION_ISOLATE,
function (r) {
if (! r) {
// annotation not used; kill our context
// annotation not used; kill this autorun
handle.stop();
} else {
range = r;
range.finalize = function () {
// Spark.finalize() was called on our range (presumably
// because it was removed from the document.) Kill
// this context and stop rerunning.
// this autorun.
handle.stop();
};
}
@@ -976,7 +976,7 @@ Spark.list = function (cursor, itemFunc, elseFunc) {
};
var later = function (f) {
Meteor._atFlush(function () {
Deps.afterFlush(function () {
if (! stopped)
withEventGuard(f);
});
@@ -1155,15 +1155,9 @@ Spark.createLandmark = function (options, htmlFunc) {
landmark = new Spark.Landmark;
if (options.created) {
// Run callback outside the current Spark.isolate's deps context.
// XXX Can't call run() on null, so this is a hack. Running inside
// a fresh context wouldn't be equivalent.
var oldCx = Meteor.deps.Context.current;
Meteor.deps.Context.current = null;
try {
Deps.nonreactive(function () {
options.created.call(landmark);
} finally {
Meteor.deps.Context.current = oldCx;
}
});
}
}
notes.landmark = landmark;

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@
" if (typeof Meteor !== 'undefined' " +
" && typeof(Meteor.status) !== 'undefined' " +
" && Meteor.status().connected) {" +
" Meteor.flush();" +
" Deps.flush();" +
" return Meteor._LivedataConnection._allSubscriptionsReady();" +
" }" +
" return false;" +

View File

@@ -47,10 +47,10 @@ Meteor._Stream = function (url) {
retry_count: 0
};
self.status_listeners = (Meteor.deps && new Meteor.deps._ContextSet);
self.status_listeners = window.Deps && new Deps.Variable;
self.status_changed = function () {
if (self.status_listeners)
self.status_listeners.invalidateAll();
self.status_listeners.changed();
};
//// Retry logic
@@ -139,7 +139,7 @@ _.extend(Meteor._Stream.prototype, {
status: function () {
var self = this;
if (self.status_listeners)
self.status_listeners.addCurrentContext();
Deps.depend(self.status_listeners);
return self.current_status;
},

View File

@@ -18,10 +18,10 @@ Tinytest.add("templating - assembly", function (test) {
document.body.appendChild(onscreen);
test.equal(canonicalizeHtml(onscreen.innerHTML), "xyhi");
Session.set("stuff", false);
Meteor.flush();
Deps.flush();
test.equal(canonicalizeHtml(onscreen.innerHTML), "xhi");
document.body.removeChild(onscreen);
Meteor.flush();
Deps.flush();
});
// Test that if a template throws an error, then pending_partials is
@@ -71,7 +71,7 @@ Tinytest.add("templating - table assembly", function(test) {
document.body.removeChild(onscreen);
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - event handler this", function(test) {
@@ -111,7 +111,7 @@ Tinytest.add("templating - event handler this", function(test) {
event_buf.length = 0;
tmpl.kill();
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - safestring", function(test) {
@@ -345,7 +345,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isTrue(hr1);
R.set('bar');
Meteor.flush();
Deps.flush();
var br2 = div.node().getElementsByTagName('br')[0];
var hr2 = div.node().getElementsByTagName('hr')[0];
test.isTrue(br2);
@@ -354,7 +354,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isFalse(hr1 === hr2);
div.kill();
Meteor.flush();
Deps.flush();
/////
@@ -379,7 +379,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isTrue(hr1);
R.set('bar');
Meteor.flush();
Deps.flush();
var br2 = div.node().getElementsByTagName('br')[0];
var hr2 = div.node().getElementsByTagName('hr')[0];
test.isTrue(br2);
@@ -388,7 +388,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isFalse(hr1 === hr2);
div.kill();
Meteor.flush();
Deps.flush();
/////
@@ -409,7 +409,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isTrue(hr1);
stuff.update({foo:'bar'}, {$set: {foo: 'baz'}});
Meteor.flush();
Deps.flush();
var br2 = div.node().getElementsByTagName('br')[0];
var hr2 = div.node().getElementsByTagName('hr')[0];
test.isTrue(br2);
@@ -418,7 +418,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isFalse(hr1 === hr2);
div.kill();
Meteor.flush();
Deps.flush();
/////
@@ -436,7 +436,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isTrue(hr1);
stuff.update({foo:'bar'}, {$set: {foo: 'baz'}});
Meteor.flush();
Deps.flush();
var br2 = div.node().getElementsByTagName('br')[0];
var hr2 = div.node().getElementsByTagName('hr')[0];
test.isTrue(br2);
@@ -445,7 +445,7 @@ Tinytest.add("templating - rendered template", function(test) {
test.isFalse(hr1 === hr2);
div.kill();
Meteor.flush();
Deps.flush();
});
@@ -477,12 +477,12 @@ Tinytest.add("templating - branch labels", function(test) {
};
var div = OnscreenDiv(Meteor.render(Template.test_branches_a));
Meteor.flush();
Deps.flush();
test.equal(DomUtils.find(div.node(), 'span').innerHTML, 'foo');
test.equal(elems.length, 3);
R.set('bar');
Meteor.flush();
Deps.flush();
var elems2 = DomUtils.findAll(div.node(), 'hr');
elems2.sort(function(a, b) { return a.myIndex - b.myIndex; });
test.equal(elems[0], elems2[0]);
@@ -491,7 +491,7 @@ Tinytest.add("templating - branch labels", function(test) {
test.equal(DomUtils.find(div.node(), 'span').innerHTML, 'bar');
div.kill();
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - matching in list", function (test) {
@@ -519,7 +519,7 @@ Tinytest.add("templating - matching in list", function (test) {
var R = ReactiveVar('foo');
var div = OnscreenDiv(Spark.render(Template.test_listmatching_a0));
Meteor.flush();
Deps.flush();
test.equal(DomUtils.find(div.node(), 'span').innerHTML, 'foo');
test.equal(div.html().match(/<p>(.*?)<\/p>/)[1].match(/\S+/g), ['a','b','c']);
@@ -527,13 +527,13 @@ Tinytest.add("templating - matching in list", function (test) {
buf.length = 0;
R.set('bar');
Meteor.flush();
Deps.flush();
test.equal(DomUtils.find(div.node(), 'span').innerHTML, 'bar');
test.equal(div.html().match(/<p>(.*?)<\/p>/)[1].match(/\S+/g), ['a','b','c']);
test.equal(buf.join(''), '*a*b*c');
div.kill();
Meteor.flush();
Deps.flush();
});
@@ -560,19 +560,19 @@ Tinytest.add("templating - isolate helper", function (test) {
test.equal(getTallies().join(','), str);
};
Meteor.flush();
Deps.flush();
expect("1,1,1,1");
bump(1); Meteor.flush(); expect("2,2,2,2");
bump(2); Meteor.flush(); expect("2,3,3,3");
bump(3); Meteor.flush(); expect("2,3,4,4");
bump(4); Meteor.flush(); expect("2,3,4,5");
Meteor.flush(); expect("2,3,4,5");
bump(3); Meteor.flush(); expect("2,3,5,6");
bump(2); Meteor.flush(); expect("2,4,6,7");
bump(1); Meteor.flush(); expect("3,5,7,8");
bump(1); Deps.flush(); expect("2,2,2,2");
bump(2); Deps.flush(); expect("2,3,3,3");
bump(3); Deps.flush(); expect("2,3,4,4");
bump(4); Deps.flush(); expect("2,3,4,5");
Deps.flush(); expect("2,3,4,5");
bump(3); Deps.flush(); expect("2,3,5,6");
bump(2); Deps.flush(); expect("2,4,6,7");
bump(1); Deps.flush(); expect("3,5,7,8");
div.kill();
Meteor.flush();
Deps.flush();
});
@@ -618,13 +618,13 @@ Tinytest.add("templating - template arg", function (test) {
}));
test.equal(div.text(), "Foo Bar Baz");
Meteor.flush();
Deps.flush();
test.equal(div.text(), "Greetings 1-bold Line");
clickElement(DomUtils.find(div.node(), 'i'));
test.equal(div.text(), "Hello 3-element World (the secret is strawberry pie)");
div.kill();
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - preserve", function (test) {
@@ -641,7 +641,7 @@ Tinytest.add("templating - preserve", function (test) {
tmpl['var'] = function () { return R.get(); };
var div = OnscreenDiv(Meteor.render(tmpl));
Meteor.flush();
Deps.flush();
test.equal(DomUtils.find(div.node(), 'u').firstChild.nodeValue.match(
/\S+/)[0], 'foo');
var spans1 = {};
@@ -650,7 +650,7 @@ Tinytest.add("templating - preserve", function (test) {
});
R.set('bar');
Meteor.flush();
Deps.flush();
test.equal(DomUtils.find(div.node(), 'u').firstChild.nodeValue.match(
/\S+/)[0], 'bar');
var spans2 = {};
@@ -668,7 +668,7 @@ Tinytest.add("templating - preserve", function (test) {
test.isFalse(spans1.z === spans2.z);
div.kill();
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - helpers", function (test) {
@@ -682,7 +682,7 @@ Tinytest.add("templating - helpers", function (test) {
var div = OnscreenDiv(Meteor.render(tmpl));
test.equal(div.text().match(/\S+/)[0], 'abc');
div.kill();
Meteor.flush();
Deps.flush();
tmpl = Template.test_template_helpers_b;
@@ -707,14 +707,14 @@ Tinytest.add("templating - helpers", function (test) {
test.expect_fail();
test.equal(txt, 'ABC4D');
div.kill();
Meteor.flush();
Deps.flush();
// test that helpers don't "leak"
tmpl = Template.test_template_helpers_c;
div = OnscreenDiv(Meteor.render(tmpl));
test.equal(div.text(), 'x');
div.kill();
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - events", function (test) {
@@ -731,7 +731,7 @@ Tinytest.add("templating - events", function (test) {
clickElement(DomUtils.find(div.node(), 'b'));
test.equal(buf, ['b']);
div.kill();
Meteor.flush();
Deps.flush();
///
@@ -750,7 +750,7 @@ Tinytest.add("templating - events", function (test) {
clickElement(DomUtils.find(div.node(), 'i'));
test.equal(buf, ['u', 'i']);
div.kill();
Meteor.flush();
Deps.flush();
});
@@ -773,34 +773,34 @@ Tinytest.add("templating - #each rendered callback", function (test) {
DomUtils.rangeToHtml(this.firstNode, this.lastNode)).replace(/\s/g, ''));
};
var div = OnscreenDiv(Meteor.render(tmpl));
Meteor.flush();
Deps.flush();
test.equal(buf, ['<div>a</div><div>b</div><div>c</div>']);
buf.length = 0;
// added
entries.insert({x:'d'});
test.equal(buf, []);
Meteor.flush();
Deps.flush();
test.equal(buf, ['<div>a</div><div>b</div><div>c</div><div>d</div>']);
buf.length = 0;
// removed
entries.remove({x:'a'});
test.equal(buf, []);
Meteor.flush();
Deps.flush();
test.equal(buf, ['<div>b</div><div>c</div><div>d</div>']);
buf.length = 0;
// moved/changed
entries.update({x:'b'}, {$set: {x: 'z'}});
test.equal(buf, []);
Meteor.flush();
Deps.flush();
test.equal(buf, ['<div>c</div><div>d</div><div>z</div>',
'<div>c</div><div>d</div><div>z</div>']);
buf.length = 0;
div.kill();
Meteor.flush();
Deps.flush();
// test pure "moved"
@@ -827,7 +827,7 @@ Tinytest.add("templating - #each rendered callback", function (test) {
buf = [];
var div = OnscreenDiv(Meteor.render(tmpl));
test.equal(buf, []);
Meteor.flush();
Deps.flush();
test.equal(buf, ['<div>a</div><div>b</div><div>c</div>']);
buf.length = 0;
@@ -835,14 +835,14 @@ Tinytest.add("templating - #each rendered callback", function (test) {
callbacks.movedBefore('a', null);
});
test.equal(buf, []);
Meteor.flush();
Deps.flush();
test.equal(div.html().replace(/\s/g, ''),
'<div>b</div><div>c</div><div>a</div>');
test.equal(buf, ['<div>b</div><div>c</div><div>a</div>']);
buf.length = 0;
div.kill();
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - landmarks in helpers", function (test) {
@@ -864,19 +864,19 @@ Tinytest.add("templating - landmarks in helpers", function (test) {
var div = OnscreenDiv(Meteor.render(tmpl));
test.equal(div.text().match(/\S+/)[0], 'xxxxfoo');
Meteor.flush();
Deps.flush();
buf.sort();
test.equal(buf.join(''), 'ccccrrrr');
buf.length = 0;
R.set('bar');
Meteor.flush();
Deps.flush();
test.equal(div.text().match(/\S+/)[0], 'xxxxbar');
test.equal(buf.join(''), 'rrrr');
buf.length = 0;
div.kill();
Meteor.flush();
Deps.flush();
test.equal(buf.join(''), 'dddd');
});
@@ -899,19 +899,19 @@ Tinytest.add("templating - bare each has no matching", function (test) {
};
var div = OnscreenDiv(Meteor.render(tmpl));
Meteor.flush();
Deps.flush();
buf.sort();
test.equal(buf.join(''), 'cccrrr');
buf.length = 0;
R.set('bar');
Meteor.flush();
Deps.flush();
buf.sort();
test.equal(buf.join(''), 'cccdddrrr');
buf.length = 0;
div.kill();
Meteor.flush();
Deps.flush();
test.equal(buf.join(''), 'ddd');
});
@@ -934,21 +934,21 @@ Tinytest.add("templating - templates are labeled", function (test) {
});
var div = OnscreenDiv(Meteor.render(tmpls[0]));
Meteor.flush();
Deps.flush();
test.equal(div.html(), "<hr><hr><hr>foo");
buf.sort();
test.equal(buf.join(''), 'cccrrr');
buf.length = 0;
R.set('bar');
Meteor.flush();
Deps.flush();
test.equal(div.html(), "<hr><hr><hr>bar");
buf.sort();
test.equal(buf.join(''), 'rrr');
buf.length = 0;
div.kill();
Meteor.flush();
Deps.flush();
test.equal(buf.join(''), 'ddd');
});
@@ -971,10 +971,10 @@ Tinytest.add("templating - unlabeled cursor", function (test) {
// This will fail with "can't create second landmark in branch"
// unless _id-less objects returned from a cursor are given
// unique branch labels in an {{#each}}.
Meteor.flush();
Deps.flush();
div.kill();
Meteor.flush();
Deps.flush();
});
Tinytest.add("templating - constant text patching", function (test) {
@@ -990,16 +990,16 @@ Tinytest.add("templating - constant text patching", function (test) {
};
var div = OnscreenDiv(Meteor.render(tmpl));
Meteor.flush();
Deps.flush();
R.set("bar");
// This flush will fail if we can't patch the constant region,
// which starts with a text node, after preserving the preceding
// paragraph.
Meteor.flush();
Deps.flush();
div.kill();
Meteor.flush();
Deps.flush();
});
@@ -1055,16 +1055,16 @@ Tinytest.add("templating - tricky branch labels", function (test) {
});
var div = OnscreenDiv(Meteor.render(Template.test_template_trickylabels_a0));
Meteor.flush();
Deps.flush();
loading.set(false);
Meteor.flush();
Deps.flush();
x.length = 0;
v.set(2);
Meteor.flush();
Deps.flush();
test.equal(x.join(''), 'r'); // no 'c' or 'd'
test.equal(div.html().replace(/\s+/g, ''), '<div>foo</div>2<div>bar</div>');
div.kill();
Meteor.flush();
Deps.flush();
});

View File

@@ -43,14 +43,14 @@ OnscreenDiv.prototype.node = function() {
};
// remove the DIV from the document and trigger
// "fast GC" -- i.e., after the next Meteor.flush()
// "fast GC" -- i.e., after the next Deps.flush()
// the DIV will be fully cleaned up by LiveUI.
OnscreenDiv.prototype.kill = function() {
var self = this;
if (self.div.parentNode)
self.div.parentNode.removeChild(self.div);
Meteor._atFlush(function () {
Deps.afterFlush(function () {
Spark.finalize(self.div);
});
};

View File

@@ -19,11 +19,11 @@ var ReactiveVar = function(initialValue) {
this._value = (typeof initialValue === "undefined" ? null :
initialValue);
this._deps = new Meteor.deps._ContextSet;
this._depsVar = new Deps.Variable;
};
ReactiveVar.prototype.get = function() {
this._deps.addCurrentContext();
Deps.depend(this._depsVar);
return this._value;
};
@@ -35,9 +35,10 @@ ReactiveVar.prototype.set = function(newValue) {
this._value = newValue;
this._deps.invalidateAll();
this._depsVar.changed();
};
ReactiveVar.prototype.numListeners = function() {
return _.keys(this._deps._contextsById).length;
// accesses private field (tests want to know)
return _.keys(this._depsVar._dependentsById).length;
};

View File

@@ -31,7 +31,7 @@ WrappedFrag.prototype.release = function() {
// decrement frag's GC protection reference count
// Clean up on flush, if hits 0. Wait to decrement
// so no one else cleans it up first.
Meteor._atFlush(function () {
Deps.afterFlush(function () {
if (! --frag["_protect"]) {
Spark.finalize(frag);
}

View File

@@ -2,8 +2,8 @@ var running = true;
var resultTree = [];
var failedTests = [];
var resultDeps = new Meteor.deps._ContextSet;
var countDeps = new Meteor.deps._ContextSet;
var resultsVar = new Deps.Variable;
var countVar = new Deps.Variable;
var totalCount = 0;
var passedCount = 0;
var failedCount = 0;
@@ -13,37 +13,37 @@ Session.setDefault("groupPath", ["tinytest"]);
Session.set("rerunScheduled", false);
Meteor.startup(function () {
Meteor.flush();
Deps.flush();
Meteor._runTestsEverywhere(reportResults, function () {
running = false;
Meteor.onTestsComplete && Meteor.onTestsComplete();
_resultsChanged();
Meteor.flush();
resultsVar.changed();
Deps.flush();
}, Session.get("groupPath"));
});
Template.progressBar.running = function () {
countDeps.addCurrentContext();
Deps.depend(countVar);
return passedCount + failedCount < totalCount;
};
Template.progressBar.percentPass = function () {
countDeps.addCurrentContext();
Deps.depend(countVar);
if (totalCount === 0)
return 0;
return 100*passedCount/totalCount;
};
Template.progressBar.percentFail = function () {
countDeps.addCurrentContext();
Deps.depend(countVar);
if (totalCount === 0)
return 0;
return 100*failedCount/totalCount;
};
Template.progressBar.anyFail = function () {
countDeps.addCurrentContext();
Deps.depend(countVar);
return failedCount > 0;
};
@@ -85,12 +85,12 @@ Template.test_group.events({
});
Template.test_table.running = function() {
resultDeps.addCurrentContext();
Deps.depend(resultsVar);
return running;
};
Template.test_table.passed = function() {
resultDeps.addCurrentContext();
Deps.depend(resultsVar);
// walk whole tree to look for failed tests
var walk = function (groups) {
@@ -119,7 +119,7 @@ Template.test_table.passed = function() {
Template.test_table.total_test_time = function() {
resultDeps.addCurrentContext();
Deps.depend(resultsVar);
// walk whole tree to get all tests
var walk = function (groups) {
@@ -142,11 +142,11 @@ Template.test_table.total_test_time = function() {
Template.test_table.data = function() {
resultDeps.addCurrentContext();
Deps.depend(resultsVar);
return resultTree;
};
Template.test_table.failedTests = function() {
resultDeps.addCurrentContext();
Deps.depend(resultsVar);
return failedTests;
};
@@ -182,7 +182,7 @@ Template.test.test_class = function() {
Template.test.events({
'click .testname': function() {
this.expanded = ! this.expanded;
_resultsChanged();
resultsVar.changed();
}
});
@@ -275,11 +275,6 @@ Template.event.is_debuggable = function() {
return !!this.cookie;
};
var _resultsChanged = function() {
resultDeps.invalidateAll();
};
var _testTime = function(t) {
if (t.events && t.events.length > 0) {
var lastEvent = _.last(t.events);
@@ -351,7 +346,7 @@ var _findTestForResults = function (results) {
test = {name: testName, parent: group, server: server, fullName: fullName};
group.tests.push(test);
totalCount++;
countDeps.invalidateAll();
countVar.changed();
}
return test;
@@ -380,7 +375,7 @@ var reportResults = function(results) {
var status = _testStatus(test);
if (status === "failed") {
failedCount++;
countDeps.invalidateAll();
countVar.changed();
// Expand a failed test (but only set this if the user hasn't clicked on the
// test name yet).
if (test.expanded === undefined)
@@ -389,7 +384,7 @@ var reportResults = function(results) {
failedTests.push(test.fullName);
} else if (status === "succeeded") {
passedCount++;
countDeps.invalidateAll();
countVar.changed();
}
_.defer(_throttled_update);
@@ -401,16 +396,16 @@ var forgetEvents = function (results) {
var status = _testStatus(test);
if (status === "failed") {
failedCount--;
countDeps.invalidateAll();
countVar.changed();
} else if (status === "succeeded") {
passedCount--;
countDeps.invalidateAll();
countVar.changed();
}
delete test.events;
_resultsChanged();
resultsVar.changed();
};
var _throttled_update = _.throttle(function() {
_resultsChanged();
Meteor.flush();
resultsVar.changed();
Deps.flush();
}, 500);