Skip to content

Commit

Permalink
Merge pull request #169 from canjs/get-paths-updates
Browse files Browse the repository at this point in the history
fixing getPathsForKey to work with functions and making read return parentHasKey data
  • Loading branch information
phillipskevin committed Jun 6, 2018
2 parents a0b3f69 + 63c7dec commit a070fff
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 23 deletions.
65 changes: 44 additions & 21 deletions can-view-scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ assign(Scope.prototype, {
readValue = observeReader.read(this, keyReads.slice(1), options);

// otherwise, check the templateContext
if (typeof readValue.value === 'undefined') {
if (typeof readValue.value === 'undefined' && !readValue.parentHasKey) {
readValue = this.readFromTemplateContext(attr.slice(6), options);
}

Expand Down Expand Up @@ -252,7 +252,7 @@ assign(Scope.prototype, {
// Retrieve the observes that were read.
var observes = getObserves();
// If a **value was was found**, return value and location data.
if (data.value !== undefined) {
if (data.value !== undefined || data.parentHasKey) {

if(!observes.length && isRecording) {
// if we didn't actually observe anything
Expand All @@ -269,7 +269,8 @@ assign(Scope.prototype, {
rootObserve: currentObserve,
value: data.value,
reads: currentReads,
thisArg: keyReads.length > 1 ? data.parent : undefined
thisArg: keyReads.length > 1 ? data.parent : undefined,
parentHasKey: data.parentHasKey
};
}
// Otherwise, save all observables that were read. If no value
Expand Down Expand Up @@ -426,19 +427,53 @@ assign(Scope.prototype, {

// ## Scope.prototype.getPathsForKey
// Finds all paths that will return a value for a specific key
// NOTE: this is for development purposes only and is removed in production
getPathsForKey: function getPathsForKey(key) {
//!steal-remove-start
var paths = {};

var getKeyDefinition = function(obj, key) {
if (!obj || typeof obj !== "object") {
return {};
}

var keyExistsOnObj = key in obj;
var objHasKey = canReflect.hasKey(obj, key);

return {
isDefined: keyExistsOnObj || objHasKey,
isFunction: keyExistsOnObj && typeof obj[key] === "function"
};
};

// scope.foo@bar -> bar
var keyParts = key.split(/\.|@/);
var reads = observeReader.reads(key);
var keyParts = reads.map(function(read) {
return read.key;
});
var scopeIndex = keyParts.indexOf("scope");

if (scopeIndex > -1) {
keyParts.splice(scopeIndex, 2);
}

var normalizedKey = keyParts.join(".");

// check scope.vm.<key>
var vm = this.getViewModel();
var vmKeyDefinition = getKeyDefinition(vm, normalizedKey);

if (vmKeyDefinition.isDefined) {
paths["scope.vm." + normalizedKey + (vmKeyDefinition.isFunction ? "()" : "")] = vm;
}

// check scope.top.<key>
var top = this.getTop();
var topKeyDefinition = getKeyDefinition(top, normalizedKey);

if (topKeyDefinition.isDefined) {
paths["scope.top." + normalizedKey + (topKeyDefinition.isFunction ? "()" : "")] = top;
}

// find specific paths (like ../key)
var cur = "";

Expand All @@ -447,8 +482,9 @@ assign(Scope.prototype, {
var canBeRead = !scope._meta.special && !scope._meta.notContext;

if (canBeRead) {
if (typeof scope._context === "object" && canReflect.hasKey(scope._context, normalizedKey)) {
paths[cur + normalizedKey] = scope._context;
var contextKeyDefinition = getKeyDefinition(scope._context, normalizedKey);
if (contextKeyDefinition.isDefined) {
paths[cur + normalizedKey + (contextKeyDefinition.isFunction ? "()" : "")] = scope._context;
}

cur += "../";
Expand All @@ -458,21 +494,8 @@ assign(Scope.prototype, {
return false;
});

// check scope.vm.<key>
var vm = this.getViewModel();

if (vm && canReflect.hasKey(vm, normalizedKey)) {
paths["scope.vm." + normalizedKey] = vm;
}

// check scope.top.<key>
var top = this.getTop();

if (top && canReflect.hasKey(top, normalizedKey)) {
paths["scope.top." + normalizedKey] = top;
}

return paths;
//!steal-remove-end
},

// ## Scope.prototype.hasKey
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"can-simple-map": "^4.0.0",
"can-single-reference": "^1.0.0",
"can-stache-helpers": "^1.0.0",
"can-stache-key": "^1.0.0",
"can-stache-key": "^1.2.0",
"can-symbol": "^1.0.0"
},
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions scope-key-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ var ScopeKeyData = function(scope, key, options){
this.reads = undefined;
this.setRoot = undefined;
this.thisArg = undefined;
this.parentHasKey = undefined;
var valueDependencies = new Set();
valueDependencies.add(observation);
this.dependencies = {valueDependencies: valueDependencies};
Expand Down Expand Up @@ -235,6 +236,7 @@ Object.assign(ScopeKeyData.prototype, {
this.root = data.rootObserve;
this.setRoot = data.setRoot;
this.thisArg = data.thisArg;
this.parentHasKey = data.parentHasKey;
return this.initialValue = data.value;
},
hasDependencies: function(){
Expand Down
61 changes: 60 additions & 1 deletion test/scope-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,6 @@ test("Optimize for compute().observableProperty (#29)", function(){

});


QUnit.ok(scopeKeyData.fastPath, "fast path");

changeNumber++;
Expand Down Expand Up @@ -1310,6 +1309,31 @@ QUnit.test("scope.getPathsForKey", function() {
});
});

QUnit.test("scope.getPathsForKey works for functions", function() {
var top = { name: function() { return "Christopher"; } };
var vm = { name: function() { return "Ryan"; } };
var nonVm = { name: function() { return "Bianca"; } };
var notContext = { index: 0 };
var special = { myIndex: 0 };

var scope = new Scope(top, null, { viewModel: true })
.add(notContext, { notContext: true })
.add(vm, { viewModel: true })
.add(special, { special: true })
.add(true)
.add(nonVm);

var paths = scope.getPathsForKey("name");

QUnit.deepEqual(paths, {
"scope.vm.name()": vm,
"scope.top.name()": top,
"name()": nonVm,
"../../name()": vm,
"../../../name()": top
});
});

QUnit.test("scope.hasKey", function() {
var top = { foo: "bar" };
var vm = { bar: "baz" };
Expand All @@ -1332,3 +1356,38 @@ QUnit.test("scope.hasKey", function() {
QUnit.equal(canReflect.hasKey(scope, "baz"), true, "hasKey baz === true");
QUnit.equal(canReflect.hasKey(scope, "foo"), false, "hasKey foo === false");
});

QUnit.test("read returns correct `parentHasKey` value", function() {
var vm = {};
canReflect.assignSymbols(vm, {
"can.hasKey": function(key) {
return key === "foo";
}
});

var scope = new Scope(vm);

QUnit.ok(scope.read("foo").parentHasKey, "parent has key 'foo'");
QUnit.notOk(scope.read("bar").parentHasKey, "parent does not have key 'bar'");
});

QUnit.test("computeData returns correct `parentHasKey` value", function() {
var vm = {};
canReflect.assignSymbols(vm, {
"can.hasKey": function(key) {
return key === "foo";
}
});

var scope = new Scope(vm);

var fooCompute = scope.computeData("foo");
var barCompute = scope.computeData("bar");

// force a read
fooCompute.read();
barCompute.read();

QUnit.ok(fooCompute.parentHasKey, "parent has key 'foo'");
QUnit.notOk(barCompute.parentHasKey, "parent does not have key 'bar'");
});

0 comments on commit a070fff

Please sign in to comment.