Skip to content

Commit

Permalink
Merge pull request #31 from canjs/minor
Browse files Browse the repository at this point in the history
can-simple-observable 2.1
  • Loading branch information
chasenlehara authored Jun 29, 2018
2 parents 44544ea + 07bcc65 commit 079941f
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 0 deletions.
214 changes: 214 additions & 0 deletions key/key-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
var QUnit = require("steal-qunit");
var keyObservable = require("./key");
var canReflect = require("can-reflect");
var canReflectDeps = require("can-reflect-dependencies");
var SimpleMap = require("can-simple-map");

var onlyDevTest = steal.isEnv("production") ? QUnit.skip : QUnit.test;

QUnit.module("can-simple-observable/key");

QUnit.test("basics", function(assert) {
var outer = {inner: {key: "hello"}};
var observable = keyObservable(outer, "inner.key");

// Unbound and unobserved behavior
assert.equal(canReflect.getValue(observable), "hello", "getValue unbound");

canReflect.setValue(observable, "aloha");
assert.equal(canReflect.getValue(outer).inner.key, "aloha", "value set");
assert.equal(canReflect.getValue(observable), "aloha", "getValue unbound");
});

QUnit.test("get and set Priority", function(assert) {
var outer = {inner: {key: "hello"}};
var observable = keyObservable(outer, "inner.key");

canReflect.setPriority(observable, 5);
assert.equal(canReflect.getPriority(observable), 5, "set priority");
});

onlyDevTest("observable has a helpful name", function() {
var outer = {inner: {key: "hello"}};
var observable = keyObservable(outer, "inner.key");
QUnit.equal(
canReflect.getName(observable),
"keyObservable<Object{}.inner.key>",
"observable has the correct name"
);
});

onlyDevTest("dependency data", function(assert) {
var outer = new SimpleMap({inner: new SimpleMap({key: "hello"})});
var observable = keyObservable(outer, "inner.key");

// The observable must be bound before it returns dependency data
canReflect.onValue(observable, function() {});

// Check the observable’s dependency data
var observableDepData = canReflectDeps.getDependencyDataOf(observable);
assert.deepEqual(
observableDepData,
{
whatChangesMe: {
derive: {
keyDependencies: new Map([
// the observable is changed by outer’s 'inner' property
[outer, new Set(["inner"])],
// the observable is changed by outer.inner’s 'key' property
[outer.get("inner"), new Set(["key"])]
])
}
},
whatIChange: {
mutate: {
keyDependencies: new Map([
// the observable changes outer.inner’s 'key' property
[outer.get("inner"), new Set(["key"])]
])
}
}
},
"the observable has the correct mutation dependencies"
);

// Check outer.inner’s dependency data
var innerDepData = canReflectDeps.getDependencyDataOf(outer, "inner");
assert.deepEqual(
innerDepData,
{
whatIChange: {
derive: {
// outer.inner changes the observable
valueDependencies: new Set([observable])
}
}
},
"outer.inner has the correct mutation dependencies"
);

// Check outer.inner.key’s dependency data
var keyDepData = canReflectDeps.getDependencyDataOf(outer.get("inner"), "key");
assert.deepEqual(
keyDepData,
{
whatChangesMe: {
mutate: {
// outer.inner.key has zero mutate.keyDependencies
keyDependencies: new Map(),
// outer.inner.key is changed by the observable
valueDependencies: new Set([observable])
}
},
whatIChange: {
derive: {
// outer.inner.key changes the observable
valueDependencies: new Set([observable])
}
}
},
"outer.inner.key has the correct mutation dependencies"
);

});

QUnit.test("works when the keys change", function(assert) {
var originalInner = new SimpleMap({key: "hello"});
var outer = new SimpleMap({inner: originalInner});
var observable = keyObservable(outer, "inner.key");

// Test initial value
assert.equal(canReflect.getValue(observable), "hello", "initial value is correct");

// Change the value of a key along the path
var newInner = new SimpleMap({key: "aloha"});
outer.set("inner", newInner);

// Check that the observable has the new value
assert.equal(canReflect.getValue(observable), "aloha", "observable updated");
});

onlyDevTest("works when the keys change - dependency data", function(assert) {
var originalInner = new SimpleMap({key: "hello"});
var outer = new SimpleMap({inner: originalInner});
var observable = keyObservable(outer, "inner.key");

// The observable must be bound before it returns dependency data
canReflect.onValue(observable, function() {});

// Change the value of a key along the path
var newInner = new SimpleMap({key: "aloha"});
outer.set("inner", newInner);

// Check the observable’s dependency data
var observableDepData = canReflectDeps.getDependencyDataOf(observable);
assert.deepEqual(
observableDepData,
{
whatChangesMe: {
derive: {
keyDependencies: new Map([
// the observable is changed by outer’s 'inner' property
[outer, new Set(["inner"])],
// the observable is changed by outer.inner’s 'key' property
[newInner, new Set(["key"])]
])
}
},
whatIChange: {
mutate: {
keyDependencies: new Map([
// the observable changes outer.inner’s 'key' property
[newInner, new Set(["key"])]
])
}
}
},
"the observable has the correct mutation dependencies"
);

// Check outer.inner’s dependency data
var innerDepData = canReflectDeps.getDependencyDataOf(outer, "inner");
assert.deepEqual(
innerDepData,
{
whatIChange: {
derive: {
// outer.inner changes the observable
valueDependencies: new Set([observable])
}
}
},
"outer.inner has the correct mutation dependencies"
);

// Check the original outer.inner.key’s dependency data
var originalKeyDepData = canReflectDeps.getDependencyDataOf(originalInner, "key");
assert.notOk(
originalKeyDepData,
"original outer.inner.key no longer has any dependencies"
);

// Check the new outer.inner.key’s dependency data
var newKeyDepData = canReflectDeps.getDependencyDataOf(newInner, "key");
assert.deepEqual(
newKeyDepData,
{
whatChangesMe: {
mutate: {
// outer.inner.key has zero mutate.keyDependencies
keyDependencies: new Map(),
// outer.inner.key is changed by the observable
valueDependencies: new Set([observable])
}
},
whatIChange: {
derive: {
// outer.inner.key changes the observable
valueDependencies: new Set([observable])
}
}
},
"new outer.inner.key has the correct mutation dependencies"
);
});
78 changes: 78 additions & 0 deletions key/key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
var canKey = require("can-key");
var canKeyUtils = require("can-key/utils");
var canReflect = require("can-reflect");
var Observation = require("can-observation");

//!steal-remove-start
if (process.env.NODE_ENV !== 'production') {
var canReflectDependencies = require("can-reflect-dependencies");
}
//!steal-remove-end

module.exports = function keyObservable(root, keyPath) {
var keyPathParts = canKeyUtils.parts(keyPath);

// Some variables used to build the dependency/mutation graph
//!steal-remove-start
if (process.env.NODE_ENV !== 'production') {
var lastIndex = keyPathParts.length - 1;
var lastKey;// This stores the last part of the keyPath, e.g. “key” in “outer.inner.key”
var lastParent;// This stores the object that the last key is on, e.g. “outer.inner” in outer: {inner: {"key": "value"}}
}
//!steal-remove-end

var observation = new Observation(function() {
var value;

// This needs to be walked every time because the objects along the key path might change
canKey.walk(root, keyPathParts, function(keyData, i) {
if (i === lastIndex) {
//!steal-remove-start
if (process.env.NODE_ENV !== 'production') {
// observation is mutating keyData.parent
if (lastParent && (keyData.key !== lastKey || keyData.parent !== lastParent)) {
canReflectDependencies.deleteMutatedBy(lastParent, lastKey, observation);
}
lastKey = keyData.key;
lastParent = keyData.parent;
canReflectDependencies.addMutatedBy(lastParent, lastKey, observation);
}
//!steal-remove-end

value = keyData.value;
}
});

return value;
});

var symbolsToAssign = {
"can.setValue": function(newVal) {
canKey.set(root, keyPathParts, newVal);
}
};

//!steal-remove-start
if (process.env.NODE_ENV !== 'production') {

// Debug name
symbolsToAssign["can.getName"] = function getName() {
var objectName = canReflect.getName(root);
return "keyObservable<" + objectName + "." + keyPath + ">";
};

// Register what this observable changes
symbolsToAssign["can.getWhatIChange"] = function getWhatIChange() {
return {
mutate: {
keyDependencies: new Map([
[lastParent, new Set([lastKey])]
])
}
};
};
}
//!steal-remove-end

return canReflect.assignSymbols(observation, symbolsToAssign);
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,19 @@
},
"dependencies": {
"can-event-queue": "^1.0.0",
"can-key": "<2.0.0",
"can-key-tree": "^1.0.0",
"can-log": "^1.0.0",
"can-namespace": "1.0.0",
"can-observation": "^4.0.0",
"can-observation-recorder": "^1.0.0",
"can-queues": "^1.0.0",
"can-reflect": "^1.10.1",
"can-reflect-dependencies": "^1.0.0",
"can-symbol": "^1.4.2"
},
"devDependencies": {
"can-simple-map": "^4.0.1",
"detect-cyclic-packages": "^1.1.0",
"jshint": "^2.9.1",
"steal": "^1.3.1",
Expand Down
1 change: 1 addition & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ require("./can-simple-observable-test");
require("./settable/settable-test");
require("./async/async-test");
require("./setter/setter-test");
require("./key/key-test");
require("./make-compute/make-compute-test");
require("./resolver/resolver-test");

0 comments on commit 079941f

Please sign in to comment.