Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

declare #36

Closed
wants to merge 7 commits into from

2 participants

@wkeese
Collaborator

split core declare() processing to independent function, and add handler for dojox/drawing/util/oo::declare()

@wkeese wkeese closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 30, 2013
  1. @neonstalwart @wkeese

    parse the return descriptions as markdown

    neonstalwart authored wkeese committed
  2. @neonstalwart @wkeese
  3. @neonstalwart @wkeese

    defer resolving the annotated object.

    neonstalwart authored wkeese committed
    by resolving this in the exporter, we can avoid a race condtion where
    sometimes relatedModule had not been indicated when it should have been.
  4. @neonstalwart @wkeese
  5. @neonstalwart @wkeese

    add raw property to hold unparsed metadata

    neonstalwart authored wkeese committed
  6. @neonstalwart @wkeese

    prevent property metadata overriding value metadata

    neonstalwart authored wkeese committed
    this makes a property's metadata independent of the value's metadata.
    
    fixes #61
  7. @wkeese

    split core declare() processing to independent function,

    wkeese authored
    and add handler for dojox/drawing/util/oo::declare()
This page is out of date. Refresh to see the latest.
View
73 lib/callHandler/dojo.js
@@ -97,8 +97,13 @@ define([
}
});
- handlers.register('declare', util.isModule('dojo/_base/declare'), function (callInfo, args) {
- function getMixinModule(value) {
+ /**
+ * Handler for dojo.declare() and similar functions.
+ * If there's a constructor, it should be in prototype.constructor.
+ */
+ function declare(mixins, prototype){
+
+ var mixinModules = mixins.map(function(value) {
// The mixin is not exposed as a module, i.e. it is local to the value itself
// In this case it seems best to just lie and report the mixin’s mixins, but maybe it is
// better to create a pseudo-module instead
@@ -107,31 +112,7 @@ define([
}
return value.relatedModule;
- }
-
- var declaredClass = (args[args.length - 3] || {}).evaluated,
- mixins = (args[args.length - 2] || {}).evaluated,
- prototype = (args[args.length - 1] || {}).evaluated;
-
- // Composition with no prototype
- if (!mixins && prototype) {
- mixins = prototype;
- prototype = new Value({ type: Value.TYPE_OBJECT });
- }
-
- var mixinModules = [];
-
- if (mixins.type === Value.TYPE_ARRAY) {
- mixins = mixins.toArray();
- mixinModules = mixins.map(getMixinModule);
- }
- else if (mixins.type !== Value.TYPE_NULL && mixins.type !== Value.TYPE_UNDEFINED) {
- mixins = [ mixins ];
- mixinModules = [ getMixinModule(mixins[0]) ];
- }
- else {
- mixins = [];
- }
+ });
// Flatten any nested arrays
mixinModules = mixinModules.concat.apply([], mixinModules);
@@ -172,6 +153,33 @@ define([
value.setProperty('prototype', prototype);
+ return value;
+ }
+
+ handlers.register('declare', util.isModule('dojo/_base/declare'), function (callInfo, args) {
+
+ var declaredClass = (args[args.length - 3] || {}).evaluated,
+ mixins = (args[args.length - 2] || {}).evaluated,
+ prototype = (args[args.length - 1] || {}).evaluated;
+
+ // Composition with no prototype
+ if (!mixins && prototype) {
+ mixins = prototype;
+ prototype = new Value({ type: Value.TYPE_OBJECT });
+ }
+
+ if (mixins.type === Value.TYPE_ARRAY) {
+ mixins = mixins.toArray();
+ }
+ else if (mixins.type !== Value.TYPE_NULL && mixins.type !== Value.TYPE_UNDEFINED) {
+ mixins = [ mixins ];
+ }
+ else {
+ mixins = [];
+ }
+
+ var value = declare(mixins, prototype);
+
if (declaredClass) {
if (declaredClass.type !== Value.TYPE_STRING) {
console.info('Cannot set object from variable');
@@ -185,6 +193,17 @@ define([
return value;
});
+ handlers.register('drawingDeclare', util.isModuleProperty('dojox/drawing/util/oo', 'declare'), function (callInfo, args) {
+
+ var mixins = args.slice(0, -2).map(function(f){ return f.evaluated; }),
+ constructor = (args[args.length - 2] || {}).evaluated,
+ prototype = (args[args.length - 1] || {}).evaluated;
+
+ prototype.setProperty('constructor', constructor);
+
+ return declare(mixins, prototype);
+ });
+
handlers.register('mixin', util.isModuleProperty('dojo/_base/lang', 'mixin'), handleMixin);
handlers.register('_mixin', util.isModuleProperty('dojo/_base/lang', '_mixin'), handleMixin);
handlers.register('safeMixin', util.isModuleProperty('dojo/_base/declare', 'safeMixin'), handleMixin);
View
76 lib/exporter/dojov1.js
@@ -1,4 +1,12 @@
-define([ '../Module', '../Value', './util', '../console', '../node!fs' ], function (Module, Value, util, console, fs) {
+define([
+ 'dojo/string',
+ '../Module',
+ '../Value',
+ './util',
+ '../console',
+ 'dojo/node!fs',
+], function (stringUtil, Module, Value, util, console, fs) {
+
/**
* Takes information from metadata stored alongside a Value and adds it to the output.
* @param node The node to add metadata to.
@@ -35,6 +43,66 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
}
/**
+ * Given metadata with a type annotation, attempt to resolve the annotated type as an object and (hackily) apply
+ * information about the object’s default properties to the metadata description property.
+ */
+ function processTypeAnnotation(/**Object*/ metadata) {
+ if (!metadata.type || typeof metadata.type === 'string') {
+ return;
+ }
+
+ var propertyTemplate = '<li>${key}${type}${summary}</li>',
+ annotationObject = metadata.type,
+ additionalDescription = '';
+
+ if (annotationObject.relatedModule) {
+ metadata.type = annotationObject.relatedModule.id;
+ return;
+ }
+
+ metadata.type = 'Object';
+ additionalDescription += '<p>' + (metadata.description ?
+ 'The following properties are supported:' :
+ 'An object with the following properties:') + '</p><ul>';
+
+ (function readProperties(object) {
+ var propertyMetadata,
+ properties = object.properties,
+ k;
+
+ // if the annotationObject is a function, we don't want to pick up any properties apart
+ // from what's on the prototype.
+ if (object.type === 'function') {
+ if (_hasOwnProperty.call(properties, 'prototype')) {
+ readProperties(properties.prototype);
+ }
+ return;
+ }
+
+ for (k in properties) {
+ if (_hasOwnProperty.call(properties, k)) {
+ // Type descriptor could be a plain JS object, or could be a constructor. It is often the
+ // latter.
+ if (k === 'prototype') {
+ readProperties(properties[k]);
+ }
+ // Filter out built-ins and constructor properties which come from dojo/_base/declare
+ else if (k !== 'constructor' && properties[k].file) {
+ propertyMetadata = properties[k].metadata;
+ additionalDescription += stringUtil.substitute(propertyTemplate, {
+ key: k,
+ type: propertyMetadata.type ? ' (' + propertyMetadata.type + (propertyMetadata.isOptional ? ', optional' : '') + ')' : '',
+ summary: propertyMetadata.summary ? ': ' + propertyMetadata.summary : ''
+ });
+ }
+ }
+ }
+ }(annotationObject));
+
+ metadata.description = (metadata.description || '') + additionalDescription + '</ul>';
+ }
+
+ /**
* Takes an array of return Values and processes it for return types, discarding all
* duplicates, and applies the resulting list of properties to the node given in returnsNode.
* @param returnsNode The XML node to add return types to.
@@ -67,6 +135,9 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
i;
for (i = 0; (parameter = property.parameters[i]); ++i) {
+ if (typeof parameter.metadata.type !== 'string') {
+ processTypeAnnotation(parameter.metadata);
+ }
parameterType = parameter.metadata.type || parameter.type || 'unknown';
parameterNode = parametersNode.createNode('parameter', {
name: parameter.name,
@@ -105,6 +176,9 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
propertyNode;
function makePropertyObject(name, value) {
+ if (typeof value.metadata.type !== 'string') {
+ processTypeAnnotation(value.metadata);
+ }
var object = {
name: name,
scope: scope,
View
125 lib/processor/dojodoc.js
@@ -45,14 +45,22 @@ define([
* avoid accidentally overwriting data that already exists with empty data.
*/
function mixinMetadata(destination, source) {
- for (var k in source) {
+ var raw,
+ k;
+
+ for (k in source) {
if (_hasOwnProperty.call(source, k) && source[k]) {
if (source[k] instanceof Array && destination[k] instanceof Array) {
destination[k] = destination[k].concat(source[k]);
}
- else if (k === 'type') {
+ else if (k === 'type' && typeof source[k] === 'string') {
destination[k] = source[k].replace(optionalTypeRe, '');
}
+ else if (k === 'raw') {
+ // this keeps the mixin/override semantics of raw the same as the metadata itself
+ raw = destination[k];
+ raw ? mixinMetadata(raw, source[k]) : destination[k] = source[k];
+ }
else if (typeof source[k] !== 'string' || trim(source[k])) {
destination[k] = source[k];
}
@@ -113,60 +121,24 @@ define([
}
/**
- * Given metadata with a type annotation, attempt to resolve the annotated type as an object and (hackily) apply
- * information about the object’s default properties to the metadata description property.
- * TODO: This should really end up happening in the dojov1 exporter instead.
+ * Given metadata with a type annotation, attempt to resolve the annotated type as an object and
+ * provide that object to the exporter as the type property of the metadata.
*/
function processTypeAnnotation(/**Object*/ metadata) {
if (!metadata.type) {
return;
}
- var propertyTemplate = '\n* ${key}${type}${summary}',
- annotationObject = env.scope.getVariable(metadata.type.replace(/[^\w$\.]+$/g, '').split('.')),
- additionalDescription = '';
+ var annotationObject = env.scope.getVariable(metadata.type.replace(/[^\w$\.]+$/g, '').split('.'));
if (!annotationObject || annotationObject.type === Value.TYPE_UNDEFINED || /* not a built-in */ !annotationObject.file) {
return;
}
- if (annotationObject.relatedModule) {
- metadata.type = annotationObject.relatedModule.id;
- return;
- }
-
- // TODO: The fact that evaluate exists on annotation objects seems to indicate that we’re failing to
- // evaluate all function expressions; this might be an issue
- annotationObject.evaluate && annotationObject.evaluate();
-
- metadata.type = 'Object';
- additionalDescription += metadata.description ?
- '\n\nThe following properties are supported:\n' :
- 'An object with the following properties:\n';
-
- (function readProperties(object) {
- var propertyMetadata;
- for (var k in object.properties) {
- if (_hasOwnProperty.call(object.properties, k)) {
- // Type descriptor could be a plain JS object, or could be a constructor. It is often the
- // latter.
- if (k === 'prototype') {
- readProperties(object.properties[k]);
- }
- // Filter out built-ins and constructor properties which come from dojo/_base/declare
- else if (k !== 'constructor' && object.properties[k].file) {
- propertyMetadata = object.properties[k].metadata;
- additionalDescription += stringUtil.substitute(propertyTemplate, {
- key: k,
- type: propertyMetadata.type ? ' (' + propertyMetadata.type + (propertyMetadata.isOptional ? ', optional' : '') + ')' : '',
- summary: propertyMetadata.summary ? ': ' + propertyMetadata.summary : ''
- });
- }
- }
- }
- }(annotationObject));
-
- metadata.description = (metadata.description || '') + parseMarkdown(additionalDescription);
+ // defer resolving this to do it in the exporter. annotationObject might be the return
+ // value of the module currently being processed and in that case it won't have been tagged
+ // with a relatedModule yet.
+ metadata.type = annotationObject;
}
/**
@@ -195,6 +167,47 @@ define([
}
/**
+ * Processes metadata to parse the markdown of certain properties
+ * @param metadata Metadata object to parse for markdown
+ */
+ function processMetadata(/**Object*/ metadata) {
+ var example,
+ i,
+ k,
+ raw = metadata.raw = {
+ summary: metadata.summary,
+ description: metadata.description,
+ // handle examples in the loop below to avoid redundant iteration. also, by
+ // recursing for properties and returns, they will get their own raw objects.
+ };
+
+ // The style guide says `summary` isn’t markdown, only `description` is markdown, but everyone sticks markdown
+ // in the summary anyway, so handle it as markdown too
+ metadata.summary = parseMarkdown(metadata.summary || '');
+ metadata.description = parseMarkdown(metadata.description || '');
+
+ if (metadata.examples) {
+ raw.examples = [];
+ for (i = 0; (example = metadata.examples[i]); ++i) {
+ raw.examples[i] = example;
+ metadata.examples[i] = parseMarkdown(example);
+ }
+ }
+
+ if (metadata.properties) {
+ for (k in metadata.properties) {
+ if (_hasOwnProperty.call(metadata.properties, k)) {
+ processMetadata(metadata.properties[k]);
+ }
+ }
+ }
+
+ if (metadata.returns) {
+ processMetadata(metadata.returns);
+ }
+ }
+
+ /**
* Processes a dojodoc multi-line comment block, which consists of key lines that identify the metadata and
* subsequent indented lines containing the actual metadata.
* @param comment The comment block.
@@ -306,19 +319,7 @@ define([
}
}
- // The style guide says `summary` isn’t markdown, only `description` is markdown, but everyone sticks markdown
- // in the summary anyway, so handle it as markdown too
- metadata.summary = parseMarkdown(metadata.summary);
- metadata.description = parseMarkdown(metadata.description);
- for (var i = 0, example; (example = metadata.examples[i]); ++i) {
- metadata.examples[i] = parseMarkdown(example);
- }
-
- for (var k in metadata.properties) {
- if (_hasOwnProperty.call(metadata.properties, k)) {
- metadata.properties[k].summary = parseMarkdown(metadata.properties[k].summary);
- }
- }
+ processMetadata(metadata);
return metadata;
}
@@ -369,8 +370,11 @@ define([
candidate = /^[^\n]*\/\/(.*?)\n/.exec(util.getSourceForRange(value.raw.range));
if (candidate) {
- metadata = { type: trim(candidate[1]) };
+ value.evaluated.metadata.type = trim(candidate[1]);
+ processTypeAnnotation(value.evaluated.metadata);
}
+ // if we get here, we have no need to mixin any metadata
+ return;
}
// Function or object body
@@ -438,6 +442,9 @@ define([
metadata = (processComment(candidate.value, value.raw.key.name).properties || {})[value.raw.key.name];
processTypeAnnotation(metadata);
+ // the localised property metadata will override the metadata inherited from the value
+ context.evaluated.properties[value.raw.key.name].metadata = metadata;
+ return;
}
}
Something went wrong with that request. Please try again.