Skip to content

Commit

Permalink
feat: allow objects in 'set' instead of keypaths
Browse files Browse the repository at this point in the history
- shallow merge
- works with observer
- works with computed properties
  • Loading branch information
kbrsh committed Aug 29, 2017
1 parent dc2596f commit d81083c
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 115 deletions.
89 changes: 34 additions & 55 deletions dist/moon.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@
var getter = option.get;
var setter = option.set;

// Flush Cache if Dependencies Change
observer.observe(prop);

// Add Getters
Object.defineProperty(instance.data, prop, {
get: function() {
Expand Down Expand Up @@ -105,43 +102,32 @@
// Computed Property Cache
this.cache = {};

// Set of events to clear cache when dependencies change
this.clear = {};

// Property Currently Being Observed for Dependencies
this.target = undefined;

// Dependency Map
this.map = {};
}

Observer.prototype.observe = function(key) {
var self = this;
this.clear[key] = function() {
self.cache[key] = undefined;
}
}

Observer.prototype.notify = function(key) {
var self = this;
var this$1 = this;

var depMap = this.map[key];
if(depMap !== undefined) {
for(var i = 0; i < depMap.length; i++) {
self.notify(depMap[i]);
var map = this.map[key];
if(map !== undefined) {
for(var i = 0; i < map.length; i++) {
this$1.notify(map[i]);
}
}

var clear = this.clear[key];
if(clear !== undefined) {
clear();
var cache = this.cache;
if(cache[key] !== undefined) {
cache[key] = undefined;
}
}


/* ======= Global Utilities ======= */

var hashRE = /\[(\w+)\]/g;
var escapeRE = /(?:(?:&(?:lt|gt|quot|amp);)|"|\\|\n)/g;
var escapeMap = {
"&lt;": "<",
Expand Down Expand Up @@ -188,26 +174,6 @@
}
}

/**
* Resolves an Object Keypath and Sets it
* @param {Object} instance
* @param {Object} obj
* @param {String} keypath
* @param {String} val
* @return {Object} resolved object
*/
var resolveKeyPath = function(instance, obj, keypath, val) {
keypath = keypath.replace(hashRE, ".$1");
var path = keypath.split('.');
var i = 0;
for(; i < path.length - 1; i++) {
var propName = path[i];
obj = obj[propName];
}
obj[path[i]] = val;
return path[0];
}

/**
* Calls a Hook
* @param {Object} instance
Expand Down Expand Up @@ -1737,37 +1703,50 @@
Moon.prototype.get = function(key) {
// Collect dependencies if currently collecting
var observer = this.observer;
var map = observer.map;
var target = observer.target;

if(target !== undefined) {
if(observer.map[key] === undefined) {
observer.map[key] = [target];
} else if(observer.map[key].indexOf(target) === -1) {
observer.map[key].push(target);
if(map[key] === undefined) {
map[key] = [target];
} else if(map[key].indexOf(target) === -1) {
map[key].push(target);
}
}

// Return value found
if("development" !== "production" && !(key in this.data)) {
// Return value
if("development" !== "production" && this.data.hasOwnProperty(key) === false) {
error(("The item \"" + key + "\" was not defined but was referenced"));
}
return this.data[key];
}

/**
* Sets Value in Data
* @param {String} key
* @param {Any} val
* @param {String|Object} key
* @param {Any} value
*/
Moon.prototype.set = function(key, val) {
Moon.prototype.set = function(key, value) {
// Get observer
var observer = this.observer;

// Get base of keypath
var base = resolveKeyPath(this, this.data, key, val);
if(typeof key === "object") {
// Shallow merge
var data = this.data;
for(var prop in key) {
// Set value
data[prop] = key[prop];

// Notify observer of change
observer.notify(base);
// Notify observer of change
observer.notify(prop);
}
} else {
// Set value
this.data[key] = value;

// Notify observer of change
observer.notify(key);
}

// Queue a build
queueBuild(this);
Expand Down
2 changes: 1 addition & 1 deletion dist/moon.min.js

Large diffs are not rendered by default.

39 changes: 26 additions & 13 deletions src/instance/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,50 @@
Moon.prototype.get = function(key) {
// Collect dependencies if currently collecting
const observer = this.observer;
let map = observer.map;
let target = observer.target;

if(target !== undefined) {
if(observer.map[key] === undefined) {
observer.map[key] = [target];
} else if(observer.map[key].indexOf(target) === -1) {
observer.map[key].push(target);
if(map[key] === undefined) {
map[key] = [target];
} else if(map[key].indexOf(target) === -1) {
map[key].push(target);
}
}

// Return value found
if("__ENV__" !== "production" && !(key in this.data)) {
// Return value
if("__ENV__" !== "production" && this.data.hasOwnProperty(key) === false) {
error(`The item "${key}" was not defined but was referenced`);
}
return this.data[key];
}

/**
* Sets Value in Data
* @param {String} key
* @param {Any} val
* @param {String|Object} key
* @param {Any} value
*/
Moon.prototype.set = function(key, val) {
Moon.prototype.set = function(key, value) {
// Get observer
const observer = this.observer;

// Get base of keypath
const base = resolveKeyPath(this, this.data, key, val);
if(typeof key === "object") {
// Shallow merge
let data = this.data;
for(let prop in key) {
// Set value
data[prop] = key[prop];

// Notify observer of change
observer.notify(base);
// Notify observer of change
observer.notify(prop);
}
} else {
// Set value
this.data[key] = value;

// Notify observer of change
observer.notify(key);
}

// Queue a build
queueBuild(this);
Expand Down
3 changes: 0 additions & 3 deletions src/observer/computed.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ const initComputed = function(instance, computed) {
const getter = option.get;
const setter = option.set;

// Flush Cache if Dependencies Change
observer.observe(prop);

// Add Getters
Object.defineProperty(instance.data, prop, {
get: function() {
Expand Down
26 changes: 7 additions & 19 deletions src/observer/observer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,23 @@ function Observer(instance) {
// Computed Property Cache
this.cache = {};

// Set of events to clear cache when dependencies change
this.clear = {};

// Property Currently Being Observed for Dependencies
this.target = undefined;

// Dependency Map
this.map = {};
}

Observer.prototype.observe = function(key) {
const self = this;
this.clear[key] = function() {
self.cache[key] = undefined;
}
}

Observer.prototype.notify = function(key) {
const self = this;

let depMap = this.map[key];
if(depMap !== undefined) {
for(let i = 0; i < depMap.length; i++) {
self.notify(depMap[i]);
let map = this.map[key];
if(map !== undefined) {
for(let i = 0; i < map.length; i++) {
this.notify(map[i]);
}
}

let clear = this.clear[key];
if(clear !== undefined) {
clear();
let cache = this.cache;
if(cache[key] !== undefined) {
cache[key] = undefined;
}
}
21 changes: 0 additions & 21 deletions src/util/util.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* ======= Global Utilities ======= */

const hashRE = /\[(\w+)\]/g;
const escapeRE = /(?:(?:&(?:lt|gt|quot|amp);)|"|\\|\n)/g;
const escapeMap = {
"&lt;": "<",
Expand Down Expand Up @@ -47,26 +46,6 @@ const queueBuild = function(instance) {
}
}

/**
* Resolves an Object Keypath and Sets it
* @param {Object} instance
* @param {Object} obj
* @param {String} keypath
* @param {String} val
* @return {Object} resolved object
*/
const resolveKeyPath = function(instance, obj, keypath, val) {
keypath = keypath.replace(hashRE, ".$1");
const path = keypath.split('.');
let i = 0;
for(; i < path.length - 1; i++) {
const propName = path[i];
obj = obj[propName];
}
obj[path[i]] = val;
return path[0];
}

/**
* Calls a Hook
* @param {Object} instance
Expand Down
10 changes: 7 additions & 3 deletions test/core/instance/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,18 @@ describe('Data', function() {
});

it('when setting new property', function() {
dataApp3.set('msg.obj.nested', 'Nested Value');
var newMsg = dataApp3.get("msg");
newMsg.obj.nested = "Nested Value";
dataApp3.set("msg", newMsg);
return wait(function() {
expect(data3.innerHTML).to.equal("Nested Value");
});
});

it('when setting new data property', function() {
dataApp3.set("msg.obj.nested", "New Nested");
it('when updating new data property', function() {
var newMsg = dataApp3.get("msg");
newMsg.obj.nested = "New Nested";
dataApp3.set("msg", newMsg);
return wait(function() {
expect(data3.innerHTML).to.equal("New Nested");
});
Expand Down

0 comments on commit d81083c

Please sign in to comment.