How to document literal objects?

8,255 views
Skip to first unread message

Tawez

unread,
Sep 19, 2011, 9:13:57 AM9/19/11
to JSDoc Users
Question is for JSDoc2 and JSDoc3 as well
and it can be treated as feature request :)
How to document literal objects efficiently and clearly?

There are three common cases:

1. Literal object as object/class/instance propery:

myNamespace.Myobject = {
property: {
bar: 1,
baz: "hit the light",
bax: true
}
};


2. Literal object as function argument:

myNamespace.Myobject = {
foo: function (options) {...}
};


3. Literal object as value returned by function


I've read Michael answer to similar question:
http://groups.google.com/group/jsdoc-users/browse_thread/thread/19da055531bb77e5/0db1686f3ad72ce4
but it seems to me as fake solution, sorry Michael ;)


As reference: In YUI Doc there is tag pair: @property + @attribute
(http://www.slideshare.net/ysaw/beautiful-documentation-with-yui-doc
[slide:71])
and it is very convenient because one can describe every detail of
attribute.


Examples above can be documented in way as follows:

1. Literal object as object/class/instance propery:

myNamespace.Myobject = {
/** @property {Object} property Default values */
defaults: {
/** @attribute {Number} a Attribute a description */
a: 1,
/** @attribute {String} b Attribute b description */
b: "Hit the light",
/** @attribute {Boolean} Attribute c description */
c: true
}
};

or shorthand:

myNamespace.Myobject = {
/**
* @property {Object} property Default values
* @attribute {Number} a Attribute a description
* @attribute {String} b Attribute b description
* @attribute {Boolean} Attribute c description
*/
defaults: {
a: 1,
b: "Hit the light",
c: true
}
};


2 + 3. Literal object as function argument and as value returned by
function:

myNamespace.Myobject = {
/**
* @param {Object} options
* @attribute {String} msg Message
* @attribute {Boolean} [auto=true] True to auto send message,
false otherwise
* @returns {Object} Message status
* @attribute {Boolean} success
* @attribute {String} status Status description
*/
foo: function (options) {...}
};



Support for @default tag would be welcome as well on each level.
Tag name "@attribute" is only an example.


--
Best Regards
Tawez

Michael Mathews

unread,
Sep 19, 2011, 10:09:09 AM9/19/11
to Tawez, JSDoc Users

Can you explain how a more complex example would be documented in this way? For example...

myNamespace.Myobject = {
/**
* ?
*/
defaults: {
a: {
aa: {
aaa: true
},
aa2: function(options){
returns {
aaa2: false
};
}
},


b: "Hit the light",
c: true
}
};


> 2 + 3. Literal object as function argument and as value returned by
> function:
>
> myNamespace.Myobject = {
> /**
> * @param {Object} options
> * @attribute {String} msg Message
> * @attribute {Boolean} [auto=true] True to auto send message,
> false otherwise
> * @returns {Object} Message status
> * @attribute {Boolean} success
> * @attribute {String} status Status description
> */
> foo: function (options) {...}
> };
>
>
>
> Support for @default tag would be welcome as well on each level.
> Tag name "@attribute" is only an example.
>

Can you show how the @default tag would be used in this specific example?

Tawez

unread,
Sep 19, 2011, 11:15:09 AM9/19/11
to JSDoc Users
Yeah, this is the weak point of this proposal. I know that ;>
Mixing levels with attribute types can give strange results like

> defaults.a.aa2()

Well, I don't have all the answers.
Giving limitations like "no functions allowed" is not wise...

In the other hand, most of the time only simple structures will be
documented that way.


If we can rely on parsed code from Rhino
we know nested level of property.
Then maybe new tag is not necessary... and there will be no doubts
about use of @default tag...

myNamespace.Myobject = {
/** @property {Object} defaults */
defaults: {
/** @property {Object} a */
a: {
/** @property {Object} aa */
aa: {
/** @property {Boolean} aaa */
aaa: true
},
/** @function aa2 */
aa2: function(options){
returns {
aaa2: false
};
}
},
/** @property {String} b */
b: "Hit the light",
/** @property {Boolean} c */
c: true
}
};


Anyway, I believe that current state brings some pain while
documenting the cases I've mentioned.
Solution should be simple as much as possible.

If every possible case should be supportet is an open question.


--
Best Regards
Tawez

Michael Mathews

unread,
Sep 19, 2011, 12:19:08 PM9/19/11
to Tawez, JSDoc Users
We do have rhino and rhino does understand JavaScript quite well. We won't use @property because that is already reserved in JSDoc 3, although I'm not opposed to repurposing it for this new role if it makes sense to do so. But let's invent a new tag for the sake of this example. Run the following through JSDoc 3, with this command line:

$ jsdoc scripts/properties.js -X


-- file: scripts/properties.js --

/** @namespace */
myobject = {
/** @attr */
defaults: {
/** @attr */
a: {
/** @attr */
aa: {
/** @attr */
aaa: true
},
/** @attr */
aa2: function(options){
return {
aaa2: false
};
}
},
/** @attr */


b: "Hit the light",

/** @attr */
c: true
}
};

----

You'll find that JSDoc does a decent job of understanding that code. It describes the defaults.a.aa2 symbol you mentioned like so:

{
"comment": "/** @attr */",
"meta": {
"lineno": 6,
"filename": "scripts/properties.js",
"code": {
"id": "astnode522583802",
"name": "aa2",
"type": "FUNCTION",
"node": <Object>,
"value": "FUNCTION"
}
},
"tags": [
{
"originalTitle": "attr",
"title": "attr",
"text": ""
}
],
"kind": "function",
"name": "aa2",
"memberof": "myobject.defaults.a",
"scope": "static",
"longname": "myobject.defaults.a.aa2"
}

Looks like it got the namepath right "myobject.defaults.a.aa2", the fact that it is a function, and the fact that it is a static member of "myobject.defaults.a". So we have this much functionality available to us today.

Consider that the model representing your source code.

The question, for me, is the view. How do you display documentation for that whole tree of symbols? Should myobject.defaults.a have it's own page with a list of all it's properties? What about the properties of those properties? Do they each get their own pages too? or should everything go on a single page? (At some point I suppose it becomes more succinct to just look at the source code directly.)

One could imagine what amounts to a JSON-viewer like [this](http://olivierlabs.com/jason/screenshots.html) where you can keep opening deeper and deeper branches of that JSON tree. Some nicely polished version of that might make a very popular template, I don't know. It's a slippery slope though. You've said it's reasonable to stop at one level deep. But I'd bet you would hear someone else say a better limit is "one more," whatever level we pick.

[Google use an @enum tag](http://code.google.com/closure/compiler/docs/js-for-compiler.html) that is sorta-kinda like this, though the properties are documented as a group of related constants. [JSDoc Toolkit has a @property tag](http://code.google.com/p/jsdoc-toolkit/wiki/TagProperty) that is similar too.

By the way, you can already [document nested properties of params](http://code.google.com/p/jsdoc-toolkit/wiki/TagParam#Parameters_With_Properties) in JSDoc Toolkit. I'm not opposed to adding a similar feature for returned values, but it would have to be compatible and symmetrical with @params. And the trouble with that is that returned values don't have names, so it will require some fudging to make it look like the same pattern that @param uses. For example:

/**
* @param {Object} options The options object.
* @param {Region} options.searchArea The area to search in.
* @param {string} options.targetDescription The name of the target to search for.
*
* @return {Object} The coordinates of the current target.
* @return {number} return.x The x value.
* @return {number} return.y The y value.
*/
function find(options) {
return { x: 0, y: 0};
}

I wonder what the other users on this list think.

Michael Mathews
mic...@gmail.com

> --
> You received this message because you are subscribed to the Google Groups "JSDoc Users" group.
> To post to this group, send email to jsdoc...@googlegroups.com.
> To unsubscribe from this group, send email to jsdoc-users...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/jsdoc-users?hl=en.
>

Terry Weiss

unread,
Sep 19, 2011, 1:22:32 PM9/19/11
to jsdoc...@googlegroups.com
I think how you present the results depends on how it is being used. If
the components of the object are intended to be consumed or set whole
(in one step or scope), I think the entire object should be on one page.
If the fields are set one at a time, or one sub-object at a time, then
separate pages, one per container, would probably be preferable.

But, that can't be gleaned from the code. I think the tag has to tell
the template how to render it by classifying the types and assigning a
tag for each, or to repurpose @kind or another similar tag.

For instance, if we consider the first type, where it is all consumed
and modified at once a "container" and the other type, which is handled
piecemeal, a "hash", then you could document it as follows:

/** @container taskContainer */
myobject = {
/** @hash defaults (would document as taskContainer.defaults */
defaults: {
/** @hash a */
a: {
/** @hash aa */
aa: {
/** when true, your hair will fall out */
aaa: true
},
/** returns an object (would you document the return here? or on the return line?) */


aa2: function(options){
return {
aaa2: false
};
}
},

/** blah (no need to decorate further, rhino will tell us this is child of taskContainer)


*/
b: "Hit the light",

/** blah
*/
c: true
}
};

Of course, the terms "hash" and "container" were just used to
illustrate the concept. Hash is definitely not the right term, but
container might work long term.

When presenting the template, just follow the logic, each container will
contain all contents that are not hashes, classes, namespaces, etc on
one page. Each hash will gets its own page and link from the documented
member's attribute, giving it a type of the named @hash and a link to
the docs for the hash. It leaves the programmer in control of how to
navigate the symbols and that may make the ultimate solution to this
problem simpler.

It does, however, pollute the tag namespace. And I don't think it solves
for documenting returned containers easily.

Michael Mathews

unread,
Sep 19, 2011, 5:10:21 PM9/19/11
to Tawez, JSDoc Users
At least one of your examples is similar to how JSDoc Toolkit currently does it, and I would consider it reasonable to migrate that feature into JSDoc 3, plus add the ability to nest properties in the same way.

I've added a ticket for this, you may follow or comment on it here:
https://github.com/micmath/jsdoc/issues/34

In regards to the other points: @param already allows for nested properties to be documented. Supporting nested properties of @returns would require some new proposal and I've already made one in this thread; if it is agreeable please comment and create a ticket for that as well.

Michael Mathews
mic...@gmail.com

Tawez

unread,
Sep 21, 2011, 8:24:41 AM9/21/11
to JSDoc Users
I was thinking about the problem a bit ;)
Here is the result.
I advocate the use of already-defined patterns.
A bunch of new tags is not the main aim of my proposition.

Please find below JS code with comments.
It is self descriptive example.

I wonder what's your opinion?


--
Best Regards
Tawez



/**
* @namespace
*/
Ns = {
/**
* Nested structures could be documented in simple way,
* we just need to reuse already defined patterns.
* Property dot notation (http://code.google.com/p/jsdoc-toolkit/wiki/
TagParam#Parameters_With_Properties) works,
* but output is flat and we lose information about property
structure.
* Michael asked about view. IMHO it should be rendered as in
following example (simplified markdown like syntax to illustrate the
idea):
* @example
* - Object staticDeepStruct
* Comments etc.
* - Number i
* Positive val
* - String s
* String var
* @type Object
*/
staticDeepStruct: {
/**
* Positive val
* @type Number
* @default 1
*/
i: 1,

/**
* String var
* @type Number
* @default "text"
*/
s: "text",
}
};


/*
-----------------------------------------------------------------------
* This comment block should give following documentation for
constructor:
* Class Detail
* Ns.Base(config)
* +-------+
* |example|
* +-------+
* +-------+
* |example|
* +-------+
*
* Parameters:
* {Object} config Optional
* Config object
* - {String} text
* Value for text property
*
-----------------------------------------------------------------------
*/

/**
* @class
* @example
* var b = new Ns.Base();
* @example
* var b = new Ns.Base({
* text: "new value"
* });
* @param {Object} [config] Config object
* @param {String} config.text Value for text property
*/
Ns.Base = createClass(Object, /** @lends Ns.Base.prototype */ {
/**
* @type String
* @default "make some noise"
*/
text: "make some noise",

/**
* deepStruct should be rendered in the same way as
Ns.staticDeepStruct
* @type Object
*/
deepStruct: {
/**
* Positive val
* @type Number
* @default 111
*/
i: 111,

/**
* String var
* @type Number
* @default "more text"
*/
s: "more text",
},

/**
* Returns text property value
* @returns {String}
*/
foo: function () {
return this.text;
}

/**
* If we have "directive" lends, then tag memberOf is unnecessary
here - we have well defined "context"
* @function
* @name bar
* @memberOf Ns.Base.prototype
* @returns Number
*/
});


/**
* @class
* @augments Ns.Base
* @property {Number} [i=1]
* Some comments to i property
* @example
* // this is an example for i property,
* // but will be parsed and rendered as constructor example, arghhhh!
* @property {String} [s=""]
* Some comments to s property
*/
Ns.Derived = createClass(Ns.Base, /** @lends Ns.Derived.prototype */ {
/**
* As far as I know this is the only working pattern
* if I'd like to redefine property documentation with a lot of text
and examples
* and without providing the code of property.
* BTW If we have "directive" lends, then tag fieldOf is unnecessary
here - we have well defined "context"
* @example
* // this is an example for text property,
* // and will be rendered in the right place
* @name text
* @fieldOf Ns.Derived.prototype
* @type String
* @default "be quiet"
*/


/**
* Dot notation works but right now function gets wrong definition as
<tt>{Object<u>, number,
number</u>} foo()</tt> (I think it is technical problem only).
* As an alternative we can use colon notation as below.
* Should be rendered in the same way as Ns.staticDeepStruct
* @returns {Object} The coordinates of the current target
* @returns {number} return:x The x value.
* @returns {number} return:y The y value.
*/
foo: function () {
return {
x: 0,
y: 0
};
}

});




On Sep 19, 11:10 pm, Michael Mathews <micm...@gmail.com> wrote:
> At least one of your examples is similar to how JSDoc Toolkit currently does it, and I would consider it reasonable to migrate that feature into JSDoc 3, plus add the ability to nest properties in the same way.
>
> I've added a ticket for this, you may follow or comment on it here:https://github.com/micmath/jsdoc/issues/34
>
> In regards to the other points: @param already allows for nested properties to be documented. Supporting nested properties of @returns would require some new proposal and I've already made one in this thread; if it is agreeable please comment and create a ticket for that as well.
>
> Michael Mathews
> micm...@gmail.com

Michael Mathews

unread,
Oct 2, 2011, 5:58:23 PM10/2/11
to JSDoc Users
I've updated the issue tracker with the work I've done on this so far.

https://github.com/micmath/jsdoc/issues/34

This unfortunately entails an API change, but the work is currently in
a separate branch. The summary is that if you are using JSDoc 3 and
are currently using the `@property` tag, you should change those to
`@member` tags. This can be a simple search & replace change. I would
expect this to impact only a very few people, since JSDoc 3 is still
in beta and the `@property` tag is not actually documented yet in that
version.

Note that this change does not have any affect on JSDoc Toolkit
whatsoever. In fact this should make JSDoc 3 more compatible with
`@property` tags as used in JSDoc Toolkit.

I expect I will merge this change into the master branch in the next
week or so.

Michael Mathews

unread,
Oct 8, 2011, 1:09:43 PM10/8/11
to JSDoc Users
This feature is now merged with the master branch in the JSDoc 3
project on github. Documentation for the new tags are here:

http://usejsdoc.org/tags-property.html
http://usejsdoc.org/tags-enum.html
Reply all
Reply to author
Forward
0 new messages