Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 0b075f1

Browse files
authored
Enable the use of an apiUse block in an apiDefine block
Merge pull request #99 from tommy87/dev Fix apidoc/apidoc#859
2 parents eae82b6 + fd3777f commit 0b075f1

File tree

3 files changed

+271
-42
lines changed

3 files changed

+271
-42
lines changed

lib/parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ Parser.prototype._parseBlockElements = function(indexApiBlocks, detectedElements
222222
preventGlobal = elementParser.preventGlobal === true;
223223

224224
// allow multiple inserts into pathTo
225-
allowMultiple = elementParser.allowMultiple === true;
225+
allowMultiple = true;
226226

227227

228228
// path to an array, where the values should be attached

lib/workers/api_use.js

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -66,70 +66,88 @@ function postProcess(parsedFiles, filenames, preProcess, packageInfos, source, t
6666

6767
parsedFiles.forEach(function(parsedFile, parsedFileIndex) {
6868
parsedFile.forEach(function(block) {
69-
if ( ! block.local[target])
70-
return;
71-
72-
block.local[target].forEach(function(definition) {
73-
var name = definition.name;
74-
var version = block.version || packageInfos.defaultVersion;
75-
76-
if ( ! preProcess[source] || ! preProcess[source][name]) {
77-
throw new WorkerError('Referenced groupname does not exist / it is not defined with @apiDefine.',
69+
var loopCounter = 0; //add a loop counter to have a break condition when the recursion depth exceed a predifined limit
70+
while (block.local[target]) {
71+
if (loopCounter > 10) {
72+
throw new WorkerError('recursion depth exceeds limit with @apiUse',
7873
filenames[parsedFileIndex],
7974
block.index,
8075
messages.common.element,
8176
messages.common.usage,
8277
messages.common.example,
8378
[
84-
{ 'Groupname': name }
79+
{ 'Groupname': block.name }
8580
]
8681
);
8782
}
83+
84+
//create a copy of the elements for save iterating of the elements
85+
var blockClone = block.local[target].slice();
8886

89-
var matchedData = {};
90-
if (preProcess[source][name][version]) {
91-
// found the version
92-
matchedData = preProcess[source][name][version];
93-
} else {
94-
// find nearest matching version
95-
var foundIndex = -1;
96-
var lastVersion = packageInfos.defaultVersion;
97-
98-
var versionKeys = Object.keys(preProcess[source][name]);
99-
versionKeys.forEach(function(currentVersion, versionIndex) {
100-
if (semver.gte(version, currentVersion) && semver.gte(currentVersion, lastVersion)) {
101-
lastVersion = currentVersion;
102-
foundIndex = versionIndex;
103-
}
104-
});
87+
// remove unneeded target before starting the loop, to allow a save insertion of new elements
88+
// TODO: create a cleanup filter
89+
delete block.local[target];
10590

106-
if (foundIndex === -1) {
107-
throw new WorkerError('Referenced definition has no matching or a higher version. ' +
108-
'Check version number in referenced define block.',
91+
for (var blockIndex = 0; blockIndex < blockClone.length; ++blockIndex) {
92+
var definition = blockClone[blockIndex];
93+
var name = definition.name;
94+
var version = block.version || packageInfos.defaultVersion;
95+
96+
if ( ! preProcess[source] || ! preProcess[source][name]) {
97+
throw new WorkerError('Referenced groupname does not exist / it is not defined with @apiDefine.',
10998
filenames[parsedFileIndex],
11099
block.index,
111100
messages.common.element,
112101
messages.common.usage,
113102
messages.common.example,
114103
[
115-
{ 'Groupname': name },
116-
{ 'Version': version },
117-
{ 'Defined versions': versionKeys },
104+
{ 'Groupname': name }
118105
]
119106
);
120107
}
121108

122-
var versionName = versionKeys[foundIndex];
123-
matchedData = preProcess[source][name][versionName];
124-
}
109+
var matchedData = {};
110+
if (preProcess[source][name][version]) {
111+
// found the version
112+
matchedData = preProcess[source][name][version];
113+
} else {
114+
// find nearest matching version
115+
var foundIndex = -1;
116+
var lastVersion = packageInfos.defaultVersion;
117+
118+
var versionKeys = Object.keys(preProcess[source][name]);
119+
for (var versionIndex = 0; versionIndex < versionKeys.length; ++versionIndex) {
120+
var currentVersion = versionKeys[versionIndex];
121+
if (semver.gte(version, currentVersion) && semver.gte(currentVersion, lastVersion)) {
122+
lastVersion = currentVersion;
123+
foundIndex = versionIndex;
124+
}
125+
}
125126

126-
// remove target, not needed anymore
127-
// TODO: create a cleanup filter
128-
delete block.local[target];
127+
if (foundIndex === -1) {
128+
throw new WorkerError('Referenced definition has no matching or a higher version. ' +
129+
'Check version number in referenced define block.',
130+
filenames[parsedFileIndex],
131+
block.index,
132+
messages.common.element,
133+
messages.common.usage,
134+
messages.common.example,
135+
[
136+
{ 'Groupname': name },
137+
{ 'Version': version },
138+
{ 'Defined versions': versionKeys },
139+
]
140+
);
141+
}
129142

130-
// copy matched elements into parsed block
131-
_recursiveMerge(block.local, matchedData);
132-
});
143+
var versionName = versionKeys[foundIndex];
144+
matchedData = preProcess[source][name][versionName];
145+
}
146+
147+
// copy matched elements into parsed block
148+
_recursiveMerge(block.local, matchedData);
149+
}
150+
}
133151
});
134152
});
135153
}

test/worker_api_use_test.js

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*jshint unused:false*/
2+
3+
/**
4+
* Test: Parser apiParam
5+
*/
6+
7+
// node modules
8+
var should = require('should');
9+
10+
// lib modules
11+
var worker = require('../lib/workers/api_use');
12+
13+
describe('Worker: apiUse', function() {
14+
15+
var packageInfos = {
16+
name: 'Test recursive apiUse',
17+
version: '0.0.1',
18+
description: 'i am a dummy description',
19+
title: 'Test recursive apiUse',
20+
url: 'http://localhost:18080',
21+
order: [ 'GetUser', 'PostUser' ],
22+
template: { withCompare: false, withGenerator: true },
23+
sampleUrl: false,
24+
defaultVersion: '0.0.0'
25+
};
26+
27+
var filenames = [ 'fileA', 'fileB', 'fileC' ];
28+
29+
// TODO: Add 1.000 more possible cases ;-)
30+
// the tree is build like
31+
// root
32+
// l
33+
var parsedFilesSimpleTest = [
34+
//file
35+
[
36+
{
37+
global: { },
38+
local: {
39+
use: [
40+
{ name: 'leaf_l' }
41+
],
42+
name: 'root',
43+
test: ['root']
44+
},
45+
expected: 'root',
46+
},
47+
{
48+
global: {
49+
define: {
50+
name: 'leaf_l',
51+
title: '',
52+
description: '',
53+
}
54+
},
55+
local: {
56+
test: ['l']
57+
},
58+
expected: 'l'
59+
}
60+
]
61+
];
62+
63+
// the tree is build like
64+
// root
65+
// l, r
66+
// ll, rr
67+
// rrl, rrr
68+
var parsedFilesRecursiveTest = [
69+
//file
70+
[
71+
{
72+
global: { },
73+
local: {
74+
use: [
75+
{ name: 'leaf_l' },
76+
{ name: 'leaf_r' },
77+
],
78+
name: 'root',
79+
test: ['root']
80+
},
81+
expected: 'root',
82+
}
83+
],
84+
[
85+
{
86+
global: {
87+
define: {
88+
name: 'leaf_l',
89+
title: '',
90+
description: '',
91+
}
92+
},
93+
local: {
94+
test: ['l'],
95+
use: [
96+
{ name: 'leaf_ll' }
97+
],
98+
},
99+
expected: 'l'
100+
},
101+
{
102+
global: {
103+
define: {
104+
name: 'leaf_rr',
105+
title: '',
106+
description: '',
107+
}
108+
},
109+
local: {
110+
test: ['rr'],
111+
use: [
112+
{ name: 'leaf_rrr' },
113+
{ name: 'leaf_rrl' }
114+
],
115+
},
116+
expected: 'rr'
117+
}
118+
],
119+
[
120+
{
121+
global: {
122+
define: {
123+
name: 'leaf_ll',
124+
title: '',
125+
description: '',
126+
}
127+
},
128+
local: {
129+
test: ['ll']
130+
},
131+
expected: 'll'
132+
},
133+
{
134+
global: {
135+
define: {
136+
name: 'leaf_r',
137+
title: '',
138+
description: '',
139+
}
140+
},
141+
local: {
142+
test: ['r'],
143+
use: [
144+
{ name: 'leaf_rr' }
145+
],
146+
},
147+
expected: 'r'
148+
},
149+
{
150+
global: {
151+
define: {
152+
name: 'leaf_rrr',
153+
title: '',
154+
description: '',
155+
}
156+
},
157+
local: {
158+
test: ['rrr']
159+
},
160+
expected: 'rrr'
161+
},
162+
{
163+
global: {
164+
define: {
165+
name: 'leaf_rrl',
166+
title: '',
167+
description: '',
168+
}
169+
},
170+
local: {
171+
test: ['rrl']
172+
},
173+
expected: 'rrl'
174+
}
175+
]
176+
];
177+
178+
// create
179+
it('case 1: simple test', function(done) {
180+
var preProcess = worker.preProcess(parsedFilesSimpleTest, filenames, packageInfos);
181+
worker.postProcess(parsedFilesSimpleTest, filenames, preProcess, packageInfos);
182+
183+
var rootBlock = parsedFilesSimpleTest[0][0];
184+
rootBlock.local.name.should.eql('root');
185+
186+
//check if the root block contains the expected value from every other block
187+
parsedFilesSimpleTest.forEach(function(parsedFile, parsedFileIndex) {
188+
parsedFile.forEach(function(block) {
189+
rootBlock.local.test.should.containEql(block.expected);
190+
});
191+
});
192+
done();
193+
});
194+
195+
it('case 2: recursive test', function(done) {
196+
var preProcess = worker.preProcess(parsedFilesRecursiveTest, filenames, packageInfos);
197+
worker.postProcess(parsedFilesRecursiveTest, filenames, preProcess, packageInfos);
198+
199+
var rootBlock = parsedFilesRecursiveTest[0][0];
200+
rootBlock.local.name.should.eql('root');
201+
202+
//check if the root block contains the expected value from every other block
203+
parsedFilesRecursiveTest.forEach(function(parsedFile, parsedFileIndex) {
204+
parsedFile.forEach(function(block) {
205+
rootBlock.local.test.should.containEql(block.expected);
206+
});
207+
});
208+
done();
209+
});
210+
211+
});

0 commit comments

Comments
 (0)