1/13/2013

Tips For Using JsDoc on Enyo Kinds, and Other Factory Pattern Types

My journey to better practices has led me through the forest of unit testing to a new destination. I have reached that dark, scary marsh that many in the IT world dread. The very mention of its name sends grown men to tears and panic. I find myself at the Marsh of Documentaion!

I understand the importance of clean code and well placed comments and try very hard to apply both principles. However, like many developers, documentation seems to be that project line item of low priority that we never have time or resources to do. The top priorities of my daily work are to meet project deliverables and collaborate with the team. Those never seem to become less important, so the documentation step is always being put off. This is not a best practice.

Documentation is as valuable to communicate with our future selves as it is to communicate with others. I can't count the number of times I have gone combing through code reading comments to find the function that does that one thing I need to know about. I would save myself a lot of time and grief if I had a document that listed what everything does and how it's parts work. My future self will need documentation as much as the customers, project managers, QA team, and others need the documentation now.

My main problem remains despite the understanding above. In projects with limited time and resources, how do we allocate sufficient time to write documentation? The answer is documentation generation. The most widely known would be javadoc from the Java world. The ability to have a program create documentation from code commenting style is amazing. What a great way to solve the problem! Create the documentation while you code. Building documentation becomes a part of the coding process and not a seperate set of tasks. Javadoc, and its partners in other languages, make documentation part of the coding process which is where we spend most of our time.

There are a few applications that bring this wonderful capability to JavaScript. For my current project, I settled on JsDoc. I found the JsDoc project to be robust and varied enouogh to fit nicely into my coding style. I did find that working wtith frameworks and certain patterns can make documenation a little challenging until you figure out the best combination of tags. I came across this problem when documenting Enyo kinds. Enyo kinds are created in a Factory Pattern. A function is called that returns an instance of an object rather than creating a new instance based on the "new" keyword. The standard tags for a defined type using the new keyword are usually @class or @module. Using either with my Enyo code created documentation that wasn't right.

/** This is a standard class commenting block
* @class Sample
*/
enyo.kind({
  /** Create the new Sample object
  * @memberOf Sample
  */
  create: function (){
  },
  /** A really useful function
  * @memberOf Sample
  * @param {String} val Incoming value
  */
  usefulFunction: function (val){
  }
})

@class generates a constructor method that doesn't exist and adds all methods as static. This isn't correct since the Enyo provided object is an instance.

/** This is a standard module commenting block
* @module Sample
*/
enyo.kind({
  /** Create the new Sample object
  */
  create: function (){
  },
  /** A really useful function
  * @param {String} val Incoming value
  */
  usefulFunction: function (val){
  }
})

@module doesn't have a constructor, and identified the methods as inner methods.

The solution to the problem is another tag called @lends. @lends isn't yet described in the JsDoc v3 documentation although it is referenced in other pages (see the @constructs tag) and is documented in the JsDoc v2 documentation. The documentation states:

The @lends tag allows you to document all the members of an anonymous object literal as if they were members of an object with the given name.

For my purpose, this meant that my Enyo object could be documented as a regular type that could be instantiated rather than a static. The important part is where to place it. Place the @lends between the kind function call and the literal object. It will document the code as if it belonged to the prototype of the object. The @lends tag needs a name to connect the prototype to. The name value can be an @name tag at the top (as shown below) or a name property inside the object literal. It should be @lends [new type's name]. prototype.

We are almost there. JsDoc expects a constructor method in order to for this to be a proper class. The class will either not document or show up as a global variable if a constructor is not called out. Identifying the constructor is also easy to do. Identify the function inside the literal that should be considered to be the constructor, and place @constructs in the comments directly preceding it. The @constructs tag will tell JsDoc to stop looking for the constructor on its own. Instead, we are telling JsDoc to trust us, that this is the function it is looking for. Here is the Enyo kind with the @name, @lends, and @constructs tag added:

/** This is a class description using the lends tag
* @name Sample
*/
enyo.kind(
/** @lends Sample.prototype */
{
  /** Create the new Sample object
  * @constructs
  */
  create: function (){
  },
  /** A really useful function
  * @param {String} val Incoming value
  */
  usefulFunction: function (val){
  }
})

The same principle can apply to the factory pattern in general:

/** This is a factory pattern using the lends tag
* @name newType
*/
var newType = createNewType (
  /** @lends newType.prototype */
  {
  /** Create the new Sample object
  * @constructs
  */
  intialize: function (){
  },
  ....
})

Documenting properties also has its own caveats worth mentioning. Idetinfying a property involves placing a comment above the property in a place that JsDoc will understand it. The two main opportunities are identification in the literal or this.property inside a function. When possible, identify type properties of interest in the literal. It makes the code and the commenting easier to read. Look at the below with a property declaration in the literal.

name: 'Sample',
/** Create the new Sample object
* @constructs
*/
create: function (){
},
/**
* @private
* @default ''
*/
myVal:'',
/** A really useful function
* @param {String} val Incoming value
*/
usefulFunction: function (val){
  this.myVal = val;
}

Here is the same declaration inside a function:
name: 'Sample',
/** Create the new Sample object
* @constructs
*/
create: function (){
},
myVal:'',
/** A really useful function
* @param {String} val Incoming value
*/
usefulFunction: function (val){
  /**
  * @private
  * @default ''
  */
this.myVal = val;
}

Both approaches create the same jsDoc output:



However, I would argue the former version is easier to read and understand for human eyes than the latter. If the property is important enough to document, give it its due and give yourself as much space as you can use to document it. Comments in the code should make understanding easier, not create confusion. The commenting required for proper documentation adds more space than a plain comment. It's important to take that in to account when writing your code.

The rest of the documentation worked as expected and I didn't encounter any more caveats worth noting. If I do find more, I'll post a Part 2 to this article. I suggest trying this style of commenting if you never have before. It makes documentation feel like coding not a seperate set of activities made up of less than exciting hours.