diff --git a/docs/client/api.html b/docs/client/api.html index bad079157c..25cbb18a01 100644 --- a/docs/client/api.html +++ b/docs/client/api.html @@ -2399,7 +2399,7 @@ whether they are being accessed reactively or not. It's very rare to need to access `currentComputation` directly. The current computation is used implicitly by [`Deps.active`](#deps_active) (which tests whether there is one), -[`Deps.depend`](#deps_depend) (which registers that it depends on a +[`dependency.depend()`](#dependency_depend) (which registers that it depends on a dependency), and [`Deps.onInvalidate`](#deps_oninvalidate) (which registers a callback with it). @@ -2417,15 +2417,6 @@ computations that need rerunning. This means that if an `afterFlush` function invalidates a computation, that computation will be rerun before any other `afterFlush` functions are called. -{{> api_box deps_depend }} - -`Deps.depend` is used in reactive data source implementations as the -primary way to record the fact that a Dependency is being accessed from -some computation. If there is a current computation, it becomes a -dependent of the Dependency, while the Dependency becomes a dependency of -the computation. Calls -[`dependency.addDependent()`](#dependency_adddependent). -

Deps.Computation

A Computation object represents code that is repeatedly rerun in @@ -2539,10 +2530,10 @@ accompanied by a Dependency object that tracks the computations that depend on it, as in this example: var weather = "sunny"; - var weatherDeps = new Deps.Dependency; + var weatherDep = new Deps.Dependency; var getWeather = function () { - Deps.depend(weatherDeps); + weatherDep.depend() return weather; }; @@ -2550,12 +2541,12 @@ on it, as in this example: weather = w; // (could add logic here to only call changed() // if the new value is different from the old) - weatherDeps.changed(); + weatherDep.changed(); }; This example implements a weather data source with a simple getter and setter. The getter records that the current computation depends on -the `weatherDeps` dependency using `Deps.depend`, while the setter +the `weatherDep` dependency using `depend()`, while the setter signals the dependency to invalidate all dependent computations by calling `changed()`. @@ -2571,7 +2562,7 @@ current computation is made to depend on an internal Dependency that does not change if the weather goes from, say, "rainy" to "cloudy". Conceptually, the only two things a Dependency can do are gain a -dependent (typically via `Deps.depend`) and change. +dependent and change. A Dependency's dependent computations are always valid (they have `invalidated === false`). If a dependent is invalidated at any time, @@ -2580,14 +2571,10 @@ removed. {{> api_box dependency_changed }} -{{> api_box dependency_adddependent }} +{{> api_box dependency_depend }} -In almost all cases, you want to declare a dependency of the current -computation and should use `Deps.depend(dependency)` instead, -which calls `dependency.addDependent(null)`. - -Computations added as dependents of the Dependency will be removed -immediately if they are ever invalidated or stopped. +`dep.depend()` is used in reactive data source implementations to record +the fact that `dep` is being accessed from the current computation. {{> api_box dependency_hasdependents }} diff --git a/docs/client/api.js b/docs/client/api.js index 2c52453552..1ecc1315bd 100644 --- a/docs/client/api.js +++ b/docs/client/api.js @@ -812,18 +812,6 @@ Template.api.deps_afterflush = { ] }; -Template.api.deps_depend = { - id: "deps_depend", - name: "Deps.depend(dependency)", - locus: "Client", - descr: ["Declares that the current computation depends on `dependency`. The current computation, if there is one, becomes a dependent of `dependency`, meaning it will be invalidated and rerun the next time `dependency` changes.", "Returns `true` if this results in `dependency` gaining a new dependent (or `false` if this relationship already exists or there is no current computation)."], - args: [ - {name: "dependency", - type: "Deps.Dependency", - descr: "The dependency for this computation to depend on."} - ] -}; - Template.api.computation_stop = { id: "computation_stop", name: "computation.stop()", @@ -878,15 +866,15 @@ Template.api.dependency_changed = { descr: ["Invalidate all dependent computations immediately and remove them as dependents."] }; -Template.api.dependency_adddependent = { - id: "dependency_adddependent", - name: "dependency.addDependent(computation)", +Template.api.dependency_depend = { + id: "dependency_depend", + name: "dependency.depend([fromComputation])", locus: "Client", - descr: ["Adds `computation` as a dependent of this Dependency, recording the fact that the computation depends on this Dependency.", "Returns true if the computation was not already a dependent of this Dependency."], + descr: ["Declares that the current computation (or `fromComputation` if given) depends on `dependency`. The computation will be invalidated the next time `dependency` changes.", "If there is no current computation and `depend()` is called with no arguments, it does nothing and returns false.", "Returns true if the computation is a new dependent of `dependency` rather than an existing one."], args: [ - {name: "computation", + {name: "fromComputation", type: "Deps.Computation", - descr: "The computation to add, or `null` to use the current computation (in which case there must be a current computation)."} + descr: "An optional computation declared to depend on `dependency` instead of the current computation."} ] }; diff --git a/docs/client/docs.js b/docs/client/docs.js index 10de2da540..791c2c7468 100644 --- a/docs/client/docs.js +++ b/docs/client/docs.js @@ -251,7 +251,6 @@ var toc = [ "Deps.currentComputation", "Deps.onInvalidate", "Deps.afterFlush", - "Deps.depend", "Deps.Computation", [ {instance: "computation", name: "stop", id: "computation_stop"}, {instance: "computation", name: "invalidate", id: "computation_invalidate"}, @@ -262,7 +261,7 @@ var toc = [ ], "Deps.Dependency", [ {instance: "dependency", name: "changed", id: "dependency_changed"}, - {instance: "dependency", name: "addDependent", id: "dependency_adddependent"}, + {instance: "dependency", name: "depend", id: "dependency_depend"}, {instance: "dependency", name: "hasDependents", id: "dependency_hasdependents"} ] ], diff --git a/packages/accounts-base/accounts_client.js b/packages/accounts-base/accounts_client.js index b07fe09c2e..d4eb2a1164 100644 --- a/packages/accounts-base/accounts_client.js +++ b/packages/accounts-base/accounts_client.js @@ -15,7 +15,7 @@ Accounts._setLoggingIn = function (x) { } }; Meteor.loggingIn = function () { - Deps.depend(loggingInDeps); + loggingInDeps.depend(); return loggingIn; }; @@ -194,6 +194,6 @@ Accounts.loginServicesConfigured = function () { return true; // not yet complete, save the context for invalidation once we are. - Deps.depend(loginServicesConfiguredDeps); + loginServicesConfiguredDeps.depend(); return false; }; diff --git a/packages/deps/deps.js b/packages/deps/deps.js index 68de2de821..e446c32dac 100644 --- a/packages/deps/deps.js +++ b/packages/deps/deps.js @@ -1,199 +1,198 @@ - Deps = {}; - Deps.active = false; - Deps.currentComputation = null; +Deps = {}; +Deps.active = false; +Deps.currentComputation = null; - var setCurrentComputation = function (c) { - Deps.currentComputation = c; - Deps.active = !! c; - }; +var setCurrentComputation = function (c) { + Deps.currentComputation = c; + Deps.active = !! c; +}; - var _debugFunc = function () { - // lazy evaluation because `Meteor` does not exist right away - return (typeof Meteor !== "undefined" ? Meteor._debug : - ((typeof console !== "undefined") && console.log ? console.log : - function () {})); - }; +var _debugFunc = function () { + // lazy evaluation because `Meteor` does not exist right away + return (typeof Meteor !== "undefined" ? Meteor._debug : + ((typeof console !== "undefined") && console.log ? console.log : + function () {})); +}; - var nextId = 1; - // computations whose callbacks we should call at flush time - var pendingComputations = []; - // `true` if a Deps.flush is scheduled, or if we are in Deps.flush now - var willFlush = false; - // `true` if we are in Deps.flush now - var inFlush = false; - // `true` if we are computing a computation now, either first time - // or recompute. This matches Deps.active unless we are inside - // Deps.nonreactive, which nullfies currentComputation even though - // an enclosing computation may still be running. - var inCompute = false; +var nextId = 1; +// computations whose callbacks we should call at flush time +var pendingComputations = []; +// `true` if a Deps.flush is scheduled, or if we are in Deps.flush now +var willFlush = false; +// `true` if we are in Deps.flush now +var inFlush = false; +// `true` if we are computing a computation now, either first time +// or recompute. This matches Deps.active unless we are inside +// Deps.nonreactive, which nullfies currentComputation even though +// an enclosing computation may still be running. +var inCompute = false; - var afterFlushCallbacks = []; +var afterFlushCallbacks = []; - var requireFlush = function () { - if (! willFlush) { - setTimeout(Deps.flush, 0); - willFlush = true; - } - }; +var requireFlush = function () { + if (! willFlush) { + setTimeout(Deps.flush, 0); + willFlush = true; + } +}; - // Deps.Computation constructor is visible but private - // (throws an error if you try to call it) - var constructingComputation = false; +// Deps.Computation constructor is visible but private +// (throws an error if you try to call it) +var constructingComputation = false; - Deps.Computation = function (f, parent) { - if (! constructingComputation) - throw new Error( - "Deps.Computation constructor is private; use Deps.autorun"); - constructingComputation = false; +Deps.Computation = function (f, parent) { + if (! constructingComputation) + throw new Error( + "Deps.Computation constructor is private; use Deps.autorun"); + constructingComputation = false; + var self = this; + self.stopped = false; + self.invalidated = false; + self.firstRun = true; + + self._id = nextId++; + self._onInvalidateCallbacks = []; + // the plan is at some point to use the parent relation + // to constrain the order that computations are processed + self._parent = parent; + self._func = f; + self._recomputing = false; + + try { + self._compute(); + } finally { + self.firstRun = false; + } +}; + +_.extend(Deps.Computation.prototype, { + + onInvalidate: function (f) { + var self = this; + + if (typeof f !== 'function') + throw new Error("onInvalidate requires a function"); + + var g = function () { + Deps.nonreactive(function () { + f(self); + }); + }; + + if (self.invalidated) + g(); + else + self._onInvalidateCallbacks.push(g); + }, + + invalidate: function () { + var self = this; + if (! self.invalidated) { + // if we're currently in _recompute(), don't enqueue + // ourselves, since we'll rerun immediately anyway. + if (! self._recomputing && ! self.stopped) { + requireFlush(); + pendingComputations.push(this); + } + + self.invalidated = true; + + // callbacks can't add callbacks, because + // self.invalidated === true. + for(var i = 0, f; f = self._onInvalidateCallbacks[i]; i++) + f(); // already bound with self as argument + self._onInvalidateCallbacks = []; + } + }, + + stop: function () { + if (! this.stopped) { + this.stopped = true; + this.invalidate(); + } + }, + + _compute: function () { var self = this; - self.stopped = false; self.invalidated = false; - self.firstRun = true; - - self._id = nextId++; - self._onInvalidateCallbacks = []; - // the plan is at some point to use the parent relation - // to constrain the order that computations are processed - self._parent = parent; - self._func = f; - self._recomputing = false; + var previous = Deps.currentComputation; + setCurrentComputation(self); + var previousInCompute = inCompute; + inCompute = true; try { - self._compute(); + self._func(self); } finally { - self.firstRun = false; + setCurrentComputation(previous); + inCompute = false; } - }; + }, - _.extend(Deps.Computation.prototype, { + _recompute: function () { + var self = this; - onInvalidate: function (f) { - var self = this; - - if (typeof f !== 'function') - throw new Error("onInvalidate requires a function"); - - var g = function () { - Deps.nonreactive(function () { - f(self); - }); - }; - - if (self.invalidated) - g(); - else - self._onInvalidateCallbacks.push(g); - }, - - invalidate: function () { - var self = this; - if (! self.invalidated) { - // if we're currently in _recompute(), don't enqueue - // ourselves, since we'll rerun immediately anyway. - if (! self._recomputing && ! self.stopped) { - requireFlush(); - pendingComputations.push(this); - } - - self.invalidated = true; - - // callbacks can't add callbacks, because - // self.invalidated === true. - for(var i = 0, f; f = self._onInvalidateCallbacks[i]; i++) - f(); // already bound with self as argument - self._onInvalidateCallbacks = []; - } - }, - - stop: function () { - if (! this.stopped) { - this.stopped = true; - this.invalidate(); - } - }, - - _compute: function () { - var self = this; - self.invalidated = false; - - var previous = Deps.currentComputation; - setCurrentComputation(self); - var previousInCompute = inCompute; - inCompute = true; + self._recomputing = true; + while (self.invalidated && ! self.stopped) { try { - self._func(self); - } finally { - setCurrentComputation(previous); - inCompute = false; + self._compute(); + } catch (e) { + _debugFunc()("Exception from Deps recompute:", e.stack || e.message); } - }, - - _recompute: function () { - var self = this; - - self._recomputing = true; - while (self.invalidated && ! self.stopped) { - try { - self._compute(); - } catch (e) { - _debugFunc()("Exception from Deps recompute:", e.stack || e.message); - } - // If _compute() invalidated us, we run again immediately. - // A computation that invalidates itself indefinitely is an - // infinite loop, of course. - // - // We could put an iteration counter here and catch run-away - // loops. - } - self._recomputing = false; + // If _compute() invalidated us, we run again immediately. + // A computation that invalidates itself indefinitely is an + // infinite loop, of course. + // + // We could put an iteration counter here and catch run-away + // loops. } - }); + self._recomputing = false; + } +}); - Deps.Dependency = function () { - this._dependentsById = {}; - }; +Deps.Dependency = function () { + this._dependentsById = {}; +}; - _.extend(Deps.Dependency.prototype, { - // Adds `computation` to this set if it is not already - // present. Returns true if `computation` is a new member of the set. - // If no argument, defaults to currentComputation (which is required to - // exist in this case). - addDependent: function (computation) { - if (! computation) { - if (! Deps.active) - throw new Error( - "Dependency.addDependent() called with no currentComputation"); +_.extend(Deps.Dependency.prototype, { + // Adds `computation` to this set if it is not already + // present. Returns true if `computation` is a new member of the set. + // If no argument, defaults to currentComputation, or does nothing + // if there is no currentComputation. + depend: function (computation) { + if (! computation) { + if (! Deps.active) + return false; - computation = Deps.currentComputation; - } - var self = this; - var id = computation._id; - if (! (id in self._dependentsById)) { - self._dependentsById[id] = computation; - computation.onInvalidate(function () { - delete self._dependentsById[id]; - }); - return true; - } - return false; - }, - changed: function () { - var self = this; - for (var id in self._dependentsById) - self._dependentsById[id].invalidate(); - }, - hasDependents: function () { - var self = this; - for(var id in self._dependentsById) - return true; - return false; + computation = Deps.currentComputation; } - }); + var self = this; + var id = computation._id; + if (! (id in self._dependentsById)) { + self._dependentsById[id] = computation; + computation.onInvalidate(function () { + delete self._dependentsById[id]; + }); + return true; + } + return false; + }, + changed: function () { + var self = this; + for (var id in self._dependentsById) + self._dependentsById[id].invalidate(); + }, + hasDependents: function () { + var self = this; + for(var id in self._dependentsById) + return true; + return false; + } +}); - _.extend(Deps, { - flush: function () { - // Nested flush could plausibly happen if, say, a flush causes +_.extend(Deps, { + flush: function () { + // Nested flush could plausibly happen if, say, a flush causes // DOM mutation, which causes a "blur" event, which runs an // app event handler that calls Deps.flush. At the moment // Spark blocks event handlers during DOM mutation anyway, @@ -297,13 +296,6 @@ Deps.currentComputation.onInvalidate(f); }, - depend: function (v) { - if (! Deps.active) - return false; - - return v.addDependent(); - }, - afterFlush: function (f) { afterFlushCallbacks.push(f); requireFlush(); diff --git a/packages/deps/deps_tests.js b/packages/deps/deps_tests.js index 1c7a3ee30c..320092aaa1 100644 --- a/packages/deps/deps_tests.js +++ b/packages/deps/deps_tests.js @@ -2,7 +2,7 @@ Tinytest.add('deps - run', function (test) { var d = new Deps.Dependency; var x = 0; var handle = Deps.autorun(function (handle) { - Deps.depend(d); + d.depend(); ++x; }); test.equal(x, 1); @@ -26,7 +26,7 @@ Tinytest.add('deps - run', function (test) { test.equal(x, 3); Deps.autorun(function (internalHandle) { - Deps.depend(d); + d.depend(); ++x; if (x == 6) internalHandle.stop(); @@ -63,22 +63,22 @@ Tinytest.add("deps - nested run", function (test) { var buf = ""; var c1 = Deps.autorun(function () { - Deps.depend(a); + a.depend(); buf += 'a'; Deps.autorun(function () { - Deps.depend(b); + b.depend(); buf += 'b'; Deps.autorun(function () { - Deps.depend(c); + c.depend(); buf += 'c'; var c2 = Deps.autorun(function () { - Deps.depend(d); + d.depend(); buf += 'd'; Deps.autorun(function () { - Deps.depend(e); + e.depend(); buf += 'e'; Deps.autorun(function () { - Deps.depend(f); + f.depend(); buf += 'f'; }); }); diff --git a/packages/livedata/livedata_connection.js b/packages/livedata/livedata_connection.js index 3794852a91..ea6cada01c 100644 --- a/packages/livedata/livedata_connection.js +++ b/packages/livedata/livedata_connection.js @@ -495,7 +495,7 @@ _.extend(Meteor._LivedataConnection.prototype, { if (!_.has(self._subscriptions, id)) return false; var record = self._subscriptions[id]; - record.readyDeps && Deps.depend(record.readyDeps); + record.readyDeps && record.readyDeps.depend(); return record.ready; } }; @@ -787,7 +787,7 @@ _.extend(Meteor._LivedataConnection.prototype, { userId: function () { var self = this; if (self._userIdDeps) - Deps.depend(self._userIdDeps); + self._userIdDeps.depend(); return self._userId; }, diff --git a/packages/minimongo/minimongo.js b/packages/minimongo/minimongo.js index 638973f014..913c09ab92 100644 --- a/packages/minimongo/minimongo.js +++ b/packages/minimongo/minimongo.js @@ -374,7 +374,7 @@ LocalCollection.Cursor.prototype._depend = function (changers) { if (Deps.active) { var v = new Deps.Dependency; - Deps.depend(v); + v.depend(); var notifyChange = _.bind(v.changed, v); var options = {_suppress_initial: true}; diff --git a/packages/past/past.js b/packages/past/past.js index a76da984c0..054cf33c37 100644 --- a/packages/past/past.js +++ b/packages/past/past.js @@ -15,6 +15,11 @@ Meteor.autosubscribe = Deps.autorun; Meteor.flush = Deps.flush; Meteor.autorun = Deps.autorun; +// Deps API that briefly existed in 0.5.9 +Deps.depend = function (d) { + return d.depend(); +}; + // Instead of the "random" package with Random.id(), we used to have this // Meteor.uuid() implementing the RFC 4122 v4 UUID. Meteor.uuid = function () { diff --git a/packages/session/session.js b/packages/session/session.js index 220fd42b6f..65e830d56a 100644 --- a/packages/session/session.js +++ b/packages/session/session.js @@ -51,7 +51,7 @@ Session = _.extend({}, { get: function (key) { var self = this; self._ensureKey(key); - Deps.depend(self.keyDeps[key]); + self.keyDeps[key].depend(); return parse(self.keys[key]); }, @@ -83,7 +83,7 @@ Session = _.extend({}, { if (! _.has(self.keyValueDeps[key], serializedValue)) self.keyValueDeps[key][serializedValue] = new Deps.Dependency; - var isNew = Deps.depend(self.keyValueDeps[key][serializedValue]); + var isNew = self.keyValueDeps[key][serializedValue].depend(); if (isNew) { Deps.onInvalidate(function () { // clean up [key][serializedValue] if it's now empty, so we don't diff --git a/packages/stream/stream_client.js b/packages/stream/stream_client.js index 1267fabe3e..8e0829021a 100644 --- a/packages/stream/stream_client.js +++ b/packages/stream/stream_client.js @@ -139,7 +139,7 @@ _.extend(Meteor._Stream.prototype, { status: function () { var self = this; if (self.status_listeners) - Deps.depend(self.status_listeners); + self.status_listeners.depend(); return self.current_status; }, diff --git a/packages/test-helpers/reactivevar.js b/packages/test-helpers/reactivevar.js index 3a7c74b7c8..1a5f6239cc 100644 --- a/packages/test-helpers/reactivevar.js +++ b/packages/test-helpers/reactivevar.js @@ -23,7 +23,7 @@ ReactiveVar = function(initialValue) { }; ReactiveVar.prototype.get = function() { - Deps.depend(this._deps); + this._deps.depend(); return this._value; }; diff --git a/packages/test-in-browser/driver.js b/packages/test-in-browser/driver.js index f1ad8cbdbc..5654b44664 100644 --- a/packages/test-in-browser/driver.js +++ b/packages/test-in-browser/driver.js @@ -26,12 +26,12 @@ Meteor.startup(function () { }); Template.progressBar.running = function () { - Deps.depend(countDeps); + countDeps.depend(); return passedCount + failedCount < totalCount; }; Template.progressBar.percentPass = function () { - Deps.depend(countDeps); + countDeps.depend(); if (totalCount === 0) return 0; return 100*passedCount/totalCount; @@ -46,14 +46,14 @@ Template.progressBar.passedCount = function () { }; Template.progressBar.percentFail = function () { - Deps.depend(countDeps); + countDeps.depend(); if (totalCount === 0) return 0; return 100*failedCount/totalCount; }; Template.progressBar.anyFail = function () { - Deps.depend(countDeps); + countDeps.depend(); return failedCount > 0; }; @@ -95,12 +95,12 @@ Template.test_group.events({ }); Template.test_table.running = function() { - Deps.depend(resultsDeps); + resultsDeps.depend(); return running; }; Template.test_table.passed = function() { - Deps.depend(resultsDeps); + resultsDeps.depend(); // walk whole tree to look for failed tests var walk = function (groups) { @@ -129,7 +129,7 @@ Template.test_table.passed = function() { Template.test_table.total_test_time = function() { - Deps.depend(resultsDeps); + resultsDeps.depend(); // walk whole tree to get all tests var walk = function (groups) { @@ -152,11 +152,11 @@ Template.test_table.total_test_time = function() { Template.test_table.data = function() { - Deps.depend(resultsDeps); + resultsDeps.depend(); return resultTree; }; Template.test_table.failedTests = function() { - Deps.depend(resultsDeps); + resultsDeps.depend(); return failedTests; };