Skip to content

Commit

Permalink
feat: add get / set utility and simple compile method
Browse files Browse the repository at this point in the history
  • Loading branch information
ala-n committed Jan 18, 2021
1 parent 5729f30 commit 8283bfa
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 10 deletions.
10 changes: 10 additions & 0 deletions src/modules/esl-utils/misc/format.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {get} from './object';

/** Convert string to kebab-case notation */
export const toKebabCase = (str: string) => {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[\s_]+/g, '-').toLowerCase();
Expand Down Expand Up @@ -35,3 +37,11 @@ export function evaluate(str: string, defaultValue?: any): any {
return defaultValue;
}
}

/** Replace '{key}' pattens in the string from the source object */
export function compile(str: string, source: Record<string, any>) {
return str.replace(/{([\w.]+)}/, (match, key) => {
const val = get(source, key);
return val === undefined ? match : val;
});
}
36 changes: 36 additions & 0 deletions src/modules/esl-utils/misc/object.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export const isObject = (obj: any): obj is Record<string, any> => obj && typeof obj === 'object';
export const isObjectLike = (obj: any) => isObject(obj) || typeof obj === 'function';

/** Deep object compare */
export function deepCompare(obj1: any, obj2: any): boolean {
if (Object.is(obj1, obj2)) return true;
Expand Down Expand Up @@ -29,3 +32,36 @@ export function defined<T>(...params: T[]) {
if (param !== undefined) return param;
}
}

/**
* Set object property using "path" key
*
* @param target - object
* @param path - key path, use '.' as delimiter
* @param value - value of property
*/
export const set = (target: any, path: string, value: any) => {
const parts = (path || '').split('.');
const depth = parts.length - 1;
parts.reduce((cur: any, key: string, index: number) => {
if (index === depth) return cur[key] = value;
return cur[key] = isObjectLike(cur[key]) ? cur[key] : {};
}, target);
};

/**
* Gets object property using "path" key
* Creates empty object if sub-key value is not presented.
*
* @param data - object
* @param path - key path, use '.' as delimiter
* @param defaultValue - default
*/
export const get = (data: any, path: string, defaultValue?: any): any => {
const parts = (path || '').split('.');
const result = parts.reduce((curr: any, key: string) => {
if (isObjectLike(curr)) return curr[key];
return undefined;
}, data);
return typeof result === 'undefined' ? defaultValue : result;
};
12 changes: 11 additions & 1 deletion src/modules/esl-utils/misc/test/format.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {toCamelCase, toKebabCase, unwrapParenthesis, parseAspectRatio, evaluate} from '../format';
import {toCamelCase, toKebabCase, unwrapParenthesis, parseAspectRatio, evaluate, compile} from '../format';

describe('misc/format helper tests', () => {
test('toKebabCase', () => {
Expand Down Expand Up @@ -59,4 +59,14 @@ describe('misc/format helper tests', () => {
expect(throwError).toBe(true);
expect(evaluate('{', 'no')).toBe('no');
});
describe('compile', () => {
test.each([
['abc', {}, 'abc'],
['abc{val}', {val: 'd'}, 'abcd'],
['{a.b.c}', {a: {b: {c: 'hi'}}}, 'hi'],
['{a}', {}, '{a}'],
])('\'%s\' using %p to \'%s\'', (tmp: string, source: any, res: string) => {
expect(compile(tmp, source)).toBe(res);
});
});
});
50 changes: 41 additions & 9 deletions src/modules/esl-utils/misc/test/object.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {defined, deepCompare, getPropertyDescriptor} from '../object';
import {defined, deepCompare, getPropertyDescriptor, get, set} from '../object';

describe('misc/object', () => {
describe('deepCompare', () => {
Expand Down Expand Up @@ -83,13 +83,45 @@ describe('misc/object', () => {
});
});

test('defined', () => {
expect(defined('a')).toBe('a');
expect(defined('', 'a')).toBe('');
expect(defined('a', '')).toBe('a');
expect(defined(undefined, 'a')).toBe('a');
expect(defined(null, 'a')).toBe(null);
const obj = {};
expect(defined(obj, null)).toBe(obj);
describe('access utils', () => {
test('defined', () => {
expect(defined('a')).toBe('a');
expect(defined('', 'a')).toBe('');
expect(defined('a', '')).toBe('a');
expect(defined(undefined, 'a')).toBe('a');
expect(defined(null, 'a')).toBe(null);
const obj = {};
expect(defined(obj, null)).toBe(obj);
});
});

describe('get', () => {
test.each([
['', {a: 1}, undefined],
['a', undefined, undefined],
['a', null, undefined],
['a', 'a', undefined],
['a', {}, undefined],
['a', {a: 1}, 1],
['a.b', {a: 1}, undefined],
['a.b', {a: {b: 2}}, 2],
['a.b.c', {a: {b: {c: {}}}}, {}],
['a.b.d', {a: {b: {c: {}}}}, undefined]
])('get key "%s" from %p', (key: string, source: any, expVal: any) => {
expect(get(source, key)).toEqual(expVal)
});
});

describe('set', () => {
test.each([
[{}, 'a', 1, {a: 1}],
[{}, 'a.b', 1, {a: {b: 1}}],
[{c: 1}, 'a.b', 1, {a: {b: 1}, c: 1}],
[{a: {c: 1}}, 'a.b', 1, {a: {b: 1, c: 1}}],
[{a: 1}, 'a.b', 1, {a: {b: 1}}]
])('get key "%s" from %p', (targ: any, key: string, val: any, expVal: any) => {
set(targ, key, val);
expect(targ).toEqual(expVal)
});
});
});

0 comments on commit 8283bfa

Please sign in to comment.