From cc749760fd59418433de5a055d1ca401f7500444 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Mon, 15 Nov 2010 01:10:16 +0000 Subject: Added basic Services, which support @memberOf and @methodOf --- docs/collect.js | 105 ++++++++++++++++++++++++++++------- docs/docs.css | 5 ++ docs/service.template | 37 +++++++++++++ docs/spec/collectSpec.js | 141 +++++++++++++++++++++++++++++++++++++++++++++++ docs/specs.js | 2 +- 5 files changed, 270 insertions(+), 20 deletions(-) create mode 100644 docs/service.template (limited to 'docs') diff --git a/docs/collect.js b/docs/collect.js index 4e845f5b..7ad1630b 100644 --- a/docs/collect.js +++ b/docs/collect.js @@ -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(//gm, '<angular/>'); - text = text.replace(/(angular\.[\w\._\-:]+)/gm, '$1'); - text = text.replace(/(`(ng:[\w\._\-]+)`)/gm, '$1'); - text = new Showdown.converter().makeHtml(text); - return text; + var parts = text.split(/(
[\s\S]*<\/pre>)/);
+  parts.forEach(function(text, i){
+    if (!text.match(/^
/)) {
+      text = text.replace(//gm, '<angular/>');
+      text = new Showdown.converter().makeHtml(text);
+      text = text.replace(/(angular\.[\$\w\._\-:]+)/gm, '$1');
+      text = text.replace(/(`(ng:[\w\._\-]+)`)/gm, '$1');
+      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());
+  });
+}
diff --git a/docs/docs.css b/docs/docs.css
index 25a8ffea..664328d0 100644
--- a/docs/docs.css
+++ b/docs/docs.css
@@ -125,3 +125,8 @@ a {
   margin-left: 2em;
   font-family: monospace;
 }
+
+#sidebar ul li.level-3 {
+  margin-left: 3em;
+  font-family: monospace;
+}
diff --git a/docs/service.template b/docs/service.template
new file mode 100644
index 00000000..2774cad2
--- /dev/null
+++ b/docs/service.template
@@ -0,0 +1,37 @@
+

{{name}}

+

Description

+{{{description}}} + +

Dependencies

+
    + {{#requires}} +
  • {{name}}
  • + {{/requires}} +
+ +

Methods

+
    + {{#method}} +
  • {{shortName}}: {{{description}}}
  • + {{/method}} +
+ +

Properties

+
    + {{#property}} +
  • {{name}}:{{#type}}{{type}}{{/type}}{{#description}}: {{{description}}}{{/description}}
  • + {{/property}} +
+ +{{#example}} +

Example

+{{{exampleDescription}}} + + +{{/example}} + {{{example}}} +{{#example}} + + {{#scenario}}{{{scenario}}}{{/scenario}} + +{{/example}} \ No newline at end of file diff --git a/docs/spec/collectSpec.js b/docs/spec/collectSpec.js index f20342b1..9de08dc8 100644 --- a/docs/spec/collectSpec.js +++ b/docs/spec/collectSpec.js @@ -11,6 +11,63 @@ describe('collect', function(){ expect(collect.markdown('')). toEqual('

<angular/>

'); }); + + it('should not replace anything in
', function(){
+      expect(collect.markdown('angular.x\n
\nangular.k\n
\nangular.x')). + toEqual( + '

angular.x

' + + '
\nangular.k\n
' + + '

angular.x

'); + }); + }); + + 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', '
abc
'); diff --git a/docs/specs.js b/docs/specs.js index 5f1cd153..d564b045 100644 --- a/docs/specs.js +++ b/docs/specs.js @@ -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]; -- cgit v1.2.3