From 48b5548899ff5e867180cb0ec624a5b068a0a5ff Mon Sep 17 00:00:00 2001 From: Bradley Momberger Date: Wed, 3 May 2017 17:33:57 -0600 Subject: [PATCH] Replace fixed ordering in connect.js with dynamic ordering based on behavior needs --- can/map/map.js | 6 + can/merge/merge.js | 5 + can/ref/ref.js | 6 + connect.js | 146 ++++++++++++------- constructor/callbacks-once/callbacks-once.js | 13 ++ constructor/constructor.js | 6 + constructor/store/store.js | 7 + constructor/store/store_test.js | 1 + data/callbacks-cache/callbacks-cache.js | 13 ++ data/callbacks/callbacks.js | 13 ++ fall-through-cache/fall-through-cache.js | 10 ++ package.json | 1 + real-time/real-time.js | 13 ++ 13 files changed, 189 insertions(+), 51 deletions(-) diff --git a/can/map/map.js b/can/map/map.js index a7ddc7d9..aafd9448 100644 --- a/can/map/map.js +++ b/can/map/map.js @@ -343,6 +343,12 @@ var canMapBehavior = connect.behavior("can/map",function(baseConnection){ return behavior; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base" }); /** diff --git a/can/merge/merge.js b/can/merge/merge.js index 08b67f46..d5b3e0f2 100644 --- a/can/merge/merge.js +++ b/can/merge/merge.js @@ -96,4 +96,9 @@ module.exports = connect.behavior("can/merge",function(baseConnection){ canBatch.stop(); } }; +}, { + "constructor/callbacks-once": "derived", + "real-time": "derived", + "can/map": "base", + "constructor": "derived" }); diff --git a/can/ref/ref.js b/can/ref/ref.js index 478769eb..7b70f47b 100644 --- a/can/ref/ref.js +++ b/can/ref/ref.js @@ -388,4 +388,10 @@ module.exports = connect.behavior("can/ref",function(baseConnection){ this.Map.Ref = makeRef(this); } }; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base" }); diff --git a/connect.js b/connect.js index af8189ed..efc36f7e 100644 --- a/connect.js +++ b/connect.js @@ -1,4 +1,6 @@ var assign = require("can-util/js/assign/assign"); +var each = require("can-util/js/each/each"); +var CID = require("can-cid"); /** * * @param {Array} behaviors - An array of behavior names or custom behaviors. @@ -6,38 +8,65 @@ var assign = require("can-util/js/assign/assign"); * @param {Object} options */ var connect = function(behaviors, options){ - - behaviors = behaviors.map(function(behavior, index){ - var sortedIndex = -1; + var i, behavior, scores = {}; + behaviors = behaviors.slice(0); + for (i = 0; i < behaviors.length; i++) { + behavior = behaviors[i]; if(typeof behavior === "string") { - sortedIndex = connect.order.indexOf(behavior); behavior = behaviorsMap[behavior]; - } else if(behavior.isBehavior) { - sortedIndex = connect.order.indexOf(behavior.behaviorName); - } else { + } else if(!behavior.isBehavior) { behavior = connect.behavior(behavior); } - return { - originalIndex: index, - sortedIndex: sortedIndex, - behavior: behavior - }; - }) - .sort(function(b1, b2){ - // if both have a sorted index - if(~b1.sortedIndex && ~b2.sortedIndex) { - return b1.sortedIndex - b2.sortedIndex; + // Make sure required dependencies are found. + each(behavior.dependencies, function(level, dep) { + if(typeof dep === "string") { + dep = behaviorsMap[dep]; + } + if(dep && + (level === "required:base" || level === "required:derived") && + behaviors.indexOf(dep) === -1 && + behaviors.indexOf(dep.behaviorName) === -1 + ) { + behaviors.push(dep); } - return b1.originalIndex - b2.originalIndex; + }); + behaviors[i] = behavior; + // give everything an initial score based on its order + scores[behavior.behaviorName] = i; + } + + var updated; + do { + updated = false; + each(behaviors, function(behavior) { + each(behavior.dependencies, function(direction, dep) { + if(typeof dep === "string") { + dep = behaviorsMap[dep]; + } + if(direction === "required:base" || direction === "base") { + if(scores[behavior.behaviorName] <= scores[dep.behaviorName]) { + updated = true; + scores[behavior.behaviorName] = scores[dep.behaviorName] + 1; + } + } else if(direction === "required:derived" || direction === "derived") { + if(scores[behavior.behaviorName] >= scores[dep.behaviorName]) { + updated = true; + scores[dep.behaviorName] = scores[behavior.behaviorName] + 1; + } + } + }); }); - behaviors = behaviors.map(function(b){ - return b.behavior; + } while(updated); + behaviors.sort(function(a, b) { + return scores[a.behaviorName] > scores[b.behaviorName] ? 1 : -1; }); - var behavior = connect.base( connect.behavior("options",function(){return options; })() ); + // set up a base behavior + behavior = connect.base( connect.behavior("options",function(){ return options; })() ); + // then add all of the others in score order behaviors.forEach(function(behave){ behavior = behave(behavior); }); @@ -47,41 +76,56 @@ var connect = function(behaviors, options){ return behavior; }; +// connect.order = ["data/localstorage-cache","data/url","data/parse","cache-requests","data/combine-requests", +// "constructor","constructor/store","can/map","can/ref", +// "fall-through-cache", -connect.order = ["data/localstorage-cache","data/url","data/parse","cache-requests","data/combine-requests", - - "constructor","constructor/store","can/map","can/ref", - "fall-through-cache", +// "data/worker","real-time", - "data/worker","real-time", +/*, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base", + "constructor": "base", + "constructor/store": "base", + "can/map": "base", + "can/ref": "base", + "fall-through-cache": "base" + "data/worker": "base", + "real-time": "base" +}*/ - "data/callbacks-cache","data/callbacks","constructor/callbacks-once" - ]; +// "data/callbacks-cache","data/callbacks","constructor/callbacks-once" +// ]; -connect.behavior = function(name, behavior){ - if(typeof name !== "string") { - behavior = name; - name = undefined; - } - var behaviorMixin = function(base){ - // basically Object.create - var Behavior = function(){}; - Behavior.name = name; - Behavior.prototype = base; - var newBehavior = new Behavior(); - // allows behaviors to be a simple object, not always a function - var res = typeof behavior === "function" ? behavior.apply(newBehavior, arguments) : behavior; - assign(newBehavior, res); - newBehavior.__behaviorName = name; - return newBehavior; - }; - if(name) { - behaviorMixin.behaviorName = name; - behaviorsMap[name] = behaviorMixin; - } - behaviorMixin.isBehavior = true; - return behaviorMixin; +connect.behavior = function(name, behavior, dependencies){ + if(typeof name !== "string") { + dependencies = behavior; + behavior = name; + name = CID({}, "behavior"); + } + var behaviorMixin = function(base){ + // basically Object.create + var Behavior = function(){}; + Behavior.name = name; + Behavior.prototype = base; + var newBehavior = new Behavior(); + // allows behaviors to be a simple object, not always a function + var res = typeof behavior === "function" ? behavior.apply(newBehavior, arguments) : behavior; + assign(newBehavior, res); + newBehavior.__behaviorName = name; + return newBehavior; + }; + if(name) { + behaviorMixin.behaviorName = name; + behaviorsMap[name] = behaviorMixin; + } + behaviorMixin.isBehavior = true; + behaviorMixin.dependencies = dependencies; + return behaviorMixin; }; var behaviorsMap = {}; diff --git a/constructor/callbacks-once/callbacks-once.js b/constructor/callbacks-once/callbacks-once.js index 057b8276..c12a13d0 100644 --- a/constructor/callbacks-once/callbacks-once.js +++ b/constructor/callbacks-once/callbacks-once.js @@ -71,4 +71,17 @@ module.exports = connect.behavior("constructor/callbacks-once",function(baseConn }); return behavior; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base", + "constructor": "base", + "constructor/store": "base", + "can/map": "base", + "can/ref": "base", + "fall-through-cache": "base", + "data/worker": "base", + "real-time": "base" }); diff --git a/constructor/constructor.js b/constructor/constructor.js index 4c9613a3..9d52fc6d 100644 --- a/constructor/constructor.js +++ b/constructor/constructor.js @@ -687,6 +687,12 @@ module.exports = connect.behavior("constructor",function(baseConnection){ return behavior; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base" }); function copyMetadata(listData, list){ diff --git a/constructor/store/store.js b/constructor/store/store.js index 091168ae..8fdab2b6 100644 --- a/constructor/store/store.js +++ b/constructor/store/store.js @@ -642,6 +642,13 @@ var constructorStore = connect.behavior("constructor/store",function(baseConnect return behavior; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base", + "constructor": "base" }); constructorStore.requests = requests; diff --git a/constructor/store/store_test.js b/constructor/store/store_test.js index 51a49f26..822c3a35 100644 --- a/constructor/store/store_test.js +++ b/constructor/store/store_test.js @@ -130,6 +130,7 @@ QUnit.test("list store is kept and re-used and possibly discarded", function(){ return new PersonList(arr.data, sets); } }); + console.log(connection) var resolvedList; connection.getList({}).then(function(list){ diff --git a/data/callbacks-cache/callbacks-cache.js b/data/callbacks-cache/callbacks-cache.js index cc691b7a..e75c7abc 100644 --- a/data/callbacks-cache/callbacks-cache.js +++ b/data/callbacks-cache/callbacks-cache.js @@ -60,4 +60,17 @@ module.exports = connect.behavior("data/callbacks-cache",function(baseConnection }; }); return behavior; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base", + "constructor": "base", + "constructor/store": "base", + "can/map": "base", + "can/ref": "base", + "fall-through-cache": "base", + "data/worker": "base", + "real-time": "base" }); diff --git a/data/callbacks/callbacks.js b/data/callbacks/callbacks.js index ddf3aede..1a322767 100644 --- a/data/callbacks/callbacks.js +++ b/data/callbacks/callbacks.js @@ -106,4 +106,17 @@ module.exports = connect.behavior("data/callbacks",function(baseConnection){ }); return behavior; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base", + "constructor": "base", + "constructor/store": "base", + "can/map": "base", + "can/ref": "base", + "fall-through-cache": "base", + "data/worker": "base", + "real-time": "base" }); diff --git a/fall-through-cache/fall-through-cache.js b/fall-through-cache/fall-through-cache.js index 7de4933d..70928719 100644 --- a/fall-through-cache/fall-through-cache.js +++ b/fall-through-cache/fall-through-cache.js @@ -284,4 +284,14 @@ module.exports = connect.behavior("fall-through-cache",function(baseConnection){ return behavior; +}, { + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base", + "constructor": "base", + "constructor/store": "base", + "can/map": "base", + "can/ref": "base" }); diff --git a/package.json b/package.json index 628dc6f8..68b661fb 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Data connection middleware and utilities", "main": "can-connect.js", "dependencies": { + "can-cid": "^1.0.3", "can-compute": "^3.0.4", "can-construct": "^3.1.0", "can-define": "^1.0.9", diff --git a/real-time/real-time.js b/real-time/real-time.js index da4b0963..d2f2619b 100644 --- a/real-time/real-time.js +++ b/real-time/real-time.js @@ -366,6 +366,19 @@ module.exports = connect.behavior("real-time",function(baseConnection){ } //!steal-remove-end }; +}, { + // dependencies + "data/callbacks": "required:derived", + "data/localstorage-cache": "base", + "data/url": "base", + "data/parse": "base", + "cache-requests": "base", + "data/combine-requests": "base", + "constructor": "base", + "constructor/store": "base", + "can/map": "base", + "can/ref": "base", + "fall-through-cache": "base" }); var create = function(props){