From d78481c1ceea86b39cdafad8cc718b3d9d611f48 Mon Sep 17 00:00:00 2001 From: David Greenspan Date: Sun, 16 Sep 2012 02:28:41 -0700 Subject: [PATCH] introduce Meteor.deps.ContextSet, use in Session --- packages/deps/deps-utils.js | 42 ++++++++++++++++++++++ packages/deps/package.js | 2 +- packages/session/session.js | 72 +++++++++++-------------------------- 3 files changed, 64 insertions(+), 52 deletions(-) create mode 100644 packages/deps/deps-utils.js diff --git a/packages/deps/deps-utils.js b/packages/deps/deps-utils.js new file mode 100644 index 0000000000..3010a0e087 --- /dev/null +++ b/packages/deps/deps-utils.js @@ -0,0 +1,42 @@ +(function () { + + // Constructor for an empty ContextSet. + var ContextSet = function () { + this._contextsById = {}; + }; + + // Adds the Context `ctx` to the set (if it is + // not already present). The Context will only + // remain in the set as long as it has not been + // invalidated. + // Returns true if the context was newly added. + ContextSet.prototype.add = function (ctx) { + var self = this; + if (ctx && ! (ctx.id in self._contextsById)) { + self._contextsById[ctx.id] = ctx; + ctx.on_invalidate(function () { + delete self._contextsById[ctx.id]; + }); + return true; + } + return false; + }; + + // Invalidate all Contexts in the set and remove + // them from the set. + ContextSet.prototype.invalidateAll = function () { + var self = this; + for (var id in self._contextsById) + self._contextsById[id].invalidate(); + }; + + // Returns true if there are no Contexts in this set. + ContextSet.prototype.isEmpty = function () { + var self = this; + for(var id in self._contextsById) + return false; + return true; + }; + + Meteor.deps.ContextSet = ContextSet; +})(); \ No newline at end of file diff --git a/packages/deps/package.js b/packages/deps/package.js index 9949cfdd73..f21ff22073 100644 --- a/packages/deps/package.js +++ b/packages/deps/package.js @@ -9,5 +9,5 @@ Package.on_use(function (api, where) { where = where || ['client', 'server']; api.use('underscore', where); - api.add_files('deps.js', where); + api.add_files(['deps.js', 'deps-utils.js'], where); }); diff --git a/packages/session/session.js b/packages/session/session.js index 11b39c580b..f2ca6683f5 100644 --- a/packages/session/session.js +++ b/packages/session/session.js @@ -1,31 +1,9 @@ // XXX could use some tests Session = _.extend({}, { - keys: {}, - key_deps: {}, // key -> context id -> context - key_value_deps: {}, // key -> value -> context id -> context - - // XXX remove debugging method (or improve it, but anyway, don't - // ship it in production) - dump_state: function () { - var self = this; - console.log("=== Session state ==="); - for (var key in self.key_deps) { - var ids = _.keys(self.key_deps[key]); - if (!ids.length) - continue; - console.log(key + ": " + _.reject(ids, function (x) {return x === "_once"}).join(' ')); - } - - for (var key in self.key_value_deps) { - for (var value in self.key_value_deps[key]) { - var ids = _.keys(self.key_value_deps[key][value]); - if (!ids.length) - continue; - console.log(key + "(" + value + "): " + _.reject(ids, function (x) {return x === "_once";}).join(' ')); - } - } - }, + keys: {}, // key -> value + key_deps: {}, // key -> ContextSet + key_value_deps: {}, // key -> value -> ContextSet set: function (key, value) { var self = this; @@ -35,30 +13,24 @@ Session = _.extend({}, { return; self.keys[key] = value; - var invalidate = function (map) { - if (map) - for (var id in map) - map[id].invalidate(); + var invalidateAll = function (set) { + set && set.invalidateAll(); }; - self._ensureKey(key); - invalidate(self.key_deps[key]); - invalidate(self.key_value_deps[key][old_value]); - invalidate(self.key_value_deps[key][value]); + invalidateAll(self.key_deps[key]); + if (self.key_value_deps[key]) { + invalidateAll(self.key_value_deps[key][old_value]); + invalidateAll(self.key_value_deps[key][value]); + } }, get: function (key) { var self = this; var context = Meteor.deps.Context.current; - self._ensureKey(key); - - if (context && !(context.id in self.key_deps[key])) { - self.key_deps[key][context.id] = context; - context.on_invalidate(function () { - delete self.key_deps[key][context.id]; - }); + if (context) { + self._ensureKey(key); + self.key_deps[key].add(context); } - return self.keys[key]; }, @@ -74,19 +46,17 @@ Session = _.extend({}, { if (context) { self._ensureKey(key); + if (!(value in self.key_value_deps[key])) - self.key_value_deps[key][value] = {}; + self.key_value_deps[key][value] = new Meteor.deps.ContextSet; - if (!(context.id in self.key_value_deps[key][value])) { - self.key_value_deps[key][value][context.id] = context; + var isNew = self.key_value_deps[key][value].add(context); + if (isNew) { context.on_invalidate(function () { - delete self.key_value_deps[key][value][context.id]; - // clean up [key][value] if it's now empty, so we don't use // O(n) memory for n = values seen ever - for (var x in self.key_value_deps[key][value]) - return; - delete self.key_value_deps[key][value]; + if (self.key_value_deps[key][value].isEmpty()) + delete self.key_value_deps[key][value]; }); } } @@ -97,8 +67,8 @@ Session = _.extend({}, { _ensureKey: function (key) { var self = this; if (!(key in self.key_deps)) { - self.key_deps[key] = {}; - self.key_value_deps[key] = {}; + self.key_deps[key] = new Meteor.deps.ContextSet; + self.key_value_deps[key] = new Meteor.deps.ContextSet; } } });