Skip to content

Commit

Permalink
adding support for alternate path notation
Browse files Browse the repository at this point in the history
  • Loading branch information
bhoriuchi committed Jan 3, 2018
1 parent 1360559 commit 5b75bb8
Show file tree
Hide file tree
Showing 9 changed files with 1,927 additions and 60 deletions.
61 changes: 44 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,20 @@ function forEach(obj, fn) {
* @returns {*}
*/
function get(obj, path, defaultValue) {
var fields = Array.isArray(path) ? path : toPath(path);
var idx = 0;
var length = fields.length;

while (obj !== null && idx < length) {
obj = obj[fields[idx++]];
try {
var o = obj;
var fields = Array.isArray(path) ? path : toPath(path);
while (fields.length) {
var prop = fields.shift();
o = o[prop];
if (!fields.length) {
return o;
}
}
} catch (err) {
return defaultValue;
}

return idx && idx === length ? obj : defaultValue;
return defaultValue;
}

/**
Expand Down Expand Up @@ -188,7 +193,7 @@ var toConsumableArray = function (arr) {
};

var INVALID_KEY_RX = /^\d|[^a-zA-Z0-9_]/gm;

var INT_KEY_RX = /^\d+$/;
/**
* returns true if object is non empty object
* @param obj
Expand All @@ -213,6 +218,20 @@ function pathJoin(base, path) {
}
}

/**
* simple helper to push paths and determine if more
* paths need to be constructed
* @param {*} obj
* @param {*} current
* @param {*} paths
*/
function pushPaths(obj, current, paths) {
paths.push(current);
if (isHash(obj) || Array.isArray(obj)) {
getPaths(obj, current, paths);
}
}

/**
* generates an array of paths to use when creating an abstracted object
* @param obj
Expand All @@ -226,16 +245,23 @@ function getPaths(obj) {

if (isHash(obj)) {
forEach(obj, function (val, key) {
var k = key.match(INVALID_KEY_RX) ? '["' + key + '"]' : '.' + key;
var cur = ('' + current + k).replace(/^\./, '');
paths.push(cur);
if (isHash(val) || Array.isArray(val)) getPaths(val, cur, paths);
if (key.match(INT_KEY_RX) !== null) {
// is index
pushPaths(val, (current + '.' + key).replace(/^\./, ''), paths);
pushPaths(val, (current + '[' + key + ']').replace(/^\./, ''), paths);
pushPaths(val, (current + '["' + key + '"]').replace(/^\./, ''), paths);
} else if (key.match(INVALID_KEY_RX) !== null) {
// must quote
pushPaths(val, (current + '["' + key + '"]').replace(/^\./, ''), paths);
} else {
pushPaths(val, (current + '.' + key).replace(/^\./, ''), paths);
}
});
} else if (Array.isArray(obj)) {
forEach(obj, function (val, idx) {
var cur = current + '[' + idx + ']';
paths.push(cur);
if (isHash(val) || Array.isArray(val)) getPaths(val, cur, paths);
pushPaths(val, (current + '.' + idx).replace(/^\./, ''), paths);
pushPaths(val, (current + '[' + idx + ']').replace(/^\./, ''), paths);
pushPaths(val, (current + '["' + idx + '"]').replace(/^\./, ''), paths);
});
}
return [].concat(toConsumableArray(new Set(paths)));
Expand Down Expand Up @@ -305,7 +331,8 @@ function buildVuexModel(vuexPath, options) {
var model = {};

var obj = get(this.$store.state, vuexPath);
forEach(getPaths(obj), function (path) {
var paths = getPaths(obj);
forEach(paths, function (path) {
var propPath = pathJoin(vuexPath, path);
Object.defineProperty(model, path, {
configurable: true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-deepset",
"version": "0.5.2",
"version": "0.5.3",
"description": "Deep set Vue.js objects",
"main": "index.js",
"scripts": {
Expand Down
37 changes: 23 additions & 14 deletions src/liteutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ export function toPath (pathString) {
let pathArray = []

pathString.replace(pathRx, (match, number, quote, string) => {
pathArray.push(quote ? string : (number !== undefined) ? Number(number) : match)
pathArray.push(
quote
? string
: (number !== undefined)
? Number(number)
: match
)
return pathArray[pathArray.length - 1]
})
return pathArray
Expand Down Expand Up @@ -50,19 +56,22 @@ export function forEach (obj, fn) {
* @returns {*}
*/
export function get (obj, path, defaultValue) {
let fields = Array.isArray(path)
? path
: toPath(path)
let idx = 0
const length = fields.length

while (obj !== null && idx < length) {
obj = obj[fields[idx++]]
try {
let o = obj
const fields = Array.isArray(path)
? path
: toPath(path)
while (fields.length) {
const prop = fields.shift()
o = o[prop]
if (!fields.length) {
return o
}
}
} catch (err) {
return defaultValue
}

return (idx && idx === length)
? obj
: defaultValue
return defaultValue
}

/**
Expand All @@ -88,4 +97,4 @@ export function hasPath(object, path) {
}

return result
}
}
37 changes: 28 additions & 9 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as _ from './liteutils'
const INVALID_KEY_RX = /^\d|[^a-zA-Z0-9_]/gm

const INT_KEY_RX = /^\d+$/
/**
* returns true if object is non empty object
* @param obj
Expand Down Expand Up @@ -28,6 +28,20 @@ export function pathJoin (base, path) {
}
}

/**
* simple helper to push paths and determine if more
* paths need to be constructed
* @param {*} obj
* @param {*} current
* @param {*} paths
*/
export function pushPaths (obj, current, paths) {
paths.push(current)
if (isHash(obj) || Array.isArray(obj)) {
getPaths(obj, current, paths)
}
}

/**
* generates an array of paths to use when creating an abstracted object
* @param obj
Expand All @@ -38,17 +52,22 @@ export function pathJoin (base, path) {
export function getPaths (obj, current = '', paths = []) {
if (isHash(obj)) {
_.forEach(obj, (val, key) => {
let k = key.match(INVALID_KEY_RX) ? `["${key}"]` : `.${key}`
let cur = `${current}${k}`.replace(/^\./, '')
paths.push(cur)
if (isHash(val) || Array.isArray(val)) getPaths(val, cur, paths)
if (key.match(INT_KEY_RX) !== null) { // is index
pushPaths(val, `${current}.${key}`.replace(/^\./, ''), paths)
pushPaths(val, `${current}[${key}]`.replace(/^\./, ''), paths)
pushPaths(val, `${current}["${key}"]`.replace(/^\./, ''), paths)
} else if (key.match(INVALID_KEY_RX) !== null) { // must quote
pushPaths(val, `${current}["${key}"]`.replace(/^\./, ''), paths)
} else {
pushPaths(val, `${current}.${key}`.replace(/^\./, ''), paths)
}
})
} else if (Array.isArray(obj)) {
_.forEach(obj, (val, idx) => {
let cur = `${current}[${idx}]`
paths.push(cur)
if (isHash(val) || Array.isArray(val)) getPaths(val, cur, paths)
pushPaths(val, `${current}.${idx}`.replace(/^\./, ''), paths)
pushPaths(val, `${current}[${idx}]`.replace(/^\./, ''), paths)
pushPaths(val, `${current}["${idx}"]`.replace(/^\./, ''), paths)
})
}
return [ ...new Set(paths) ]
}
}
3 changes: 2 additions & 1 deletion src/vue-deepset.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ function buildVuexModel (vuexPath, options) {
let model = {}

let obj = _.get(this.$store.state, vuexPath)
_.forEach(getPaths(obj), path => {
const paths = getPaths(obj)
_.forEach(paths, path => {
let propPath = pathJoin(vuexPath, path)
Object.defineProperty(model, path, {
configurable: true,
Expand Down
51 changes: 51 additions & 0 deletions test/issue8.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Reproduce issue 8</title>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
<script src="../vue-deepset.js"></script>
</head>
<body>
<div id="app">
<div>
<template v-for="(element, idx) in order['elements']">
<input :key='idx' v-model.number="order[`elements[&quot;${idx}&quot;].val`]" /><br/>
</template>
</div>
<pre v-text="$store.state.order"></pre>
</div>

<script>
Vue.use(VueDeepSet)
var store = new Vuex.Store({
strict: true,
state: {
order: {
elements: {
1:{
val: 1
},
2:{
val: 2
}
}
}
},
mutations: VueDeepSet.extendMutation({})
})
var app = new Vue({
el: '#app',
store,
computed: {
order() {
return this.$deepModel('order')
}
}
})
</script>

</body>
</html>
61 changes: 44 additions & 17 deletions vue-deepset.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,20 @@ function forEach(obj, fn) {
* @returns {*}
*/
function get(obj, path, defaultValue) {
var fields = Array.isArray(path) ? path : toPath(path);
var idx = 0;
var length = fields.length;

while (obj !== null && idx < length) {
obj = obj[fields[idx++]];
try {
var o = obj;
var fields = Array.isArray(path) ? path : toPath(path);
while (fields.length) {
var prop = fields.shift();
o = o[prop];
if (!fields.length) {
return o;
}
}
} catch (err) {
return defaultValue;
}

return idx && idx === length ? obj : defaultValue;
return defaultValue;
}

/**
Expand Down Expand Up @@ -189,7 +194,7 @@ var toConsumableArray = function (arr) {
};

var INVALID_KEY_RX = /^\d|[^a-zA-Z0-9_]/gm;

var INT_KEY_RX = /^\d+$/;
/**
* returns true if object is non empty object
* @param obj
Expand All @@ -214,6 +219,20 @@ function pathJoin(base, path) {
}
}

/**
* simple helper to push paths and determine if more
* paths need to be constructed
* @param {*} obj
* @param {*} current
* @param {*} paths
*/
function pushPaths(obj, current, paths) {
paths.push(current);
if (isHash(obj) || Array.isArray(obj)) {
getPaths(obj, current, paths);
}
}

/**
* generates an array of paths to use when creating an abstracted object
* @param obj
Expand All @@ -227,16 +246,23 @@ function getPaths(obj) {

if (isHash(obj)) {
forEach(obj, function (val, key) {
var k = key.match(INVALID_KEY_RX) ? '["' + key + '"]' : '.' + key;
var cur = ('' + current + k).replace(/^\./, '');
paths.push(cur);
if (isHash(val) || Array.isArray(val)) getPaths(val, cur, paths);
if (key.match(INT_KEY_RX) !== null) {
// is index
pushPaths(val, (current + '.' + key).replace(/^\./, ''), paths);
pushPaths(val, (current + '[' + key + ']').replace(/^\./, ''), paths);
pushPaths(val, (current + '["' + key + '"]').replace(/^\./, ''), paths);
} else if (key.match(INVALID_KEY_RX) !== null) {
// must quote
pushPaths(val, (current + '["' + key + '"]').replace(/^\./, ''), paths);
} else {
pushPaths(val, (current + '.' + key).replace(/^\./, ''), paths);
}
});
} else if (Array.isArray(obj)) {
forEach(obj, function (val, idx) {
var cur = current + '[' + idx + ']';
paths.push(cur);
if (isHash(val) || Array.isArray(val)) getPaths(val, cur, paths);
pushPaths(val, (current + '.' + idx).replace(/^\./, ''), paths);
pushPaths(val, (current + '[' + idx + ']').replace(/^\./, ''), paths);
pushPaths(val, (current + '["' + idx + '"]').replace(/^\./, ''), paths);
});
}
return [].concat(toConsumableArray(new Set(paths)));
Expand Down Expand Up @@ -306,7 +332,8 @@ function buildVuexModel(vuexPath, options) {
var model = {};

var obj = get(this.$store.state, vuexPath);
forEach(getPaths(obj), function (path) {
var paths = getPaths(obj);
forEach(paths, function (path) {
var propPath = pathJoin(vuexPath, path);
Object.defineProperty(model, path, {
configurable: true,
Expand Down
2 changes: 1 addition & 1 deletion vue-deepset.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit 5b75bb8

Please sign in to comment.