Duckumentation

Document Your Code

Generally speaking, there are three common ways to document a project:

  • Internal: Inline comments that explain your code’s intent.
  • Internal/External: Special purpose comments that describe your code and are used by a generator to generate an external reference.
  • External: End-user documentation. This is usually a document that explains how to use the (web)application/UI (i.e. the manual).

The kind of documentation you write is of course up to you (or your employer). However, I do hope you will agree that you should leave your eventual successor some information to work with. If you’re open sourcing your project then documentation is even more crucial. After all, you will want to make it as easy as possible for others to use and contribute to your code.

A lot has been written concerning the use of inline comments, and writing end-user documentation is definitely outside the scope of TiDev. In this post we will focus on creating an API reference, which is the type of documentation you and I probably use on a daily basis. Because you’ll use comments to generate your project’s external API reference, you have the added benefit that this documentation is also available while browsing through the code.

History

Back in the days when I was coding in Java or PHP, I naturally used JavaDoc or phpDocumentor respectively to generate my documentation. I didn’t particularly enjoy their presentation, but they were both more or less their industry’s standard.

In those days the size of a website’s JavaScript (files) was very small. Consequently no one bothered with generating documentation for them. But when the lines of JavaScript used in websites started to balloon, a clear need for documentation came with it.

A few homegrown projects quickly sprouted up, but not one of these evolved into the de facto standard. When frameworks came into existence, most created their own tool to generate an API reference. I personally worked a lot with the Ext JS JavaScript framework (later acquired by Sencha) and really loved their documentation. This sentiment was echoed by a lot of other developers, which ultimately led to them releasing their documentation generator to the public. This tool was named JSDuck, and I’ve used it ever since.

JSDuck has come a long way since then and currently sits at version 5. Since all our Titanium apps are in JavaScript, there was never a doubt on my mind which documentation generator to use. And in this article I will explain how you can harness its power for your Titanium applications!

Docblocks

The most common way to create an API reference is by adding so-called docblock’s to your code. Docblocks are specially formatted comments that contain a short description of the code that (usually) follows it. The following list represents a selection of elements that can be preceded by docblocks (when using JSDuck):

  • classes / namespaces
  • methods
  • properties
  • events
  • require-statements

A docblock usually contains two elements. It contains a description of the code that is directly below, and one or several tags. The description can contain examples, links to other parts of the API and even external links. The tags are used to explicitly tell JSDuck what this code is about. Most generators require tags to be preceded by an @-symbol and JSDuck is one of them. Below is an example that shows how a docblock is generally structured:

/**
...
A very useful and interesting description that explains the function of the code directly following this comment.
...
@sometag ...
@someothertag...
...
*/

Note the double *’s at the top of the comment. This is how you turn a regular comment into a docblock. By default, only comments that are structured like this will be included into the documentation.

JSDuck

Originally JSDuck was meant for projects that are built on Ext JS. This has two very important consequences:

  • Ext JS is an OO JavaScript framework. It works with concepts like classes, methods and inheritance. Since JavaScript is a procedural language there is no need for you to follow the OOP paradigm when developing your app, though some do. Fortunately you can work around this quite easily. The only thing you will need to get used to is seeing a lot @class and @method tags, that you simply apply to files and its functions.
  • When you build your project on top of Ext JS, JSDuck is able to infer a whole lot of information from just your code. There is almost no need for you to include tags at all. However this is not the case here. Unless it’s a function, you will need to add at least one tag to every docblock to explain JSDuck what it’s looking at.

If at this point you are wondering if JSDuck is really the tool to use, know that even Appcelerator decided to use it to document their Titanium and Alloy frameworks. So even though you might never have heard of JSDuck, you have been working with it all along!

Installation

Let’s start by setting everything up, so you can experience JSDuck firsthand. Follow these steps to get up and running:

  1. JSDuck was written in Ruby, so you’ll need to install that first if you didn’t already.
  2. Afterwards, open your favorite command-line utility and type gem install jsduck (or sudo it if you need to)
  3. Navigate to the folder containing the Titanium project you like to document
  4. Execute jsduck /app --output docs. Here /app is the folder for which documentation will be generated, which is then stored in the docs folder.

Note that step 4 will probably throw a bunch of warnings. This is OK for now, we will make them disappear later on. If you stumbled upon an error, please read the official documentation on the installation and usage of JSDuck.

If your code contains no docblocks yet, your documentation will be (near) empty at this point. Let’s fix this!

Duckumenting Your Code

In general you will add docblocks to the top of each file and to each (public) function and property it contains.

All of the text inside a docblock that is not directly preceded by a tag (and often most of the text that is) will be transformed into a description. If you type text below, in between, or above a tag, all of it (depending on the tag used) will end up forming the description of the tagged structure. Because these descriptions could grow quite lengthy, know that JSDuck has full MarkDown support. Please use this to improve the readability of your lengthier descriptions.

Tags

Since JSDuck won’t be able to infer much information, the use of tags is crucial. Most tags expect one or several attributes to follow it, like a name or a type. Be sure to check the linked documentation or have a look at the example in the next section.

The following list contains a few of the most common top-level tags. These are tags that can be used on their own:

  • @class: Add a docblock to the very top of each of your JavaScript files that contains a @class-tag. The term that follows it will be used to name the file. It doesn’t matter if it’s not really a class, you could interpret it as a namespace of sorts. All docblocks describing your functions and properties that follow a class-tagged docblock will be automatically assigned to it. To separate your controller classes/namespaces from the models and from your library files, you can prefix your class names. For example, you could prefix your class name with a string value (like ‘controller’) followed by a ‘.’ like this: @class controller.HelloWorld. This will make sure your class’ documentation for HelloWorld will end up in a container named controller.
  • @method: All functions that are exposed in a class (because they are part of the exports-object) can be preceded by a docblock containing the @method-tag. Note that this is one of the few tags you can safely omit. JSDuck knows a function when it sees one!
  • @property: All properties that you expose in a class should be preceded by a docblock containing a `@property’-tag that describes it.

There are also tags that only makes sense when preceded by a top-level tag. These are tags that add additional information:

  • @param: While you are describing methods make sure to add a `@param’-tag for every parameter it expects.
  • @return: A close cousin of @param, use it to describe the output of your methods.
  • @private: There is no need to include private functions and properties in your documentation. If you however prefer to have them there, one way to make this happen is by adding a @private-tag somewhere in the docblock. JSDuck will then label it as such. By toggling a flag when generating your documentation, you can choose to include your private documentation or not.
  • @extends: If you do actually use inheritance (for example in a Controller), you can add this tag to a @class-block. This will greatly enhance your documentation, so give it a try!
  • @constructor: If you do actually work with classes or if you work with Backbone Models directly or via Alloy, adding this tag to a @method-block will give it a special constructor status

There are many, many more tags that you could use if you feel adventurous!

Example

Below is a short example of how you could document a User model:

var Alloy = require('alloy'), 
    _ = Alloy._, 
    Backbone = Alloy.Backbone;

/**
 * Model representing a user.
 * 
 * @class model.User
 */
exports.definition = {
    config : {
        // table schema and adapter information
    },

    extendModel: function(Model) {      
        _.extend(Model.prototype, {
            // Extend, override or implement Backbone.Model 

            /**
             * @event befriended
             * Fired when this user has become the current user's new friend.
             *
             * @param {model.User} user This user (that became a friend)
             * @param {Moment} date The exact moment this friendship sprung into existence
             */

            /**
             * @event unfriended
             * Fired when this user has fallen from grace and his/her friendship with the current user has ended.
             *
             * @param {model.User} user This user (that was unfriended)
             * @param {Moment} date The exact moment this friendship came to an end
             */

            /**
             * @property {Boolean} isFriend True if this user is a friend of the currently authorized user, false otherwise
             */
            isFriend: false,

            /**
             * Initialize this user.
             *
             * @constructor
             * @param {Object} attributes The attributes passed to this User
             */
            initialize: function(attributes) {
                //...
            },

            /**
             * Retrieve a collection containing this user's friends.
             * 
             * @param {Boolean} [mutual=false] True to only return mutual friends (with current user), false to return all of them
             * @return {Backbone.Collection} Collection containing friends
             */
            getFriends: function(mutual) {},
                if (mutual) {
                    //...
                }
                //...
            }
        });

        return Model;
    },

    extendCollection: function(Collection) {        
        _.extend(Collection.prototype, {
            // Extend, override or implement Backbone.Collection 
        });

        return Collection;
    }
};

Configuration

Now your code is in shape to be documented, but there are still a few loose ends to solve. You need to configure JSDuck so that it stops throwing around a bunch of errors and warnings. It does this because it will inevitably run into classes that are not included in the folder you are generating documentation for. Instead of feeding it a bunch of exceptions via the CLI each time you generate the documentation, we can add all of these options into a configuration file.

If you would -for example- call this file myconfig.json then you would invoke JSDuck by executing jsduck --config=myconfig.json. An even easier solution is simply calling it jsduck.json, then you can simply execute jsduck without any parameters at all.

A configuration file can include all sorts of handy stuff, but some things are more or less essential when using it to document your Titanium code. Below is a list of the most important things I use:

  • “–“: Here you can provide an array containing all the input sources for which documentation needs to be generated.
  • “–output”: Put the path to the folder that is to contain the generated documentation here.
  • “–external”: Because your code will include references to namespaces/classes that are not defined in your application’s folder, you need to make sure JSDuck does not expect them to be there either. By supplying an array of these object-names, JSDuck will stop throwing around errors and warnings when it encounters them.
  • “–title”: Instead of the title that is generated by default, supply a more fitting title for your documentation here.

There are many more options you can use, to see all of them run jsduck --help from the command line.

Example

Below is an example of a config file I use:

{
    "--title": "My insanely great app - API",
    "--": [
        "./app"
    ],
    "--output": "docs",
    "--external": [
        "Alloy",
        "Backbone.*",
        "Moment",
        "Controller",
        "Ti.*",
        "Titanium.*",
        "Point",
        "Dimension"
    ]
}

Module

If configuring and using JSDuck sounds overly complex to you, there also exists a titanium-jsduck module that might make your life easier. I haven’t tested it myself, but it comes with the added benefit of automatically installing a post-compile hook into your project. This hook will make sure your documentation is regenerated each time you recompile your app.

To Conclude

I hope this introduction into generating documentation using JSDuck has been useful to you. Please realize that we’ve only dipped our toes into what JSDuck has to offer us. You can really create stunning documentation using this generator if you want to. Reading the full documentation should help you on your way!

Ronald Treur is a freelance developer and the co-founder of Snowciety, a multi-platform mobile app created using Titanium & Alloy. He has been developing websites since the age of 11 (starting out on paper due to lack of a real computer) and has been working on mobile apps since Titanium 1.5.
For development he has experience with Javascript, PHP and Java and to a lesser extent C and C++. For webdesign he relies on his knowledge of HTML5 and CSS3. Other interests include game-design, movies and talking at meetups (recent addition!).


Comments