├── .gitignore
├── test
├── 05_tpl-insert-template-library.test.js
├── 15_errors.test.js
├── 13_tools.test.js
├── 02_tpl-find-placeholders.test.js
├── 03_tpl-interpret-template.test.js
├── 07_process-insert.test.js
├── 01_tpl-chop.test.js
├── 06_process-validation.test.js
├── 04_tpl-insert-template.test.js
├── 09_process-tools.test.js
├── 10_hook-modifiers.test.js
├── 14_data.test.js
├── 11_code-assembly.test.js
├── 08_process-operations.test.js
└── 12_run.test.js
├── src
├── general-tools.js
├── template-chop.js
├── errors.js
├── template-tools.js
├── process-tools.js
├── process-operations.js
└── index.js
├── dist
├── index.html
└── code-assembly-line.min.js
├── webpack.config.js
├── LICENSE
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .npmignore
3 | .vscode
4 | coverage/**/*
5 | package-lock.json
6 | node_modules
7 | types/
8 | concept.md
9 | PLAY-test-samples.md
--------------------------------------------------------------------------------
/test/05_tpl-insert-template-library.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , codeAssemblyLine = require ( '../src/index' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 |
11 | describe ( 'Insert a template Library', () => {
12 |
13 |
14 |
15 | }) // Describe Add template
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/15_errors.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , CodeAssemblyLine = require ( '../src/index' )
7 | , showError = require ( '../src/errors' )
8 | ;
9 |
10 |
11 |
12 | describe ( 'Errors', () => {
13 |
14 | it ( 'Call a non-existing error', () => {
15 | const result = showError ( 'Fake')
16 | expect ( result ).to.be.equal ( 'Not defined error.' )
17 | }) // it call a non-existing error
18 |
19 | }) // describe Tools
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/general-tools.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const tools = {
4 |
5 | renameTemplate : function ( extLib, mod ) { // (externalTemplate, mod{}) -> externalTemplate
6 | // * Change-name tool for externalTemplate object
7 | // mod - { existingTemplateName: newTemplateName }
8 | const
9 | updateKeys = Object.keys ( mod )
10 | , existingKeys = Object.keys ( extLib )
11 | ;
12 |
13 | return existingKeys.reduce ( (res,key) => {
14 | if ( updateKeys.includes(key) ) res [ mod[key]] = extLib[key]
15 | else res [ key ] = extLib[key]
16 | return res
17 | },{})
18 | } // modifyTemplate func.
19 |
20 | } // tools
21 |
22 | module.exports = tools
--------------------------------------------------------------------------------
/test/13_tools.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , CodeAssemblyLine = require ( '../src/index' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 |
11 |
12 | describe ( 'Tools', () => {
13 |
14 | it ( 'Rename templates', () => {
15 | const
16 | tplEngine = new CodeAssemblyLine()
17 | , tplLib = {
18 | 'random' : 'Some text with {{place}}.'
19 | , 'hi' : 'Hi {{user}}'
20 | }
21 | ;
22 |
23 | const result = tplEngine.tools.renameTemplate ( tplLib, { 'hi':'hello'} )
24 |
25 | expect ( result ).to.have.property('hello')
26 | expect ( result ).to.not.have.property('hi')
27 | }) // it modify template
28 |
29 | }) // describe Tools
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Code Assembly Line
8 |
9 |
10 |
11 | Code Assembly Line
12 |
13 |
28 |
29 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const
2 | path = require('path')
3 | , webpack = require('webpack')
4 | , webpackAnalyzer = require ('webpack-bundle-analyzer').BundleAnalyzerPlugin
5 | ;
6 |
7 | const
8 | PROD = (process.env.NODE_ENV === 'production') ? true : false
9 | , uglifyOptions = {
10 | minimize : true
11 | , mangle : true
12 | }
13 | ;
14 |
15 |
16 |
17 | module.exports = {
18 | entry: './src/index.js',
19 | output: {
20 | path: path.resolve(__dirname, 'dist')
21 | , filename: 'code-assembly-line.min.js'
22 | , library: 'CodeAssemblyLine'
23 | , libraryTarget : 'umd'
24 | , umdNamedDefine : true
25 | },
26 | module: {
27 | loaders: [
28 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/, query: { presets: ['env'] } }
29 | ],
30 | },
31 | plugins: PROD ? [
32 | new webpack.optimize.UglifyJsPlugin( uglifyOptions )
33 | ]
34 | : []
35 | };
36 |
37 |
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Peter Naydenov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code-assembly-line",
3 | "version": "1.0.0",
4 | "description": "Javascript template engine. Works server and client side",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "NODE_ENV='test' mocha test",
8 | "cover": "istanbul cover _mocha",
9 | "build": "webpack",
10 | "prod": "NODE_ENV='production' webpack"
11 | },
12 | "keywords": [
13 | "template",
14 | "engine",
15 | "js",
16 | "render"
17 | ],
18 | "repository" : {
19 | "type" : "git"
20 | , "url" : "https://github.com/PeterNaydenov/code-assembly-line.git"
21 | },
22 | "author": "Peter Naydenov",
23 | "license": "MIT",
24 | "devDependencies": {
25 | "babel-core": "^6.26.0",
26 | "babel-loader": "^7.1.2",
27 | "babel-preset-env": "^1.6.1",
28 | "chai": "^4.1.2",
29 | "istanbul": "^0.4.5",
30 | "mocha": "^4.0.1",
31 | "webpack": "^3.10.0",
32 | "webpack-bundle-analyzer": "^2.9.1"
33 | },
34 | "dependencies": {}
35 | }
36 |
--------------------------------------------------------------------------------
/src/template-chop.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const showError = require ('./errors');
4 |
5 | /*
6 | Converts string template to internal template format
7 | ( text: string ) -> string[]
8 |
9 | */
10 |
11 |
12 |
13 | function chopTemplate ( text ) {
14 |
15 | let
16 | start // placeholder start
17 | , end // placeholder end
18 | , checkPoint // check
19 | , res = [] // template result array
20 | ;
21 |
22 | if ( typeof(text) != 'string' ) return [ showError('brokenTemplate') ]
23 | if ( text.length == 0 ) return []
24 |
25 | start = text.indexOf ( '{{' )
26 | if ( 0 < start ) res.push ( text.slice(0, start) )
27 | if ( -1 == start ) {
28 | res.push( text )
29 | return res;
30 | }
31 | else {
32 | checkPoint = text.indexOf ( '{{', start+2 )
33 | end = text.indexOf("}}")
34 |
35 | if ( end == -1 ) return [ showError('brokenTemplate') ] // Placeholder with missing closing tags
36 | if ( end < start ) return [ showError('brokenTemplate') ] // Placeholder closing tags without starting ones
37 | else end += 2
38 |
39 | if ( checkPoint != -1 && checkPoint < end ) {
40 | // New placeholder before first ends
41 | return [ showError('brokenTemplate') ]
42 | }
43 |
44 | res.push( text.slice(start,end) )
45 | let nextText = text.slice (end)
46 | let additional = chopTemplate ( nextText )
47 | return res.concat ( additional )
48 | }
49 | } // chopTemplate func.
50 |
51 |
52 |
53 | module.exports = chopTemplate;
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/errors.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // * Error Messages
4 |
5 |
6 | const msg = {
7 | 'notDefined' : 'Not defined error.'
8 | // Template errors
9 | , 'brokenTemplate' : 'Broken string template. Placeholder with missing closing tag.'
10 | , 'wrongDataTemplate' : 'Broken data template. Object is not according the standards.'
11 | , 'overwriteTemplate' : 'Error: Overwrite of existing template is not permited by configuration.'
12 | , 'missingTemplate' : `Error: Template "%s" doesn't exist.`
13 |
14 | // Process errors
15 | , 'wrongExtProcess' : 'Wrong process data. Should be an array of "processSteps".'
16 | , 'emptyExtProcess' : 'Empty process! Process should contain steps(processSteps)'
17 | , 'brokenProcess' : 'Broken process description.'
18 | , 'missingOperation' : 'Process has step with missing operation. Set parameter "do".'
19 | , 'overwriteProcess' : 'Error: Process with name "%s" is already defined.'
20 | , 'notaValidOperation' : `Error: "%s" is not a valid operation`
21 |
22 | , 'blockExpectString' : `Block expects string data. %s`
23 | , 'dataExpectObject' : `Data operations require objects. %s`
24 |
25 |
26 | // Data errors
27 | , 'overwriteData' : 'Error: Data with name "%s" is already defined.'
28 |
29 | // Validation errors
30 | , 'processNotExists' : 'Error: Process "%s" does not exist.'
31 | , 'templateNotExists' : 'Error: Template "%s" is not available'
32 | , 'invalidStorageName' : 'Error: Process-step "save" has param "as: %s". Param "as" should be "data", "template" or "process". '
33 | , 'invalidJSON' : 'Error: Invalid JSON format was provided for saving in "processes": %s'
34 |
35 | }
36 |
37 |
38 |
39 | const showError = function ( msgName, vars ) {
40 | let result = msg[msgName] || msg['notDefined'];
41 | if ( vars ) {
42 | if ( !(vars instanceof Array)) vars = [vars]
43 | vars.forEach ( v => result = result.replace('%s', v ) )
44 | }
45 | return result
46 | } // msg func.
47 |
48 | module.exports = showError;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Code Assembly Line
2 |
3 | Yet another template engine...
4 | Documentation will late a bit.
5 |
6 |
7 |
8 |
9 |
10 | ## Installation
11 | Code-Assembly-Line works for node.js and browsers.
12 |
13 |
14 |
15 | ### Installation for Node.js based projects:
16 | From the console:
17 | ```
18 | npm i -S code-assembly-line
19 |
20 | ```
21 | It will install module in to the project. You can start using it by
22 | ```js
23 | const CodeAssemblyLine = require ( 'code-assembly-line' );
24 | const tplEngine = new CodeAssemblyLine(); // Create instance of template engine
25 | ```
26 |
27 |
28 |
29 | ### Installation for browsers
30 | Just go to '/dist' folder and get copy of '**code-assembly-line.min.js**'. Put the file inside your project folder and add a reference to it from HTML code by using this script tag:
31 | ```html
32 |
33 | ```
34 | Library is avalable as '**CodeAssemblyLine**' and start using it.
35 | ```js
36 | const tplEngine = new CodeAssemblyLine();
37 | ```
38 | Find working example in file '**/dist/index.html**'.
39 |
40 |
41 |
42 |
43 |
44 | ## Motivation Factors for creating the 'Code Assembly Line'
45 | - Decouple render-processes from templates;
46 | - Decouple templates from data;
47 | - All compnonents(templates,processes,and data) provisioning in a JSON compatible format;
48 | - Built-in hooks for modifying render process if needed;
49 | - Posible default value for missing data;
50 | - Strategies on missing data: Hide a field, hide the record or provide alternative content;
51 | - Chaining render-processes;
52 | - Alternate template's placeholder names;
53 | - Enrich data by render new data-fields;
54 | - Partial rendering. Create new templates by render part of the available placeholders;
55 | - Optional spaces;
56 |
57 |
58 |
59 |
60 |
61 | ## Examples
62 |
63 | ### Example name
64 |
65 | ## Known bugs
66 | _(Nothing yet)_
67 |
68 |
69 |
70 |
71 |
72 | ## Release History
73 |
74 | ### 1.0.0 (2017-12-17)
75 |
76 | - [x] Node.js module;
77 | - [x] Browser module;
78 | - [x] Test package;
79 |
80 |
81 |
82 |
83 |
84 | ## Credits
85 | 'code-assembly-line' was created by Peter Naydenov.
86 |
87 |
88 |
89 |
90 |
91 | ## License
92 | 'code-assembly-line' is released under the [MIT License](http://opensource.org/licenses/MIT).
93 |
94 |
95 |
--------------------------------------------------------------------------------
/test/02_tpl-find-placeholders.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require ( 'chai' );
4 | const expect = chai.expect
5 | const findPlaceholdersAndSpaces = require ('../src/template-tools')._findPlaceholdersAndSpaces;
6 |
7 |
8 |
9 | describe ( 'Find a placeholders', () => {
10 |
11 | it ( 'No placeholders', () => {
12 | const tpl = [ 'Good morning!' ];
13 | const result = findPlaceholdersAndSpaces ( tpl ).placeholders;
14 |
15 | expect ( result ).to.be.an('object')
16 | expect ( result ).to.be.empty
17 | }) // it no placeholders
18 |
19 |
20 |
21 | it ( 'One placeholder', () => {
22 | const tpl = [ 'Good morning, ', '{{user}}' ];
23 | const result = findPlaceholdersAndSpaces ( tpl ).placeholders;
24 |
25 | expect ( result ).to.be.an('object')
26 | expect ( result ).to.contain.property( 'user' )
27 | expect ( result.user ).to.be.an('array')
28 | expect ( result.user ).to.includes(1)
29 | }) // it one placeholder
30 |
31 |
32 |
33 | it ( 'Many placeholders', () => {
34 | const tpl = [ 'Today user ', '{{user}}', ' has a birthday. Turns ', '{{age}}', ' years.' ];
35 | const result = findPlaceholdersAndSpaces ( tpl ).placeholders;
36 |
37 | expect ( result ).to.be.an('object')
38 | expect ( result ).to.contain.property( 'user' ).that.is.an ( 'array' )
39 | expect ( result ).to.contain.property( 'age' ).that.is.an ( 'array' )
40 | expect ( result.user ).to.be.an('array')
41 | expect ( result.user ).to.includes(1)
42 | expect ( result.age ).to.be.an('array')
43 | expect ( result.age ).to.includes(3)
44 | }) // it many placeholders
45 |
46 |
47 |
48 | it ( 'Multiple used placeholders', () => {
49 | const tpl = [ '{{age}}', ' is like ', '{{age}}', '. Happy birthday ', '{{user}}' ];
50 | const result = findPlaceholdersAndSpaces ( tpl ).placeholders;
51 |
52 | expect ( result ).to.be.an('object');
53 | expect ( result ).to.contain.property ('age' ).that.is.an ( 'array' )
54 | expect ( result ).to.contain.property ('user').that.is.an ( 'array' )
55 | expect ( result.age ).to.have.length(2)
56 | expect ( result.age ).to.includes (0)
57 | expect ( result.age ).to.includes (2)
58 | expect ( result.user).to.includes (4)
59 | }) // it multiple used placeholders
60 |
61 | }) // describe findPlaceholders
62 |
63 |
64 |
--------------------------------------------------------------------------------
/test/03_tpl-interpret-template.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const
4 | chai = require ('chai')
5 | , showError = require ( '../src/errors' )
6 | , templateTools = require ('../src/template-tools' )
7 |
8 | , expect = chai.expect
9 | , str2intTemplate = templateTools.str2intTemplate
10 | , load_interpretTemplate = templateTools.load_interpretTemplate
11 | , interpret = load_interpretTemplate ( str2intTemplate )
12 | ;
13 |
14 |
15 |
16 | describe ( 'Template interpreter', () => {
17 |
18 | it ( 'Well formated string template', () => {
19 | const str = 'Someone should {{action}} it.';
20 | const result = interpret ( str );
21 |
22 | expect ( result ).to.be.an('object')
23 | expect ( result ).to.have.property('tpl')
24 | expect ( result ).to.not.have.property ( 'errors' )
25 | expect ( result ).to.have.property('placeholders').that.has.property('action')
26 | expect ( result.placeholders.action ).to.be.an ( 'array' )
27 | expect ( result.placeholders.action ).to.have.length ( 1 )
28 | expect ( result.placeholders.action ).to.contain ( 1 )
29 | }) // it well formated template
30 |
31 |
32 |
33 | it ( 'Errors within string template', () => {
34 | const str = 'So }} errors happend{{';
35 | const result = interpret ( str );
36 |
37 | expect ( result ).to.have.property ('errors' )
38 | expect ( result.errors[0] ).to.be.equal ( showError('brokenTemplate') )
39 | expect ( result ).to.have.property ('tpl')
40 | expect ( result.tpl ).to.be.an('array')
41 | expect ( result.tpl ).contains ( showError('brokenTemplate') )
42 | expect ( result ).to.have.property ( 'placeholders' ).that.is.empty
43 | }) // it error string template
44 |
45 |
46 |
47 | it ( 'Wrong data format: object', () => {
48 | const data = { hello: 'hello, {{user}}' };
49 | const result = interpret ( data );
50 |
51 | expect ( result ).to.have.property ( 'errors' )
52 | expect ( result ).to.not.have.property ( 'tpl' )
53 | expect ( result ).to.not.have.property ( 'placeholders' )
54 | }) // it light data template
55 |
56 |
57 |
58 | it ( 'Wrong data format: number', () => {
59 | const data = 43;
60 | const result = interpret ( data );
61 |
62 | expect ( result ).to.have.property ( 'errors' );
63 | expect ( result.errors[0] ).to.be.equal ( showError('wrongDataTemplate') )
64 | }) // it wrong data format
65 |
66 | }) // Describe interpret a template
67 |
68 |
69 |
--------------------------------------------------------------------------------
/test/07_process-insert.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , processTools = require ( '../src/process-tools' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 |
11 |
12 | describe ( 'Process interpreter', () => {
13 |
14 | it ('General', () => {
15 | const
16 | ext = [
17 | { do: 'draw', tpl: 'any' }
18 | , { do: 'block', name: 'anyBlock' }
19 | ]
20 | , result = processTools.interpret ( ext )
21 | ;
22 |
23 | expect ( result ).to.have.property ( 'steps' )
24 | expect ( result ).to.have.property ( 'arguments' )
25 | expect ( result ).to.have.property ( 'hooks' )
26 | expect ( result ).to.not.have.property ( 'errors' )
27 |
28 | expect ( result.steps ).to.be.an( 'array' )
29 | expect ( result.steps ).includes ('draw')
30 | expect ( result.steps ).includes ('block')
31 |
32 | expect ( result.hooks).to.be.an('array')
33 | expect ( result.hooks).to.be.empty
34 | }) // it general
35 |
36 |
37 |
38 | it ( 'Hooks', () => {
39 | const
40 | ext = [
41 | { do: 'draw', tpl: 'link', hook:'drawHook' }
42 | , { do: 'hook', name: 'test'}
43 | ]
44 | , result = processTools.interpret ( ext )
45 | ;
46 |
47 | expect ( result ).to.not.have.property ( 'errors' )
48 | expect ( result.hooks ).to.have.length (2)
49 | expect ( result.hooks ).to.includes( 'test' )
50 | expect ( result.hooks ).to.includes( 'drawHook' )
51 |
52 | expect ( result.steps ).to.have.length (2)
53 | expect ( result.steps ).to.includes ( 'draw' )
54 | expect ( result.steps ).to.includes ( 'hook' )
55 | }) // it hook
56 |
57 |
58 |
59 | it ( 'Error', () => {
60 | const
61 | ext = {}
62 | , result = processTools.interpret ( ext )
63 | ;
64 |
65 | expect ( result ).to.have.property ( 'errors' )
66 | expect ( result ).to.have.property ( 'steps' )
67 | expect ( result ).to.have.property ( 'arguments' )
68 | expect ( result ).to.have.property ( 'hooks' )
69 |
70 | expect ( result.errors ).to.have.length (1)
71 | expect ( result.steps ).to.be.empty
72 | expect ( result.arguments ).to.be.empty
73 | expect ( result.hooks ).to.be.empty
74 | }) // it error
75 |
76 | }) // describe process interpreter
--------------------------------------------------------------------------------
/test/01_tpl-chop.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chai = require ( 'chai' );
4 | const expect = chai.expect
5 | const chop = require ('../src/template-chop');
6 | const callError = require ('../src/errors');
7 |
8 |
9 |
10 |
11 |
12 | describe ( 'Chop string templates', () => {
13 |
14 | it ( 'No placeholder', () => {
15 | const tpl = 'Hello, Peter.';
16 | const result = chop (tpl);
17 |
18 | expect ( result ).to.be.an('array')
19 | expect ( result ).to.have.length(1)
20 | }) // it no placeholder
21 |
22 |
23 |
24 | it ( 'Regular template', () => {
25 | const tpl = 'Hello, {{user}}.';
26 | const result = chop (tpl);
27 |
28 | expect ( result ).to.be.an('array')
29 | expect ( result ).to.have.length(3)
30 | expect ( result[1] ).to.be.equal('{{user}}')
31 | }) // it regular
32 |
33 |
34 |
35 | it ( 'Starts with placeholder', () => {
36 | const tpl = '{{user}} is logged in.';
37 | const result = chop (tpl);
38 |
39 | expect ( result ).to.be.an('array')
40 | expect ( result ).to.have.length(2)
41 | expect ( result[0] ).to.be.equal('{{user}}')
42 | }) // it Starts with
43 |
44 |
45 |
46 | it ( 'Many placeholders', () => {
47 | const tpl = '{{user}} is {{age}} years old.';
48 | const result = chop (tpl);
49 |
50 | expect ( result ).to.be.an('array')
51 | expect ( result ).to.have.length(4)
52 | expect ( result[0] ).to.be.equal('{{user}}')
53 | expect ( result[2] ).to.be.equal('{{age}}')
54 | }) // it Many placeholders
55 |
56 |
57 |
58 | it ( 'Finish with placeholder', () => {
59 | const tpl = '{{user}} and {{user}}';
60 | const result = chop (tpl);
61 |
62 | expect ( result ).to.be.an('array')
63 | expect ( result ).to.have.length(3)
64 | expect ( result[0] ).to.be.equal('{{user}}')
65 | expect ( result[2] ).to.be.equal('{{user}}')
66 | }) // it finish with placeholder
67 |
68 |
69 |
70 | it ( 'Missing closing tags', () => {
71 | const tpl = 'Pardon {{error is coming out.';
72 | const result = chop (tpl);
73 |
74 | expect ( result ).to.be.an('array')
75 | expect ( result ).to.have.length(1)
76 | expect ( result[0] ).to.be.equal ( callError('brokenTemplate') )
77 | }) // it missing closing tag
78 |
79 |
80 |
81 | it ( 'Other placeholder after missing closing tag', () => {
82 | const tpl = '{{user is {{age}} years old.';
83 | const result = chop (tpl);
84 |
85 | expect ( result ).to.be.an('array')
86 | expect ( result ).to.have.length(1)
87 | expect ( result).to.include ( callError('brokenTemplate') )
88 | }) // it
89 |
90 |
91 |
92 | it ( 'Closing tags before open ones', () => {
93 | const tpl = 'user}} is {{age years}} old.';
94 | const result = chop (tpl);
95 |
96 | expect ( result ).to.be.an('array')
97 | expect ( result).to.include ( callError('brokenTemplate') )
98 | }) // it Closing tags before open ones
99 |
100 |
101 |
102 | it ( 'Not a string template', () => {
103 | const tpl = { a: 'something', b: 12 };
104 | const result = chop ( tpl );
105 |
106 | expect ( result ).to.be.an ( 'array' )
107 | expect ( result[0] ).to.be.equal ( callError('brokenTemplate') )
108 | }) // it not a string template
109 |
110 | }) // describe
--------------------------------------------------------------------------------
/test/06_process-validation.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 |
4 | const
5 | chai = require ('chai')
6 | , expect = chai.expect
7 | , processTools = require ( '../src/process-tools' )
8 | , callErrors = require ( '../src/errors' )
9 | ;
10 |
11 |
12 |
13 | describe ( 'Process Validation', () => {
14 |
15 | it ( 'Valid data', () => {
16 | const ext = [
17 | { do: 'draw', tpl: 'dummy' }
18 | , { do: 'block', name: 'dummyBlock' }
19 | ];
20 |
21 | const result = processTools._validate ( ext );
22 |
23 | expect ( result ).to.be.an ( 'array' )
24 | expect ( result ).to.be.empty
25 | }) // it valid data
26 |
27 |
28 |
29 | it ( 'Empty array', () => {
30 | const
31 | ext = []
32 | , result = processTools._validate ( ext )
33 | ;
34 |
35 | expect ( result ).to.be.an ( 'array' )
36 | expect ( result ).to.have.length ( 1 )
37 | expect ( result[0] ).to.be.equal ( callErrors('emptyExtProcess') )
38 | }) // it empty array
39 |
40 |
41 |
42 | it ( 'Wrong data format - primitive', () => {
43 | const
44 | ext = 'none'
45 | , result = processTools._validate ( ext )
46 | ;
47 |
48 | expect ( result ).to.be.an ( 'array' )
49 | expect ( result ).to.have.length ( 1 )
50 | expect ( result[0] ).to.be.equal ( callErrors('wrongExtProcess') )
51 | }) // it primitive
52 |
53 |
54 |
55 | it ( 'Wrong data format - object', () => {
56 | const
57 | ext = { do: 'draw', tpl: 'any' }
58 | , result = processTools._validate ( ext )
59 | ;
60 |
61 | expect ( result ).to.be.an ( 'array' )
62 | expect ( result ).to.have.length ( 1 )
63 | expect ( result[0] ).to.be.equal ( callErrors('wrongExtProcess') )
64 | }) // it object
65 |
66 |
67 |
68 | it ( 'Step with missing operation',() => {
69 | const
70 | ext = [
71 | { do: 'draw', tpl: 'any' }
72 | , { name: 'newBlock' }
73 | ]
74 | , result = processTools._validate ( ext )
75 | ;
76 |
77 | expect ( result ).to.be.an ( 'array' )
78 | expect ( result ).to.have.length ( 1 )
79 | expect ( result[0] ).to.be.equal ( callErrors('missingOperation') )
80 | }) // it missing operation
81 |
82 |
83 |
84 | it ( 'Step with wrong operation', () => {
85 | const
86 | ext = [
87 | { do: 'wrongOperation', tpl: 'any' }
88 | , { do: 'test', name: 'newBlock' }
89 | ]
90 | , result = processTools._validate ( ext )
91 | ;
92 |
93 | expect ( result ).to.be.an ( 'array' )
94 | expect ( result ).to.have.length ( 2 )
95 | expect ( result[0] ).to.be.equal ( 'Error: "wrongOperation" is not a valid operation' )
96 | expect ( result[1] ).to.be.equal ( 'Error: "test" is not a valid operation' )
97 | }) // it wrong operation
98 |
99 | }) // describe process tools
100 |
101 |
102 |
--------------------------------------------------------------------------------
/src/template-tools.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const chop = require ('./template-chop');
4 | const showError = require ('./errors' );
5 |
6 | /*
7 | Converts ExtTempalteString to InternalTpl.
8 |
9 | • Import string template
10 | ( stringTemplate: string ) -> internalTemplate
11 | internalTemplate: { tpl: tplData, placeholders?:indexOfPlaceholders, error?: errorMsg }
12 | indexOfPlaceholders: { placeholderName: indexInTplData }
13 | errorMsg: string
14 |
15 | tplData: string[]; Contains text and placeholders.
16 | Example:
17 | [
18 | 'Some words from ' //text
19 | , '{{person}}' // placeholder
20 | , ' - See how simple is it' //text
21 | ]
22 |
23 | placeholderName is extracted value of placeholder.
24 | Example:
25 | placeholder: {{person}}
26 | placeholderName: person
27 | */
28 |
29 |
30 | const lib = {
31 |
32 | str2intTemplate (str) {
33 | const r = chop ( str );
34 | const error = r.includes ( showError('brokenTemplate') );
35 | let
36 | placeholders = {}
37 | , spaces = {} // { placeholderName: spaceType }
38 | ;
39 | /*
40 | placeholderName: string. Name of the placeholder.
41 | spaceType:enum.
42 | 1-space before,
43 | 2-space after,
44 | 3-both spaces
45 | */
46 |
47 | if ( !error ) {
48 | const t = lib._findPlaceholdersAndSpaces ( r )
49 | spaces = t.spaces
50 | placeholders = t.placeholders
51 | }
52 |
53 | const template = {
54 | tpl:r
55 | , placeholders
56 | , spaces
57 | };
58 | if ( error ) template.errors = [showError('brokenTemplate')]
59 | return template
60 | } // str2inTemplate func.
61 |
62 |
63 |
64 | , load_interpretTemplate ( convert2intTemplate ) {
65 | return ( tplItem ) => {
66 | let intTemplate = {}; // internal template
67 | const type = (typeof tplItem == 'string') ? 'string' : 'error';
68 |
69 | if ( type == 'string' ) intTemplate = convert2intTemplate ( tplItem )
70 | else intTemplate = { errors: [showError('wrongDataTemplate')] }
71 |
72 | return intTemplate
73 | }} // load_interpretTemplate func.
74 |
75 |
76 |
77 | , _findPlaceholdersAndSpaces ( tplArray ) {
78 | const
79 | placeholders = {}
80 | , spaces = {}
81 | ;
82 | tplArray.forEach ( (item,i) => {
83 | if ( item.slice(0,2) == '{{' ) {
84 | let start = 2, end = -2;
85 | const before = item.slice(2,4) == '~~';
86 | const after = item.slice (-4,-2) == '~~';
87 | if (before) start = 4
88 | if ( after ) end = -4
89 | const phName = item.slice ( start, end );
90 | if ( before && after ) spaces [ phName ] = 3 // before and after
91 | else if ( after ) spaces [ phName ] = 2 // only after
92 | else if ( before ) spaces [ phName ] = 1 // only before
93 | if ( placeholders.hasOwnProperty(phName) ) placeholders [ phName ].push (i)
94 | else placeholders [ phName ] = [i]
95 | }
96 | })
97 | return { spaces, placeholders }
98 | } // findPlaceholders func.
99 |
100 | } // lib
101 |
102 |
103 |
104 | module.exports = lib
105 |
106 |
107 |
--------------------------------------------------------------------------------
/test/04_tpl-insert-template.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , codeAssemblyLine = require ( '../src/index' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 |
11 | describe ( 'Templates', () => {
12 |
13 | it ( 'Start Code Assembly Line', () => {
14 | const tplEngine = new codeAssemblyLine ();
15 |
16 | expect ( tplEngine ).to.have.property ( 'templates' )
17 | expect ( tplEngine ).to.have.property ( 'processes' )
18 | expect ( tplEngine ).to.have.property ( 'data' )
19 | expect ( tplEngine ).to.have.property ( 'config' )
20 | }) // it create Grain Tuple
21 |
22 |
23 |
24 | it ( 'Insert ExternalTemplate', () => {
25 | const tplEngine = new codeAssemblyLine ();
26 | const tpl = { 'hello' : 'Hello, {{name}}!'};
27 |
28 | tplEngine.insertTemplate( tpl )
29 |
30 | expect ( tplEngine.templates ).to.have.property ( 'hello' )
31 | expect ( tplEngine.templates.hello ).to.have.property ('tpl')
32 | expect ( tplEngine.templates.hello ).to.have.property ('placeholders')
33 | expect ( tplEngine.templates.hello.tpl ).to.be.an ('array')
34 | }) // it insert ExternalTemplate
35 |
36 |
37 |
38 | it ( 'Insert template as a library member', () => {
39 | const tplEngine = new codeAssemblyLine ();
40 | const tpl = { 'simple/ah': 'Hello, {{name}}!'};
41 |
42 | tplEngine.insertTemplate( tpl )
43 |
44 | expect ( tplEngine.templates ).to.have.property ('simple/ah')
45 | }) // it use alternative name
46 |
47 |
48 |
49 | it ( 'Wrong data-format', () => {
50 | const tplEngine = new codeAssemblyLine ();
51 | const tpl = { hello: { name: 'Hello', age: 43}};
52 |
53 | tplEngine.insertTemplate( tpl )
54 |
55 | expect ( tplEngine.templates.hello ).to.have.property('errors')
56 | expect ( tplEngine.templates.hello.errors[0] ).to.be.equal( errors('wrongDataTemplate') )
57 | expect ( tplEngine.templates.hello ).to.not.have.property ('tpl')
58 | expect ( tplEngine.templates.hello ).to.not.have.property ('placeholders')
59 | }) // it wrong data-format
60 |
61 |
62 |
63 | it ( 'Insert many ExternalTemplate at once', () => {
64 | const tplEngine = new codeAssemblyLine ();
65 | const tpl = {
66 | hello: 'Hello, {{name}}!'
67 | , birthday: 'Happy birthday {{name}}'
68 | };
69 |
70 | tplEngine.insertTemplate ( tpl )
71 |
72 | expect ( tplEngine.templates ).to.have.property ( 'hello' )
73 | expect ( tplEngine.templates ).to.have.property ( 'birthday' )
74 | }) // it insert many ExtSimpleTemplate
75 |
76 |
77 |
78 | it ( 'Overwrite templates is forbidden', () => {
79 | const tplEngine = new codeAssemblyLine ({overwriteTemplates : false});
80 | const tpl = { 'hello': 'Hello, {{name}}!'};
81 | const tpl2 = { 'hello': 'Changes are allowed, {{name}}!'};
82 |
83 | tplEngine.insertTemplate( tpl )
84 | tplEngine.insertTemplate( tpl2 )
85 |
86 | expect ( tplEngine.templates ).to.have.property ('hello')
87 | expect ( tplEngine.templates.hello.tpl ).to.contain ('Hello, ')
88 | }) // it use alternative name
89 |
90 |
91 |
92 | it ( 'Overwrite templates is allowed', () => {
93 | const tplEngine = new codeAssemblyLine ({overwriteTemplates : true});
94 | const tpl = { 'hello': 'Hello, {{name}}!'};
95 | const tpl2 = { 'hello': 'Changes are allowed, {{name}}!'};
96 |
97 | tplEngine.insertTemplate( tpl )
98 | tplEngine.insertTemplate( tpl2 )
99 |
100 | expect ( tplEngine.templates ).to.have.property ('hello')
101 | expect ( tplEngine.templates.hello.tpl ).to.contain ('Changes are allowed, ')
102 | }) // it use alternative name
103 |
104 | }) // Describe Add template
105 |
106 |
107 |
--------------------------------------------------------------------------------
/test/09_process-tools.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 |
4 | const
5 | chai = require ('chai')
6 | , expect = chai.expect
7 | , processOps = require ( '../src/process-tools' )
8 | , callErrors = require ( '../src/errors' )
9 | ;
10 |
11 | describe ( 'Process Tools', () => {
12 |
13 | it ( 'Interpet: Valid process name', () => {
14 | const linkProcess = [
15 | { do: 'draw', tpl: 'a' }
16 | , { do: 'hook', name: 'sanitize'}
17 | , { do: 'block' }
18 | , { do: 'set', as: 'text' }
19 | ]
20 | const result = processOps.interpret ( linkProcess )
21 |
22 | expect ( result ).to.have.property ( 'steps' )
23 | expect ( result ).to.have.property ( 'arguments' )
24 | expect ( result ).to.have.property ( 'hooks' )
25 | expect ( result ).to.not.have.property ( 'error' )
26 |
27 | expect ( result.hooks ).to.contain ( 'sanitize' )
28 |
29 | expect ( result.steps ).to.contain ( 'draw' )
30 | expect ( result.steps ).to.contain ( 'hook' )
31 | expect ( result.steps ).to.contain ( 'block' )
32 | expect ( result.steps ).to.contain ( 'set' )
33 | }) // it interpret: valid process name
34 |
35 |
36 |
37 | it ( 'Interpret: Not a valid process name', () => {
38 | const
39 | fake = 'alternate'
40 | , processChain = [
41 | { do: fake, name: 'something' }
42 | ]
43 | ;
44 |
45 | const result = processOps.interpret ( processChain )
46 |
47 | expect ( result ).to.have.property ( 'steps' )
48 | expect ( result ).to.have.property ( 'arguments' )
49 | expect ( result ).to.have.property ( 'hooks' )
50 | expect ( result ).to.have.property ( 'errors' )
51 | expect ( result.errors[0] ).to.be.equal ( `Error: "${fake}" is not a valid operation` )
52 | }) // it interpret: not a valid process name
53 |
54 |
55 | it ( 'Interpret: Missing operation', () => {
56 | const processChain = [ { name: 'something' } ]
57 | const result = processOps.interpret ( processChain )
58 |
59 | expect ( result ).to.have.property ( 'errors' )
60 | expect ( result.errors[0] ).to.be.equal ( callErrors('missingOperation') )
61 | }) // it interpret: missing operation
62 |
63 |
64 |
65 | it ( 'Interpret: Not a valid arguments', () => {
66 | const processFake = { name: 'fakeProcess' };
67 | const result = processOps.interpret ( processFake );
68 |
69 | expect ( result ).to.have.property ( 'errors' )
70 | expect ( result.errors[0] ).to.be.equal ( callErrors('wrongExtProcess') )
71 | }) // it interpret: not a valid arguments
72 |
73 |
74 |
75 | it ( 'Interpret: Empty process', () => {
76 | const emptyProcess = [];
77 | const result = processOps.interpret ( emptyProcess );
78 |
79 | expect ( result ).to.have.property ( 'errors' )
80 | expect ( result.errors[0] ).to.be.equal ( callErrors('emptyExtProcess') )
81 | }) // it interpret: Empty process
82 |
83 |
84 |
85 | it ( 'string helper: string', () => {
86 | const a = 'ala-bala';
87 | const result = processOps._findIfString ( a )
88 |
89 | expect ( result ).to.be.equal ( true )
90 | }) // it string helper: string
91 |
92 |
93 |
94 | it ( 'string helper: not a string', () => {
95 | const a = 4;
96 | const result = processOps._findIfString ( a )
97 |
98 | expect ( result ).to.be.equal ( false )
99 | }) // it string helper: not a string
100 |
101 |
102 |
103 | it ( 'List copier', () => {
104 | const a = [ 'one', 'two' ];
105 | const result = processOps._copyList ( a )
106 | a[0] = 'update'
107 |
108 | expect ( result[0] ).to.not.be.equal ( a[0] )
109 | }) // it list copier
110 |
111 | // Note: method "run" has separate testing page
112 |
113 | }) // describe Process Tools
114 |
115 |
116 |
--------------------------------------------------------------------------------
/test/10_hook-modifiers.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , processOps = require ( '../src/process-operations' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 |
11 | describe ( 'Hook Modifiers', () => {
12 |
13 | it ( 'Alter', () => {
14 | const
15 | data = [
16 | { user: 'Peter' , age: 43 }
17 | , { user: 'Ivo' , age: 19 }
18 | , { user: 'Ivan' , age: 39 }
19 | ]
20 | , hook = {}
21 | ;
22 |
23 | let sample,update;
24 |
25 | hook.test = function ( data , modify ) {
26 | const step = { do:'alter', data: {'age':'alt'}, select: 'first' };
27 | update = modify ( data, step );
28 | sample = data;
29 | return update;
30 | }
31 |
32 | const final = processOps.hook ( data, hook.test )
33 | expect ( sample[0] ).to.have.property ('age')
34 | expect ( sample[0] ).to.not.have.property ('alt')
35 | expect ( update[0] ).to.not.have.property ( 'age' )
36 | expect ( update[0] ).to.have.property ( 'alt' )
37 | expect ( final[0] ).to.not.have.property ( 'age' )
38 | expect ( final[0] ).to.have.property ( 'alt' )
39 | expect ( final[0]['alt'] ).to.be.equal ( 43 )
40 |
41 | expect ( update[1] ).to.have.property ('age')
42 | expect ( update[1] ).to.not.have.property ('alt')
43 | expect ( final[1] ).to.have.property ('age')
44 | expect ( final[1] ).to.not.have.property ('alt')
45 | }) // it alter
46 |
47 |
48 |
49 | it ( 'Add', () => {
50 | const
51 | data = [{ user: 'Peter' }]
52 | , hook = {}
53 | ;
54 |
55 | let sample,update;
56 |
57 | hook.test = function ( data , modify ) {
58 | const step = { do:'add', data: {'age':43} };
59 | update = modify ( data, step );
60 | sample = data;
61 | return update;
62 | }
63 |
64 | const final = processOps.hook ( data, hook.test )
65 | expect ( sample[0] ).to.not.have.property ('age')
66 | expect ( update[0] ).to.have.property ( 'age' )
67 | expect ( final[0] ).to.have.property ( 'age' )
68 | expect ( final[0]['age'] ).to.be.equal ( 43 )
69 | }) // it add
70 |
71 |
72 |
73 | it ( 'Copy', () => {
74 | const
75 | data = [{ user: 'Peter', age:43 }]
76 | , hook = {}
77 | ;
78 |
79 | let sample,update;
80 |
81 | hook.test = function ( data , modify ) {
82 | const step = { do:'copy', data: {'age':'alt'} };
83 | update = modify ( data, step );
84 | sample = data;
85 | return update;
86 | }
87 |
88 | const final = processOps.hook ( data, hook.test )
89 | expect ( sample[0] ).to.have.property ('age')
90 | expect ( sample[0] ).to.not.have.property ('alt')
91 | expect ( update[0] ).to.have.property ('age')
92 | expect ( update[0] ).to.have.property ('alt')
93 | expect ( update[0]['alt'] ).to.be.equal ( data[0].age )
94 | expect ( final[0] ).to.have.property ('age')
95 | expect ( final[0] ).to.have.property ('alt')
96 | expect ( final[0]['alt'] ).to.be.equal ( data[0].age )
97 | }) // it copy
98 |
99 |
100 |
101 | it ( 'Remove', () => {
102 | const
103 | todo = { do: 'hook', name: 'test' }
104 | , data = [{ user: 'Peter', age:43 }]
105 | , hook = {}
106 | ;
107 | let update, sample;
108 |
109 | hook.test = function ( data , modify ) {
110 | update = modify ( data, {do:'remove', keys:['age']} );
111 | sample = data;
112 | return update;
113 | }
114 |
115 | const final = processOps.hook ( data, hook.test )
116 | expect ( sample[0] ).to.have.property ('age')
117 | expect ( update[0] ).to.not.have.property ('age')
118 | expect ( final[0] ).to.not.have.property ('age')
119 | }) // it remove
120 |
121 |
122 |
123 | it ( 'Modification not allowed', () => {
124 | const
125 | todo = { do: 'hook', name: 'test' }
126 | , data = ['Hello Peter', 'Hello Ivan' ]
127 | , hook = {}
128 | ;
129 | let update, sample;
130 |
131 | hook.test = function ( data , modify ) {
132 | update = modify ( data, {do:'remove', keys:['age']} );
133 | sample = data;
134 | return update;
135 | }
136 |
137 | const final = processOps.hook ( data, hook.test )
138 | expect ( sample[0] ).to.be.a('string')
139 | expect ( update[0] ).to.be.equal ( 'Hello Peter' )
140 | expect ( final[0] ).to.be.equal ( 'Hello Peter' )
141 | }) // it not allowed
142 |
143 |
144 |
145 | }) // describe
146 |
147 |
148 |
--------------------------------------------------------------------------------
/src/process-tools.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const
4 | showError = require ( './errors' )
5 | , operation = require ( './process-operations')
6 | , validSteps = [ 'draw', 'alterTemplate', 'alter', 'set', 'add', 'copy', 'remove', 'hook', 'block', 'save' ]
7 | ;
8 |
9 |
10 |
11 | const lib = {
12 |
13 | interpret ( ext ) { // (ext: extProcess) -> int: intProcess
14 | const internal = {};
15 |
16 | internal.steps = []
17 | internal.arguments = []
18 | internal.hooks = []
19 |
20 | const errorLog = lib._validate ( ext )
21 | if ( errorLog.length > 0 ) {
22 | internal.errors = errorLog;
23 | return internal;
24 | }
25 |
26 | ext.forEach ( step => {
27 | internal.steps.push ( step.do )
28 | internal.arguments.push ( step )
29 | if ( step.do == 'hook' ) internal.hooks.push ( step.name )
30 | if ( step.hook ) internal.hooks.push ( step.hook )
31 | })
32 |
33 | return internal
34 | } // interpret func.
35 |
36 |
37 |
38 | , _validate (ext) {
39 | const
40 | log = []
41 | , validType = ext instanceof Array
42 | ;
43 |
44 | if ( !validType ) return [ showError('wrongExtProcess') ]
45 | if ( ext.length == 0 ) return [ showError('emptyExtProcess') ]
46 |
47 | ext.forEach ( step => {
48 | let validOperation = false;
49 | if ( !step.do ) {
50 | log.push ( showError('missingOperation'))
51 | return
52 | }
53 | validOperation = validSteps.includes ( step.do )
54 | if ( !validOperation ) log.push ( showError('notaValidOperation', [step.do] ) )
55 | })
56 | return log
57 | } // _validate func.
58 |
59 |
60 |
61 | , _findIfString : (list) => (typeof(list[0]) == 'string')
62 |
63 |
64 |
65 | , _copyList ( source ) { // (string[]) -> string[]
66 | let
67 | size = source.length
68 | , result = []
69 | ;
70 | while (size--) result[size] = source[size]
71 | return result
72 | } // _copyList func.
73 |
74 |
75 |
76 | , _parse ( data ) { // (string) -> {} | false
77 | try { return JSON.parse(data) }
78 | catch (er) { return false }
79 | } // _parse func.
80 |
81 |
82 |
83 | , run ( proccessItems, data, hooks ) {
84 | // * Executes process/processes
85 | let
86 | me = this
87 | , libTemplates = me.templates
88 | , current = data
89 | , currentIsStr = lib._findIfString(current) // current is array of strings or array of objects
90 | , contextPlaceholders = {}
91 | , answer
92 | ;
93 |
94 | proccessItems.steps.forEach ( (step,id) => {
95 | const todo = proccessItems.arguments[id]; // Get full step instruction
96 | let tplName;
97 |
98 | switch ( step ) {
99 | case 'draw' :
100 | tplName = todo.tpl
101 | if ( currentIsStr ) console.warn ( showError ('dataExpectObject', `Step "draw" with template "${tplName}"`) )
102 |
103 | const
104 | missField = todo.missField || false
105 | , missData = todo.missData || false
106 | , hookFn = hooks ? hooks[todo.hook] || false : false
107 | , tpl = libTemplates[tplName]['tpl']
108 | , spaces = libTemplates[tplName]['spaces']
109 | , originalPlaceholders = libTemplates[tplName]['placeholders']
110 | , holdData = !(todo.as == null)
111 | ;
112 |
113 | let localTemplate = {};
114 | localTemplate.tpl = tpl
115 | localTemplate.placeholders = contextPlaceholders[tplName] || originalPlaceholders
116 | localTemplate.spaces = spaces
117 |
118 | const update = operation[step] ( { template:localTemplate, data:current, sharedData:me.data, htmlAttributes:me.config.htmlAttributes, missField, missData, hookFn} );
119 | if ( holdData ) {
120 | current = current.reduce ( (res,el,i) => {
121 | el[todo.as] = update[i]
122 | res.push(el)
123 | return res
124 | }, [])
125 | }
126 | else {
127 | current = update
128 | currentIsStr = true
129 | }
130 | break
131 | case 'block':
132 | if ( !currentIsStr ) {
133 | console.error ( showError ('blockExpectString', JSON.stringify(current)) )
134 | return
135 | }
136 | const blockSpace = todo.space || '';
137 | current = operation[step] ( current, blockSpace )
138 | if ( todo.name ) {
139 | let newData = {};
140 | newData[`block/${todo.name}`] = current.join('')
141 | me.insertData ( newData )
142 | }
143 | break
144 | case 'alterTemplate' :
145 | tplName = todo.tpl
146 | const intTemplate = libTemplates[tplName];
147 | contextPlaceholders[tplName] = operation[step] ( todo, intTemplate.placeholders )
148 | break
149 | case 'set' :
150 | current = operation[step] (todo, current )
151 | currentIsStr = false
152 | break
153 | case 'hook' :
154 | let hooked = operation[step] ( current, hooks[todo.name] )
155 | if ( hooked instanceof Array ) current = hooked
156 | else current = [hooked]
157 | currentIsStr = lib._findIfString ( current )
158 | break
159 | case 'save' :
160 | const saveName = (todo.as != 'block') ? todo.name : `block/${todo.name}`;
161 | let currentData = {};
162 |
163 | switch ( todo.as ) {
164 | case 'block':
165 | case 'data':
166 | currentData[saveName] = current.join('')
167 | me.insertData ( currentData )
168 | break
169 | case 'template':
170 | currentData[saveName] = current[0]
171 | me.insertTemplate ( currentData )
172 | break
173 | case 'process':
174 | let newProcess = lib._parse ( current[0] )
175 | if ( newProcess ) me.insertProcess ( newProcess, saveName )
176 | else console.error ( showError ( 'invalidJSON', current[0] ) )
177 | break
178 | default:
179 | console.error ( showError ('invalidStorageName', todo.as) )
180 | return
181 | }
182 | break
183 | } // switch step
184 | }) // forEach step
185 | return current
186 | } // run func.
187 | } // lib
188 |
189 |
190 |
191 | module.exports = lib
192 |
193 |
194 |
--------------------------------------------------------------------------------
/test/14_data.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , CodeAssemblyLine = require ( '../src/index' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 |
11 |
12 | describe ( 'Data', () => {
13 |
14 | it ( 'Insert single data record', () => {
15 | const
16 | tplEngine = new CodeAssemblyLine()
17 | , defaultAlt = 'image description is missing'
18 | ;
19 |
20 | tplEngine.insertData ( { 'alt' : defaultAlt } )
21 |
22 | expect ( tplEngine.data ).to.have.property ( 'alt' )
23 | expect ( tplEngine.data.alt ).to.be.equal ( defaultAlt )
24 | }) // it insert single data record
25 |
26 |
27 |
28 | it ( 'Insert many records', () => {
29 | const
30 | tplEngine = new CodeAssemblyLine()
31 | , records = {
32 | 'alt' : 'image description is missing'
33 | , 'home' : 'Sweet home'
34 |
35 | }
36 | ;
37 |
38 | tplEngine.insertData ( records )
39 |
40 | expect ( tplEngine.data ).to.have.property ( 'alt' )
41 | expect ( tplEngine.data ).to.have.property ( 'home' )
42 | expect ( tplEngine.data.alt ).to.be.equal ( records.alt )
43 | expect ( tplEngine.data.home ).to.be.equal ( records.home )
44 | }) // it Insert many records
45 |
46 |
47 |
48 | it ( 'Insert deep nestled objects', () => {
49 | const
50 | tplEngine = new CodeAssemblyLine()
51 | , records = {
52 | 'alt' : 'image description is missing'
53 | , 'settings' : {
54 | 'access' : 'admin'
55 | , 'users' : [ 'Peter', 'Ivan', 'Stefan' ]
56 | }
57 | }
58 | ;
59 |
60 | tplEngine.insertData ( records )
61 |
62 | expect ( tplEngine.data ).to.have.property ( 'alt' )
63 | expect ( tplEngine.data ).to.have.property ( 'settings/access' )
64 | expect ( tplEngine.data ).to.have.property ( 'settings/users/0' )
65 | expect ( tplEngine.data ).to.not.have.property ( 'settings/users' )
66 | }) // it Insert deep nestled objects
67 |
68 |
69 |
70 | it ( 'Try to overwrite existing data-record', () => {
71 | const
72 | tplEngine = new CodeAssemblyLine()
73 | , record = { 'alt' : 'image description is missing'}
74 | , nextrecord = { 'alt' : 'new alt '}
75 | ;
76 |
77 | tplEngine.insertData ( record )
78 | tplEngine.insertData ( nextrecord )
79 |
80 | expect ( tplEngine.data ).to.have.property ( 'alt' )
81 | expect ( tplEngine.data.alt ).to.be.equal ( record.alt )
82 | }) // it Try to overwrite existing data-record
83 |
84 |
85 |
86 | it ( 'Insert data as a library', () => {
87 | const
88 | tplEngine = new CodeAssemblyLine()
89 | , records = {
90 | 'alt' : 'image description is missing'
91 | , 'settings' : {
92 | 'access' : 'admin'
93 | , 'users' : [ 'Peter', 'Ivan', 'Stefan' ]
94 | }
95 | }
96 | ;
97 |
98 | tplEngine.insertDataLib ( records, 'special' )
99 | const r = tplEngine.data;
100 |
101 | expect ( r ).to.not.have.property ( 'alt' )
102 | expect ( r ).to.have.property ( 'special/alt' )
103 | expect ( r ).to.have.property ( 'special/settings/users/0')
104 | expect ( r ).to.have.property ( 'special/settings/access')
105 | expect ( r['special/settings/users/0'] ).to.be.equal ( records.settings.users[0] )
106 | }) // it Insert data as a library
107 |
108 |
109 |
110 | it ( 'Insert data as a library. Overwriting is forbidden', () => {
111 | const
112 | tplEngine = new CodeAssemblyLine ()
113 | , oldRecord = { 'lib/alt' : 'old record' }
114 | , record = {
115 | 'alt' : 'new record'
116 | , 'other' : 'other record'
117 | }
118 | ;
119 |
120 | tplEngine.insertData ( oldRecord )
121 | tplEngine.insertDataLib ( record, 'lib' )
122 |
123 | const d = tplEngine.data;
124 | expect (d).to.have.property ('lib/alt' )
125 | expect (d).to.have.property ('lib/other')
126 |
127 | expect(d['lib/alt']).to.be.equal ( oldRecord['lib/alt'])
128 | expect(d['lib/other']).to.be.equal ( record['other'] )
129 | }) // it Insert data as a library. Overwriting is forbidden
130 |
131 |
132 |
133 | it ( 'Rename data-record', () => {
134 | const
135 | tplEngine = new CodeAssemblyLine()
136 | , record = { 'alt' : 'image description is missing'}
137 | ;
138 |
139 | tplEngine.insertData ( record )
140 | tplEngine.renameData ( {'alt':'test'} )
141 |
142 | expect ( tplEngine.data ).to.not.have.property ( 'alt' )
143 | expect ( tplEngine.data ).to.have.property ( 'test' )
144 | }) // it Rename data-record
145 |
146 |
147 |
148 | it ( 'Rename a non-existing data-record', () => {
149 | const
150 | tplEngine = new CodeAssemblyLine()
151 | , record = { 'alt' : 'record text' }
152 | ;
153 |
154 | tplEngine.insertData ( record )
155 | tplEngine.renameData ( {'alt':'test', 'missing' : 'fake' } )
156 |
157 | const d = tplEngine.data;
158 | expect (d).to.not.have.property ( 'missing' )
159 | expect (d).to.not.have.property ( 'fake' )
160 | expect (d).to.not.have.property ( 'alt' )
161 |
162 | expect (d).to.have.property ( 'test')
163 | }) // it rename a non-existing data-record
164 |
165 |
166 | it ( 'Rename is not allowed', () => {
167 | const
168 | tplEngine = new CodeAssemblyLine()
169 | , record = {
170 | 'one' : 'first record'
171 | , 'two' : 'second record'
172 | }
173 | ;
174 |
175 | tplEngine.insertData ( record )
176 | tplEngine.renameData ( {'two':'one'} )
177 |
178 | const d = tplEngine.data;
179 | expect (d).to.have.property ( 'one' )
180 | expect (d).to.have.property ( 'two' )
181 |
182 | expect ( d.one ).to.be.equal ( record.one )
183 | expect ( d.two ).to.be.equal ( record.two )
184 | }) // it rename is not allowed
185 |
186 |
187 |
188 | it ( 'Remove data-record', () => {
189 | const
190 | tplEngine = new CodeAssemblyLine()
191 | , record = { 'alt' : 'image description is missing'}
192 | ;
193 |
194 | tplEngine.insertData ( record )
195 | tplEngine.removeData ( 'alt' )
196 |
197 | expect ( tplEngine.data ).to.not.have.property ( 'alt' )
198 | }) // it Remove data-record
199 |
200 |
201 |
202 | it ( 'Remove data-records presented as array', () => {
203 | const
204 | tplEngine = new CodeAssemblyLine()
205 | , record = {
206 | 'one' : 'record one'
207 | , 'two' : 'record two'
208 | , 'three' : 'record three'
209 | }
210 | ;
211 |
212 | tplEngine.insertData ( record )
213 | tplEngine.removeData ( ['one'] )
214 |
215 | const d = tplEngine.data;
216 | expect (d).to.not.have.property ( 'one' )
217 | expect (d).to.have.property ( 'two' )
218 | expect (d).to.have.property ( 'three' )
219 | }) // it Remove data-record presented as array
220 |
221 |
222 |
223 | it ( 'Get missing block', () => {
224 | const
225 | tplEngine = new CodeAssemblyLine()
226 | , template = { 'dummy' : 'image description is missing'}
227 | , pr = [
228 | { do:'draw', tpl: 'dummy' }
229 | , { do: 'save', as: 'block', name: 'dummy' }
230 | ]
231 | ;
232 |
233 | tplEngine.insertTemplate ( template )
234 | tplEngine.insertProcess ( pr, 'test' )
235 | tplEngine.run ( 'test' )
236 |
237 | const result = tplEngine.getBlock ( 'missing' )
238 | expect ( result ).to.be.equal ( '' )
239 | }) // it Get missing block
240 |
241 |
242 |
243 | it ( 'Get block with no arguments', () => {
244 | const
245 | tplEngine = new CodeAssemblyLine()
246 | , template = { 'dummy' : 'image description is missing'}
247 | , testProcess = [
248 | { do:'draw', tpl: 'dummy' }
249 | , { do: 'save', as: 'block', name: 'dummy' }
250 | ]
251 | ;
252 |
253 | tplEngine
254 | .insertTemplate ( template )
255 | .insertProcess ( testProcess, 'test' )
256 | .run ( 'test' )
257 |
258 | const result = tplEngine.getBlock ( )
259 | expect ( result ).to.be.equal ( '' )
260 | }) // it Get block with no arguments
261 |
262 |
263 |
264 | it ( 'Get single block', () => {
265 | const
266 | tplEngine = new CodeAssemblyLine()
267 | , template = { 'dummy' : 'image description is missing'}
268 | , pr = [
269 | { do:'draw', tpl: 'dummy' }
270 | , { do: 'save', as: 'block', name: 'dummy' }
271 | ]
272 | ;
273 |
274 | tplEngine.insertTemplate ( template )
275 | tplEngine.insertProcess ( pr, 'test' )
276 | tplEngine.run ( 'test' )
277 |
278 | const result = tplEngine.getBlock ( 'dummy' )
279 | expect ( result ).to.be.equal ( template.dummy )
280 | }) // it Get single block
281 |
282 |
283 |
284 | it ( 'Get blocks in order', () => {
285 | const
286 | tplEngine = new CodeAssemblyLine ()
287 | , templates = {
288 | 'one' : 'First sentence.'
289 | , 'two' : 'Second sentence.'
290 | }
291 | , pr1 = [
292 | { do:'draw', tpl: 'one' }
293 | , { do: 'save', as: 'block', name: 'first' }
294 | ]
295 | , pr2 = [
296 | { do:'draw', tpl: 'two' }
297 | , { do: 'save', as: 'block', name: 'second' }
298 | ]
299 |
300 | tplEngine
301 | .insertTemplate ( templates )
302 | .insertProcess ( pr1, 'doOne')
303 | .insertProcess ( pr2, 'doTwo')
304 | .run ( [ 'doTwo' , 'doOne'] )
305 |
306 | const result = tplEngine.getBlock ( 'first', 'second' )
307 |
308 | expect ( result ).to.be.a ( 'string' )
309 | expect ( result ).to.be.equal ( templates.one + templates.two )
310 | }) // it Get blocks in order
311 |
312 |
313 |
314 | it ( 'Get blocks in order as array', () => {
315 | const
316 | tplEngine = new CodeAssemblyLine ()
317 | , templates = {
318 | 'one' : 'First sentence.'
319 | , 'two' : 'Second sentence.'
320 | }
321 | , pr1 = [
322 | { do:'draw', tpl: 'one' }
323 | , { do: 'save', as: 'block', name: 'first' }
324 | ]
325 | , pr2 = [
326 | { do:'draw', tpl: 'two' }
327 | , { do: 'save', as: 'block', name: 'second' }
328 | ]
329 |
330 | tplEngine
331 | .insertTemplate ( templates )
332 | .insertProcess ( pr1, 'doOne')
333 | .insertProcess ( pr2, 'doTwo')
334 | .run ( [ 'doTwo' , 'doOne'] )
335 |
336 | const result = tplEngine.getBlock ( ['first', 'second'] )
337 |
338 | expect ( result ).to.be.a ( 'string' )
339 | expect ( result ).to.be.equal ( templates.one + templates.two )
340 | }) // it Get blocks in order as array
341 |
342 |
343 |
344 | it ( 'Try to insert function in data' , () => {
345 | const
346 | tplEngine = new CodeAssemblyLine()
347 | , data = {
348 | 'a' : 'Some string information'
349 | , 'fx' : function () { console.log('Hey, I am in!') }
350 | , 'b' : 12
351 | }
352 | ;
353 |
354 | tplEngine.insertData ( data )
355 |
356 | const d = tplEngine.data;
357 | expect (d).to.have.property('a')
358 | expect (d).to.have.property('b')
359 | expect (d).to.not.have.property('fx')
360 | }) // it Try to insert function in data
361 |
362 |
363 |
364 | }) // describe Tools
365 |
366 |
367 |
--------------------------------------------------------------------------------
/dist/code-assembly-line.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("CodeAssemblyLine",[],t):"object"==typeof exports?exports.CodeAssemblyLine=t():e.CodeAssemblyLine=t()}("undefined"!=typeof self?self:this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,r){"use strict";var n={notDefined:"Not defined error.",brokenTemplate:"Broken string template. Placeholder with missing closing tag.",wrongDataTemplate:"Broken data template. Object is not according the standards.",overwriteTemplate:"Error: Overwrite of existing template is not permited by configuration.",missingTemplate:'Error: Template "%s" doesn\'t exist.',wrongExtProcess:'Wrong process data. Should be an array of "processSteps".',emptyExtProcess:"Empty process! Process should contain steps(processSteps)",brokenProcess:"Broken process description.",missingOperation:'Process has step with missing operation. Set parameter "do".',overwriteProcess:'Error: Process with name "%s" is already defined.',notaValidOperation:'Error: "%s" is not a valid operation',blockExpectString:"Block expects string data. %s",dataExpectObject:"Data operations require objects. %s",overwriteData:'Error: Data with name "%s" is already defined.',processNotExists:'Error: Process "%s" does not exist.',templateNotExists:'Error: Template "%s" is not available',invalidStorageName:'Error: Process-step "save" has param "as: %s". Param "as" should be "data", "template" or "process". ',invalidJSON:'Error: Invalid JSON format was provided for saving in "processes": %s'},o=function(e,t){var r=n[e]||n.notDefined;return t&&(t instanceof Array||(t=[t]),t.forEach(function(e){return r=r.replace("%s",e)})),r};e.exports=o},function(e,t,r){"use strict";function n(e){var t=this;this.templates={},this.processes={},this.data={},this.config={},Object.keys(l).forEach(function(e){return t.config[e]=l[e]}),e&&Object.keys(e).forEach(function(r){return t.config[r]=e[r]})}var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a=r(2),i=r(4),s=r(6),c=r(0),u=a.str2intTemplate,f=a.load_interpretTemplate(u),l={overwriteTemplates:!1,overwriteProcesses:!1,overwriteData:!1,htmlAttributes:["id","name","href","src","value","data","alt","role","class"]},p={_isWritingForbidden:function(e,t,r){var n=void 0;return n="templates"==t?"overwriteTemplates":"processes"==t?"overwriteProcesses":"overwriteData",e[t].hasOwnProperty(r)&&!e.config[n]},_normalizeExternalData:function(e){return e instanceof Array?e.map(function(e){return"object"==(void 0===e?"undefined":o(e))?p._flatten(e):e}):[e]},_validateProcess:function(e,t){var r=[],n=e.processes.hasOwnProperty(t),o=e.processes[t];return n&&o.errors&&(r=r.concat(o.errors)),n?o.arguments.reduce(function(e,t){return"draw"==t.do&&t.tpl&&e.push(t.tpl),e},[]).forEach(function(t){var n=e.templates[t];n&&n.errors&&(r=r.concat(n.errors)),n||r.push(c("templateNotExists",t))}):r.push(c("processNotExists",t)),r},_validateProcessLib:function(e){try{var t=JSON.parse(e);return Object.keys(t).reduce(function(e,r){return t[r]instanceof Array&&(e[r]=[].concat(t[r])),e},{})}catch(e){return!1}},_extractLib:function(e,t){var r=Object.keys(e).filter(function(e){return e.includes("/")});return t.reduce(function(t,n){return r.filter(function(e){return 0==e.indexOf(n)}).forEach(function(r){var n=r.indexOf("/"),o=r.slice(n+1);t[o]=e[r].tpl.join("")}),t},{})},_flatten:function(e,t,r){var n=t||{};r=r||"";for(var o in e){var a=e[o],i=o;if(r&&(i=r+"/"+o),"function"!=typeof a){if(!p._isPrimitive(a))return p._flatten(a,n,i);n[i]=a}}return n},_isPrimitive:function(e){return"object"!==(void 0===e?"undefined":o(e))}},d={insert:function(e){var t=this;return Object.keys(e).forEach(function(r){if(p._isWritingForbidden(t,"templates",r))return void console.error(c("overwriteTemplate"));t.templates[r]=f(e[r])}),t},insertLib:function(e,t){var r=this;return Object.keys(e).forEach(function(n){var o={},a=t+"/"+n;if(p._isWritingForbidden(r,"templates",a))return void console.error(c("overwriteTemplate"));o[a]=e[n],d.insert.call(r,o)}),r},rename:function(e){var t=this,r=Object.keys(e),n=t.config.overwriteTemplates;return r.forEach(function(r){if(t.templates[r]){var o=e[r];if(!!t.templates[o]&&!n)return void console.error(c("overwriteTemplate"));t.templates[o]=t.templates[r],delete t.templates[r]}}),t},remove:function(e){var t=arguments,r=this,n=void 0;if(e instanceof Array)n=e;else{n=Object.keys(arguments).map(function(e){return t[e]})}return n.forEach(function(e){return delete r.templates[e]}),r},get:function(e){var t=arguments,r=this,n=r.templates,o=void 0;if(e instanceof Array)o=e;else{o=Object.keys(arguments).map(function(e){return t[e]})}return o.reduce(function(e,t){return n[t]?e[t]=n[t].tpl.join(""):e[t]="",e},{})},getLib:function(e){var t=arguments,r=this,n=void 0;if(e instanceof Array)n=e;else{n=Object.keys(arguments).map(function(e){return t[e]})}return p._extractLib(r.templates,n)},getPlaceholders:function(e){var t=this.templates[e];return t?Object.keys(t.placeholders):[]}},v={insert:function(e,t){var r=this;return p._isWritingForbidden(r,"processes",t)?void console.error(c("overwriteProcess",t)):(r.processes[t]=i.interpret(e),r)},insertLib:function(e,t){var r=this,n=p._validateProcessLib(e),o=Object.keys(n);return n&&o.forEach(function(e){var o=t?t+"/"+e:e;v.insert.call(r,n[e],o)}),r},mix:function(e,t){var r=this,n=r.processes,o={};return p._isWritingForbidden(r,"processes",t)?void console.error(c("overwriteProcess",t)):(o.steps=[],o.arguments=[],o.hooks=[],e.forEach(function(e){if(n[e]){var t=n[e],r=t.hooks.map(function(t){return e+"/"+t}),a=t.arguments.map(function(t){return"hook"!=t.do?t:{do:"hook",name:e+"/"+t.name}});o.steps=o.steps.concat(t.steps),o.arguments=o.arguments.concat(a),o.hooks=o.hooks.concat(r)}}),r.processes[t]=o,r)},getLib:function(e){var t=this,r=Object.keys(t.processes),n=!e,o=r.reduce(function(r,o){if(n)r[o]=t.processes[o];else if(o.includes(e)){var a=o.split("/");a.shift();var i=a.join("/");r[i]=t.processes[o]}return r},{});return JSON.stringify(o)},rename:function(e){var t=this,r=Object.keys(e),n=t.config.overwriteProcesses;return r.forEach(function(r){if(t.processes[r]){var o=e[r];if(!!t.processes[o]&&!n)return void console.error(c("overwriteProcess",o));t.processes[o]=t.processes[r],delete t.processes[r]}}),t},remove:function(e){var t=arguments,r=this,n=void 0;if(e instanceof Array)n=e;else{n=Object.keys(arguments).map(function(e){return t[e]})}return n.forEach(function(e){return delete r.processes[e]}),r},getHooks:function(e){if(this.processes.hasOwnProperty(e))return this.processes[e].hooks.reduce(function(e,t){return e[t]=void 0,e},{});return{}},run:function(e,t,r){var n=this;e instanceof Array||(e=[e]);var o=p._normalizeExternalData(t),a=e.reduce(function(e,t){var r=p._validateProcess(n,t);return e.concat(r)},[]);if(0==a.length){var s=o;return e.forEach(function(e){s=i.run.call(n,n.processes[e],s,r)}),s}return a}},m={insert:function(e){var t=this,r=p._flatten(e);return Object.keys(r).forEach(function(e){if(p._isWritingForbidden(t,"data",e))return void console.error(c("overwriteData",e));t.data[e]=r[e]}),t},insertLib:function(e,t){var r=this,n=p._flatten(e);return Object.keys(n).forEach(function(e){var o=t+"/"+e;if(p._isWritingForbidden(r,"data",o))return void console.error(c("overwriteData",o));r.data[o]=n[e]}),r},rename:function(e){var t=this,r=Object.keys(e),n=t.config.overwriteData;return r.forEach(function(r){if(t.data[r]){var o=e[r];if(!!t.data[o]&&!n)return void console.error(c("overwriteData",o));t.data[o]=t.data[r],delete t.data[r]}}),t},remove:function(e){var t=arguments,r=this,n=void 0;if(e instanceof Array)n=e;else{n=Object.keys(arguments).map(function(e){return t[e]})}return n.forEach(function(e){return delete r.data[e]}),r},getBlock:function(e){var t=arguments,r=this,n=[];if(e instanceof Array)n=e;else{n=Object.keys(arguments).map(function(e){return t[e]})}return n.reduce(function(e,t){return e+=r.data["block/"+t]?r.data["block/"+t]:""},"")}};n.prototype={tools:s,insertTemplate:d.insert,insertTemplateLib:d.insertLib,getTemplate:d.get,getTemplateLib:d.getLib,getPlaceholders:d.getPlaceholders,renameTemplate:d.rename,removeTemplate:d.remove,insertProcess:v.insert,insertProcessLib:v.insertLib,mixProcess:v.mix,getProcessLib:v.getLib,getHooks:v.getHooks,run:v.run,renameProcess:v.rename,removeProcess:v.remove,insertData:m.insert,insertDataLib:m.insertLib,getBlock:m.getBlock,getBlocks:m.getBlock,renameData:m.rename,removeData:m.remove},e.exports=n},function(e,t,r){"use strict";var n=r(3),o=r(0),a={str2intTemplate:function(e){var t=n(e),r=t.includes(o("brokenTemplate")),i={},s={};if(!r){var c=a._findPlaceholdersAndSpaces(t);s=c.spaces,i=c.placeholders}var u={tpl:t,placeholders:i,spaces:s};return r&&(u.errors=[o("brokenTemplate")]),u},load_interpretTemplate:function(e){return function(t){return"string"==("string"==typeof t?"string":"error")?e(t):{errors:[o("wrongDataTemplate")]}}},_findPlaceholdersAndSpaces:function(e){var t={},r={};return e.forEach(function(e,n){if("{{"==e.slice(0,2)){var o=2,a=-2,i="~~"==e.slice(2,4),s="~~"==e.slice(-4,-2);i&&(o=4),s&&(a=-4);var c=e.slice(o,a);i&&s?r[c]=3:s?r[c]=2:i&&(r[c]=1),t.hasOwnProperty(c)?t[c].push(n):t[c]=[n]}}),{spaces:r,placeholders:t}}};e.exports=a},function(e,t,r){"use strict";function n(e){var t=void 0,r=void 0,a=void 0,i=[];if("string"!=typeof e)return[o("brokenTemplate")];if(0==e.length)return[];if(t=e.indexOf("{{"),00?(t.errors=r,t):(e.forEach(function(e){t.steps.push(e.do),t.arguments.push(e),"hook"==e.do&&t.hooks.push(e.name),e.hook&&t.hooks.push(e.hook)}),t)},_validate:function(e){var t=[];return e instanceof Array?0==e.length?[n("emptyExtProcess")]:(e.forEach(function(e){var r=!1;if(!e.do)return void t.push(n("missingOperation"));(r=a.includes(e.do))||t.push(n("notaValidOperation",[e.do]))}),t):[n("wrongExtProcess")]},_findIfString:function(e){return"string"==typeof e[0]},_copyList:function(e){for(var t=e.length,r=[];t--;)r[t]=e[t];return r},_parse:function(e){try{return JSON.parse(e)}catch(e){return!1}},run:function(e,t,r){var a=this,s=a.templates,c=t,u=i._findIfString(c),f={};return e.steps.forEach(function(t,l){var p=e.arguments[l],d=void 0;switch(t){case"draw":d=p.tpl,u&&console.warn(n("dataExpectObject",'Step "draw" with template "'+d+'"'));var v=p.missField||!1,m=p.missData||!1,h=!!r&&(r[p.hook]||!1),b=s[d].tpl,y=s[d].spaces,g=s[d].placeholders,k=!(null==p.as),_={};_.tpl=b,_.placeholders=f[d]||g,_.spaces=y;var O=o[t]({template:_,data:c,sharedData:a.data,htmlAttributes:a.config.htmlAttributes,missField:v,missData:m,hookFn:h});k?c=c.reduce(function(e,t,r){return t[p.as]=O[r],e.push(t),e},[]):(c=O,u=!0);break;case"block":if(!u)return void console.error(n("blockExpectString",JSON.stringify(c)));var E=p.space||"";if(c=o[t](c,E),p.name){var j={};j["block/"+p.name]=c.join(""),a.insertData(j)}break;case"alterTemplate":d=p.tpl;var w=s[d];f[d]=o[t](p,w.placeholders);break;case"set":c=o[t](p,c),u=!1;break;case"hook":var x=o[t](c,r[p.name]);c=x instanceof Array?x:[x],u=i._findIfString(c);break;case"save":var P="block"!=p.as?p.name:"block/"+p.name,S={};switch(p.as){case"block":case"data":S[P]=c.join(""),a.insertData(S);break;case"template":S[P]=c[0],a.insertTemplate(S);break;case"process":var T=i._parse(c[0]);T?a.insertProcess(T,P):console.error(n("invalidJSON",c[0]));break;default:return void console.error(n("invalidStorageName",p.as))}}}),c}};e.exports=i},function(e,t,r){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o={draw:function(e){var t=e.template,r=e.data,n=e.sharedData,a=e.htmlAttributes,i=e.missField,s=e.missData,c=e.hookFn,u=[];return r.forEach(function(e,r){var f=e?Object.keys(e):[],l=o._copyList(t.tpl),p=Object.assign({},t.placeholders),d=t.spaces,v="",m="";if(p._attr){var h=p._attr[0];v=d._attr&&d._attr%2?" ":"",m=d._attr&&d._attr>1?" ":"";var b=o._createAttributes(e,a).trim();l[h]=b?""+v+b+m:"",delete p._attr}if(p._count){var y=p._count;v=d._count&&d._count%2?" ":"",m=d._count&&d._count>1?" ":"",y.forEach(function(e){return l[e]=""+v+(r+1)+m}),delete p._count}f.forEach(function(t){var r=p[t];if(v=d[t]&&d[t]%2?" ":"",m=d[t]&&d[t]>1?" ":"",r){var n=!0,o=!1,a=void 0;try{for(var i,s=r[Symbol.iterator]();!(n=(i=s.next()).done);n=!0){var c=i.value;l[c]=""+v+e[t]+m}}catch(e){o=!0,a=e}finally{try{!n&&s.return&&s.return()}finally{if(o)throw a}}delete p[t]}});var g=Object.keys(p),k=g.length>0;if(k&&n){var _=!0,O=!1,E=void 0;try{for(var j,w=g[Symbol.iterator]();!(_=(j=w.next()).done);_=!0){var x=j.value;!function(e){if(n[e]){var t=p[e];v=d[e]&&d[e]%2?" ":"",m=d[e]&&d[e]>1?" ":"",t.forEach(function(t){return l[t]=""+v+n[e]+m}),delete p[e]}}(x)}}catch(e){O=!0,E=e}finally{try{!_&&w.return&&w.return()}finally{if(O)throw E}}}if(g=Object.keys(p),k=g.length>0,k&&i&&function(){var e=void 0;switch("_fn"==i&&"function"!=typeof c&&(i="_hide"),i){case"_fn":e=c;break;case"_hide":e=function(){return""};break;case"_position":e=function(e){return e};break;default:e=function(){return i}}var t=!0,r=!1,n=void 0;try{for(var o,a=g[Symbol.iterator]();!(t=(o=a.next()).done);t=!0){var s=o.value;!function(t){p[t].forEach(function(r){return l[r]=e(t)})}(s)}}catch(e){r=!0,n=e}finally{try{!t&&a.return&&a.return()}finally{if(r)throw n}}}(),k&&s){var P=void 0;switch("_fn"==s&&"function"!=typeof c&&(s="_hide"),s){case"_fn":P=function(e){return[c(e)]};break;case"_hide":P=function(){return[]};break;default:P=function(){return[s]}}l=P(g)}l.length>0&&u.push(l.join(""))}),u},_createAttributes:function(e,t){var r={},n=new RegExp("^data-"),o="";for(var a in e)"name"!==a&&("id"!==a?"className"!==a?t.includes(a)?r[a]=a+'="'+e[a]+'"':n.test(a)&&(r[a]=a+'="'+e[a]+'"',o+=" "+r[a]):r.class='class="'+e[a]+'"':(r.id='id="'+e[a]+'"',r.name='name="'+e[a]+'"'));return t.reduce(function(e,t){return r[t]&&(e+=" "+r[t]),"data"==t&&(e+=" "+o),e},"")},_copyList:function(e){for(var t=e.length,r=[];t--;)r[t]=e[t];return r},alterTemplate:function(e,t){var r=e.data,n=Object.keys(t),o={};return n.forEach(function(e){var n=r[e]||e;o[n]=t[e]}),o},block:function(e,t){return[e.join(t)]},set:function(e,t){var r=e.as;return t.reduce(function(e,t){var n={};return n[r]=t,e.push(n),e},[])},alter:function(e,t){var r=e.data,n=Object.keys(r),a=o._normalizeSelection(e.select,t.length),i=t.map(function(e){return Object.assign({},e)});return a.forEach(function(e){var t=i[e],o=Object.keys(t),a={};o.forEach(function(e){var o=t[e];n.includes(e)&&(e=r[e]),a[e]=o}),i[e]=a}),i},_normalizeSelection:function(e,t){var r=[];return e?("object"!=(void 0===e?"undefined":n(e))&&(e=[e]),e.forEach(function(e){if("all"==e)return r=o._generateList(t);switch(e){case"first":r.push(0);break;case"last":r.push(t-1);break;default:e<0?r.push(t-1+e):r.push(e-1)}}),r):r=o._generateList(t)},_generateList:function(e){for(var t=[],r=0;r string[]
21 | // * Render template.
22 | /*
23 | Params:
24 | template:{intTemplate}. Selected template for this draw operation;
25 | data:{any}[]. Current data;
26 | sharedData:{any}. Engine data object;
27 | htmlAttributes: string[]. Render '_attr' will consider this list of items and their order;
28 | missField?:string. Strategy for fulfill missing data-fields;
29 | missData?:string. Strategy for substitute data-record on missing data-fields;
30 | hookFn: (placeholderName:string) -> renderResult:string. Result will be added to render result array;
31 |
32 | MissField argument could be a string but also has two predefined options:
33 | '_position': will render placeholder name;
34 | '_hide' : will not render anything;
35 | '_fn' : hook function will take care about missing fields;
36 |
37 | MissData argument could be a string or some of predefined options:
38 | '_hide': will not render anything from this data-record
39 | '_fn' : hook function will take care about the record;
40 |
41 | */
42 | let
43 | { template, data, sharedData, htmlAttributes, missField, missData, hookFn } = params
44 | , result = []
45 | ;
46 |
47 | data.forEach ( (obj, _count) => {
48 | const keys = obj ? Object.keys(obj) : [];
49 | let
50 | tpl = lib._copyList ( template.tpl )
51 | , places = Object.assign ( {}, template.placeholders )
52 | , spaces = template.spaces
53 | , spaceBefore = ''
54 | , spaceAfter = ''
55 | ;
56 |
57 | if ( places['_attr'] ) { // calculate html attributes only if '_attr' placeholder exists in template
58 | const attrTarget = places['_attr'][0]
59 | spaceBefore = ( spaces['_attr'] && spaces['_attr']%2 ) ? ' ' : ''
60 | spaceAfter = ( spaces['_attr'] && spaces['_attr']>1 ) ? ' ' : ''
61 | const attr = lib._createAttributes(obj, htmlAttributes ).trim()
62 | if ( attr ) tpl[attrTarget] = `${spaceBefore}${attr}${spaceAfter}` // generate attributes string
63 | else tpl[attrTarget] = ''
64 | delete places['_attr']
65 | }
66 |
67 | if ( places['_count'] ) { // replace _count if '_count' placeholder exists in template
68 | const countTarget = places['_count'];
69 | spaceBefore = ( spaces['_count'] && spaces['_count']%2 ) ? ' ' : ''
70 | spaceAfter = ( spaces['_count'] && spaces['_count']>1 ) ? ' ' : ''
71 | countTarget.forEach ( position => tpl[position] = `${spaceBefore}${_count + 1}${spaceAfter}` )
72 | delete places['_count']
73 | }
74 |
75 | keys.forEach ( k => {
76 | const positions = places[k];
77 | spaceBefore = ( spaces[k] && spaces[k]%2 ) ? ' ' : ''
78 | spaceAfter = ( spaces[k] && spaces[k]>1 ) ? ' ' : ''
79 | if ( positions ) {
80 | for (let position of positions ) tpl[position] = `${spaceBefore}${obj[k]}${spaceAfter}`
81 | delete places[k]
82 | }
83 | })
84 |
85 | let neglected = Object.keys ( places )
86 | , someNeglected = ( neglected.length > 0 )
87 | ;
88 |
89 | if ( someNeglected && sharedData ) { // find values in sharedData
90 | for ( let placeholder of neglected ) {
91 | if ( sharedData[placeholder] ) {
92 | const list = places [placeholder]
93 | spaceBefore = ( spaces[placeholder] && spaces[placeholder]%2 ) ? ' ' : ''
94 | spaceAfter = ( spaces[placeholder] && spaces[placeholder]>1 ) ? ' ' : ''
95 | list.forEach ( id => tpl[id] = `${spaceBefore}${sharedData[placeholder]}${spaceAfter}` )
96 | delete places[placeholder]
97 | }
98 | }
99 | } // if someNeglected
100 |
101 | neglected = Object.keys ( places )
102 | someNeglected = ( neglected.length > 0 )
103 | if ( someNeglected && missField ) { // miss field strategy
104 | let missFieldUpdate;
105 | if ( missField == '_fn' && typeof(hookFn) != 'function' ) missField = '_hide'
106 | switch ( missField ) {
107 | case '_fn' :
108 | missFieldUpdate = hookFn
109 | break
110 | case '_hide' :
111 | missFieldUpdate = () => ''
112 | break
113 | case '_position' :
114 | missFieldUpdate = (pos) => pos
115 | break
116 | default :
117 | missFieldUpdate = () => missField
118 | } // switch missField
119 | for ( let position of neglected ) {
120 | const list = places[position]
121 | list.forEach ( el => tpl[el] = missFieldUpdate(position) )
122 | }
123 | } // if missField
124 |
125 | if ( someNeglected && missData ) { // miss data strategy
126 | let missDataUpdate;
127 | if ( missData =='_fn' && typeof(hookFn) != 'function' ) missData = '_hide'
128 | switch ( missData ) {
129 | case '_fn':
130 | missDataUpdate = (x) => [hookFn(x)]
131 | break
132 | case '_hide':
133 | missDataUpdate = () => []
134 | break
135 | default:
136 | missDataUpdate = () => [ missData ]
137 | } // switch missData
138 | tpl = missDataUpdate(neglected)
139 | } // if missData
140 |
141 | if ( tpl.length > 0 ) result.push ( tpl.join('') )
142 | })
143 | return result
144 | } // draw func.
145 |
146 |
147 |
148 |
149 |
150 | , _createAttributes ( data, attributes ) { // ({}, string[]) -> string
151 | // *** 'attr' placeholder calculation
152 | const
153 | attr = {}
154 | , dataAttr = new RegExp('^data-')
155 | ;
156 | let dataItems = '';
157 |
158 | for ( let item in data ) {
159 | if ( item === 'name' ) continue // Ignore 'name'
160 | if ( item === 'id' ) {
161 | attr['id'] = `id="${data[item]}"`
162 | attr['name'] = `name="${data[item]}"`
163 | continue
164 | }
165 | if ( item === 'className' ) {
166 | attr['class'] = `class="${data[item]}"`
167 | continue
168 | }
169 | if ( attributes.includes (item) ) {
170 | attr[item] = `${item}="${data[item]}"`
171 | continue
172 | }
173 | if ( dataAttr.test (item) ) {
174 | attr[item] = `${item}="${data[item]}"`
175 | dataItems += ` ${attr[item]}`
176 | }
177 | }
178 |
179 | return attributes.reduce ( (res,item) => {
180 | if ( attr[item] ) res += ` ${attr[item]}`
181 | if ( item == 'data' ) res += ` ${dataItems}`
182 | return res
183 | },'')
184 | } // _createAttributes func.
185 |
186 |
187 |
188 |
189 |
190 | , _copyList ( source ) { // (string[]) -> string[]
191 | let
192 | size = source.length
193 | , result = []
194 | ;
195 | while (size--) result[size] = source[size]
196 | return result
197 | } // _copyList func.
198 |
199 |
200 |
201 | , alterTemplate ( step, sourcePlaceholders ) { // (step{}, internalTpl.placeholers) -> internalTpl.placeholders
202 | // * Rename of placeholders
203 | const
204 | changes = step.data // { oldPlaceholderName : newPlaceholderName }
205 | , keysPh = Object.keys ( sourcePlaceholders )
206 | , placeholders = {};
207 | ;
208 |
209 | keysPh.forEach ( key => {
210 | const k = changes[key] || key;
211 | placeholders[k] = sourcePlaceholders[key]
212 | })
213 |
214 | return placeholders
215 | } // alterTemplate func.
216 |
217 |
218 |
219 | , block ( data, space ) { // ( string[] ) -> string[]
220 | // * Concatinate strings from array. Returns new array with a single entry
221 | return [ data.join( space ) ]
222 | } // block func.
223 |
224 |
225 |
226 | , set ( step, data) { // ( step{}, string[] ) -> {}[]
227 | // * Converts renders and blocks in object
228 | const
229 | name = step.as
230 | , result = data.reduce ( (res,item) => {
231 | let obj = {};
232 | obj[name] = item;
233 | res.push ( obj )
234 | return res
235 | },[])
236 | return result;
237 | } // set func.
238 |
239 |
240 |
241 | , alter ( step, data ) { // ( step{}, {}[] ) -> {}[]
242 | // * Change property names.
243 | const
244 | changes = step.data
245 | , keys = Object.keys ( changes )
246 | , forUpdate = lib._normalizeSelection ( step.select, data.length )
247 | ;
248 | let result = data.map ( dataRecord => Object.assign ({}, dataRecord) );
249 | forUpdate.forEach ( el => {
250 | const selected = result[el]
251 | const dataKeys = Object.keys(selected)
252 | let update = {}
253 | dataKeys.forEach ( k => {
254 | let value = selected[k]
255 | if ( keys.includes(k) ) k = changes[k]
256 | update[k] = value
257 | })
258 | result[el] = update
259 | })
260 | return result
261 | } // alter func.
262 |
263 |
264 |
265 |
266 | , _normalizeSelection ( list, length ) {
267 | let result = [];
268 | if ( !list ) {
269 | result = lib._generateList ( length )
270 | return result
271 | }
272 | // if ( typeof(list) == 'string' ) list = [ list ]
273 | if ( typeof(list) != 'object' ) list = [ list ]
274 |
275 | list.forEach ( el => {
276 | if ( el == 'all') {
277 | result = lib._generateList(length)
278 | return result
279 | }
280 | switch ( el ) {
281 | case 'first':
282 | result.push(0)
283 | break;
284 | case 'last' :
285 | result.push(length-1)
286 | break
287 | default :
288 | if (el<0) result.push(length-1+el)
289 | else result.push(el-1)
290 | }
291 | })
292 | return result
293 | } // _normalizeSelection func.
294 |
295 |
296 |
297 | , _generateList ( size ) {
298 | let result = [];
299 | for ( let i=0; i < size; i++ ) result.push(i)
300 | return result
301 | } // _generateList func.
302 |
303 |
304 |
305 | , add ( step, data ) { // ( step{}, {}[] ) -> {}[]
306 | // * Add property names
307 | const
308 | changes = step.data
309 | , keys = Object.keys ( changes )
310 | , listForUpdate = lib._normalizeSelection ( step.select, data.length )
311 | ;
312 | let result = data.map ( dataRecord => Object.assign ({}, dataRecord) );
313 | listForUpdate.forEach ( el => {
314 | const selected = result[el]
315 | const dataKeys = Object.keys(selected)
316 | let update = keys.reduce ( (res,k) => {
317 | if ( !selected[k] ) res[k] = changes[k]
318 | else res[k] = lib._combineValues (selected[k], changes[k])
319 | return res
320 | }, {})
321 | result[el] = Object.assign( selected, update )
322 | })
323 | return result
324 | } // add func.
325 |
326 |
327 |
328 | , _combineValues ( existing, update ) {
329 | let
330 | primitive = false
331 | , result
332 | ;
333 |
334 | if ( typeof existing != 'object' ) primitive = true
335 | if ( primitive ) result = [ existing ]
336 | else result = existing
337 |
338 | return result.concat ( update ).join ( ' ' )
339 | } // _combineValues func.
340 |
341 |
342 |
343 | , copy ( step, data ) { // ( step{}, {}[] ) -> {}[]
344 | // * Create new property copy the value.
345 | const
346 | changes = step.data
347 | , keys = Object.keys ( changes )
348 | , forUpdate = lib._normalizeSelection ( step.select, data.length )
349 | , result = data.map ( record => Object.assign ({},record) )
350 | ;
351 |
352 | forUpdate.forEach ( dataRecord => {
353 | keys.forEach ( k => {
354 | const value = result[dataRecord][k]
355 | const copyKey = changes[k]
356 | result[dataRecord][copyKey] = value
357 | })
358 | })
359 |
360 | return result
361 | } // copy func.
362 |
363 |
364 |
365 | , remove ( step, data ) { // ( step{}, {}[] ) -> {}[]
366 | // * Remove existing property
367 | const
368 | keys = step.keys
369 | , forUpdate = lib._normalizeSelection ( step.select, data.length )
370 | , result = data.map ( record => Object.assign ({}, record) )
371 | ;
372 |
373 | forUpdate.forEach ( dataRecord => {
374 | keys.forEach ( k => {
375 | delete result[dataRecord][k]
376 | })
377 | })
378 | return result
379 | } // remove func.
380 |
381 |
382 |
383 | , hook ( data, cb ) { // ( {}[], Function ) -> {}[]
384 | // * Function placeholder within render process
385 | if ( !cb ) return data
386 | return cb ( data , lib.modify )
387 | } // hook func.
388 |
389 |
390 |
391 | , _findIfString : ( list ) => (typeof(list[0]) == 'string')
392 |
393 |
394 |
395 | , modify ( data, step ) { // ( {}[], step{} ) -> {}[]
396 | // * Access step-operations inside hook callbacks.
397 | let
398 | act = step.do
399 | , dataIsStr = lib._findIfString ( data )
400 | ;
401 |
402 | if ( dataIsStr ) {
403 | console.error (`Data operations require objects but have strings. Data: ${data}`)
404 | return data
405 | }
406 | return lib[act] ( step, data )
407 | } // modify func.
408 |
409 | } // lib
410 |
411 |
412 |
413 | module.exports = lib
414 |
415 |
416 |
--------------------------------------------------------------------------------
/test/11_code-assembly.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , CodeAssemblyLine = require ( '../src/index' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 |
11 | describe ( 'Code Assembly', () => {
12 |
13 | it ( 'Start Code Assembly', () => {
14 | const tplEngine = new CodeAssemblyLine ();
15 |
16 | expect ( tplEngine ).have.property ( 'templates' )
17 | expect ( tplEngine ).have.property ( 'processes' )
18 | expect ( tplEngine ).have.property ( 'data' )
19 | }) // it start code Assembly
20 |
21 |
22 |
23 | it ( 'Insert Template Library', () => {
24 | const
25 | tplEngine = new CodeAssemblyLine ()
26 | , tplLib = {
27 | 'hello' : 'Hello {{user}}'
28 | , 'bye' : 'Good bye {{user}}'
29 | }
30 | , tplLib2 = {
31 | 'hello' : 'Hi {{user}}'
32 | , 'welcome' : 'Welcome {{user}}'
33 | }
34 | ;
35 |
36 | tplEngine.insertTemplateLib ( tplLib, 'custom' )
37 | tplEngine.insertTemplateLib ( tplLib2, 'custom' )
38 |
39 | expect ( tplEngine.templates ).to.have.property ('custom/hello' )
40 | expect ( tplEngine.templates ).to.have.property ('custom/bye' )
41 | expect ( tplEngine.templates ).to.have.property ('custom/welcome' )
42 | const tpl = tplEngine.getTemplate ( 'custom/hello' );
43 | expect ( tpl['custom/hello'] ).to.be.equal ( tplLib.hello )
44 | }) // it insert template library
45 |
46 |
47 |
48 | it ( 'Get Template', () => {
49 | const
50 | tplEngine = new CodeAssemblyLine ()
51 | , tpl = {
52 | 'hello' : 'Hello {{user}}'
53 | , 'bye' : 'Good bye {{user}}'
54 | }
55 | ;
56 |
57 | tplEngine.insertTemplate ( tpl )
58 | const
59 | single = tplEngine.getTemplate ( 'hello' )
60 | , multiple = tplEngine.getTemplate ( 'hello', 'bye', 'dummy' )
61 | , altMultiple = tplEngine.getTemplate (['hello', 'bye'])
62 | ;
63 |
64 | expect ( single ).to.have.property ( 'hello' )
65 | expect ( single.hello ).to.be.a ( 'string' )
66 | expect ( single.hello ).to.be.equal ( tpl.hello )
67 |
68 | expect ( multiple ).to.have.property ( 'hello' )
69 | expect ( multiple ).to.have.property ( 'bye' )
70 | expect ( multiple ).to.have.property ( 'dummy' )
71 | expect ( multiple.hello ).to.be.a ( 'string' )
72 | expect ( multiple.bye ).to.be.a ( 'string' )
73 | expect ( multiple.dummy ).to.be.empty
74 |
75 | expect ( multiple.hello ).to.be.equal ( tpl.hello )
76 | expect ( multiple.bye ).to.be.equal ( tpl.bye )
77 |
78 | expect ( altMultiple.hello ).to.be.equal ( multiple.hello )
79 | expect ( altMultiple.bye ).to.be.equal ( multiple.bye )
80 | }) // it getTemplate
81 |
82 |
83 |
84 | it ( 'Get Template Library' , () => {
85 | const
86 | tplEngine = new CodeAssemblyLine ()
87 | , tpl = { 'hi': 'Hi {{user}}' }
88 | , tplLib = {
89 | 'hello' : 'Hello {{user}}'
90 | , 'bye' : 'Good bye {{user}}'
91 | }
92 | ;
93 |
94 | tplEngine.insertTemplate ( tpl )
95 | tplEngine.insertTemplateLib ( tplLib, 'test' )
96 |
97 | const
98 | result = tplEngine.getTemplateLib ('test')
99 | , alt = tplEngine.getTemplateLib (['test'])
100 | ;
101 |
102 | expect ( result ).to.have.property ( 'hello' )
103 | expect ( result ).to.have.property ( 'bye' )
104 | expect ( result ).to.not.have.property ( 'hi' )
105 | expect ( result.hello ).to.be.equal ( tplLib.hello )
106 | expect ( result.bye ).to.be.equal ( tplLib.bye )
107 |
108 | expect ( alt ).to.have.property ( 'hello' )
109 | expect ( alt ).to.have.property ( 'bye' )
110 | }) // it getTemplateLib
111 |
112 |
113 |
114 | it ( 'Get Placeholders', () => {
115 | const
116 | tplEngine = new CodeAssemblyLine ()
117 | , tpl = {
118 | 'hello': 'Hello {{user}}. Welcome to {{place}}'
119 | }
120 | ;
121 |
122 | tplEngine.insertTemplate ( tpl )
123 | const result = tplEngine.getPlaceholders ( 'hello' )
124 | const emptyRes = tplEngine.getPlaceholders ( 'fail' )
125 |
126 | expect ( result ).to.be.an ( 'array' )
127 | expect ( result ).to.includes ( 'user' )
128 | expect ( result ).to.includes ( 'place' )
129 |
130 | expect ( emptyRes ).to.be.an ( 'array' )
131 | expect ( emptyRes ).to.be.empty
132 | }) // it getPlaceholders
133 |
134 |
135 |
136 | it ( 'Template rename normal', () => {
137 | const
138 | tplEngine = new CodeAssemblyLine ()
139 | , tpl = {
140 | 'hello' : 'Hello {{user}}'
141 | , 'bye' : 'Good bye {{user}}'
142 | }
143 | , instructions = { 'hello':'sayHello', 'bye': 'sayGoodbye' }
144 | ;
145 |
146 | tplEngine.insertTemplate ( tpl )
147 | tplEngine.renameTemplate ( instructions )
148 |
149 | expect ( tplEngine.templates).to.have.property ( 'sayHello' )
150 | expect ( tplEngine.templates).to.have.property ( 'sayGoodbye' )
151 | expect ( tplEngine.templates).to.not.have.property ( 'hello' )
152 | expect ( tplEngine.templates).to.not.have.property ( 'bye' )
153 | }) // it template rename normal
154 |
155 |
156 |
157 | it ( 'Template rename with overwrite', () => {
158 | const
159 | tplEngine = new CodeAssemblyLine ()
160 | , tpl = {
161 | 'hello' : 'Hello {{user}}'
162 | , 'bye' : 'Good bye {{user}}'
163 | , 'sayHello' : 'Should stay'
164 | }
165 | , instructions = { 'hello':'sayHello', 'bye': 'sayGoodbye'}
166 | ;
167 |
168 | tplEngine.insertTemplate ( tpl )
169 | tplEngine.renameTemplate ( instructions )
170 |
171 | expect ( tplEngine.templates).to.have.property ( 'hello' )
172 | expect ( tplEngine.templates).to.have.property ( 'sayHello' )
173 | expect ( tplEngine.templates).to.have.property ( 'sayGoodbye' )
174 | expect ( tplEngine.templates).to.not.have.property ( 'bye' )
175 | }) // it template rename with overwrite
176 |
177 |
178 |
179 | it ( 'Rename a non existing template', () => {
180 | const
181 | tplEngine = new CodeAssemblyLine ()
182 | , t = { 'bye' : 'Good bye {{user}}' }
183 | ;
184 |
185 | tplEngine
186 | .insertTemplate ( t )
187 | .renameTemplate ( { 'fake': 'hello', 'bye' : 'goodBye'} )
188 |
189 | const tpl = tplEngine.templates;
190 | expect ( tpl ).to.not.have.property ( 'hello' )
191 | expect ( tpl ).to.not.have.property ( 'fake' )
192 | expect ( tpl ).to.not.have.property ( 'bye' )
193 | expect ( tpl ).to.have.property ( 'goodBye' )
194 | }) // it rename a non existing template
195 |
196 |
197 |
198 | it ( 'Remove Template', () => {
199 | const
200 | tplEngine = new CodeAssemblyLine ()
201 | , tpl = {
202 | 'hello' : 'Hello {{user}}'
203 | , 'bye' : 'Good bye {{user}}'
204 | }
205 |
206 | tplEngine.insertTemplate ( tpl )
207 | tplEngine.removeTemplate ( 'hello' )
208 |
209 | expect ( tplEngine.templates ).have.property('bye')
210 | expect ( tplEngine.templates ).not.have.property('hello')
211 | }) // it remove template
212 |
213 |
214 |
215 | it ( 'Remove list of templates with array ', () => {
216 | const
217 | tplEngine = new CodeAssemblyLine ()
218 | , tpl = {
219 | 'hello' : 'Hello {{user}}'
220 | , 'bye' : 'Good bye {{user}}'
221 | }
222 |
223 | tplEngine.insertTemplate ( tpl )
224 | tplEngine.removeTemplate ( ['hello'] )
225 |
226 | expect ( tplEngine.templates ).have.property('bye')
227 | expect ( tplEngine.templates ).not.have.property('hello')
228 | }) // it remove list of templates with array
229 |
230 |
231 |
232 | it ( 'Insert Process', () => {
233 | const
234 | tplEngine = new CodeAssemblyLine ()
235 | , process = [ { do: 'draw' , tpl: 'some'} ]
236 | , altProcess = [ {do:'draw', tpl: 'alt' } ]
237 | ;
238 |
239 | tplEngine.insertProcess ( process, 'test' )
240 | tplEngine.insertProcess ( altProcess, 'test' )
241 |
242 | expect ( tplEngine.processes ).to.have.property ('test')
243 | expect ( tplEngine.processes['test'] ).to.have.property ('steps' )
244 | expect ( tplEngine.processes['test'] ).to.have.property ('arguments')
245 | expect ( tplEngine.processes['test'] ).to.have.property ('hooks' )
246 | expect ( tplEngine.processes['test']['arguments'][0]['tpl']).to.be.equal('some')
247 | expect ( tplEngine.processes['test']['arguments'][0]['tpl']).to.not.be.equal('alt')
248 | }) // it insert process
249 |
250 |
251 |
252 | it ( 'Insert Process Lib', () => {
253 | const
254 | tplEngine = new CodeAssemblyLine ()
255 | , processLib = JSON.stringify ({
256 | one : [{ do: 'draw' , tpl: 'some'}]
257 | , two : [{do:'draw', tpl: 'alt' }]
258 | , broken : {do:'draw', tpl: 'broken' }
259 | })
260 | ;
261 |
262 | tplEngine.insertProcessLib ( processLib , 'ext' )
263 | const pr = tplEngine.processes;
264 | expect ( pr ).to.have.property ('ext/one')
265 | expect ( pr ).to.have.property ( 'ext/two' )
266 | expect ( pr ).to.not.have.property ( 'two' )
267 | expect ( pr ).to.not.have.property ( 'ext/broken' )
268 |
269 | // insert lib without specify libName
270 | tplEngine.insertProcessLib ( processLib )
271 | expect ( pr ).to.have.property ( 'two' )
272 | expect ( pr ).to.not.have.property ( 'broken' )
273 |
274 | // Try to insert a non valid JSON
275 | tplEngine.insertProcessLib ( 'fakeLib', 'fake')
276 | const count = Object.keys ( pr )
277 | expect ( count.length ).to.be.equal ( 4 )
278 | }) // it insert process lib
279 |
280 |
281 |
282 | it ( 'Get process library', () => {
283 | const
284 | tplEngine = new CodeAssemblyLine()
285 | , first = [{do: 'draw', tpl: 'first'}]
286 | , second = [{do: 'draw', tpl: 'second'}]
287 | , third = [{do:'draw', tpl: 'third'}]
288 | ;
289 |
290 | tplEngine
291 | .insertProcess ( first, 'first')
292 | .insertProcess ( second, 'test/second')
293 | .insertProcess ( third, 'test/third')
294 |
295 | const jsonResult = tplEngine.getProcessLib ( 'test' );
296 | const r = JSON.parse ( jsonResult );
297 |
298 | expect ( r ).to.have.property ( 'second' )
299 | expect ( r ).to.have.property ( 'third' )
300 | expect ( r ).to.not.have.property ( 'first' )
301 |
302 | const jsonEverything = tplEngine.getProcessLib ( );
303 | const allR = JSON.parse ( jsonEverything );
304 |
305 | expect ( allR ).to.have.property ( 'first' )
306 | expect ( allR ).to.have.property ( 'test/second' )
307 | expect ( allR ).to.have.property ( 'test/third' )
308 | }) // get process library
309 |
310 |
311 | it ( 'Mix Process', () => {
312 | const
313 | tplEngine = new CodeAssemblyLine ()
314 | , liProcess = [
315 | { do: 'draw', tpl: 'link'}
316 | , { do: 'set', as: 'text'}
317 | , { do: 'draw' , tpl: 'li' }
318 | , { do: 'hook' , name: 'navButtons'}
319 | ]
320 | , ulProcess = [ {do:'draw', tpl: 'ul'} ]
321 | , ulTransition = [
322 | { do: 'set', as: 'text'}
323 | , { do: 'block' }
324 | ]
325 | ;
326 |
327 | tplEngine.insertProcess ( liProcess, 'li')
328 | tplEngine.insertProcess ( ulProcess, 'ul')
329 | tplEngine.insertProcess ( ulTransition, 'ulTransition')
330 | tplEngine.mixProcess ( ['li', 'ulTransition', 'ul','fake'], 'navigation' )
331 | tplEngine.mixProcess ( ['li','fail'],'navigation' )
332 | const res = tplEngine.processes['navigation'];
333 |
334 | expect ( res['steps'] ).to.have.length (7)
335 | expect ( res['arguments'] ).to.have.length (7)
336 | expect ( res['hooks'] ).to.have.length (1)
337 | expect ( res['hooks'][0]).to.be.equal ( 'li/navButtons' )
338 | expect ( res['arguments'][3]['name']).to.be.equal ( 'li/navButtons' )
339 |
340 | expect ( tplEngine.processes['li']['hooks'][0] ).to.be.equal('navButtons')
341 | }) // it mix process
342 |
343 |
344 |
345 | it ( 'Rename Process', () => {
346 | const
347 | tplEngine = new CodeAssemblyLine()
348 | , action = [ { do: 'draw', tpl:'random'} ]
349 | , willStay = [ {do:'draw', tpl:'nothing'} ]
350 | ;
351 | tplEngine
352 | .insertProcess ( action, 'action' )
353 | .insertProcess ( willStay, 'willStay' )
354 | .renameProcess ({
355 | 'action' : 'activity'
356 | , 'empty' : 'fake'
357 | , 'willStay' : 'activity'
358 | })
359 | const pr = tplEngine.processes;
360 |
361 | expect ( pr ).to.have.property ( 'activity' )
362 | expect ( pr ).to.not.have.property ( 'action' )
363 | expect ( pr ).to.not.have.property ( 'fake' )
364 | expect ( pr ).to.not.have.property ( 'empty' )
365 | }) // it rename process
366 |
367 |
368 |
369 | it ( 'Remove Process', () => {
370 | const
371 | tplEngine = new CodeAssemblyLine()
372 | , action = [ { do: 'draw', tpl:'random'} ]
373 | , willStay = [ {do:'draw', tpl:'nothing'} ]
374 | ;
375 | tplEngine
376 | .insertProcess ( action, 'action' )
377 | .insertProcess ( action, 'duplicate' )
378 | .insertProcess ( action, 'again' )
379 | .insertProcess ( willStay, 'willStay' )
380 | .removeProcess ( 'action' )
381 | .removeProcess ( [ 'duplicate', 'again'] )
382 |
383 | const pr = tplEngine.processes;
384 | expect ( pr ).to.have.property ( 'willStay' )
385 | expect ( pr ).to.not.have.property ( 'action' )
386 | expect ( pr ).to.not.have.property ( 'duplicate' )
387 | expect ( pr ).to.not.have.property ( 'again' )
388 | }) // it remove process
389 |
390 |
391 |
392 | it ( 'Get existing hooks', () => {
393 | const
394 | tplEngine = new CodeAssemblyLine()
395 | , templateLib = { random: 'Some text with {{place}}.' }
396 | , processData = [
397 | { do: 'draw', tpl: 'random' }
398 | , { do: 'hook', name: 'testingHook' }
399 | ]
400 | , renderData = [{ place: 'test string'}]
401 | ;
402 |
403 | tplEngine.insertProcess ( processData, 'test')
404 | tplEngine.insertTemplate ( templateLib )
405 |
406 | const result = tplEngine.getHooks ( 'test' )
407 |
408 | expect ( result ).to.have.property ( 'testingHook' )
409 | expect ( result['testingHook'] ).to.be.undefined
410 | }) // it get existing hooks
411 |
412 |
413 |
414 | it ( 'Get non existing hooks', () => {
415 | const
416 | tplEngine = new CodeAssemblyLine()
417 | , templateLib = { random: 'Some text with {{place}}.' }
418 | , processData = [
419 | { do: 'draw', tpl: 'random' }
420 | ]
421 | , renderData = [{ place: 'test string'}]
422 | ;
423 |
424 | tplEngine.insertProcess ( processData, 'test')
425 | tplEngine.insertTemplate ( templateLib )
426 |
427 | const result = tplEngine.getHooks ( 'test' )
428 |
429 | expect ( result ).to.be.empty
430 | }) // it get existing hooks
431 |
432 |
433 |
434 | it ( 'Get hooks of non existing process', () => {
435 | const
436 | tplEngine = new CodeAssemblyLine()
437 | , templateLib = { random: 'Some text with {{place}}.' }
438 | , processData = [
439 | { do: 'draw', tpl: 'random' }
440 | ]
441 | , renderData = [{ place: 'test string'}]
442 | ;
443 |
444 | tplEngine.insertProcess ( processData, 'test')
445 | tplEngine.insertTemplate ( templateLib )
446 |
447 | const result = tplEngine.getHooks ( 'fail' )
448 |
449 | expect ( result ).to.be.empty
450 | }) // it get hooks of non existing process
451 |
452 | }) // describe Run
453 |
454 |
455 |
--------------------------------------------------------------------------------
/test/08_process-operations.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , processOps = require ( '../src/process-operations' )
7 | , errors = require ( '../src/errors' )
8 | ;
9 |
10 | describe ( 'Process Operations', () => {
11 |
12 | it ( 'Draw: One placeholder', () => {
13 | const
14 | template = {
15 | tpl : [ 'Hello, ', '{{name}}', '.' ]
16 | , placeholders: { name: [1] }
17 | , spaces: {}
18 | }
19 | , data = [ { name : 'Peter' }, {name: 'Ivan'} ]
20 | ;
21 | const result = processOps.draw ( {template, data} )
22 | expect ( result ).to.be.an ( 'array' )
23 | expect ( result ).to.have.length (2)
24 | expect ( result[0] ).to.be.equal ( 'Hello, Peter.')
25 | expect ( result[1] ).to.be.equal ( 'Hello, Ivan.' )
26 | }) // it Draw: one placeholder
27 |
28 |
29 |
30 | it ( 'Draw: Two placeholder', () => {
31 | const
32 | template = {
33 | tpl : [ '{{name1}}', ' and ', '{{name2}}', ' are working together.' ]
34 | , placeholders: { name1: [0], name2: [2] }
35 | , spaces: {}
36 | }
37 | , data = [ { name1: 'Peter', name2: 'Ivan'} ]
38 | ;
39 |
40 | const result = processOps.draw ( {template, data} )
41 | expect ( result ).to.be.an ( 'array' )
42 | expect ( result ).to.have.length (1)
43 | expect ( result[0] ).to.be.equal ( 'Peter and Ivan are working together.')
44 | }) // it Draw: one placeholder
45 |
46 |
47 |
48 | it ( 'Draw: Multiple placeholders', () => {
49 | const
50 | template = {
51 | tpl : [ '{{name}}', ' and ', '{{name}}', ' product ', '{{what}}' ]
52 | , placeholders: { name: [0,2], what:[4] }
53 | , spaces: {}
54 | }
55 | , data = [ { name: 'Johnson', what: 'list'} ]
56 | ;
57 |
58 | const result = processOps.draw ( {template, data} )
59 | expect ( result ).to.be.an ( 'array' )
60 | expect ( result ).to.have.length (1)
61 | expect ( result[0] ).to.be.equal ( 'Johnson and Johnson product list')
62 | }) // it Draw: Multiple placeholders
63 |
64 |
65 |
66 | it ( 'Draw: data > placeholders', () => {
67 | const
68 | template = {
69 | tpl : [ 'Happy Birthday to my friend ', '{{name}}', '!']
70 | , placeholders: { name: [1] }
71 | , spaces: {}
72 | }
73 | , data = [ { name: 'Peter', age: '43'} ]
74 | ;
75 |
76 | const result = processOps.draw ( {template, data} )
77 | expect ( result ).to.have.length (1)
78 | expect ( result[0] ).to.be.equal ( 'Happy Birthday to my friend Peter!')
79 | }) // it Draw: data > placeholders
80 |
81 |
82 |
83 | it ( 'Draw: missField strategy "_hide"', () => {
84 | const
85 | template = {
86 | tpl : [ 'Hey ', '{{name}}', '!']
87 | , placeholders: { name: [1] }
88 | , spaces: {}
89 | }
90 | , data = [ { myName: 'Peter', age: '43'} ]
91 | ;
92 |
93 | const result = processOps.draw ( { template, data, missField:'_hide' })
94 | expect ( result ).to.have.length (1)
95 | expect ( result[0] ).to.be.equal ( 'Hey !')
96 | }) // it Draw: missField strategy _hide
97 |
98 |
99 |
100 | it ( 'Draw: missField strategy "_position"', () => {
101 | const
102 | template = {
103 | tpl : [ 'Hey ', '{{name}}', '!']
104 | , placeholders: { name: [1] }
105 | , spaces: {}
106 | }
107 | , data = [ { myName: 'Peter', age: '43'} ]
108 | ;
109 |
110 | const result = processOps.draw ( { template, data, missField: '_position'} )
111 | expect ( result ).to.have.length (1)
112 | expect ( result[0] ).to.be.equal ( 'Hey name!')
113 | }) // it Draw: missField strategy _position
114 |
115 |
116 |
117 | it ( 'Draw: missField error strategy ', () => {
118 | const
119 | template = {
120 | tpl : [ 'Hey ', '{{name}}', '!']
121 | , placeholders: { name: [1] }
122 | , spaces: {}
123 | }
124 | , data = [ { myName: 'Peter', age: '43'} ]
125 | ;
126 |
127 | const result = processOps.draw ( { template, data, missField:'{error: No data}'} )
128 | expect ( result ).to.have.length (1)
129 | expect ( result[0] ).to.be.equal ( 'Hey {error: No data}!')
130 | }) // it Draw: missField error strategy
131 |
132 |
133 |
134 | it ( 'Draw: missData strategy "_hide"', () => {
135 | const
136 | template = {
137 | tpl : [ 'Hey ', '{{name}}', '!']
138 | , placeholders: { name: [1] }
139 | , spaces : {}
140 | }
141 | , data = [
142 | { myName: 'Peter', age: '43'}
143 | , { name: 'Ivan', age: '41'}
144 | , { name: 'Ivo', age: '19'}
145 | ]
146 | ;
147 |
148 | const result = processOps.draw ( { template, data, missData: '_hide'} )
149 | expect ( result ).to.have.length (2)
150 | expect ( result[0] ).to.be.equal ( 'Hey Ivan!')
151 | expect ( result[1] ).to.be.equal ( 'Hey Ivo!' )
152 | }) // it Draw: missData strategy "hide"
153 |
154 |
155 |
156 | it ( 'Draw: missData error strategy', () => {
157 | const
158 | template = {
159 | tpl : [ 'Hey ', '{{name}}', '!']
160 | , placeholders: { name: [1] }
161 | , spaces : {}
162 | }
163 | , data = [
164 | { myName: 'Peter', age: '43'}
165 | , { name: 'Ivan', age: '41'}
166 | , { name: 'Ivo', age: '19'}
167 | ]
168 | ;
169 |
170 | const result = processOps.draw ({ template, data, missData: 'Error: Missing data' })
171 | expect ( result ).to.have.length (3)
172 | expect ( result[0] ).to.be.equal ( 'Error: Missing data')
173 | expect ( result[1] ).to.be.equal ( 'Hey Ivan!')
174 | expect ( result[2] ).to.be.equal ( 'Hey Ivo!' )
175 | }) // it Draw: missData error strategy
176 |
177 |
178 |
179 | it ( 'Alter Template', () => {
180 | const
181 | step = { do: 'alterTemplate', data: { 'name':'user', 'age': 'alt'} }
182 | , sourcePlaceholders = { name : 1, fixed: 'will stay' }
183 | ;
184 | const result = processOps.alterTemplate ( step, sourcePlaceholders )
185 | expect ( sourcePlaceholders ).to.have.property ( 'name' )
186 | expect ( sourcePlaceholders ).to.not.have.property ( 'user' )
187 | expect ( sourcePlaceholders ).to.not.have.property ( 'age' )
188 | expect ( sourcePlaceholders ).to.not.have.property ( 'alt' )
189 |
190 | expect ( result ).to.have.property ( 'user' )
191 | expect ( result ).to.not.have.property ( 'name' )
192 | expect ( result ).to.not.have.property ( 'age' )
193 | expect ( result ).to.not.have.property ( 'alt' )
194 | }) // it alter template
195 |
196 |
197 |
198 | it ( 'Block', () => {
199 | const
200 | data = [
201 | 'Hey Ivan!'
202 | ,'Hey Ivo!'
203 | ]
204 | , space = ' '
205 | ;
206 | const result = processOps.block ( data, space )
207 | expect ( result ).to.be.an ('array')
208 | expect ( result ).to.have.length (1)
209 | expect ( result[0] ).to.be.equal ( 'Hey Ivan! Hey Ivo!')
210 | }) // it Block
211 |
212 |
213 |
214 | it ( 'Set', () => {
215 | const
216 | data = [
217 | 'Hey Ivan!'
218 | ,'Hey Ivo!'
219 | ]
220 | , step = { do:'set', as: 'text' }
221 | ;
222 |
223 | const result = processOps.set ( step, data )
224 | expect ( result ).to.be.an ( 'array' )
225 | expect ( result ).to.have.length (2)
226 | expect ( result[0] ).to.be.an ('object')
227 | expect ( result[0] ).to.have.property('text')
228 | expect ( result[0]['text'] ).to.be.equal ('Hey Ivan!')
229 | }) // it Set
230 |
231 |
232 |
233 | it ( 'Alter', () => {
234 | const
235 | step = { do: 'alter', data: {'user':'text'} }
236 | , data = [
237 | { user: 'Peter', age: 43 }
238 | , { user: 'Ivo', age: 19 }
239 | ]
240 |
241 | const result = processOps.alter ( step, data )
242 | expect ( result ).to.be.an('array')
243 | expect ( result ).to.have.length (2)
244 | expect ( result[0] ).to.have.property('text')
245 | expect ( result[0]['text'] ).to.be.equal ( 'Peter' )
246 | expect ( result[0] ).to.have.property('age')
247 | expect ( result[0] ).to.not.have.property('user')
248 | }) // it alter
249 |
250 |
251 |
252 | it ( 'Alter with selection "all"', () => {
253 | const
254 | step = { do: 'alter', select:'all', data: {'user':'text'} }
255 | , data = [
256 | { user: 'Peter', age: 43 }
257 | , { user: 'Ivo', age: 19 }
258 | ]
259 |
260 | const result = processOps.alter ( step, data )
261 | expect ( result ).to.be.an('array')
262 | expect ( result ).to.have.length (2)
263 | expect ( result[0] ).to.have.property('text')
264 | expect ( result[0]['text'] ).to.be.equal ( 'Peter' )
265 | expect ( result[0] ).to.have.property('age')
266 | expect ( result[0] ).to.not.have.property('user')
267 | }) // it alter with selection "all"
268 |
269 |
270 |
271 | it ( 'Alter with selection "first"', () => {
272 | const
273 | step = { do: 'alter', select:'first', data: {'user':'text'} }
274 | , data = [
275 | { user: 'Peter', age: 43 }
276 | , { user: 'Ivo', age: 19 }
277 | ]
278 |
279 | const result = processOps.alter ( step, data )
280 | expect ( result ).to.be.an('array')
281 | expect ( result ).to.have.length (2)
282 | expect ( result[0] ).to.have.property('text')
283 | expect ( result[0]['text'] ).to.be.equal ( 'Peter' )
284 | expect ( result[0] ).to.have.property('age')
285 | expect ( result[0] ).to.not.have.property('user')
286 | expect ( result[1] ).to.have.property ('user')
287 | expect ( result[1] ).to.not.have.property ('text')
288 | }) // it alter with selection first
289 |
290 |
291 |
292 | it ( 'Alter with selection "last"', () => {
293 | const
294 | step = { do: 'alter', select:'last', data: {'user':'text'} }
295 | , data = [
296 | { user: 'Peter', age: 43 }
297 | , { user: 'Ivo', age: 19 }
298 | ]
299 |
300 | const result = processOps.alter ( step, data )
301 | expect ( result ).to.be.an('array')
302 | expect ( result ).to.have.length (2)
303 | expect ( result[1] ).to.have.property('text')
304 | expect ( result[1]['text'] ).to.be.equal ( 'Ivo' )
305 | expect ( result[1] ).to.have.property('age')
306 | expect ( result[1] ).to.not.have.property('user')
307 | expect ( result[0] ).to.have.property ('user')
308 | expect ( result[0] ).to.not.have.property ('text')
309 | }) // it alter with selection last
310 |
311 |
312 |
313 | it ( 'Alter with selection -1', () => {
314 | const
315 | step = { do: 'alter', select:-1, data: {'user':'text'} }
316 | , data = [
317 | { user: 'Peter', age: 43 }
318 | , { user: 'Ivo', age: 19 }
319 | ]
320 |
321 | const result = processOps.alter ( step, data )
322 | expect ( result ).to.be.an('array')
323 | expect ( result ).to.have.length (2)
324 | expect ( result[0] ).to.have.property('text')
325 | expect ( result[0]['text'] ).to.be.equal ( 'Peter' )
326 | expect ( result[0] ).to.have.property('age')
327 | expect ( result[0] ).to.not.have.property('user')
328 | expect ( result[1] ).to.have.property ('user')
329 | expect ( result[1] ).to.not.have.property ('text')
330 | }) // it alter with selection -1
331 |
332 |
333 |
334 | it ( 'Alter with selection 1', () => {
335 | const
336 | step = { do: 'alter', select:1, data: {'user':'text'} }
337 | , data = [
338 | { user: 'Peter', age: 43 }
339 | , { user: 'Ivo', age: 19 }
340 | ]
341 | ;
342 |
343 | const result = processOps.alter ( step, data )
344 | expect ( result ).to.be.an('array')
345 | expect ( result ).to.have.length (2)
346 | expect ( result[0] ).to.have.property('text')
347 | expect ( result[0]['text'] ).to.be.equal ( 'Peter' )
348 | expect ( result[0] ).to.have.property('age')
349 | expect ( result[0] ).to.not.have.property('user')
350 | expect ( result[1] ).to.have.property ('user')
351 | expect ( result[1] ).to.not.have.property ('text')
352 | }) // it alter with selection 1
353 |
354 |
355 |
356 | it ( 'Add', () => {
357 | const
358 | step = { do: 'add', select:['first',2], data: {'className': 'simple'} }
359 | , data = [
360 | { user: 'Peter' , age: 43, className: 'test' }
361 | , { user: 'Stefan', age: 42, className: ['test','second'] }
362 | , { user: 'Ivo' , age: 19, className: 'test' }
363 | ]
364 | ;
365 | const result = processOps.add ( step, data );
366 | expect ( result ).to.have.length (3)
367 | expect ( result[0]['className'] ).to.be.an ( 'string' )
368 | expect ( result[0]['className'] ).to.be.equal ( 'test simple' )
369 | expect ( result[1]['className'] ).to.be.an ( 'string' )
370 | expect ( result[1]['className'] ).to.be.equal ( 'test second simple' )
371 | expect ( result[2]['className'] ).to.be.a ( 'string' )
372 | expect ( result[2]['className'] ).to.be.equal ( 'test' )
373 | }) // it add
374 |
375 |
376 |
377 | it ( 'Copy', () => {
378 | const
379 | step = { do: 'copy', select:['first','last'], data: {'className': 'alt'} }
380 | , data = [
381 | { user: 'Peter', age: 43, className: ['test','first'] }
382 | , { user: 'Ivo', age: 19, className: 'test' }
383 | , { user: 'Ivan', age: 39, className: 'test' }
384 | ]
385 | ;
386 |
387 | const result = processOps.copy ( step, data )
388 | expect ( result ).to.have.length (3)
389 | expect ( result[0] ).to.have.property ( 'alt' )
390 | expect ( result[0]['alt']).to.be.an ( 'array' )
391 | expect ( result[0]['alt']).to.includes ( 'test' )
392 | expect ( result[0]['alt']).to.includes ( 'first' )
393 | expect ( result[1] ).to.not.have.property ( 'alt')
394 | expect ( result[2] ).to.have.property ( 'alt')
395 | expect ( result[2]['alt'] ).to.be.a ('string')
396 | expect ( result[2]['alt'] ).to.be.equal ('test')
397 | }) // it copy
398 |
399 |
400 |
401 | it ( 'Remove', () => {
402 | const
403 | step = { do: 'remove', select:['first','last'], keys: ['className', 'age', 'alt'] }
404 | , data = [
405 | { user: 'Peter', age: 43, className: ['test','first'] }
406 | , { user: 'Ivo', age: 19, className: 'test' }
407 | , { user: 'Ivan', age: 39, className: 'test' }
408 | ]
409 | ;
410 |
411 | const result = processOps.remove ( step, data )
412 | expect ( result ).to.have.length (3)
413 | expect ( result[0] ).to.not.have.property ( 'alt' )
414 | expect ( result[0] ).to.not.have.property ( 'className' )
415 | expect ( result[0] ).to.not.have.property ( 'age' )
416 | expect ( result[1] ).to.have.property ( 'className')
417 | expect ( result[2] ).to.not.have.property ( 'alt')
418 | expect ( result[2] ).to.not.have.property ( 'age')
419 | expect ( result[2] ).to.not.have.property ( 'className')
420 | }) // it remove
421 |
422 |
423 |
424 | it ( 'Hook. Function defined', () => {
425 | const data = [{ user: 'Peter', age:43}];
426 | const fn = data => [{ user: 'Ivo', age:21}];
427 |
428 | const result = processOps.hook ( data, fn )
429 | expect ( result[0].user ).to.be.equal ('Ivo')
430 | expect ( result[0].age ).to.be.equal (21)
431 | }) // it hook
432 |
433 |
434 |
435 | it ( 'Hook. Use step-operation to modify hook data', () => {
436 | // * Get more details in 'Hook Modifiers' chapter
437 | const
438 | data = [{ user: 'Peter', age: 43 }]
439 | , fn = (data, modify) => modify ( data, { do:'alter', data: {'user':'name'}} )
440 | ;
441 |
442 | const result = processOps.hook ( data, fn )
443 | expect ( result[0] ).to.not.have.property ( 'user' )
444 | expect ( result[0].name ).to.be.equal ( 'Peter' )
445 | expect ( result[0].age ).to.be.equal (43)
446 | }) // it hook
447 |
448 |
449 |
450 | it ( 'Hook. Function is not defined', () => {
451 | const
452 | data = [{ user: 'Peter', age:43}]
453 | , hook = {}
454 | ;
455 |
456 |
457 | const result = processOps.hook ( data, hook.test )
458 | expect ( result).is.an ( 'array' )
459 | expect ( result ).has.length(1)
460 | expect ( result[0] ).has.property ( 'user' )
461 | expect ( result[0] ).has.property ( 'age' )
462 | expect ( result[0].user ).is.equal ( 'Peter' )
463 | }) // it hook, no function
464 |
465 |
466 | it ( 'Calculate "attr"', () => {
467 | const
468 | createAttributes = processOps._createAttributes
469 | , attributes = [ 'id', 'name', 'href', 'src', 'value', 'data-test', 'alt', 'role', 'class' ]
470 | , altAttributes = [ 'id', 'data', 'class']
471 | , data = {
472 | 'href' : 'test.com/anypage.html'
473 | , 'text' : 'Link to nowhere'
474 | , 'className' : 'fashion'
475 | , 'data-test' : 'yo'
476 | , 'data-again': 'hey'
477 | , 'id' : 'top'
478 | , 'name' : 'Ivan'
479 | }
480 | ;
481 |
482 | const result = createAttributes ( data, attributes );
483 | expect ( result ).to.be.a ( 'string' )
484 | expect ( result ).to.not.include ( 'text' )
485 | expect ( result ).to.include ( 'data-test' )
486 | expect ( result ).to.not.include ( 'data-again' )
487 |
488 | const altResult = createAttributes ( data, altAttributes );
489 | expect ( altResult ).to.be.a ( 'string' )
490 | expect ( altResult ).to.not.include ( 'text' )
491 | expect ( altResult ).to.include ( 'data-test' )
492 | expect ( altResult ).to.include ( 'data-again' )
493 | expect ( altResult ).to.include ( 'class="fashion"' )
494 |
495 | const
496 | placeId = result.indexOf('id')
497 | , placeHref = result.indexOf('href="')
498 | , placeClass = result.indexOf ('class="fashion"')
499 | ;
500 |
501 | expect ( placeClass, "Class attribute is missing" ).to.not.be.equal(-1)
502 | expect ( null, 'Attribute "id" should be first' ).to.satisfies ( () => placeId < placeHref )
503 | expect ( null, 'Attribute "href" should be before "class"').to.satisfies ( () => placeHref < placeClass )
504 | }) // it calculate attr
505 |
506 | }) // describe
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /*
4 | Code Assembly Line
5 | - started at 23 september 2017
6 | */
7 |
8 | const
9 | templateTools = require ( './template-tools' )
10 | , processTools = require ( './process-tools' )
11 | , tools = require ( './general-tools' )
12 | , showError = require ( './errors' )
13 | ;
14 |
15 | const str2intTemplate = templateTools.str2intTemplate;
16 | const interpretTemplate = templateTools.load_interpretTemplate ( str2intTemplate );
17 |
18 |
19 | // Default config object applied to every codeAssembly object.
20 | const codeAssemblyConfig = {
21 | overwriteTemplates : false
22 | , overwriteProcesses : false
23 | , overwriteData : false
24 | , htmlAttributes : [ 'id', 'name', 'href', 'src', 'value', 'data', 'alt', 'role', 'class' ]
25 | } // config
26 |
27 |
28 |
29 | function codeAssembly ( cfg ) {
30 | this.templates = {}
31 | this.processes = {}
32 | this.data = {}
33 | this.config = {}
34 |
35 | Object.keys(codeAssemblyConfig).forEach ( k => this.config[k] = codeAssemblyConfig[k] )
36 | if ( cfg ) Object.keys(cfg).forEach ( k => this.config[k] = cfg[k] )
37 | } // codeAssembly func.
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | const help = {
58 |
59 | _isWritingForbidden ( engine , type, name ) {
60 | let configProperty;
61 | if ( type == 'templates' ) configProperty = 'overwriteTemplates'
62 | else if ( type == 'processes' ) configProperty = 'overwriteProcesses'
63 | else configProperty = 'overwriteData'
64 | const entryExists = engine[type].hasOwnProperty ( name ); // 'true' if template/process/data with this name already exists
65 | const entryForbidden = entryExists && !engine.config[configProperty]
66 | return entryForbidden
67 | } // _isWritingForbidden func.
68 |
69 |
70 |
71 | , _normalizeExternalData ( data ) {
72 | let result;
73 | if ( !(data instanceof Array )) result = [data]
74 | else {
75 | result = data.map ( el => {
76 | if ( typeof el == 'object' ) return help._flatten(el)
77 | else return el
78 | })
79 | } // else no array
80 | return result
81 | } // _normalizeExternalData func.
82 |
83 |
84 |
85 | , _validateProcess ( engine, processName ) {
86 | // * Find if process exists and no errors in it. Find if all templates needed are available.
87 | let errors = [];
88 | const
89 | processExists = engine.processes.hasOwnProperty ( processName )
90 | , intProcess = engine.processes[processName]
91 | ;
92 |
93 | if ( processExists && intProcess['errors'] ) errors = errors.concat( intProcess['errors'])
94 |
95 | if ( !processExists ) errors.push ( showError('processNotExists',processName) )
96 | else {
97 | intProcess['arguments']
98 | .reduce ( (listTemplates, step) => {
99 | if ( step.do == 'draw' && step.tpl ) listTemplates.push ( step.tpl )
100 | return listTemplates
101 | },[])
102 | .forEach ( name => {
103 | const tpl = engine.templates[name];
104 | if ( tpl && tpl['errors'] ) errors = errors.concat ( tpl['errors'] )
105 | if ( !tpl ) errors.push ( showError('templateNotExists',name) )
106 | })
107 | }
108 | return errors
109 | } // _validateProcess func.
110 |
111 |
112 |
113 | , _validateProcessLib ( ext ) { // (JSON) -> { processName : intProcess } | false
114 | // * Find if JSON is valid, find if every process is an array.
115 | try {
116 | const
117 | list = JSON.parse ( ext )
118 | , processNames = Object.keys(list)
119 | ;
120 | return processNames.reduce ( (res,name) => {
121 | if ( list[name] instanceof Array ) res[name] = [].concat(list[name])
122 | return res
123 | },{})
124 | } // try
125 | catch ( e ) {
126 | return false
127 | }
128 | } // _validateProcessLib func.
129 |
130 |
131 |
132 | , _extractLib ( tpl, libRequst ) { // (tpl:inTemplates[], libRequest:string[]) -> ExternalTemplate
133 | // * Creates new ExternalTemplate by removing libName from template name
134 | const tplNames = Object.keys ( tpl ).filter ( name => name.includes('/') );
135 |
136 | return libRequst.reduce ( (res, libItem ) => {
137 | tplNames
138 | .filter ( name => name.indexOf(libItem) == 0 )
139 | .forEach ( name => {
140 | const
141 | sliceIndex = name.indexOf('/')
142 | , prop = name.slice ( sliceIndex+1 )
143 | ;
144 |
145 | res [prop] = tpl[name].tpl.join('')
146 | })
147 | return res
148 | },{})
149 | } // _extractLib func.
150 |
151 |
152 |
153 | , _flatten ( data, res , keyIn ) {
154 | let result = res || {};
155 |
156 | keyIn = keyIn || ''
157 |
158 | for ( let key in data ) {
159 | const value = data[key]
160 | let newKey = key
161 | if (keyIn) newKey = `${keyIn}/${key}`
162 | if ( typeof value == 'function' ) continue // Data can't contain functions
163 | if ( help._isPrimitive(value) ) result[newKey] = value
164 | else return help._flatten ( value, result, newKey )
165 | }
166 | return result
167 | } // _flatten func. -- Help
168 |
169 |
170 |
171 | , _isPrimitive ( value ) {
172 | return ( typeof value === 'object' ) ? false : true;
173 | } // isPrimitive func. -- Help
174 |
175 | } // help lib
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | const lib_Template = {
197 |
198 | insert ( extTemplate ) { // (extTemplate: ExternalTemplate) -> engine
199 | let
200 | me = this
201 | , templateNames = Object.keys( extTemplate )
202 | ;
203 |
204 | templateNames.forEach ( name => {
205 | if ( help._isWritingForbidden(me,'templates',name) ) {
206 | console.error ( showError('overwriteTemplate') )
207 | return
208 | }
209 | me.templates [ name ] = interpretTemplate ( extTemplate[name] )
210 | })
211 | return me
212 | } // insert func. -- Template
213 |
214 |
215 |
216 | , insertLib ( extLib, libName ) { // ( extLib: ExternalTemplate, name: string ) -> engine
217 | let
218 | me = this
219 | , simpleTemplates = Object.keys ( extLib )
220 | ;
221 |
222 | simpleTemplates.forEach ( extName => {
223 | const
224 | newTpl = {}
225 | , name = `${libName}/${extName}`
226 | ;
227 | if ( help._isWritingForbidden(me,'templates',name) ) {
228 | console.error ( showError('overwriteTemplate') )
229 | return
230 | }
231 | newTpl[name] = extLib[extName]
232 | lib_Template.insert.call ( me, newTpl )
233 | })
234 | return me
235 | } // insertLib func. -- Template
236 |
237 |
238 |
239 | , rename ( ops ) { // ({oldName:newName}) -> engine
240 | // * Change template names
241 | const
242 | me = this
243 | , list = Object.keys (ops)
244 | , doOverwrite = me.config.overwriteTemplates
245 | ;
246 |
247 | list.forEach ( key => {
248 | if ( !me.templates[key] ) return
249 | const
250 | newKey = ops[key]
251 | , keyExists = (me.templates[newKey]) ? true : false
252 | ;
253 |
254 | if ( keyExists && !doOverwrite ){
255 | console.error ( showError('overwriteTemplate') )
256 | return
257 | }
258 | else {
259 | me.templates[newKey] = me.templates[key]
260 | delete me.templates[key]
261 | }
262 | })
263 | return me
264 | } // rename func. -- Template
265 |
266 |
267 |
268 | , remove ( tplName ) { // ( tplName:string|string[]) -> engine
269 | const me = this;
270 | let listDelete;
271 |
272 | if ( tplName instanceof Array ) listDelete = tplName
273 | else {
274 | const t = Object.keys ( arguments );
275 | listDelete = t.map ( k => arguments[k] )
276 | }
277 |
278 | listDelete.forEach ( item => delete me.templates[item])
279 | return me
280 | } // remove func. -- Template
281 |
282 |
283 |
284 | , get ( tplName ) { // (tplName: string|string[]) -> ExternalTemplate
285 | const
286 | me = this
287 | , tpl = me.templates
288 | ;
289 | let tplRequst;
290 |
291 | if ( tplName instanceof Array ) tplRequst = tplName
292 | else {
293 | const t = Object.keys ( arguments );
294 | tplRequst = t.map ( k => arguments[k] )
295 | }
296 |
297 | return tplRequst.reduce ( (res, item ) => {
298 | if ( tpl[item] ) res[item] = tpl[item].tpl.join('')
299 | else res[item] = ''
300 | return res
301 | },{})
302 |
303 | } // get func. -- Template
304 |
305 |
306 |
307 | , getLib ( libName ) { // (libName:string|string[]) -> ExternalTemplate
308 | const me = this;
309 | let libRequst;
310 |
311 | if ( libName instanceof Array ) libRequst = libName
312 | else {
313 | const t = Object.keys ( arguments );
314 | libRequst = t.map ( k => arguments[k] )
315 | }
316 |
317 | return help._extractLib ( me.templates, libRequst )
318 | } // getLib func. -- Template
319 |
320 |
321 |
322 | , getPlaceholders ( templateName ) { // (templateName:string) -> placeholderNames:string[].
323 | const tpl = this.templates [ templateName ];
324 |
325 | if ( tpl ) return Object.keys ( tpl.placeholders )
326 | else return []
327 | } // getPlaceholders func. -- Template
328 |
329 | } // lib_Template lib
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 | const lib_Process = {
351 |
352 | insert ( ext, name ) { // (ext: extProcess, name: string) -> engine
353 | const me = this;
354 |
355 | if ( help._isWritingForbidden(me,'processes',name) ) {
356 | console.error ( showError('overwriteProcess',name) )
357 | return
358 | }
359 |
360 | me.processes[name] = processTools.interpret ( ext )
361 | return me
362 | } // insert func. -- Process
363 |
364 |
365 |
366 | , insertLib ( extLib, libName ) { // ( processLib: JSON, name: string ) -> engine
367 | let
368 | me = this
369 | , listOfProcesses = help._validateProcessLib ( extLib )
370 | , processNames = Object.keys ( listOfProcesses )
371 | ;
372 | if ( listOfProcesses ) {
373 | processNames.forEach ( extName => {
374 | const name = ( libName ) ? `${libName}/${extName}` : extName;
375 | lib_Process.insert.call ( me, listOfProcesses[extName], name )
376 | })
377 | }
378 | return me
379 | } // insertLib func. -- Process
380 |
381 |
382 |
383 | , mix ( mixList, newProcessName ) { // ( mixList:string[], processName:string ) -> engine
384 | // * Set new process as combination of existing processes
385 | const
386 | me = this
387 | , processes = me.processes
388 | ;
389 |
390 | let mix = {}; // new process container
391 |
392 | if ( help._isWritingForbidden(me,'processes',newProcessName) ) {
393 | console.error ( showError('overwriteProcess', newProcessName) )
394 | return
395 | }
396 |
397 | mix.steps = []
398 | mix.arguments =[]
399 | mix.hooks = []
400 |
401 | mixList.forEach ( requestedName => {
402 | if ( !processes[requestedName] ) return
403 | const
404 | source = processes[requestedName]
405 | , hookNames = source.hooks.map ( name => `${requestedName}/${name}` )
406 | , newArguments = source.arguments.map ( arg => {
407 | if ( arg.do != 'hook' ) return arg
408 | return { do: 'hook', name: `${requestedName}/${arg.name}` }
409 | })
410 | ;
411 |
412 | mix['steps'] = mix['steps'].concat ( source['steps'] )
413 | mix['arguments'] = mix['arguments'].concat ( newArguments )
414 | mix['hooks'] = mix['hooks'].concat ( hookNames )
415 | })
416 | me.processes[newProcessName] = mix
417 | return me
418 | } // mix func. -- Process
419 |
420 |
421 |
422 | , getLib ( name ) {
423 | // * Extract process library as JSON
424 | const
425 | me = this
426 | , allKeys = Object.keys ( me.processes )
427 | , takeEverything = (name) ? false : true
428 | ;
429 | const result = allKeys.reduce ( (res,key) => {
430 | if ( !takeEverything ) {
431 | if ( key.includes(name) ) {
432 | const t = key.split('/');
433 | t.shift()
434 | const newKey = t.join('/');
435 | res[newKey] = me.processes[key]
436 | }
437 | }
438 | else res[key] = me.processes[key]
439 | return res
440 | }, {})
441 | return JSON.stringify ( result )
442 | } // getLib func. -- Process
443 |
444 |
445 |
446 | , rename ( ops ) { // ({oldName:newName}) -> engine
447 | // * Change process names
448 | const
449 | me = this
450 | , list = Object.keys (ops)
451 | , doOverwrite = me.config.overwriteProcesses
452 | ;
453 |
454 | list.forEach ( key => {
455 | if ( !me.processes[key] ) return
456 | const
457 | newKey = ops[key]
458 | , keyAlreadyDefined = (me.processes[newKey]) ? true : false
459 | ;
460 |
461 | if ( keyAlreadyDefined && !doOverwrite ) {
462 | console.error ( showError('overwriteProcess', newKey) )
463 | return
464 | }
465 | else {
466 | me.processes[newKey] = me.processes[key]
467 | delete me.processes[key]
468 | }
469 | })
470 | return me
471 | } // rename func. -- Process
472 |
473 |
474 |
475 | , remove ( processName ) { // ( processName:string|string[]) -> engine
476 | const me = this;
477 | let listDelete;
478 |
479 | if ( processName instanceof Array ) listDelete = processName
480 | else {
481 | const t = Object.keys ( arguments );
482 | listDelete = t.map ( k => arguments[k] )
483 | }
484 |
485 | listDelete.forEach ( item => delete me.processes[item])
486 | return me
487 | } // remove func. -- Process
488 |
489 |
490 |
491 | , getHooks ( processName ) { // (processName) -> { hookName : undefined }
492 | const recordExists = this.processes.hasOwnProperty ( processName );
493 | if ( recordExists ) {
494 | const hooks = this.processes[ processName ].hooks;
495 | return hooks.reduce ( (res, name) => {
496 | res[name] = undefined
497 | return res
498 | },{})
499 | }
500 | else return {}
501 | } // getHooks func. -- Process
502 |
503 |
504 |
505 | , run ( processList, data, hooks ) {
506 | if ( !(processList instanceof Array )) processList = [processList]
507 | const
508 | internalData = help._normalizeExternalData(data)
509 | , error = processList.reduce ( (res, processName ) => {
510 | const errorUpdate = help._validateProcess(this, processName);
511 | return res.concat ( errorUpdate )
512 | },[])
513 | ;
514 |
515 | if ( error.length == 0 ) {
516 | let current = internalData;
517 | processList.forEach ( processName => {
518 | current = processTools.run.call ( this, this.processes[processName], current, hooks )
519 | })
520 | return current
521 | }
522 | else return error
523 | } // run func. -- Process
524 |
525 | } // lib_Process lib
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 | const lib_Data = {
547 | insert ( data ) { // ({}) -> engine
548 | const
549 | me = this
550 | , flatData = help._flatten ( data )
551 | , dataNames = Object.keys( flatData )
552 | ;
553 |
554 | dataNames.forEach ( name => {
555 | if ( help._isWritingForbidden(me,'data',name) ) {
556 | console.error ( showError('overwriteData', name) )
557 | return
558 | }
559 | me.data [ name ] = flatData[name]
560 | })
561 | return me
562 | } // insert func. -- Data
563 |
564 |
565 |
566 | , insertLib ( data, libName ) { // ({}, string) -> engine
567 | const
568 | me = this
569 | , flatData = help._flatten ( data )
570 | , dataNames = Object.keys ( flatData )
571 | ;
572 |
573 | dataNames
574 | .forEach ( name => {
575 | const newKey = `${libName}/${name}`
576 | if ( help._isWritingForbidden ( me, 'data', newKey ) ) {
577 | console.error ( showError('overwriteData',newKey) )
578 | return
579 | }
580 | me.data [ newKey ] = flatData[name]
581 | })
582 | return me
583 | } // insertLib func. -- Data
584 |
585 |
586 |
587 | , rename ( ops ) { // ({oldName:newName}) -> engine
588 | // * Change data-record names
589 | const
590 | me = this
591 | , list = Object.keys (ops)
592 | , doOverwrite = me.config.overwriteData
593 | ;
594 |
595 | list.forEach ( key => {
596 | if ( !me.data[key] ) return
597 | const
598 | newKey = ops[key]
599 | , keyAlreadyDefined = (me.data[newKey]) ? true : false
600 | ;
601 |
602 | if ( keyAlreadyDefined && !doOverwrite ) {
603 | console.error ( showError('overwriteData', newKey) )
604 | return
605 | }
606 | else {
607 | me.data[newKey] = me.data[key]
608 | delete me.data[key]
609 | }
610 | })
611 | return me
612 | } // rename func --- Data
613 |
614 |
615 |
616 | , remove ( dataName ) { // ( dataName:string|string[]) -> engine
617 | const me = this;
618 | let listDelete;
619 |
620 | if ( dataName instanceof Array ) listDelete = dataName
621 | else {
622 | const t = Object.keys ( arguments );
623 | listDelete = t.map ( k => arguments[k] )
624 | }
625 |
626 | listDelete.forEach ( item => delete me.data[item])
627 | return me
628 | } // remove func. -- Data
629 |
630 |
631 |
632 | , getBlock ( blockName ) { // ( string ) -> string
633 | const me = this;
634 | let blockRequst = [];
635 | if ( blockName instanceof Array ) blockRequst = blockName
636 | else {
637 | const t = Object.keys ( arguments );
638 | blockRequst = t.map ( k => arguments[k] )
639 | }
640 | return blockRequst.reduce ( (res, name) => {
641 | const snippet = ( me.data[`block/${name}`] ) ? me.data[`block/${name}`] : '';
642 | res += snippet
643 | return res
644 | }, '')
645 | } // getBlock func. -- Data
646 | } // lib_Data
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 | // * Code-Assembly-Line API
668 | codeAssembly.prototype = {
669 | tools : tools // Usefull template and process related functions (external)
670 |
671 | // Template I/O Operations
672 | , insertTemplate : lib_Template.insert // Insert templates;
673 | , insertTemplateLib : lib_Template.insertLib // Insert templates as a template-library;
674 | , getTemplate : lib_Template.get // Export templates;
675 | , getTemplateLib : lib_Template.getLib // Export templates from template-library;
676 | , getPlaceholders : lib_Template.getPlaceholders // Return placeholders per template;
677 |
678 | // Template Manipulation
679 | , renameTemplate : lib_Template.rename // Change name of template/templates;
680 | , removeTemplate : lib_Template.remove // Remove template/templates;
681 |
682 | // Processes
683 | , insertProcess : lib_Process.insert // Insert new process;
684 | , insertProcessLib : lib_Process.insertLib // Insert list of processes with a single operation. JSON required;
685 | , mixProcess : lib_Process.mix // Set new process as combination of existing processes;
686 | , getProcessLib : lib_Process.getLib // Export processes from process-library as JSON;
687 | , getHooks : lib_Process.getHooks // Provide information about hooks available
688 | , run : lib_Process.run // Execute process/processes
689 |
690 | // Process Manipulation
691 | , renameProcess : lib_Process.rename // Renames a process
692 | , removeProcess : lib_Process.remove // Remove process/processes
693 |
694 | // Data I/O
695 | , insertData : lib_Data.insert // Insert data. Save data. Word 'blocks'
696 | , insertDataLib : lib_Data.insertLib // Insert set of data
697 | , getBlock : lib_Data.getBlock // Obtain rendered code snippets
698 | , getBlocks : lib_Data.getBlock
699 |
700 | // Data Manipulation
701 | , renameData : lib_Data.rename // Change name of data record
702 | , removeData : lib_Data.remove // Remove data record from template engine
703 | } // codeAssembly prototype
704 |
705 |
706 |
707 | module.exports = codeAssembly;
708 |
709 |
710 |
--------------------------------------------------------------------------------
/test/12_run.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const
4 | chai = require ('chai')
5 | , expect = chai.expect
6 | , CodeAssemblyLine = require ( '../src/index' )
7 | , showError = require ( '../src/errors' )
8 | ;
9 |
10 |
11 |
12 | describe ( 'Run', () => {
13 |
14 |
15 |
16 | it ( 'Process has error', () => {
17 | const
18 | tplEngine = new CodeAssemblyLine()
19 | , fakeProcess = [{tpl:'fake'}]
20 | ;
21 |
22 | tplEngine.insertProcess ( fakeProcess, 'fake')
23 | const result = tplEngine.run ( 'fake' )
24 |
25 | expect ( result ).to.be.an('array')
26 | expect ( result[0] ).to.be.equal( showError('missingOperation') )
27 | }) // it Process has error
28 |
29 |
30 |
31 | it ( 'Template has error', () => {
32 | const
33 | tplEngine = new CodeAssemblyLine()
34 | , brokenTpl = { 'break' : 'Something}} {{broken'}
35 | , drawBreak = [ { do:'draw', tpl:'break'} ]
36 | ;
37 |
38 | tplEngine.insertTemplate ( brokenTpl )
39 | tplEngine.insertProcess ( drawBreak, 'drawBreak' )
40 |
41 | const result = tplEngine.run ( 'drawBreak' );
42 | expect ( result ).to.be.an('array')
43 | expect ( result[0] ).to.be.equal ( showError('brokenTemplate') )
44 | }) // it Template has error
45 |
46 |
47 |
48 | it ( 'Draw. Valid operation', () => {
49 | const
50 | tplEngine = new CodeAssemblyLine()
51 | , templateLib = { random: 'Some text with {{place}}.' }
52 | , processData = [
53 | { do: 'draw', tpl: 'random' }
54 | , { do: 'block' }
55 | ]
56 | , renderData = [{ place: 'test string'}]
57 | ;
58 |
59 | tplEngine.insertProcess ( processData, 'test')
60 | tplEngine.insertTemplate ( templateLib )
61 |
62 | const result = tplEngine.run ( 'test', renderData )
63 |
64 | expect ( result ).to.be.an ('array')
65 | expect ( result[0] ).to.be.equal ( 'Some text with test string.' )
66 | }) // it Draw. Valid operation
67 |
68 |
69 |
70 | it ( 'Draw. Enrich data', () => {
71 | const
72 | tplEngine = new CodeAssemblyLine()
73 | , simple = 'Some text with {{place}}.'
74 | , complex = '{{place}}: {{text}}'
75 | , processTest = [
76 | { do: 'draw', tpl: 'simple', as: 'text' }
77 | , { do: 'draw', tpl: 'complex' }
78 | , { do: 'block', name: 'result' }
79 | ]
80 | , renderData = [{ place: 'test string'}]
81 | ;
82 |
83 | tplEngine.insertProcess ( processTest, 'test')
84 | tplEngine.insertTemplate ( {simple, complex} )
85 |
86 | const result = tplEngine.run ( 'test', renderData )
87 |
88 | expect ( result ).to.be.an ('array')
89 | expect ( result[0] ).to.be.equal ( 'test string: Some text with test string.' )
90 | }) // it Draw. Enrich data
91 |
92 |
93 |
94 |
95 | it ( 'Draw templates with _attr', () => {
96 | const
97 | tplEngine = new CodeAssemblyLine()
98 | , templateLib = {
99 | link: '{{text}}'
100 | }
101 | , processData = [
102 | { do: 'draw', tpl: 'link' }
103 | , { do: 'block' }
104 | ]
105 | , renderData = [
106 | { text: 'Test link', href: '#link', className:'hello'}
107 | ]
108 | ;
109 |
110 | tplEngine.insertProcess ( processData, 'test')
111 | tplEngine.insertTemplate ( templateLib )
112 |
113 | const result = tplEngine.run ( 'test', renderData )
114 |
115 | expect ( result ).to.be.an ('array')
116 | expect ( result[0] ).to.be.equal ( 'Test link' )
117 | }) // it Draw templates with _attr
118 |
119 |
120 |
121 | it ( 'Draw with missField: string', () => {
122 | const
123 | tplEngine = new CodeAssemblyLine()
124 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
125 | , processData = [
126 | { do: 'draw', tpl: 'random', missField: 'ERROR!' }
127 | ]
128 | , renderData = [
129 | { place: 'test string'}
130 | , { place: 'ala-bala' }
131 | , { place: 'pasion', more: 'Great to be here' }
132 | ]
133 | ;
134 |
135 | tplEngine.insertProcess ( processData, 'test')
136 | tplEngine.insertTemplate ( templateLib )
137 |
138 | const result = tplEngine.run ( 'test', renderData )
139 |
140 | expect ( result ).to.be.an ('array')
141 | expect ( result[0] ).to.be.equal ( 'Some text with test string.ERROR!' )
142 | expect ( result[2] ).to.be.equal ( 'Some text with pasion.Great to be here' )
143 | }) // it Draw with missField string.
144 |
145 |
146 |
147 | it ( 'Draw with missField: _hide', () => {
148 | const
149 | tplEngine = new CodeAssemblyLine()
150 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
151 | , processData = [
152 | { do: 'draw', tpl: 'random', missField:'_hide' }
153 | ]
154 | , renderData = [
155 | { place: 'test string'}
156 | , { place: 'ala-bala' }
157 | , { place: 'pasion', more: 'Great to be here' }
158 | ]
159 | ;
160 |
161 | tplEngine.insertProcess ( processData, 'test')
162 | tplEngine.insertTemplate ( templateLib )
163 |
164 | const result = tplEngine.run ( 'test', renderData )
165 |
166 | expect ( result ).to.be.an ('array')
167 | expect ( result[0] ).to.be.equal ( 'Some text with test string.' )
168 | expect ( result[2] ).to.be.equal ( 'Some text with pasion.Great to be here' )
169 | }) // it Draw with missField _hide.
170 |
171 |
172 |
173 | it ( 'Draw with missField: _fn', () => {
174 | const
175 | tplEngine = new CodeAssemblyLine()
176 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
177 | , processData = [
178 | { do: 'draw', tpl: 'random', missField:'_fn', hook: 'ole' }
179 | ]
180 | , renderData = [
181 | { place: 'test string'}
182 | , { place: 'ala-bala' }
183 | , { place: 'pasion', more: 'Great to be here' }
184 | ]
185 | ;
186 |
187 | tplEngine.insertProcess ( processData, 'test')
188 | tplEngine.insertTemplate ( templateLib )
189 |
190 | const result = tplEngine.run ( 'test', renderData , { ole: (x) => `Error: Missing data - ${x}` } )
191 |
192 | expect ( result ).to.be.an ('array')
193 | expect ( result[0] ).to.be.equal ( 'Some text with test string.Error: Missing data - more' )
194 | expect ( result[1] ).to.be.equal ( 'Some text with ala-bala.Error: Missing data - more' )
195 | expect ( result[2] ).to.be.equal ( 'Some text with pasion.Great to be here' )
196 | }) // it Draw with missField: _fn.
197 |
198 |
199 |
200 | it ( 'Draw with missField: _fn but missing hook', () => {
201 | const
202 | tplEngine = new CodeAssemblyLine()
203 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
204 | , processData = [
205 | { do: 'draw', tpl: 'random', missField:'_fn', hook: 'ole' }
206 | ]
207 | , renderData = [
208 | { place: 'test string'}
209 | , { place: 'ala-bala' }
210 | , { place: 'pasion', more: 'Great to be here' }
211 | ]
212 | ;
213 |
214 | tplEngine.insertProcess ( processData, 'test')
215 | tplEngine.insertTemplate ( templateLib )
216 |
217 | const result = tplEngine.run ( 'test', renderData , { oled: (x) => `Error: Missing data - ${x}` } )
218 |
219 | expect ( result ).to.be.an ('array')
220 | expect ( result[0] ).to.be.equal ( 'Some text with test string.' )
221 | expect ( result[2] ).to.be.equal ( 'Some text with pasion.Great to be here' )
222 | }) // it Draw with missField: _fn but missing hook.
223 |
224 |
225 |
226 | it ( 'Draw with missField: _fn but no hook object', () => {
227 | const
228 | tplEngine = new CodeAssemblyLine()
229 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
230 | , processData = [
231 | { do: 'draw', tpl: 'random', missField:'_fn', hook: 'ole' }
232 | ]
233 | , renderData = [
234 | { place: 'test string'}
235 | , { place: 'ala-bala' }
236 | , { place: 'pasion', more: 'Great to be here' }
237 | ]
238 | ;
239 |
240 | tplEngine.insertProcess ( processData, 'test')
241 | tplEngine.insertTemplate ( templateLib )
242 |
243 | const result = tplEngine.run ( 'test', renderData )
244 |
245 | expect ( result ).to.be.an ('array')
246 | expect ( result[0] ).to.be.equal ( 'Some text with test string.' )
247 | expect ( result[2] ).to.be.equal ( 'Some text with pasion.Great to be here' )
248 | }) // it Draw with missField: _fn but no hook object.
249 |
250 |
251 |
252 | it ( 'Draw with missData: _fn.', () => {
253 | const
254 | tplEngine = new CodeAssemblyLine()
255 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
256 | , processData = [
257 | { do: 'draw', tpl: 'random', missData:'_fn', hook: 'ole' }
258 | ]
259 | , renderData = [
260 | { place: 'test string'}
261 | , { place: 'ala-bala' }
262 | , { place: 'pasion', more: 'Great to be here' }
263 | ]
264 | ;
265 |
266 | tplEngine.insertProcess ( processData, 'test')
267 | tplEngine.insertTemplate ( templateLib )
268 |
269 | const result = tplEngine.run ( 'test', renderData , { ole: (x) => `Error: Missing data - ${x.join(',')}` } )
270 |
271 | expect ( result ).to.be.an ('array')
272 | expect ( result[0] ).to.be.equal ( 'Error: Missing data - more' )
273 | expect ( result[1] ).to.be.equal ( 'Error: Missing data - more' )
274 | expect ( result[2] ).to.be.equal ( 'Some text with pasion.Great to be here' )
275 | }) // it Draw with missData: _fn.
276 |
277 |
278 |
279 | it ( 'Draw with missData: _fn. but no hook function', () => {
280 | const
281 | tplEngine = new CodeAssemblyLine()
282 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
283 | , processData = [
284 | { do: 'draw', tpl: 'random', missData:'_fn', hook: 'ole' }
285 | ]
286 | , renderData = [
287 | { place: 'test string'}
288 | , { place: 'ala-bala' }
289 | , { place: 'pasion', more: 'Great to be here' }
290 | ]
291 | ;
292 |
293 | tplEngine.insertProcess ( processData, 'test')
294 | tplEngine.insertTemplate ( templateLib )
295 |
296 | const result = tplEngine.run ( 'test', renderData , { oled: (x) => `Error: Missing data - ${x.join(',')}` } )
297 |
298 | expect ( result ).to.be.an ('array')
299 | expect ( result ).to.have.length (1)
300 | expect ( result[0] ).to.be.equal ( 'Some text with pasion.Great to be here' )
301 | }) // it Draw with missData: _fn but no hook function.
302 |
303 |
304 |
305 | it ( 'Draw with missData: _fn. but no hooks', () => {
306 | const
307 | tplEngine = new CodeAssemblyLine()
308 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
309 | , processData = [
310 | { do: 'draw', tpl: 'random', missData:'_fn', hook: 'ole' }
311 | ]
312 | , renderData = [
313 | { place: 'test string'}
314 | , { place: 'ala-bala' }
315 | , { place: 'pasion', more: 'Great to be here' }
316 | ]
317 | ;
318 |
319 | tplEngine.insertProcess ( processData, 'test')
320 | tplEngine.insertTemplate ( templateLib )
321 |
322 | const result = tplEngine.run ( 'test', renderData )
323 |
324 | expect ( result ).to.be.an ('array')
325 | expect ( result ).to.have.length (1)
326 | expect ( result[0] ).to.be.equal ( 'Some text with pasion.Great to be here' )
327 | }) // it Draw with missData: _fn but no hooks.
328 |
329 |
330 |
331 | it ( 'Draw with missData: _hide', () => {
332 | const
333 | tplEngine = new CodeAssemblyLine()
334 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
335 | , processData = [
336 | { do: 'draw', tpl: 'random', missData:'_hide' }
337 | ]
338 | , renderData = [
339 | { place: 'test string'}
340 | , { place: 'ala-bala' }
341 | , { place: 'pasion', more: 'Great to be here' }
342 | ]
343 | ;
344 |
345 | tplEngine.insertProcess ( processData, 'test')
346 | tplEngine.insertTemplate ( templateLib )
347 |
348 | const result = tplEngine.run ( 'test', renderData )
349 |
350 | expect ( result ).to.be.an ('array')
351 | expect ( result ).to.have.length (1)
352 | expect ( result[0] ).to.be.equal ( 'Some text with pasion.Great to be here' )
353 | }) // it Draw with missData: _hide.
354 |
355 |
356 |
357 | it ( 'Draw with missData: string', () => {
358 | const
359 | tplEngine = new CodeAssemblyLine()
360 | , templateLib = { random: 'Some text with {{place}}.{{more}}' }
361 | , processData = [
362 | { do: 'draw', tpl: 'random', missData:'ERROR' }
363 | ]
364 | , renderData = [
365 | { place: 'test string'}
366 | , { place: 'ala-bala' }
367 | , { place: 'pasion', more: 'Great to be here' }
368 | ]
369 | ;
370 |
371 | tplEngine.insertProcess ( processData, 'test')
372 | tplEngine.insertTemplate ( templateLib )
373 |
374 | const result = tplEngine.run ( 'test', renderData )
375 |
376 | expect ( result ).to.be.an ('array')
377 | expect ( result ).to.have.length (3)
378 | expect ( result[0] ).to.be.equal ( 'ERROR' )
379 | expect ( result[1] ).to.be.equal ( 'ERROR' )
380 | expect ( result[2] ).to.be.equal ( 'Some text with pasion.Great to be here' )
381 | }) // it Draw with missData: string.
382 |
383 |
384 |
385 | it ( 'Draw with a non existing template.', () => {
386 | const
387 | tplEngine = new CodeAssemblyLine()
388 | , tplName = 'random'
389 | , processData = [ { do: 'draw', tpl: tplName } ]
390 | , renderData = [{ place: 'test string'}]
391 | ;
392 |
393 | tplEngine.insertProcess ( processData, 'test')
394 | const result = tplEngine.run ( 'test', renderData )
395 |
396 | expect ( result ).to.be.an ('array')
397 | expect ( result[0] ).to.be.equal ( `Error: Template "${tplName}" is not available` )
398 | }) // it Draw with a non existing template.
399 |
400 |
401 |
402 | it ( 'Alter template', () => {
403 | const
404 | tplEngine = new CodeAssemblyLine ()
405 | , tpl = { test: 'Find {{who}}!' }
406 | , data = { name: 'Peter' }
407 | , processData = [
408 | { do: 'alterTemplate', tpl:'test', data: { 'who':'name'} }
409 | , { do: 'draw', tpl: 'test' }
410 | ]
411 | ;
412 |
413 | tplEngine.insertTemplate ( tpl )
414 | tplEngine.insertProcess ( processData, 'change' )
415 |
416 | const result = tplEngine.run ( 'change', data )
417 | expect ( result[0] ).to.be.equal ( 'Find Peter!' )
418 | }) // it alter template
419 |
420 |
421 |
422 | it ( 'Write Block', () => {
423 | const
424 | tplEngine = new CodeAssemblyLine ()
425 | , tpl = { test: 'Find {{who}}!' }
426 | , blockName = 'finding'
427 | , data = [
428 | { name: 'Peter' }
429 | , { name: 'Ivan' }
430 | ]
431 | , processData = [
432 | { do: 'alterTemplate', tpl:'test', data: { 'who':'name'} }
433 | , { do: 'draw', tpl: 'test' }
434 | , { do: 'block', name: blockName }
435 | ]
436 | ;
437 |
438 | tplEngine.insertTemplate ( tpl )
439 | tplEngine.insertProcess ( processData, 'change' )
440 |
441 | const result = tplEngine.run ( 'change', data );
442 |
443 | expect ( tplEngine.data ).to.have.property ( `block/${blockName}` )
444 | }) // it write block
445 |
446 |
447 |
448 | it ( 'Can not overwrite Block', () => {
449 | const
450 | tplEngine = new CodeAssemblyLine ()
451 | , tpl = { test: 'Find {{who}}!' }
452 | , blockName = 'finding'
453 | , data = [
454 | { name: 'Peter' }
455 | , { name: 'Ivan' }
456 | ]
457 | , altData = [ { name: 'Joseph'} ]
458 | , processData = [
459 | { do: 'alterTemplate', tpl:'test', data: { 'who':'name'} }
460 | , { do: 'draw', tpl: 'test' }
461 | , { do: 'block', name: blockName }
462 | ]
463 | , badProcess = [ { do: 'block', name: 'bad' } ]
464 | ;
465 |
466 | tplEngine.insertTemplate ( tpl )
467 | tplEngine.insertProcess ( processData, 'processData' )
468 | tplEngine.insertProcess ( badProcess, 'badProcess' )
469 |
470 | tplEngine.run ( 'badProcess', data )
471 | tplEngine.run ( 'processData', data )
472 | tplEngine.run ( 'processData', altData )
473 |
474 | const D = tplEngine.data;
475 | expect ( D ).to.have.property ( `block/${blockName}` )
476 | expect ( D[`block/${blockName}`] ).to.be.equal ( 'Find Peter!Find Ivan!' )
477 | expect ( D ).to.not.have.property ( 'bad' )
478 | }) // it can not overwrite block
479 |
480 |
481 |
482 | it ( 'Save Template', () => {
483 | const
484 | tplEngine = new CodeAssemblyLine ()
485 | , tpl = { test: 'Find {{who}}!' }
486 | , blockName = 'finding'
487 | , processData = [
488 | { do: 'draw', tpl: 'test' }
489 | , { do: 'save', as:'template', name: blockName }
490 | ]
491 | ;
492 |
493 | tplEngine.insertTemplate ( tpl )
494 | tplEngine.insertProcess ( processData, 'change' )
495 |
496 | tplEngine.run ( 'change', [{name:'Peter'},{name:'Ivan'}] );
497 |
498 | const templates = tplEngine.templates;
499 | expect ( templates ).to.have.property ( blockName )
500 | const test = templates[blockName]['tpl'].join('');
501 | expect ( test ).to.be.equal ( tpl.test )
502 | }) // it save template
503 |
504 |
505 |
506 | it ( 'Save Process, save Block', () => {
507 | const
508 | tplEngine = new CodeAssemblyLine ()
509 | , testTPL = 'Find {{who}}!'
510 | , renderBlock = `[
511 | { "do": "draw", "tpl": "{{tpl}}" }
512 | , { "do": "save", "as": "block", "name": "{{name}}" }
513 | ]`
514 | , setBlock = [
515 | { do: 'draw', tpl: 'renderBlock' }
516 | , { do: 'save', as: 'process', name: 'build/block' }
517 | ]
518 | ;
519 |
520 | tplEngine.insertTemplate ({
521 | renderBlock
522 | , test : testTPL
523 | })
524 | tplEngine.insertProcess ( setBlock, 'setBlock' )
525 |
526 | tplEngine.run ( 'setBlock', { name: 'first', tpl:'test' } )
527 | tplEngine.run ( 'build/block', { who: 'Peter' })
528 |
529 | const processes = tplEngine.processes;
530 | expect ( processes ).to.have.property ( 'setBlock' )
531 | expect ( processes ).to.have.property ( 'build/block' )
532 | expect ( tplEngine.data ).to.have.property ( 'block/first' )
533 | }) // it save process, save Block
534 |
535 |
536 |
537 | it ( 'Try to save as a process invalid JSON', () => {
538 | const
539 | tplEngine = new CodeAssemblyLine ()
540 | , testTPL = 'Find {{who}}!'
541 | , renderBlock = `{
542 | 'name': "{{name}}"
543 | }`
544 | , setBlock = [
545 | { do: 'draw', tpl: 'renderBlock' }
546 | , { do: 'save', as: 'process', name: 'result' }
547 | ]
548 | ;
549 |
550 | tplEngine.insertTemplate ({
551 | renderBlock
552 | , test : testTPL
553 | })
554 | tplEngine.insertProcess ( setBlock, 'setBlock' )
555 |
556 | tplEngine.run ( 'setBlock', { name: 'first', tpl:'test' } )
557 | tplEngine.run ( 'build/block', { who: 'Peter' })
558 |
559 | const processes = tplEngine.processes;
560 | expect ( processes ).to.not.have.property ( 'result' )
561 | }) // it Try to save as a process invalid JSON
562 |
563 |
564 |
565 | it ( 'Save as Data', () => {
566 | const
567 | tplEngine = new CodeAssemblyLine ()
568 | , testTPL = 'Find {{who}}!'
569 | , findWho = '{{findWho}}'
570 | , setData = [
571 | { do: 'draw', tpl: 'test' }
572 | , { do: 'save', as: 'data', name: 'findWho' }
573 | ]
574 | , renderMyBlock = [
575 | { do: 'draw', tpl: 'findWho' }
576 | , { do: 'save', as: 'block', name: 'myBlock' }
577 | ]
578 | ;
579 |
580 | tplEngine.insertTemplate ({ test : testTPL, findWho })
581 | tplEngine.insertProcess ( setData, 'setData' )
582 | tplEngine.insertProcess ( renderMyBlock, 'renderMyBlock' )
583 |
584 | tplEngine.run ( 'setData', { who: 'Peter' } )
585 | tplEngine.run ( 'renderMyBlock' )
586 |
587 | const result = tplEngine.data;
588 | expect ( result ).to.have.property ( 'findWho' )
589 | expect ( result ).to.have.property ( 'block/myBlock' )
590 | expect ( result['findWho']).to.be.equal ( result['block/myBlock'])
591 | }) // it save as Data
592 |
593 |
594 |
595 | it ( 'Save as non existing item', () => {
596 | const
597 | tplEngine = new CodeAssemblyLine ()
598 | , testTPL = 'Find {{who}}!'
599 | , renderMyBlock = [
600 | { do: 'draw', tpl: 'test' }
601 | , { do: 'save', as: 'memo', name: 'myBlock' }
602 | ]
603 | ;
604 |
605 | tplEngine.insertTemplate ({ test : testTPL })
606 | tplEngine.insertProcess ( renderMyBlock, 'renderMyBlock' )
607 |
608 | const result = tplEngine.run ( 'renderMyBlock', { who: 'Peter' } )
609 |
610 | expect ( result[0] ).to.be.equal ( 'Find Peter!')
611 | expect ( tplEngine ).to.not.have.property ( 'memo' )
612 | expect ( tplEngine.data ).to.not.have.property ( 'memo' )
613 | expect ( tplEngine.templates ).to.not.have.property ( 'memo' )
614 | expect ( tplEngine.processes ).to.not.have.property ( 'memo' )
615 | }) // it save as non existing item
616 |
617 |
618 |
619 | it ( 'Set', () => {
620 | const
621 | tplEngine = new CodeAssemblyLine()
622 | , tpl = { test: 'Find {{who}}!' }
623 | , data = [ 'Peter', 'Ivan' ]
624 | , processData = [
625 | { do: 'set', as: 'who'}
626 | , { do: 'draw', tpl: 'test' }
627 | ]
628 | ;
629 | tplEngine.insertTemplate ( tpl )
630 | tplEngine.insertProcess ( processData, 'findList' )
631 |
632 | const result = tplEngine.run ( 'findList', data )
633 |
634 | expect ( result ).to.be.an('array')
635 | expect ( result ).to.have.length (2)
636 | expect ( result[0] ).to.be.equal ( 'Find Peter!' )
637 | expect ( result[1] ).to.be.equal ( 'Find Ivan!' )
638 | }) // it set
639 |
640 |
641 |
642 | it ( 'Set with high depth objects', () => {
643 | const
644 | tplEngine = new CodeAssemblyLine()
645 | , tpl = { test: '{{who}} has a {{profile/eyes}} eyes!' }
646 | , data = [
647 | { who: 'Peter', profile: { age: 43, eyes: 'blue' } }
648 | , { who: 'Ivan', profile: { age: 42, eyes: 'brown' } }
649 | ]
650 | , processData = [
651 | { do: 'draw', tpl: 'test' }
652 | ]
653 | ;
654 | tplEngine.insertTemplate ( tpl )
655 | tplEngine.insertProcess ( processData, 'findList' )
656 |
657 | const result = tplEngine.run ( 'findList', data )
658 |
659 | expect ( result ).to.be.an('array')
660 | expect ( result ).to.have.length (2)
661 | expect ( result[0] ).to.be.equal ( 'Peter has a blue eyes!' )
662 | expect ( result[1] ).to.be.equal ( 'Ivan has a brown eyes!' )
663 | }) // it set with high depth objects
664 |
665 |
666 |
667 | it ( 'List of processes', () => {
668 | const
669 | tplEngine = new CodeAssemblyLine()
670 | , tpl = { test: 'Find {{who}}!' }
671 | , data = [ 'Peter', 'Ivan' ]
672 | , processSetData = [ { do: 'set', as: 'who' }]
673 | , processDraw = [ { do: 'draw', tpl: 'test' }]
674 | ;
675 |
676 | tplEngine.insertTemplate ( tpl )
677 | tplEngine.insertProcess( processSetData, 'setData')
678 | tplEngine.insertProcess( processDraw, 'draw' )
679 | const result = tplEngine.run ( ['setData','draw'], data )
680 |
681 | expect ( result ).to.be.an('array')
682 | expect ( result ).to.have.length (2)
683 | expect ( result[0] ).to.be.equal ( 'Find Peter!' )
684 | expect ( result[1] ).to.be.equal ( 'Find Ivan!' )
685 | }) // it list of processes
686 |
687 |
688 |
689 | it ( 'Consume "data" for missing fields', () => {
690 | const
691 | tplEngine = new CodeAssemblyLine()
692 | , tpl = { test : 'Find {{who}}! Address: {{location}}' }
693 | , data = [
694 | { who: 'Peter', 'location' : 'Bulgaria,Sofia' }
695 | , { who: 'Ivan' }
696 | ]
697 | , processDraw = [{ do: 'draw', tpl: 'test' }]
698 | , defaultLocation = 'Location unknown'
699 | ;
700 |
701 | tplEngine.insertTemplate ( tpl )
702 | tplEngine.insertProcess ( processDraw, 'draw' )
703 | tplEngine.insertData ({'location': defaultLocation })
704 |
705 | const result = tplEngine.run ( 'draw', data )
706 | expect ( result[1] ).to.be.equal ( `Find Ivan! Address: ${defaultLocation}` )
707 | }) // it consume "data" for missing fields
708 |
709 |
710 | it ( 'Hook', () => {
711 | const
712 | tplEngine = new CodeAssemblyLine ({ overwriteData: true})
713 | , simple = 'Just simple text, {{name}}!'
714 | , processWithHook = [
715 | { do: 'draw', tpl: 'simple' }
716 | , { do: 'hook', name: 'afterSimple' }
717 | , { do: 'block', name: 'result' }
718 | ]
719 | ;
720 |
721 | const hookFn = function ( ) { return 'ala-bala' };
722 | const hookAlt = function ( ) { return ['brum-brum']};
723 |
724 | tplEngine.insertProcess ( processWithHook, 'withHook' )
725 | tplEngine.insertTemplate ( {simple} )
726 |
727 | const hookObject = tplEngine.getHooks ( 'withHook' );
728 | hookObject['afterSimple'] = hookFn
729 | tplEngine.run ( 'withHook', {name:'Johny'}, hookObject )
730 |
731 | const D = tplEngine.data;
732 | expect ( D ).to.have.property ( 'block/result' )
733 | expect ( D['block/result'] ).to.be.equal ('ala-bala')
734 |
735 | hookObject['afterSimple'] = hookAlt
736 | tplEngine.run ( 'withHook', {name:'Johny'}, hookObject )
737 | expect ( D['block/result'] ).to.be.equal ('brum-brum')
738 | }) // it hook
739 |
740 |
741 |
742 | it ( 'Draw templates with _count placeholder', () => {
743 | const
744 | tplEngine = new CodeAssemblyLine()
745 | , userName = '{{_count}}. User: {{name}},{{_count}}'
746 | , data = ['Peter', 'Ivan', 'Ivo', 'Stefan' ]
747 | , processUserList = [
748 | { do: 'set', as: 'name' }
749 | , { do: 'draw', tpl:'userName'}
750 | ]
751 | ;
752 |
753 | tplEngine.insertTemplate ( {userName})
754 | tplEngine.insertProcess ( processUserList, 'userList' )
755 |
756 | const result = tplEngine.run ( 'userList', data );
757 |
758 | expect ( result ).to.be.an ('array')
759 | expect ( result[0] ).to.be.equal ( '1. User: Peter,1' )
760 | expect ( result[1] ).to.be.equal ( '2. User: Ivan,2' )
761 | expect ( result[2] ).to.be.equal ( '3. User: Ivo,3' )
762 | expect ( result[3] ).to.be.equal ( '4. User: Stefan,4' )
763 | }) // it Draw templates with _count placeholder
764 |
765 |
766 |
767 | it ( 'Whitespaces with external data', () => {
768 | const
769 | tplEngine = new CodeAssemblyLine()
770 | , simple = '{{_count~~}}{{~~combine~~}}{{~~name}}'
771 | , data = ['Peter', 'Ivan', 'Ivo', 'Stefan' ]
772 | , renderSimple = [
773 | { do: 'set', as: 'name' }
774 | , { do: 'draw', tpl:'simple', as: 'combine', missField:'_hide' }
775 | , { do: 'draw', tpl:'simple'}
776 | ]
777 | ;
778 |
779 | tplEngine.insertTemplate ({simple})
780 | tplEngine.insertProcess ( renderSimple, 'renderSimple' )
781 |
782 | const result = tplEngine.run ( 'renderSimple', data );
783 |
784 | expect ( result[0] ).to.be.equal ( '1 1 Peter Peter' )
785 | expect ( result[1] ).to.be.equal ( '2 2 Ivan Ivan' )
786 | expect ( result[2] ).to.be.equal ( '3 3 Ivo Ivo' )
787 | expect ( result[3] ).to.be.equal ( '4 4 Stefan Stefan' )
788 | }) // it whitespaces with external data
789 |
790 |
791 |
792 | it ( 'Whitespaces with system placeholders', () => {
793 | const
794 | tplEngine = new CodeAssemblyLine()
795 | , simple = '{{text}}{{~~_count}}
'
796 | , data = [
797 | { text:'Peter', id:'first'}
798 | , { text:'Ivan', id: 'second'}
799 | , { text:'Stefan'}
800 | ]
801 | , renderSimple = [
802 | { do: 'draw', tpl:'simple'}
803 | ]
804 | ;
805 |
806 | tplEngine.insertTemplate ({simple})
807 | tplEngine.insertProcess ( renderSimple, 'renderSimple' )
808 |
809 | const result = tplEngine.run ( 'renderSimple', data );
810 |
811 | expect ( result[0] ).to.be.equal ( 'Peter 1
' )
812 | expect ( result[1] ).to.be.equal ( 'Ivan 2
' )
813 | expect ( result[2] ).to.be.equal ( 'Stefan 3
' )
814 | }) // it Whitespaces with system placeholders
815 |
816 |
817 |
818 | it ( 'Whitespaces with data', () => {
819 | const
820 | tplEngine = new CodeAssemblyLine()
821 | , simple = '{{name}}:{{~~info~~}}.'
822 | , data = [
823 | { name:'Peter', info: 'General info'}
824 | , { name:'Ivan'}
825 | , { name:'Stefan'}
826 | ]
827 | , info = 'Missing information'
828 | , renderSimple = [
829 | { do: 'draw', tpl:'simple'}
830 | ]
831 | ;
832 |
833 | tplEngine.insertTemplate ({simple})
834 | tplEngine.insertData ({info})
835 | tplEngine.insertProcess ( renderSimple, 'renderSimple' )
836 |
837 | const result = tplEngine.run ( 'renderSimple', data );
838 |
839 | expect ( result[0] ).to.be.equal ( 'Peter: General info .' )
840 | expect ( result[1] ).to.be.equal ( 'Ivan: Missing information .' )
841 | expect ( result[2] ).to.be.equal ( 'Stefan: Missing information .' )
842 | }) // it Whitespaces with data
843 |
844 |
845 |
846 | }) // describe Run
--------------------------------------------------------------------------------