Skip to content

Commit

Permalink
Support numeric keys on objects (#124)
Browse files Browse the repository at this point in the history
* Add check in setValue() to only change key to index if target is an Array

* Fix bug when creating new objects when path specifies indices

* Linting fixes

* Update tests

* Update version

* Update changelog
  • Loading branch information
davestewart authored Dec 17, 2020
1 parent 5ee7a07 commit 454d1ee
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 123 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [1.4.5] - 2020-12-17
### Fixed
- Setting values on objects using numeric keys is now supported
- Setting deep properties using numeric keys now correctly creates arrays

## [1.4.4] - 2020-12-02
### Fixed
- Fix Payload type - #101 / @VesterDe
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vuex-pathify",
"version": "1.4.4",
"version": "1.4.5",
"description": "Ridiculously simple Vuex setup + wiring",
"main": "dist/vuex-pathify.js",
"module": "dist/vuex-pathify.esm.js",
Expand All @@ -14,7 +14,7 @@
"dev": "rollup -c build/rollup.js -w",
"build": "rollup -c build/rollup.js",
"docs": "node-sass docs/assets/scss -o docs/assets/css -w docs/assets/scss/**/*.scss & docsify serve docs",
"test": "jest tests/",
"test": "jest tests/ --verbose",
"test:types": "tsc -p types/test"
},
"author": "Dave Stewart <[email protected]>",
Expand Down
1 change: 0 additions & 1 deletion src/helpers/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export function call (path, props) {
})
}


// -------------------------------------------------------------------------------------------------------------------
// utility
// -------------------------------------------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @param {object} [options] Optional Vuex module registration options
* @returns {object} The mixin
*/
export function registerModule(path, module, callback, options) {
export function registerModule (path, module, callback, options) {
return {
beforeCreate () {
this.$store.registerModule(path, module, options)
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function makeActions (state) {
.reduce(function (obj, key) {
const action = resolveName('actions', key)
const mutation = resolveName('mutations', key)
obj[action] = function ({commit}, value) {
obj[action] = function ({ commit }, value) {
commit(mutation, value)
}
return obj
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/vuex.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ const vuex = {
}
}

export function commit(...args) {
export function commit (...args) {
vuex.store.commit(...args)
}

export function dispatch(...args) {
export function dispatch (...args) {
return vuex.store.dispatch(...args)
}

Expand Down
8 changes: 4 additions & 4 deletions src/services/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const resolvers = {
* @returns {string}
*/
standard (type, name, formatters) {
switch(type) {
switch (type) {
case 'mutations':
return formatters.const('set', name) // SET_BAR
case 'actions':
Expand Down Expand Up @@ -165,19 +165,19 @@ export function resolve (store, path) {
/**
* Error generation function for accessors
*/
export function getError(path, resolver, aName, a, bName, b) {
export function getError (path, resolver, aName, a, bName, b) {
let error = `[Vuex Pathify] Unable to map path '${path}':`
if (path.indexOf('!') > -1) {
error += `
- Did not find ${aName} or ${bName} named '${resolver.name}' on ${resolver.module ? `module '${resolver.module}'`: 'root store'}`
- Did not find ${aName} or ${bName} named '${resolver.name}' on ${resolver.module ? `module '${resolver.module}'` : 'root store'}`
}
else {
const aText = a
? `${aName} '${a.trgName}' or `
: ''
const bText = `${bName} '${b.trgName}'`
error += `
- Did not find ${aText}${bText} on ${resolver.module ? `module '${resolver.module}'`: 'store'}
- Did not find ${aText}${bText} on ${resolver.module ? `module '${resolver.module}'` : 'store'}
- Use direct syntax '${resolver.target.replace(/(@|$)/, '!$1')}' (if member exists) to target directly`
}
return error
Expand Down
2 changes: 1 addition & 1 deletion src/services/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export function makeGetter (store, path, stateOnly) {
* @param {string} path The full dot-path on the source object
* @returns {*}
*/
function getValueIfEnabled(expr, source, path) {
function getValueIfEnabled (expr, source, path) {
if (!options.deep && expr.indexOf('@') > -1) {
console.error(`[Vuex Pathify] Unable to access sub-property for path '${expr}':
- Set option 'deep' to 1 to allow it`)
Expand Down
4 changes: 1 addition & 3 deletions src/services/wildcards.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getValue} from '../utils/object'
import { getValue } from '../utils/object'

// -------------------------------------------------------------------------------------------------------------------
// external
Expand Down Expand Up @@ -50,7 +50,6 @@ export function expandCall (path, actions) {
return resolveHandlers(path, actions)
}


// -------------------------------------------------------------------------------------------------------------------
// internal
// -------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -102,7 +101,6 @@ export function resolveHandlers (path, hash) {
return Object.keys(hash).filter(key => rx.test(key))
}


// -------------------------------------------------------------------------------------------------------------------
// utility
// -------------------------------------------------------------------------------------------------------------------
Expand Down
46 changes: 35 additions & 11 deletions src/utils/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,24 @@ export function isObject (value) {
return !!value && typeof value === 'object'
}

/**
* Tests whether a string is numeric
*
* @param {string|number} value The value to be assessed
* @returns {boolean}
*/
export function isNumeric (value) {
return typeof value === 'number' || /^\d+$/.test(value)
}

/**
* Tests whether a passed value is an Object and has the specified key
*
* @param {Object} obj The source object
* @param {string} key The key to check that exists
* @returns {boolean} Whether the predicate is satisfied
*/
export function hasKey(obj, key) {
export function hasKey (obj, key) {
return isObject(obj) && key in obj
}

Expand Down Expand Up @@ -83,25 +93,38 @@ export function getValue (obj, path) {
*/
export function setValue (obj, path, value, create = false) {
const keys = getKeys(path)
return keys.reduce((obj, key, index) => {
const isIndex = /^\d+$/.test(key)
if (isIndex) {
key = parseInt(key)
}
return keys.reduce((obj, key, index) => {
// early return if no object
if (!obj) {
return false
}
else if (index === keys.length - 1) {

// convert key to index if obj is an array and key is numeric
if (Array.isArray(obj) && isNumeric(key)) {
key = parseInt(key)
}

// if we're at the end of the path, set the value
if (index === keys.length - 1) {
obj[key] = value
return true
}

// if the target property doesn't exist...
else if (!isObject(obj[key]) || !(key in obj)) {
// ...create one, or cancel
if (create) {
obj[key] = isIndex ? [] : {}
} else {
// create object or array, depending on next key
obj[key] = isNumeric(keys[index + 1])
? []
: {}
}
else {
return false
}
}

// if we get here, return the target property
return obj[key]
}, obj)
}
Expand All @@ -113,14 +136,15 @@ export function setValue (obj, path, value, create = false) {
* @param {string|Array|Object} path The path to a sub-property
* @returns {boolean} Boolean true or false
*/
export function hasValue(obj, path) {
export function hasValue (obj, path) {
let keys = getKeys(path)
if (isObject(obj)) {
while (keys.length) {
let key = keys.shift()
if (hasKey(obj, key)) {
obj = obj[key]
} else {
}
else {
return false
}
}
Expand Down
16 changes: 16 additions & 0 deletions tests/helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Vue from 'vue';
import Vuex from 'vuex'
import { make } from '../../src/main'
import pathify from './pathify'

Vue.use(Vuex)

export function makeStore (store) {
if (!store.mutations) {
store.mutations = make.mutations(store.state)
}
return new Vuex.Store({
plugins: [pathify.plugin],
...store
})
}
8 changes: 8 additions & 0 deletions tests/helpers/pathify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pathify from '../../src/main'

// options
pathify.options.mapping = 'simple'
pathify.options.deep = 2

// export
export default pathify
Loading

0 comments on commit 454d1ee

Please sign in to comment.