Dokumentiranje Groovyja s Groovydocom

Groovydoc je predstavljen 2007. godine kako bi pružio Groovyju ono što Javadoc pruža za Javu. Groovydoc se koristi za generiranje API dokumentacije za klase Groovy i Java koje čine jezik Groovy. U ovom postu gledam na pozivanje Groovydoca putem naredbenog retka i putem prilagođenog zadatka Ant koji nudi Groovy.

Izvorni kod Groovy i Java s komentarima Groovydoc / Javadoc

Koristit ću prilagođene verzije Groovy skripte i klase prvi put predstavljene u mom blogu, Jednostavno ubrizgavanje i čuvanje dnevnika Groovy Logger za demonstraciju Groovydoca. Glavna skripta Groovy i klase Groovy iz tog posta izmijenjeni su tako da uključuju više komentara u Javadoc stilu kako bi Groovydoc bolje pokazao na djelu. Izmijenjena skripta i pripadajuće klase prikazani su u sljedećim popisima kodova.

demoGroovyLogTransformation.groovy

#!/usr/bin/env groovy /** * demoGroovyLogTransformation.groovy * * Grab SLF4J, Log4j, and Apache Commons Logging dependencies using @Grab and * demonstrate Groovy 1.8's injected logging handles. * * //marxsoftware.blogspot.com/2011/05/easy-groovy-logger-injection-an... */ // No need to "grab" java.util.logging: it's part of the JDK! /* * Specifying 'slf4j-simple' rather than 'slf4j-api' to avoid the error * "Failed to load class "org.slf4j.impl.StaticLoggerBinder" that is caused by * specifying no or more than one of the actual logging binding libraries to * be used (see //www.slf4j.org/codes.html#StaticLoggerBinder). One should * be selected from 'slf4j-nop', 'slf4j-simple', 'slf4j-log4j12.jar', * 'slf4j-jdk14', or 'logback-classic'. An example of specifying the SLF4J * dependency via @Grab is available at * //mvnrepository.com/artifact/org.slf4j/slf4j-api/1.6.1. */ @Grab(group='org.slf4j', module="slf4j-simple", version="1.6.1") /* * An example of specifying the Log4j dependency via @Grab is at * //mvnrepository.com/artifact/log4j/log4j/1.2.16. */ @Grab(group='log4j', module="log4j", version="1.2.16") /* * An example of specifying the Apache Commons Logging dependency via @Grab is at * //mvnrepository.com/artifact/commons-logging/commons-logging-api/1..... */ @Grab(group='commons-logging', module="commons-logging-api", version="1.1") /* * Run the tests... */ int headerSize = 79 printHeader("java.util.logger", headerSize) def javaUtilLogger = new JavaUtilLoggerClass() printHeader("Log4j", headerSize) def log4jLogger = new Log4jLoggerClass() printHeader("SLF4j", headerSize) def slf4jLogger = new Slf4jLoggerClass() printHeader("Apache Commons", headerSize) def commonsLogger = new ApacheCommonsLoggerClass() /** * Print header with provided text. * * @param textForHeader Text to be included in the header. * @param sizeOfHeader Number of characters in each row of header. */ def printHeader(final String textForHeader, final int sizeOfHeader) { println "=".multiply(sizeOfHeader) println "= ${textForHeader}${' '.multiply(sizeOfHeader-textForHeader.size()-4)}=".multiply(sizeOfHeader) } 

JavaUtilLoggerClass.groovy

import groovy.util.logging.Log /** * Sample Groovy class using {@code @Log} to inject java.util.logging logger * into the class. */ @Log class JavaUtilLoggerClass { /** * Constructor. */ public JavaUtilLoggerClass() { println "\njava.util.logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.finer "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of JDK's java.util.logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and JDK for java.util.logging. */ public String printAndReturnValue(int newValue) { println "JDK: Print method invoked for ${newValue}" return "JDK: ${newValue}" } } 

Log4jLoggerClass.groovy

import groovy.util.logging.Log4j import org.apache.log4j.Level /** * Sample Groovy class using {@code @Log4j} to inject Log4j logger * into the class. */ @Log4j class Log4jLoggerClass { /** * Constructor. */ Log4jLoggerClass() { // It is necessary to set logging level here because default is FATAL and // we are not using a Log4j external configuration file in this example log.setLevel(Level.INFO) println "\nLog4j Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of Log4j. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and Log4j. */ public String printAndReturnValue(int newValue) { println "Log4j: Print method invoked for ${newValue}" return "Log4j: ${newValue}" } } 

Slf4jLoggerClass.groovy

import groovy.util.logging.Slf4j /** * Sample Groovy class using {@code @Slf4j} to inject Simple Logging Facade for * Java (SLF4J) logger into the class. */ @Slf4j class Slf4jLoggerClass { /** * Constructor. */ public Slf4jLoggerClass() { println "\nSLF4J Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of SLF4J logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and SLF4J. */ public String printAndReturnValue(int newValue) { println "SLF4J: Print method invoked for ${newValue}" return "SLF4J: ${newValue}" } } 

ApacheCommonsLoggerClass.groovy

import groovy.util.logging.Commons /** * Sample Groovy class using {@code @Commons} to inject Apache Commons logger * into the class. */ @Commons class ApacheCommonsLoggerClass { /** * Constructor. */ public ApacheCommonsLoggerClass() { println "\nApache Commons Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of Apache Commons Logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and Apache Commons Logging. */ public String printAndReturnValue(int newValue) { println "Commons: Print method invoked for ${newValue}" return "Commons: ${newValue}" } } 

Pored gore navedene Groovy skripte i klasa, ovdje također koristim novu Java klasu kako bih ilustrirao da Groovydoc radi na Java klasama, kao i na Groovy klasama. Klasa Java ne čini puno osim što pruža Javadoc komentare koje Groovydoc obrađuje.

DoNothingClass.java

/** * Class that does not do anything, but is here to be a Java class run through * groovydoc. */ public class DoNothingClass { /** * Simple method that returns literal "Hello _addressee_!" string where * _addressee_ is the name provided to this method. * * @param addressee Name for returned salutation to be addressed to. * @return "Hello!" */ public String sayHello(final String addressee) { return "Hello, " + addressee; } /** * Main executable function. */ public static void main(final String[] arguments) { final DoNothingClass me = new DoNothingClass(); me.sayHello("Dustin"); } /** * Provide String representation of this object. * * @return String representation of me. */ @Override public String toString() { return "Hello!"; } } 

Pokretanje Groovydoca na naredbenom retku

S gore prikazanom skriptom Groovy, Groovy i Java tečajevima, vrijeme je da skrenemo pažnju na pokretanje Groovydoca protiv ovih klasa i skripte. Kao što je slučaj s Javadocom, Groovydoc se može pokrenuti iz naredbenog retka. Naredba za pokretanje Groovydoca protiv gore navedenih klasa i skripti (pod pretpostavkom da su svi u istom direktoriju u kojem se pokreće naredba) izgleda otprilike ovako:

groovydoc -classpath C:\groovy-1.8.0\lib\ -d output -windowtitle "Groovy 1.8 Logging Example" -header "Groovy 1.8 Logging (Inspired by Actual Events)" -footer "Inspired by Actual Events: Logging in Groovy 1.8" -doctitle "Logging in Groovy 1.8 Demonstrated" *.groovy *.java 

Gornja naredba se izvodi u jednom retku. Međutim, radi poboljšane čitljivosti dodao sam prelome redaka kako bih razbio naredbu.

groovydoc -classpath C:\groovy-1.8.0\lib\ -d output -windowtitle "Groovy 1.8 Logging Example" -header "Groovy 1.8 Logging (Inspired by Actual Events)" -footer "Inspired by Actual Events: Logging in Groovy 1.8" -doctitle "Logging in Groovy 1.8 Demonstrated" *.groovy *.java 

Parametri naredbe groovydoc izgledaju poznati svima koji su koristili javadoc iz naredbenog retka. Posljednji dio naredbe određuje da groovydoc treba pokrenuti protiv Groovy i Java koda.

Trčanje Groovydoca iz Ant

Groovydocu se također može lako pristupiti putem prilagođenog zadatka Ant kako je opisano u korisničkom vodiču Groovy. Prilično je jednostavno primijeniti zadatak groovydoc Ant prvo postavljanjem odgovarajućeg taskdef, a zatim upotrebom te definirane oznake. To je prikazano u sljedećem XML isječku iz relevantne build.xmldatoteke.

Dijelovi datoteke Ant build.xml koji demonstriraju groovydoc zadatak


    
    

Gore prikazani dio Mrava build.xmlotprilike je ekvivalentan onome koji se koristi na naredbenom retku. Dostupnost Groovydoca putem Ant-a važno je jer olakšava integraciju izrade Groovy-jeve dokumentacije iz sustava gradnje temeljenih na Ant-u.

Groovydoc generirana dokumentacija

Budući da svaki pristup generiranju Groovyjeve dokumentacije putem Groovydoca (naredbeni redak ili zasnovan na Ant-u) djeluje približno isto kao i drugi, sada ću se usredotočiti na HTML izlaz koji bi mogao doći iz bilo kojeg pristupa. Sljedeća serija snimaka zaslona prikazuje generiranu dokumentaciju koja započinje s glavnom stranicom, nakon čega slijedi stranica DefaultPackage (lijeno sam ostavila skriptu, Groovy klase i Java klasu u trenutnom direktoriju i bez izjave paketa), nakon čega slijedi izlaz za skriptu Groovy, za primjer klase Groovy i za izmišljenu klasu Java. Posljednje tri slike pomažu u razlikovanju rezultata za Groovy Script od Groovy klase od Java klase.

Primjer glavne stranice Groovydoca

Groovydoc izlaz za primjer paketa (zadani paket)

Groovydoc izlaz za primjer Groovy skripte

Izlaz Groovydoc za primjer Groovy klase

Izlaz Groovydoc za primjer Java klase

Iz gore prikazanih rezultata Groovydoca može se izvesti nekoliko zapažanja. Prvo, generirana dokumentacija za Groovy skriptu dokumentirala je samo metode definirane u skripti (uključujući implicitnu mainmetodu). Ono što iz gornjih statičnih slika nije toliko očito jest da se zapravo ne stvara Groovydoc izlaz za skriptu ako barem jedna metoda nije izričito definirana u skripti. Ako je u skripti definirana jedna metoda, tada se generira izlaz Groovydoc za sve definirane metode i za implicitnu glavnu metodu. Opcija -nomainforscriptsse može proslijediti Groovydocu da nema generiranog Groovydoca za implicitnu mainmetodu. Izlaz dodavanja ove opcije prikazan je sljedeći (imajte na umu da se maindokumentacija više ne prikazuje).

-nommainforscriptsOpcija je lijepo, jer mi često ne žele da mainfunkcija se implicitno dokumentirani za naše skripte. Zapravo, mainfunkcija je obično "skrivena" od nas kao scenarista i održavatelja.

Drugo zapažanje iz promatranja rezultata generiranog Groovydoc je da generirani izlaz razlikuje između izvornog koda Groovy i Java. Groovy skripte i razredi označeni su s "[Groovy]", a Java klase s "[Java]". To je vidljivo i iz Groovydoc-generirane Groovy API dokumentacije gdje ove značajke olakšavaju prepoznavanje da su groovy.sql.Sql i AntBuilder Java klase, dok su JmxBuilder i SwingBuilder Groovy klase.