diff --git a/can-stache-key-test.js b/can-stache-key-test.js index 389e137..9217f72 100644 --- a/can-stache-key-test.js +++ b/can-stache-key-test.js @@ -1,24 +1,19 @@ var observeReader = require("can-stache-key"); var QUnit = require('steal-qunit'); var Observation = require('can-observation'); -var canEvent = require('can-event'); +var eventQueue = require('can-event-queue/map/map'); +var SimpleObservable = require("can-simple-observable"); var testHelpers = require('can-test-helpers'); +var ObservationRecorder = require("can-observation-recorder"); -var assign = require("can-util/js/assign/assign"); -var eventAsync = require("can-event/async/async"); var SimpleMap = require("can-simple-map"); var canReflect = require("can-reflect"); -QUnit.module('can-observation/reader',{ - setup: function(){ - eventAsync.sync(); - }, - teardown: function(){ - eventAsync.async(); - } +QUnit.module('can-stache-key',{ + }); -test("can.Compute.read can read a promise (#179)", function(){ +test("can read a promise (#179)", function(){ var data = { promise: new Promise(function(resolve){ setTimeout(function(){ @@ -29,16 +24,14 @@ test("can.Compute.read can read a promise (#179)", function(){ var calls = 0; var c = new Observation(function(){ return observeReader.read(data,observeReader.reads("promise.value")).value; - }, null, { - updater: function(newVal, oldVal){ - calls++; - equal(calls, 1, "only one call"); - equal(newVal, "Something", "new value"); - equal(oldVal, undefined, "oldVal"); - start(); - } }); - c.start(); + canReflect.onValue(c, function(newVal, oldVal){ + calls++; + equal(calls, 1, "only one call"); + equal(newVal, "Something", "new value"); + equal(oldVal, undefined, "oldVal"); + start(); + }); stop(); @@ -57,16 +50,14 @@ test("can.Compute.read can read a promise-like (#82)", function(){ var calls = 0; var c = new Observation(function(){ return observeReader.read(data,observeReader.reads("promiseLike.value")).value; - }, null, { - updater: function(newVal, oldVal){ - calls++; - equal(calls, 1, "only one call"); - equal(newVal, "Something", "new value"); - equal(oldVal, undefined, "oldVal"); - start(); - } }); - c.start(); + canReflect.onValue(c, function(newVal, oldVal){ + calls++; + equal(calls, 1, "only one call"); + equal(newVal, "Something", "new value"); + equal(oldVal, undefined, "oldVal"); + start(); + }); stop(); @@ -88,11 +79,11 @@ test('can.compute.reads', function(){ }); test('able to read things like can-define', 3, function(){ - var obj = assign({}, canEvent); + var obj = eventQueue({}); var prop = "PROP"; Object.defineProperty(obj, "prop",{ get: function(){ - Observation.add(obj,"prop"); + ObservationRecorder.add(obj,"prop"); return prop; }, set: function(val){ @@ -113,41 +104,38 @@ test('able to read things like can-define', 3, function(){ } }).value; equal(value, "PROP"); - }, null, { - updater: function(){ - - } }); - c.start(); - - + canReflect.onValue(c, function(){}); }); test("foundObservable called with observable object (#7)", function(){ - var map = { + var map = new SimpleMap({ isSaving: function(){ - Observation.add(this, "_saving"); + ObservationRecorder.add(this, "_saving"); }, addEventListener: function(){} - }; + }); // must use an observation to make sure things are listening. var c = new Observation(function(){ observeReader.read(map,observeReader.reads("isSaving"),{ foundObservable: function(obs){ QUnit.equal(obs, map); - } + }, + callMethodsOnObservables: true }); - }, null,{}); - c.start(); - + }); + canReflect.onValue(c, function(){}); }); test("can read from strings", function(){ var context = " hi there "; - var result = observeReader.read(context,observeReader.reads("trim"),{}); - QUnit.ok(result, context.trim); + QUnit.equal( + result.value(context), + context.trim(context), + 'trim method works' + ); }); test("read / write to DefineMap", function(){ @@ -159,10 +147,10 @@ test("read / write to DefineMap", function(){ } }); return data.value; - }, null,function(newVal){ + }); + canReflect.onValue(c, function(newVal){ QUnit.equal(newVal, 1, "got updated"); }); - c.start(); observeReader.write(map,"value",1); }); @@ -233,63 +221,42 @@ test("it returns null when promise getter is null #2", function(){ QUnit.equal(typeof nullPromise,"object"); }); -testHelpers.dev.devOnlyTest("a warning is displayed when functions are called by read()", function() { - var teardown = testHelpers.dev.willWarn(/"func" is being called as a function/); - var func = function() { - QUnit.ok(true, "method called"); - }; - var data = { func: func }; - var reads = observeReader.reads("func"); - - observeReader.read(data, reads, { - warnOnFunctionCall: "A Warning" - }); - - QUnit.equal(teardown(), 1, "warning displayed"); -}); +QUnit.test("set onto observable objects and values", function(){ + var map = new SimpleMap(); + observeReader.write({map: map},"map", {a: "b"}); -testHelpers.dev.devOnlyTest("a warning is displayed when methods on observables are called by read()", function() { - var teardown = testHelpers.dev.willWarn(/"func" is being called as a function/); - var func = function() { - QUnit.ok(true, "method called"); - }; - var data = new SimpleMap({ func: func }); - var reads = observeReader.reads("func"); + QUnit.equal(map.get("a"), "b", "merged"); - observeReader.read(data, reads, { - callMethodsOnObservables: true - }); - QUnit.equal(teardown(), 1, "warning displayed"); + var simple = new SimpleObservable(); + observeReader.write({simple: simple},"simple", 1); + QUnit.equal(simple.get(), 1); }); -testHelpers.dev.devOnlyTest("a warning is not displayed when functions are read but not called", function() { - var teardown = testHelpers.dev.willWarn(/"func" is being called as a function/); +testHelpers.dev.devOnlyTest("functions are not called by read()", function() { var func = function() { QUnit.ok(false, "method called"); }; - var data = new SimpleMap({ func: func }); - var reads = observeReader.reads("@func"); + var data = { func: func }; + var reads = observeReader.reads("func"); - observeReader.read(data, reads, { - callMethodsOnObservables: true - }); + observeReader.read(data, reads); - QUnit.equal(teardown(), 0, "warning not displayed"); + QUnit.ok(true); }); -testHelpers.dev.devOnlyTest("a warning is not displayed when functions are read but not called due to proxyMethods=false (#15)", function() { - var teardown = testHelpers.dev.willWarn(/"func" is being called as a function/); +testHelpers.dev.devOnlyTest("a warning is given for `callMethodsOnObservables: true`", function() { + var teardown = testHelpers.dev.willWarn("can-stache-key: read() called with `callMethodsOnObservables: true`."); var func = function() { - QUnit.ok(false, "method not called"); + QUnit.ok(true, "method called"); }; var data = new SimpleMap({ func: func }); var reads = observeReader.reads("func"); observeReader.read(data, reads, { - isArgument: true, - proxyMethods: false + callMethodsOnObservables: true }); - QUnit.equal(teardown(), 0, "warning not displayed"); + QUnit.equal(teardown(), 1, "warning displayed"); }); + diff --git a/can-stache-key.js b/can-stache-key.js index 1322938..c46b216 100644 --- a/can-stache-key.js +++ b/can-stache-key.js @@ -1,4 +1,4 @@ -var Observation = require('can-observation'); +var ObservationRecorder = require('can-observation-recorder'); var dev = require('can-log/dev/dev'); var each = require('can-util/js/each/each'); var canSymbol = require("can-symbol"); @@ -10,7 +10,20 @@ var getValueSymbol = canSymbol.for("can.getValue"); var setValueSymbol = canSymbol.for("can.setValue"); var isValueLikeSymbol = canSymbol.for("can.isValueLike"); +var peek = ObservationRecorder.ignore(canReflect.getKeyValue.bind(canReflect)); var observeReader; + +var bindName = Function.prototype.bind; +//!steal-remove-start +bindName = function(source){ + var fn = Function.prototype.bind.call(this, source); + Object.defineProperty(fn, "name", { + value: canReflect.getName(source) + "."+canReflect.getName(this) + }); + return fn; +}; +//!steal-remove-end + var isAt = function(index, reads) { var prevRead = reads[index-1]; return prevRead && prevRead.at; @@ -37,8 +50,8 @@ var specialRead = {index: true, key: true, event: true, element: true, viewModel var checkForObservableAndNotify = function(options, state, getObserves, value, index){ if(options.foundObservable && !state.foundObservable) { - if(Observation.trapsCount()) { - Observation.addAll( getObserves() ); + if(ObservationRecorder.trapsCount()) { + ObservationRecorder.addMany( getObserves() ); options.foundObservable(value, index); state.foundObservable = true; } @@ -49,8 +62,6 @@ observeReader = { // there are things that you need to evaluate when you get them back as a property read // for example a compute or a function you might need to call to get the next value to // actually check - // - isArgument - should be renamed to something like "onLastPropertyReadReturnFunctionInsteadOfCallingIt". - // This is used to make a compute out of that function if necessary. // - readCompute - can be set to `false` to prevent reading an ending compute. This is used by component to get a // compute as a delegate. In 3.0, this should be removed and force people to write "{@prop} change" // - callMethodsOnObservables - this is an overwrite ... so normal methods won't be called, but observable ones will. @@ -68,7 +79,7 @@ observeReader = { }; var getObserves; if(options.foundObservable) { - getObserves = Observation.trap(); + getObserves = ObservationRecorder.trap(); } // `cur` is the current value. @@ -145,47 +156,13 @@ observeReader = { return value && canReflect.isFunctionLike(value) && !canReflect.isConstructorLike(value); }, read: function(value, i, reads, options, state, prev){ - if( isAt(i, reads) ) { - return i === reads.length ? value.bind(prev) : value; - } - - //!steal-remove-start - var showWarning = function() { - dev.warn( - (options.filename ? options.filename + ':' : '') + - (options.lineNumber ? options.lineNumber + ': ' : '') + - '"' + reads[0].key + '" is being called as a function.\n' + - '\tThis will not happen automatically in an upcoming release.\n' + - '\tYou should call it explicitly using "' + reads[0].key + '()".\n\n' - ); - }; - //!steal-remove-end - - if(options.callMethodsOnObservables && canReflect.isObservableLike(prev) && canReflect.isMapLike(prev)) { - //!steal-remove-start - showWarning(); - //!steal-remove-end + dev.warn("can-stache-key: read() called with `callMethodsOnObservables: true`."); return value.apply(prev, options.args || []); } - else if ( options.isArgument && i === reads.length ) { - if (options.proxyMethods === false) { - return value; - } - - //!steal-remove-start - showWarning(); - //!steal-remove-end - - return value.bind(prev); - } - //!steal-remove-start - showWarning(); - //!steal-remove-end - - return value.apply(prev, options.args || []); + return options.proxyMethods !== false ? bindName.call(value, prev) : value; } }, { @@ -263,7 +240,17 @@ observeReader = { } }, write: function(base, prop, newVal){ - base[prop] = newVal; + var propValue = base[prop]; + // if newVal is observable object, lets try to update + if(canReflect.isMapLike(propValue) && newVal && typeof newVal === "object") { + dev.warn("can-stache-key: Merging data into \"" + prop + "\" because its parent is non-observable"); + canReflect.update(propValue, newVal); + } else if(canReflect.isValueLike(propValue) && canReflect.isObservableLike(propValue)){ + canReflect.setValue(propValue, newVal); + } else { + base[prop] = newVal; + } + } } ], @@ -315,11 +302,12 @@ observeReader = { } else { last = keys[0]; } + var keyValue = peek(parent, last.key); // here's where we need to figure out the best way to write // if property being set points at a compute, set the compute - if( observeReader.valueReadersMap.isValueLike.test(parent[last.key], keys.length - 1, keys, options) ) { - observeReader.valueReadersMap.isValueLike.write(parent[last.key], value, options); + if( observeReader.valueReadersMap.isValueLike.test(keyValue, keys.length - 1, keys, options) ) { + observeReader.valueReadersMap.isValueLike.write(keyValue, value, options); } else { if(observeReader.valueReadersMap.isValueLike.test(parent, keys.length - 1, keys, options) ) { parent = parent[getValueSymbol](); diff --git a/package.json b/package.json index e3dea0d..d5dd362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "can-stache-key", - "version": "0.1.4", + "version": "1.0.0-pre.17", "description": "Read and write keys on a value", "homepage": "https://canjs.com", "repository": { @@ -13,12 +13,12 @@ "url": "http://donejs.com" }, "scripts": { - "preversion": "npm test && npm run build", - "version": "git commit -am \"Update version number\" && git checkout -b release && git add -f dist/", - "postpublish": "git push --tags && git checkout master && git branch -D release && git push", + "preversion": "npm test", + "postpublish": "git push --tags && git push", "testee": "testee test.html --browsers firefox", "test": "npm run detect-cycle && npm run jshint && npm run testee", "jshint": "jshint ./*.js --config", + "release:pre": "npm version prerelease && npm publish --tag pre", "release:patch": "npm version patch && npm publish", "release:minor": "npm version minor && npm publish", "release:major": "npm version major && npm publish", @@ -26,23 +26,10 @@ "develop": "done-serve --static --develop --port 8080", "detect-cycle": "detect-cyclic-packages --ignore done-serve" }, - "main": "dist/cjs/can-stache-key", - "browser": { - "transform": [ - "cssify" - ] - }, - "browserify": { - "transform": [ - "cssify" - ] - }, + "main": "can-stache-key", "keywords": [ - "Done", - "JS", - "Can", - "JS", - "donejs-plugin" + "DoneJS", + "CanJS" ], "steal": { "main": "can-stache-key", @@ -54,25 +41,22 @@ "generator-donejs", "donejs-cli", "steal-tools" - ], - "plugins": [ - "steal-less", - "steal-stache" ] }, "dependencies": { - "can-cid": "^1.0.0", - "can-event": "^3.5.0", "can-log": "^1.0.0", "can-namespace": "1.0.0", - "can-observation": "^3.3.0", + "can-observation-recorder": "^1.0.0", "can-reflect": "^1.1.0", - "can-reflect-promise": "^1.1.0", + "can-reflect-promise": "^2.0.0", "can-symbol": "^1.0.0", "can-util": "^3.9.0" }, "devDependencies": { - "can-simple-map": "^3.3.0", + "can-observation": "^4.0.0-pre.2", + "can-event-queue": "<2.0.0", + "can-simple-map": "^4.0.0", + "can-simple-observable": "^2.0.0-pre.11", "can-test-helpers": "^1.1.1", "detect-cyclic-packages": "^1.1.0", "jshint": "^2.9.1",