Added basic Services, which support @memberOf and @methodOf

This commit is contained in:
Vojta Jina
2010-11-15 01:10:16 +00:00
committed by Misko Hevery
parent b467a50bc7
commit cc749760fd
9 changed files with 840 additions and 67 deletions

View File

@@ -7,7 +7,8 @@ var fs = require('fs'),
Showdown = require('showdown').Showdown;
var documentation = {
pages:[]
pages:[],
byName: {}
};
var keywordPages = [];
@@ -23,18 +24,7 @@ var work = callback.chain(function () {
//console.log('reading', file, '...');
findNgDoc(file, work.waitMany(function(doc) {
parseNgDoc(doc);
if (doc.ngdoc) {
keywordPages.push({
name:doc.name,
keywords:keywords(doc.raw.text)
}
);
documentation.pages.push(doc);
console.log('Found:', doc.ngdoc + ':' + doc.shortName);
mergeTemplate(
doc.ngdoc + '.template',
doc.name + '.html', doc, work.waitFor());
}
processNgDoc(documentation, doc);
}));
}));
}));
@@ -42,6 +32,7 @@ var work = callback.chain(function () {
console.log('ERROR:', err.stack || err);
}).onDone(function(){
keywordPages.sort(function(a,b){ return a.name == b.name ? 0:(a.name < b.name ? -1 : 1);});
writeDoc(documentation.pages);
mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(keywordPages)}, callback.chain());
mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, callback.chain());
copy('docs-scenario.html', callback.chain());
@@ -156,11 +147,17 @@ function markdownTag(doc, name, value) {
}
function markdown(text) {
text = text.replace(/<angular\/>/gm, '<tt>&lt;angular/&gt;</tt>');
text = text.replace(/(angular\.[\w\._\-:]+)/gm, '<a href="#$1">$1</a>');
text = text.replace(/(`(ng:[\w\._\-]+)`)/gm, '<a href="#angular.directive.$2">$1</a>');
text = new Showdown.converter().makeHtml(text);
return text;
var parts = text.split(/(<pre>[\s\S]*<\/pre>)/);
parts.forEach(function(text, i){
if (!text.match(/^<pre>/)) {
text = text.replace(/<angular\/>/gm, '<tt>&lt;angular/&gt;</tt>');
text = new Showdown.converter().makeHtml(text);
text = text.replace(/(angular\.[\$\w\._\-:]+)/gm, '<a href="#!$1">$1</a>');
text = text.replace(/(`(ng:[\w\._\-]+)`)/gm, '<a href="#!angular.directive.$2">$1</a>');
parts[i] = text;
}
});
return parts.join('');
}
function markdownNoP(text) {
@@ -171,6 +168,43 @@ function markdownNoP(text) {
return lines.join('\n');
}
function requiresTag(doc, name, value) {
doc.requires = doc.requires || [];
doc.requires.push({name: value});
}
function propertyTag(doc, name, value) {
doc[name] = doc[name] || [];
var match = value.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/);
if (match) {
var tag = {
type: match[2],
name: match[3],
description: match[5] || false
};
} else {
throw "[" + doc.raw.file + ":" + doc.raw.line +
"]: @" + name + " must be in format '{type} name description' got: " + value;
}
return doc[name].push(tag);
}
function returnsTag(doc, name, value) {
var match = value.match(/^({(\S+)}\s*)?(.*)?/);
if (match) {
var tag = {
type: match[2],
description: match[3] || false
};
} else {
throw "[" + doc.raw.file + ":" + doc.raw.line +
"]: @" + name + " must be in format '{type} description' got: " + value;
}
return doc[name] = tag;
}
var TAG = {
ngdoc: valueTag,
example: escapedHtmlTag,
@@ -186,6 +220,7 @@ var TAG = {
paramDescription: markdownTag,
exampleDescription: markdownTag,
element: valueTag,
methodOf: valueTag,
name: function(doc, name, value) {
doc.name = value;
doc.shortName = value.split(/\./).pop();
@@ -212,7 +247,10 @@ var TAG = {
throw "[" + doc.raw.file + ":" + doc.raw.line +
"]: @param must be in format '{type} name=value description' got: " + value;
}
}
},
property: propertyTag,
requires: requiresTag,
returns: returnsTag
};
function parseNgDoc(doc){
@@ -291,3 +329,32 @@ function findJsFiles(dir, callback){
callback.done();
}));
}
function processNgDoc(documentation, doc) {
if (!doc.ngdoc) return;
console.log('Found:', doc.ngdoc + ':' + doc.name);
documentation.byName[doc.name] = doc;
if (doc.methodOf) {
if (parent = documentation.byName[doc.methodOf]) {
(parent.method = parent.method || []).push(doc);
} else {
throw 'Owner "' + doc.methodOf + '" is not defined.';
}
} else {
documentation.pages.push(doc);
keywordPages.push({
name:doc.name,
keywords:keywords(doc.raw.text)
});
}
}
function writeDoc(pages) {
pages.forEach(function(doc) {
mergeTemplate(
doc.ngdoc + '.template',
doc.name + '.html', doc, callback.chain());
});
}

View File

@@ -125,3 +125,8 @@ a {
margin-left: 2em;
font-family: monospace;
}
#sidebar ul li.level-3 {
margin-left: 3em;
font-family: monospace;
}

37
docs/service.template Normal file
View File

@@ -0,0 +1,37 @@
<h1><tt>{{name}}</tt></h1>
<h2>Description</h2>
{{{description}}}
<h2>Dependencies</h2>
<ul>
{{#requires}}
<li><tt>{{name}}</tt></li>
{{/requires}}
</ul>
<h2>Methods</h2>
<ul>
{{#method}}
<li><tt>{{shortName}}</tt>: {{{description}}}</li>
{{/method}}
</ul>
<h2>Properties</h2>
<ul>
{{#property}}
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}</tt>{{#description}}: {{{description}}}{{/description}}</li>
{{/property}}
</ul>
{{#example}}
<h2>Example</h2>
{{{exampleDescription}}}
<doc:example>
<doc:source>
{{/example}}
{{{example}}}
{{#example}}
</doc:source>
{{#scenario}}<doc:scenario>{{{scenario}}}</doc:scenario>{{/scenario}}
</doc:example>
{{/example}}

View File

@@ -11,6 +11,63 @@ describe('collect', function(){
expect(collect.markdown('<angular/>')).
toEqual('<p><tt>&lt;angular/&gt;</tt></p>');
});
it('should not replace anything in <pre>', function(){
expect(collect.markdown('angular.x\n<pre>\nangular.k\n</pre>\nangular.x')).
toEqual(
'<p><a href="#!angular.x">angular.x</a></p>' +
'<pre>\nangular.k\n</pre>' +
'<p><a href="#!angular.x">angular.x</a></p>');
});
});
describe('processNgDoc', function() {
var processNgDoc = collect.processNgDoc,
documentation;
beforeEach(function() {
documentation = {
pages: [],
byName: {}
};
});
it('should store references to docs by name', function() {
var doc = {ngdoc: 'section', name: 'fake', raw: {text:''}};
processNgDoc(documentation, doc);
expect(documentation.byName.fake).toBe(doc);
});
it('should connect doc to owner (specified by @methodOf)', function() {
var parentDoc = {ngdoc: 'section', name: 'parent', raw: {text:''}};
var doc = {ngdoc: 'section', name: 'child', methodOf: 'parent', raw: {text:''}};
processNgDoc(documentation, parentDoc);
processNgDoc(documentation, doc);
expect(documentation.byName.parent.method).toBeDefined();
expect(documentation.byName.parent.method[0]).toBe(doc);
});
it('should not add doc to sections if @memberOf specified', function() {
var parentDoc = {ngdoc: 'parent', name: 'parent', raw: {text:''}};
var doc = {ngdoc: 'child', name: 'child', methodOf: 'parent', raw: {text:''}};
processNgDoc(documentation, parentDoc);
processNgDoc(documentation, doc);
expect(documentation.pages.child).not.toBeDefined();
});
it('should throw exception if owner does not exist', function() {
expect(function() {
processNgDoc(documentation, {ngdoc: 'section', methodOf: 'not.exist', raw: {text:''}});
}).toThrow('Owner "not.exist" is not defined.');
});
it('should ignore non-ng docs', function() {
var doc = {name: 'anything'};
expect(function() {
processNgDoc(documentation, doc);
}).not.toThrow();
expect(documentation.pages).not.toContain(doc);
});
});
describe('TAG', function(){
@@ -41,6 +98,90 @@ describe('collect', function(){
});
});
describe('@requires', function() {
it('should parse more @requires tag into array', function() {
TAG.requires(doc, 'requires', '$service');
TAG.requires(doc, 'requires', '$another');
expect(doc.requires).toEqual([
{name: '$service'},
{name: '$another'}
]);
});
});
describe('@property', function() {
it('should parse @property tags into array', function() {
TAG.property(doc, 'property', '{type} name1 desc');
TAG.property(doc, 'property', '{type} name2 desc');
expect(doc.property.length).toEqual(2);
});
it('should parse @property with only name', function() {
TAG.property(doc, 'property', 'fake');
expect(doc.property[0].name).toEqual('fake');
});
it('should parse @property with optional type', function() {
TAG.property(doc, 'property', '{string} name');
expect(doc.property[0].name).toEqual('name');
expect(doc.property[0].type).toEqual('string');
});
it('should parse @property with optional description', function() {
TAG.property(doc, 'property', 'name desc rip tion');
expect(doc.property[0].name).toEqual('name');
expect(doc.property[0].description).toEqual('desc rip tion');
});
it('should parse @property with type and description both', function() {
TAG.property(doc, 'property', '{bool} name desc rip tion');
expect(doc.property[0].name).toEqual('name');
expect(doc.property[0].type).toEqual('bool');
expect(doc.property[0].description).toEqual('desc rip tion');
});
/**
* If property description is undefined, this variable is not set in the template,
* so the whole @description tag is used instead
*/
it('should set undefined description to "false"', function() {
TAG.property(doc, 'property', 'name');
expect(doc.property[0].description).toBe(false);
});
});
describe('@methodOf', function() {
it('should parse @methodOf tag', function() {
expect(function() {
TAG.methodOf(doc, 'methodOf', 'parentName');
}).not.toThrow();
expect(doc.methodOf).toEqual('parentName');
});
});
describe('@returns', function() {
it('should parse @returns', function() {
expect(function() {TAG.returns(doc, 'returns', '');})
.not.toThrow();
});
it('should parse @returns with type', function() {
TAG.returns(doc, 'returns', '{string}');
expect(doc.returns.type).toEqual('string');
});
it('should parse @returns with description', function() {
TAG.returns(doc, 'returns', 'descrip tion');
expect(doc.returns.description).toEqual('descrip tion');
});
it('should parse @returns with type and description', function() {
TAG.returns(doc, 'returns', '{string} description');
expect(doc.returns).toEqual({type: 'string', description: 'description'});
});
});
describe('@describe', function(){
it('should support pre blocks', function(){
TAG.description(doc, 'description', '<pre>abc</pre>');

View File

@@ -1,6 +1,6 @@
require.paths.push("./lib");
var jasmine = require('jasmine-1.0.1');
var sys = require('sys');
var sys = require('util');
for(var key in jasmine) {
global[key] = jasmine[key];