From 87be85698d31f19874c94e8f9655eed37d129e4a Mon Sep 17 00:00:00 2001 From: Edward Andrew Robinson Date: Tue, 9 Apr 2019 16:14:35 -0400 Subject: [PATCH] Adds templateOpts access via strings (#43) * Adds templateOpts access via strings (#2) * Allow basic templateOpts access * Added a test for arrays * Added in better support for templateOpts and Arrays * removed the skip * Added in more tests for objects * handle * bumped package version * updated the repo to reflect new location --- CHANGELOG.md | 10 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 2 +- LICENSE | 2 +- README.md | 8 +- lib/jyson.js | 29 +- package-lock.json | 2 +- package.json | 10 +- spec/lib/jyson/jyson.templateOpts.spec.js | 321 ++++++++++++++++++++++ 9 files changed, 360 insertions(+), 26 deletions(-) create mode 100644 spec/lib/jyson/jyson.templateOpts.spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a3ba6fb..3bc6506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,7 +89,7 @@ jyson.buildTemplateFunction({ 'd': new jyson.Value({ path: 'd' }) }); ``` -- Closes [Issue #10](https://github.com/hubba/jyson/issues/10) +- Closes [Issue #10](https://github.com/earobinson/jyson/issues/10) ## v3.0.1 - Bug fixes @@ -108,19 +108,19 @@ jyson.buildTemplateFunction({ }] }); ``` -- Closes [Issue #22](https://github.com/hubba/jyson/issues/22) +- Closes [Issue #22](https://github.com/earobinson/jyson/issues/22) ## v2.0.0 - Jyson no longer throws an error to handle arrays it reads ahead to determine array length - A Jyson template can now access two different arrays -- Closes [Issue #15](https://github.com/hubba/jyson/issues/15) +- Closes [Issue #15](https://github.com/earobinson/jyson/issues/15) ## v1.3.0 - Made jyson less dependent on lodash -- Closes [Issue #13](https://github.com/hubba/jyson/issues/13) +- Closes [Issue #13](https://github.com/earobinson/jyson/issues/13) ## v1.2.1 - Fixed a bug when if an array was not provide when it was in the template jyson would crash ## v1.2.0 -- jyson now supports an array of objects Issue [Issue #9](https://github.com/hubba/jyson/issues/9) +- jyson now supports an array of objects Issue [Issue #9](https://github.com/earobinson/jyson/issues/9) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d1433b1..fa2b45e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behaviour may be -reported by contacting the project team at [oss@hubba.com]. All +reported by contacting the project team at [os@earobinson.net]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4899fa8..813944e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ process easy and effective for everyone involved. ## Using the issue tracker -[GitHub Issues](https://github.com/hubba/jyson/issues) is the preferred channel +[GitHub Issues](https://github.com/earobinson/jyson/issues) is the preferred channel for [bug reports](#bug-reports), [features requests](#feature-requests) and [submitting pull requests](#pull-requests). diff --git a/LICENSE b/LICENSE index a26a6b2..2d5dedc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Hubba +Copyright (c) 2017 Edward Andrew Robinson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index b4112c9..aad4265 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ This package lets you generate fast json templates for your apis. It lets you quickly build powerful api templates. [![NPM Version](https://img.shields.io/npm/v/jyson.svg)](https://www.npmjs.com/package/jyson) -[![Travis Build Status](https://img.shields.io/travis/hubba/jyson.svg)](https://travis-ci.org/hubba/jyson) -[![Coveralls](https://img.shields.io/coveralls/github/hubba/jyson.svg)](https://coveralls.io/github/hubba/jyson) +[![Travis Build Status](https://img.shields.io/travis/earobinson/jyson.svg)](https://travis-ci.org/earobinson/jyson) +[![Coveralls](https://img.shields.io/coveralls/github/earobinson/jyson.svg)](https://coveralls.io/github/earobinson/jyson) [![NPM Downloads](https://img.shields.io/npm/dm/jyson.svg)](https://www.npmjs.com/package/jyson) ## Install @@ -14,7 +14,7 @@ npm install jyson --save ## Usage -jyson can create many different types of templates, for a full list of examples check out the [example tests](https://github.com/hubba/jyson/blob/master/spec/lib/jyson/jyson.example.spec.js). +jyson can create many different types of templates, for a full list of examples check out the [example tests](https://github.com/earobinson/jyson/blob/master/spec/lib/jyson/jyson.example.spec.js). ```js const jyson = require('jyson'); @@ -73,4 +73,4 @@ for opening issues, coding standards, and notes on development. *** -Built with ❤️ at [Hubba](https://www.hubba.com?utm_campaign=hubba_oss). +Built with ❤️ at [earobinson](https://www.earobinson.com?utm_campaign=earobinson_oss). diff --git a/lib/jyson.js b/lib/jyson.js index 6f2899a..dee4748 100644 --- a/lib/jyson.js +++ b/lib/jyson.js @@ -16,9 +16,22 @@ const getKeyFromArrayIndexes = (key, arrayIndexes) => { return keyFromArrayIndexes; }; -const getKey = (object, key, undefinedValue, arrayIndexes) => { - const jysonValuePath = JysonValue.getPath(key); +const getKey = (object, key, templateOpts, undefinedValue, arrayIndexes) => { + let jysonValuePath = JysonValue.getPath(key); const jysonValueUndefinedValue = JysonValue.getUndefinedValue(key, undefinedValue); + + if(jysonValuePath[0] === '#') { + const indexRegex = /\[([^)]+)\]/; + + jysonValuePath = jysonValuePath.substring(1); + const match = indexRegex.exec(jysonValuePath); + if (match) { + const indexValue = getKey(object, match[1], templateOpts, undefinedValue, arrayIndexes); + jysonValuePath = jysonValuePath.replace(indexRegex, `.${indexValue}`); + } + + return get(templateOpts, getKeyFromArrayIndexes(jysonValuePath, arrayIndexes), jysonValueUndefinedValue); + } return get(object, getKeyFromArrayIndexes(jysonValuePath, arrayIndexes), jysonValueUndefinedValue); }; @@ -30,18 +43,18 @@ const setValue = (json, key, value) => { return json[key] = value; }; -const getArrayValueLength = (object, json, arrayIndexes, arrayKey) => { +const getArrayValueLength = (object, json, arrayIndexes, arrayKey, templateOpts) => { if(JysonValue.isAPath(json)) { const jysonValuePath = JysonValue.getPath(json); const arrayLocation = getKeyFromArrayIndexes(jysonValuePath, arrayIndexes).split('.$').shift(); - return get(object, `${arrayLocation}.length`, -1); + return getKey(object, `${arrayLocation}.length`, templateOpts, -1, arrayIndexes); } if(typeof json === 'object') { return Object.keys(json).reduce((maxLength, key) => { - return Math.max(maxLength, getArrayValueLength(object, json[key], arrayIndexes, arrayKey)); + return Math.max(maxLength, getArrayValueLength(object, json[key], arrayIndexes, arrayKey, templateOpts)); }, -1); } @@ -49,7 +62,7 @@ const getArrayValueLength = (object, json, arrayIndexes, arrayKey) => { }; const getKeyAndSetValue = (jsonResult, key, path, object, templateOpts, opts) => { - const value = getKey(object, path, opts.undefinedValue, opts.arrayIndexes); + const value = getKey(object, path, templateOpts, opts.undefinedValue, opts.arrayIndexes); // If we encounter an array without a $ in jyson, we consider that an error assert.ok(!Array.isArray(value) || path.indexOf('$') !== -1, `jyson encountered an array when it was not expecting one: ${path}`); @@ -72,7 +85,7 @@ const fillKeys = (json, object, templateOpts, opts) => { assert.ok(json[key].length === 1, `jyson template arrays must be of length one at key: ${key}`); const jysonArrayValue = JysonArray.getValue(json[key]); - const arrayValueLength = getArrayValueLength(object, jysonArrayValue, opts.arrayIndexes, key); + const arrayValueLength = getArrayValueLength(object, jysonArrayValue, opts.arrayIndexes, key, templateOpts); const arrayIndexesIndex = opts.arrayIndexes.length; const arrayValue = []; @@ -89,7 +102,7 @@ const fillKeys = (json, object, templateOpts, opts) => { let result; if(JysonValue.isAPath(jysonArrayValue)) { - result = setValue(jsonResult, key, getKey(object, jysonArrayValue, opts.undefinedValue, opts.arrayIndexes)); + result = setValue(jsonResult, key, getKey(object, jysonArrayValue, templateOpts, opts.undefinedValue, opts.arrayIndexes)); } else { result = fillKeys(jysonArrayValue, object, templateOpts, opts); } diff --git a/package-lock.json b/package-lock.json index 71f0b4f..b05b3bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "jyson", - "version": "4.0.0", + "version": "4.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4575293..ca60197 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jyson", - "version": "4.0.1", + "version": "4.1.0", "description": "A template engine for json.", "main": "index.js", "scripts": { @@ -12,18 +12,18 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/hubba/jyson.git" + "url": "git+https://github.com/earobinson/jyson.git" }, "keywords": [ "json", "template" ], - "author": "Edward Andrew Robinson ", + "author": "Edward Andrew Robinson ", "license": "MIT", "bugs": { - "url": "https://github.com/hubba/jyson/issues" + "url": "https://github.com/earobinson/jyson/issues" }, - "homepage": "https://github.com/hubba/jyson#readme", + "homepage": "https://github.com/earobinson/jyson#readme", "devDependencies": { "@ear/eslint-config": "^1.2.0", "coveralls": "^3.0.2", diff --git a/spec/lib/jyson/jyson.templateOpts.spec.js b/spec/lib/jyson/jyson.templateOpts.spec.js new file mode 100644 index 0000000..eba5ac8 --- /dev/null +++ b/spec/lib/jyson/jyson.templateOpts.spec.js @@ -0,0 +1,321 @@ +const jyson = require('../../../lib/jyson'); + +describe('jyson.templateOpts.spec: access templateOpts via #', () => { + describe('simple templateOpts', () => { + beforeEach(() => { + this.templateFunction = jyson.buildTemplateFunction({ + a: 'a', + b: '#b' + }); + }); + + it('must convert an object to "json" with templateOpts', () => { + const input = { + a: 'a', + }; + const templateOpts = { + b: 'b', + }; + const json = this.templateFunction(input, templateOpts); + + expect(json.a).toEqual('a'); + expect(json.b).toEqual('b'); + }); + }); + + describe('templateOpts accessed by an object paramter', () => { + beforeEach(() => { + this.templateFunction = jyson.buildTemplateFunction({ + a: 'a', + b: '#b[bIndex]' + }); + }); + + it('must convert an object to "json" with templateOpts', () => { + const input = { + a: 'a', + bIndex: 1 + }; + const templateOpts = { + b: ['b', 'bb', 'bbb'], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json.a).toEqual('a'); + expect(json.b).toEqual('bb'); + }); + + it('must handle an arrayed input', () => { + const input = [{ + a: 'a', + bIndex: 0 + }, { + a: 'b', + bIndex: 1 + }, { + a: 'c', + bIndex: 2 + }]; + const templateOpts = { + b: ['b', 'bb', 'bbb'], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json).toEqual([{ + a: 'a', + b: 'b' + }, { + a: 'b', + b: 'bb' + }, { + a: 'c', + b: 'bbb' + }]); + }); + }); + + describe('templateOpts that are deeply nested', () => { + beforeEach(() => { + this.templateFunction = jyson.buildTemplateFunction({ + a: 'a', + b: '#b[b.c.d]' + }); + }); + + it('must convert an object to "json" with templateOpts', () => { + const input = { + a: 'a', + b: { + c: { + d: 1 + } + } + }; + const templateOpts = { + b: ['b', 'bb', 'bbb'], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json.a).toEqual('a'); + expect(json.b).toEqual('bb'); + }); + + it('must handle an arrayed input', () => { + const input = [{ + a: 'a', + b: { + c: { + d: 0 + } + } + }, { + a: 'b', + b: { + c: { + d: 1 + } + } + }, { + a: 'c', + b: { + c: { + d: 2 + } + } + }]; + const templateOpts = { + b: ['b', 'bb', 'bbb'], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json).toEqual([{ + a: 'a', + b: 'b' + }, { + a: 'b', + b: 'bb' + }, { + a: 'c', + b: 'bbb' + }]); + }); + }); + + describe('templateOpts via array', () => { + describe('templateOpts arrays defined by strings', () => { + beforeEach(() => { + this.templateFunction = jyson.buildTemplateFunction({ + a: 'a', + b: ['#b[bIndex].$'] + }); + }); + + it('must convert an object to "json" with templateOpts', () => { + const input = { + a: 'a', + bIndex: 1 + }; + const templateOpts = { + b: [['b0'], ['bb0', 'bb1'], ['bbb0', 'bbb1', 'bbb2']], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json.a).toEqual('a'); + expect(json.b[0]).toBe('bb0'); + expect(json.b[1]).toBe('bb1'); + }); + + it('must handle an arrayed input', () => { + const input = [{ + a: 'a', + bIndex: 0 + }, { + a: 'b', + bIndex: 1 + }, { + a: 'c', + bIndex: 2 + }]; + const templateOpts = { + b: [['b0'], ['bb0', 'bb1'], ['bbb0', 'bbb1', 'bbb2']], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json).toEqual([{ + a: 'a', + b: ['b0'] + }, { + a: 'b', + b: ['bb0', 'bb1'] + }, { + a: 'c', + b: ['bbb0', 'bbb1', 'bbb2'] + }]); + }); + }); + + describe('templateOpts arrays defined by objects', () => { + beforeEach(() => { + this.templateFunction = jyson.buildTemplateFunction({ + a: 'a', + b: [{ + b: '#b[bIndex].$' + }] + }); + }); + + it('must convert an object to "json" with templateOpts', () => { + const input = { + a: 'a', + bIndex: 1 + }; + const templateOpts = { + b: [['b0'], ['bb0', 'bb1'], ['bbb0', 'bbb1', 'bbb2']], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json.a).toBe('a'); + expect(json.b[0].b).toBe('bb0'); + expect(json.b[1].b).toBe('bb1'); + }); + + it('must handle an arrayed input', () => { + const input = [{ + a: 'a', + bIndex: 0 + }, { + a: 'b', + bIndex: 1 + }, { + a: 'c', + bIndex: 2 + }]; + const templateOpts = { + b: [['b0'], ['bb0', 'bb1'], ['bbb0', 'bbb1', 'bbb2']], + }; + const json = this.templateFunction(input, templateOpts); + + expect(json).toEqual([{ + a: 'a', + b: [{ b: 'b0' }] + }, { + a: 'b', + b: [{ b: 'bb0' }, { b: 'bb1' }] + }, { + a: 'c', + b: [{ b: 'bbb0' }, { b: 'bbb1' }, { b: 'bbb2' }] + }]); + }); + }); + }); + + describe('templateOpts via objects', () => { + beforeEach(() => { + this.templateFunction = jyson.buildTemplateFunction({ + a: 'a', + id: 'id', + b: '#b[id].b' + }); + }); + + it('must convert an object to "json" with templateOpts', () => { + const input = { + a: 'a', + id: '679cd501-09bc-4acd-ae5a-1521352d49fa' + }; + const templateOpts = { + b: { + '679cd501-09bc-4acd-ae5a-1521352d49fa': { + b: 'b0' + } + } + }; + const json = this.templateFunction(input, templateOpts); + + expect(json.a).toEqual('a'); + expect(json.id).toEqual('679cd501-09bc-4acd-ae5a-1521352d49fa'); + expect(json.b).toEqual('b0'); + }); + + it('must handle an arrayed input', () => { + const input = [{ + a: 'a', + id: '2f14d15f-b468-4af9-964e-005d9b7c8296' + }, { + a: 'b', + id: '81ee981c-d29d-4ec3-accd-4b0392f126c9' + }, { + a: 'c', + id: '124ca089-a961-4307-8678-4ac7049b4b30' + }]; + const templateOpts = { + b: { + '2f14d15f-b468-4af9-964e-005d9b7c8296': { + b: 'b0' + }, + '81ee981c-d29d-4ec3-accd-4b0392f126c9': { + b: 'b1' + }, + '124ca089-a961-4307-8678-4ac7049b4b30': { + b: 'b2' + } + } + }; + const json = this.templateFunction(input, templateOpts); + + expect(json).toEqual([{ + a: 'a', + id: '2f14d15f-b468-4af9-964e-005d9b7c8296', + b: 'b0' + }, { + a: 'b', + id: '81ee981c-d29d-4ec3-accd-4b0392f126c9', + b: 'b1' + }, { + a: 'c', + id: '124ca089-a961-4307-8678-4ac7049b4b30', + b: 'b2' + }]); + }); + }); +});