Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

JsDoc Toolkit: documentazione professionale per JavaScript

Uno strumento efficace per documentare al meglio il nostro codice
Uno strumento efficace per documentare al meglio il nostro codice
Link copiato negli appunti

Molto spesso l'attività del programmatore va al di là della semplice scrittura di codice. Questo è uno degli aspetti che molti, incluso il sottoscritto, amano in questa professione che permette di non soffermarsi su una sola attività ma di spostarsi da compiti diversi.

Una di queste attività è proprio quella di documentare il codice.

Creare delle documentazioni, nonostante possa sembrare un'attività spesso inutile e molto laboriosa, presenta notevoli vantaggi sia quando si lavora in team sia quando si lavora da soli. Ovviamente lavorando in team una documentazione esaustiva non può che giovare sui componenti in quanto hanno una base di partenza che gli permette di capire il comportamento del codice scritto da altri. Nel caso invece si lavori ad un progetto in solitaria una buona documentazione è utile non tanto durante lo sviluppo ma nei periodi successivi, quando magari si riprende in mano il codice per eventuali bug-fix o implementazioni di nuove funzionalità: è molto frequente infatti dimenticarsi cosa si è fatto in passato.

Qualsiasi linguaggio presenta almeno un tool per la generazione di documentazioni, spesso rilasciato proprio dal team di sviluppo del linguaggio stesso. Questi strumenti non fanno nient'altro che leggere i commenti al codice inseriti nei file dei sorgenti e formattarli (spesso in pagine HTML) per renderli più leggibili, navigabili (grazie ai link HTML) e indipendenti dal codice. Ovviamente i commenti gestiti da questi strumenti di documentazione devono sottostare ad alcune regole che permettono di identificarli come “commenti da documentazione” e non come semplici commenti inseriti nel codice dallo sviluppatore.

JsDoc Toolkit: un primo sguardo

JsDoc Toolkit è un'applicazione scritta in JavaScript ed eseguita all'interno di una macchina virtuale Java che permette di generare documentazione in HTML (ma anche XML, JSON e qualsiasi altro formato di testo) partendo da sorgenti scritti in JavaScript.

è uno strumento molto avanzato e permette di configurare praticamente qualsiasi suo aspetto sia grafico (utilizzando diversi template o realizzandone uno personalizzato) sia logici (creando veri e propri plugin grazie alle comode API esposte).

JsDoc Toolkit richiede una versione della JRE abbastanza recente (la 6) e viene avviato eseguendo il file jsrun.jar (tramite il comando java -jar). Un esempio di utilizzo (a partire dalla root di JsDoc Toolkit) potrebbe essere:

java -jar jsrun.jar apprun.js -a -t=templatesjsdoc MiaClasse.js

Cosi facendo verrà generata la documentazioni per il file MiaClasse.js utilizzando il template definito all'interno della cartella templates/jsdoc (ovviamente ci sono altre impostazioni che possono essere configurate, ma le vedremo successivamente).

Attualmente il progetto è arrivato alla release 2.0.2 ed è hostato sulla piattaforma Google Code al seguente indirizzo http://code.google.com/p/jsdoc-toolkit/ mentre il sito ufficiale del progetto è http://www.jsdoctoolkit.org.

Un esempio di documentazione realizzata con JsDoc Toolkit è la documentazione del framework JavaScript Proton reperibile a questo indirizzo: http://docs.jproton.com.br (è in portoghese ma serve solo per far capire cosa andremo a generare).

Questo articolo seguirà un filone abbastanza pratico e pragmatico: vedremo in pratica come documentare un insieme di classi introducendo mano a mano nuovi argomenti.

Primi passi con JsDoc Tookit

La prima cosa da fare prima di documentare un insieme di classi è senza dubbio quelle di scrivere il codice. Visto che non è argomento di questo articolo, ipotizziamo di avere le seguenti classi JavaScript:

var Person = function(firstname, lastname) {
	this.firstname = firstname;
	this.lastname = lastname;
}
Person.prototype = {
	toString: function() {
		return "PERSONnfirstname: " + this.firstname+"nlastname: " + this.lastname;
	}
}

var Player = new function(firstname, lastname, team) {
	Person.call(firstname, lastname);
	this.team = team;
	this.matches = new Array();
}
Player.inherits(Person);
Player.prototype = {
	toString: function() {
		return "PLAYERnfirstname: " + this.firstname+"nlastname: " + this.lastname+"nteam: "+this.team.name+"nmatch played: "+this.matches.length;
	},
	playMatch: function(match) {
		this.matches.push(match);
	}
}

var Manager = new function(firstname, lastname, balance) {
	Person.call(firstname, lastname);
	this.balance = balance;
	this.lastTransaction;
}
Manager.inherits(Person);
Manager.prototype = {
	toString: function() {
		return "MANAGERnfirstname: " + this.firstname+"nlastname: " + this.lastname+"nbalance: "+this.balance;
	},
	earn: function(money) {
		this.balance += money;
		this.lastTransaction = money;
	},
	lose: function(money) {
		this.balance -= money;
		this.lastTransaction = - money;
	}
}

var Team = function(name, city, players) {
	this.name = name;
	this.city = city;
	this.players = players || new Array();
	
}
Team.prototype = {
	toString: function() {
		return "TEAMnname: " + this.name+"ncity: " + this.city+"nplayers " + this.players.length;
	},
	buyPlayer(player) {
		this.players.push(player);
	},
	sellPlayer(player) {
		for(var i = 0; i < this.players.length; i++) {
			if(player.firstname == this.players[i].firstname && player.lastname == this.players[i].lastname) {
				this.players.slice(i,1);
			}
		}
	}
}

var Match = function(homeTeam, awayTeam) {
	this.homeTeam = homeTeam;
	this.awayTeam = awayTeam;
}
Match.prototype = {
	toString: function() {
		return "MATCHnhome team: " + this.homeTeam.name+"naway team: " + this.awayTeam.name;
	}
}

Da come è facilmente intuibile, le classi create sono abbastanza banali e soprattutto inutili da un punto di vista programmatico, ma in questo articolo tratteremo esclusivamente l'attività di documentazione. Unico aspetto che voglio sottolineare è l'utilizzo della funzione inherits per gestire l'ereditarietà (approfondimento in Tecniche Javascript: la programmazione ad oggetti).

Le classi che documenteremo sono in totale cinque e due di esse (Player e Manager) estendono un'altra classe (Person) overridando un metodo presente nella superclasse (toString). Le altre due classi (Team e Match) non estendono nessuno.

Per documentare questi file basterà inserire dei commenti all'interno dei separatori /** e */ e utilizzando particolari costrutti che iniziano sempre con il simbolo della chiocciola (@).

Iniziamo quindi a inserire una descrizione globale per ciascuna delle nostre classi. Prendendo come spunto Person aggiungiamo all'inizio del file:

/**
 *	@class Rappresenta un oggetto di tipo Persona
*/
var Person = function(firstname, lastname) {
[...]

Utilizzando il costrutto @class comunichiamo al parser di JsDoc Toolkit che seguirà una classe che dovrà essere descritta un messaggio.

Il compito successivo sarà quello di documentare il costruttore di ciascuna classe definendo quali e di che tipo sono i parametri che devono essere passati. Una delle caratteristiche principali di JsDoc Toolkit è l'abilità di riconoscimento dei tipi di parametro. Questo strumento permette infatti sia di utilizzare parametri standard di JavaScript (stringhe, numeri, booleani...) ma anche di impostare un parametro come un oggetto di un'altra classe presente nella nostra applicazione. In questo caso JsDoc capirà e oltre a documentare, presenterà un link in HTML per collegarsi alla documentazione della classe in oggetto.

Ecco la documentazione del costruttore della classe Match (che va inserito tra la definizione della classe inserita prima e il codice del costruttore):

/**
 *	@constructor Costruisce un oggetto Manager
 *	@param {Team} homeTeam Squadra che gioca in casa
 *	@param {Team} awayTeam Squadra che gioca in trasferta
 */

Ecco che oltre al costrutto @class abbiamo introdotto due nuovi identificatori molto importanti: @constructor (che serve ad identificare una funzione come costruttore) e @param che definisce un parametro della funzione che segue.

Un costruttore particolare che vale la pena di osservare nel dettaglio è quello di Team. Esso infatti presenta un parametro (players) che è un vettore ma è opzionale (basta guardare nel codice il controllo). In questo caso esso può essere documentato cosi:

/**
 *	@constructor Costruisce un oggetto Squadra
 *	@param {String} name Nome della squadra
 *	@param {String} city Città di provenienza
 *	@param {Player[]} [players] Giocatori che appartengono alla squadra
 */

Grazie all'utilizzo delle parentesi quadre abbiamo comunicato a JsDoc che il parametro è opzionale.

Un'altra caratteristica del costrutto @param non introdotta nelle classi di demo è la possibilità di definire un doppio tipo (o anche superiore)  di parametro, grazie alle caratteristiche dinamiche di JavaScript. Ipotizzando che un parametro possa essere una stringa o un boolean potremmo scrivere cosi:

@param {String|Boolean} nomeParametro Descrizione del parametro

Prima di proseguire vale la pena soffermarsi un attimo. Visto che scrivere documentazioni in questo modo è un po' come scrivere parti di codice con una particolare sintassi e un ordine da seguire, il mio consiglio è quello di generare passo passo la documentazione e controllare che sia tutto corretto per evitare di trovarsi alla fine con molti errori e molti file da correggere.

Quindi creiamo una sottocartella dentro la root di JsDoc Toolkit e la chiamiamo html.it. Copiamo al suo interno i nostri 5 file JavaScript ed eseguiamo il comando (sempre dalla root):

java -jar jsrun.jar app/run.js html.it/ -t=templates/jsdoc

Se tutto è stato eseguito correttamente dovremmo trovarci una nuova cartella di nome out che contiene i file HTML della documentazione. Ben fatto!

Il prossimo passo è migliorare la definizione iniziali delle classi andando a definire eventuali superclassi tramite il costrutto @augments. Ecco l'esempio per Manager:

/**
 *	@constructor Costruisce un oggetto Manager
 *	@param {String} firstname Nome della persona
 *	@param {String} lastname Cognome della persona
 *	@param {Double} balance Bilancio iniziale
 *	@augments Person
 */

L'ultima cosa da documentare oltre ai metodi sono eventuali proprietà presenti nei nostri oggetti. Esso può spesso sembrare una ripetizione rispetto ai parametri presenti nel costruttore ma non è cosi. Non è infatti detto che un costruttore accetti tutte un numero di parametri pari alle proprietà dell'oggetto ed esiste la possibilità che alcuni parametri passati non siano proprietà ma solo variabili temporanee. Per queste ragioni è sempre meglio distinguere chiaramente tra parametri del costruttore e proprietà della classe. Per documentare le proprietà possiamo seguire questo esempio:

var Team = function(name, city, players) {
	/**
	 *	Nome della squadra
	 *	@type String
	 */
	this.name = name;
	/**
	 * Città di provenienza
	 * @type String
	 */
	this.city = city;
	/**
	 * Giocatori che appartengono alla squadra
	 * @type Player[]
	 * @default new Array()
	 */
	this.players = players || new Array();
}

Da notare l'utilizzo dei costrutti @type e @default per definire rispettivamente il tipo di dato e eventuali parametri di default.Ovviamente nel caso di classi ereditate (vedi Player e Manager) non è necessario definire le proprietà presenti nella superclasse in quanto JsDoc Toolkit creerà per noi un riferimento; stessa cosa vedremo dopo per i metodi.

La definizione dei metodi è simile a quella utilizzata per le proprietà. Vediamo un esempio:

Team.prototype = {
	/**
	 *	Ritorna una stringa identificativa dell'oggetto
	 *	@return {String} string Stringa identificativa
	 */
	toString: function() {
		return "TEAMnname: " + this.name+"ncity: " + this.city+"nplayers " + this.players.length;
	},
	/**
	 *	Acquista un giocatore
	 *	@param {Player} player Giocatore acquistato
	 */
	buyPlayer(player) {
		this.players.push(player);
	},
	/**
	 *	Vende un giocatore
	 *	@param {Player} player Giocatore da cedere
	 */
	sellPlayer(player) {
		for(var i = 0; i < this.players.length; i++) {
			if(player.firstname == this.players[i].firstname && player.lastname == this.players[i].lastname) {
				this.players.slice(i,1);
			}
		}
	}
}

Da notare l'utilizzo di @param e quello di @return. Quest'ultimo permette di definire che tipo di dato viene ritornato dal metodo. Se la funzione non ritorna niente @type non deve essere aggiunto.

La documentazione delle nostre semplici classi ora è terminata. JsDoc però include altri costrutti particolari per qualsiasi situazione particolare. 

In allegato all'articolo troverete i sorgenti completi di documentazione e il piccolo sito web generato grazie a JsDoc Toolkit.

I costrutti per i più esigenti

In questo secondo paragrafo analizzeremo alcuni costrutti che non sono stati analizzati in precedenza con l'esempio, ma che comunque possono essere utili in alcuni casi. 

Per i descrittori non citati in questo paragrafo non posso che rimandare alla documentazione ufficiale sul wiki del progetto.

  • @author: permette di definire all'interno della nostra classe l'autore del codice sorgente. E' possibile inserire link utilizzando codice HTML. Questo costrutto non presenta un contesto fisso ma può essere applicato ad una classe, ma anche ad un metodo particolare, differenziando l'autore anche all'interno della stessa classe;
  • @constant: documenta una costante all'interno del codice;
  • @example: permette di inserire un esempio di utilizzo di una particolare classe o di un metodo all'interno della documentazione per spiegare meglio il comportamento dell'oggetto;
  • @fileOverview: aggiunge informazioni relative al file all'interno dell'HTML generato;
  • @ignore: comunica al parser di JsDoc di ignorare un particolare componente e quindi di non inserirlo nella documentazione (è differente da @private)
  • @link: permette di inserire all'interno di una descrizione un link verso un'altra classe documentata dell'applicazione;
  • @private: permette di definire una proprietà o un metodo come privato e quindi non direttamente richiamabile dall'esterno;
  • @static: definisce un particolare membro come statico e quindi relativo alla classe in sé e non ad una particolare istanza;
  • @throws: definisce se il metodo può lanciare particolari eccezioni;
  • @version: permette di aggiungere il numero di versione alla documentazione.

I parametri da linea di comando

Per generare la documentazione abbiamo utilizzato un comando abbastanza semplice al quale abbiamo semplicemente impostato la cartella iniziale contenente i file .js e quale template utilizzare. È possibile però, tramite opzioni apposite, migliorare l'usabilità del comando dalla console. Anche in questo caso introdurremo i principali. Per chi volesse approfondire rimando alla pagina del wiki.

  • -a: include nella documentazione tutti i metodi, anche quelli non documentati;
  • -d=<PATH>: permette di impostare un percorso di destinazione differente da quello di default (che ricordo è la cartella out all'interno di JsDoc Toolkit);
  • -o=<PATH>: stampa eventuali log in un file invece che nello standard output (la console dove viene eseguito il comando);
  • -p: include nella documentazione anche i membri definiti come privati o quelli che iniziano con un underscore (che nella convenzione JavaScript rappresentano proprio i membri privati);
  • -r=<DEPTH>: permette di eseguire uno scan ricorsivo della cartella dei sorgenti fino ad un massimo di profondità impostabile tramite <DEPTH>;
  • -s: esclude i sorgenti dall'HTML generato;
  • -t=<PATH>: imposta una cartella contenente il template da utilizzare (questo è l'unico parametro richiesto oltre alla cartella contenente i sorgenti);
  • -v: esegue il comando con una verbosità maggiore.

Gli aspetti avanzati

Nonostante tutto quello visto finora non può che essere sufficente per uno strumento per generare documentazioni, in JsDoc Toolkit sono presenti alcune caratteristiche che gli permettono di essere considerato senza dubbi il miglior strumento di questo tipo per JavaScript.

Oltre alla presenza di componenti avanzati come il gestore di namespace e la possibilità di documentazioni inline quello che più colpisce in JsDoc Toolkit è la modularità e quindi la possibilità di estenderlo. 

È possibile aggiungere dei template personalizzati utilizzando JavaScript e un linguaggio di templating creato apposta dagli sviluppatori di nome JsPlate. Tramite JavaScript è possibile lavorare direttamente con i metadati presenti nei nostri sorgenti, analizzarli e eventualmente modificarli, mentre con JsPlate è possibile generare file HTML ma non solo in maniera rapida e indolore garantendo quindi una personalizzazione pressochè totale dell'output.

Sul sito ufficiale sono presenti due tutorial per comprendere il meccanismo e per iniziare a realizzare il proprio template, uno appunto per la componente JavaScript e uno per JsPlate. Inoltre per chi è interessato è possibile anche analizzare i sorgenti presenti nel template fornito in bundle con JsDoc.

Conclusioni

In questo articolo abbiamo imparato non solo come realizzare una documentazione in maniera rapida dai sorgenti, ma anche quali sono le motivazioni che ci devono spingere a eseguire questo compito molto spesso troppo noioso ma, ahimè, necessario soprattutto quando si lavora con altre persone. 

Il miglior modo per evitare la noia è quello di documentare passo passo il codice in modo da non trovarsi alla fine dell'implementazione con uno scoglio troppo imponente da superare. Alla prossima :)


Ti consigliamo anche