Документиране на Groovy с Groovydoc

Groovydoc е въведен през 2007 г., за да предостави на Groovy това, което Javadoc предоставя за Java. Groovydoc се използва за генериране на API документация за класовете Groovy и Java, които съставят езика Groovy. В този пост разглеждам извикването на Groovydoc чрез командния ред и чрез персонализираната задача на Ant, предоставена от Groovy.

Изходен код на Groovy и Java с коментари на Groovydoc / Javadoc

Ще използвам адаптирани версии на скрипта и класовете на Groovy, въведени за първи път в моя блог пост Easy Groovy Logger Injection and Log Guarding, за да демонстрирам Groovydoc. Основният скрипт Groovy и класовете Groovy от тази публикация са модифицирани, за да включват повече коментари в стил Javadoc, за да демонстрират по-добре Groovydoc в действие. Ревизираният скрипт и свързаните класове са показани в следващите списъци с кодове.

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}" } } 

В допълнение към горния скрипт и класове на Groovy, тук използвам и нов клас Java, за да илюстрирам, че Groovydoc работи върху класове Java, както и класове Groovy. Класът Java не прави много освен предоставянето на коментарите на Javadoc, които да бъдат обработвани от Groovydoc.

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!"; } } 

Стартиране на Groovydoc в командния ред

С показания по-горе скрипт Groovy, класове Groovy и клас Java е време да насочим вниманието към стартиране на Groovydoc срещу тези класове и скрипт. Както е случаят с Javadoc, Groovydoc може да се стартира от командния ред. Командата за стартиране на Groovydoc срещу горните класове и скриптове (ако приемем, че всички са в една и съща директория, в която се изпълнява командата) изглежда по следния начин:

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 

Горната команда се изпълнява на един ред. За по-добра четливост обаче съм добавил прекъсвания на редове, за да разбия командата.

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 

Параметрите на командата groovydoc изглеждат познати на всеки, който е използвал javadoc от командния ред. Последната част на командата посочва, че groovydoc трябва да се изпълнява срещу Groovy и Java код.

Стартира Groovydoc от Ant

Groovydoc може лесно да бъде достъпен и чрез персонализирана задача Ant, както е описано в Ръководството за потребителя на Groovy. Доста лесно е да приложите задачата на groovydoc Ant, като първо настроите подходящия taskdef и след това използвате този дефиниран таг. Това е демонстрирано в следващия XML фрагмент от съответния build.xmlфайл.

Части от Ant build.xml файл, демонстриращ groovydoc задача


    
    

build.xmlПоказаната по-горе част от Ant е приблизително еквивалентна на тази, използвана в командния ред. Наличието на Groovydoc чрез Ant е важно, защото улеснява интегрирането на изграждането на Groovy документация от базирани на Ant системи за изграждане.

Генерирана документация на Groovydoc

Тъй като всеки подход за генериране на Groovy документация чрез Groovydoc (команден ред или базиран на Ant) работи почти по същия начин като другия, сега ще се съсредоточа върху HTML изхода, който може да дойде от всеки от подходите. Следващата поредица от екранни снимки показва генерираната документация, започваща с главната страница, последвана от страницата DefaultPackage (аз мързеливо оставих скрипта, Groovy класовете и Java класа в текущата директория и без никаква декларация за пакета), последвана съответно от изхода за скрипта Groovy, за пример Groovy клас и за измисления клас Java. Последните три изображения помагат да се направи разлика между изхода за Groovy Script спрямо Groovy клас спрямо Java клас.

Пример за основната страница на Groovydoc

Изход Groovydoc за примерния пакет (DefaultPackage)

Изход на Groovydoc за примерния скрипт на Groovy

Изход на Groovydoc за примерен клас Groovy

Изход Groovydoc за примерния клас Java

Няколко наблюдения могат да бъдат направени от изхода на Groovydoc, показан по-горе. Първо, генерираната документация за скрипта Groovy документира само методите, дефинирани в скрипта (включително неявния mainметод). Това, което не е толкова очевидно от статичните изображения по-горе, е, че всъщност изобщо не се създава изход Groovydoc за скрипт, освен ако поне един метод не е изрично дефиниран в скрипта. Ако в скрипта е дефиниран един метод, тогава изходът на Groovydoc се генерира за всички дефинирани методи и за неявния основен метод. Опцията -nomainforscriptsможе да бъде предадена на Groovydoc, за да няма генериран Groovydoc за неявния mainметод. Резултатът от добавянето на тази опция е показан по-нататък (имайте предвид, че mainдокументацията на 'вече не се показва).

Най -nommainforscriptsвариант е хубаво, защото ние често не искат mainфункция трябва задължително да се документира за нашите скриптове. Всъщност mainфункцията обикновено е „скрита“ от нас като сценаристи и поддръжници.

Второ наблюдение от разглеждането на резултатите, генерирани от Groovydoc, е, че генерираните резултати разграничават изходния код на Groovy и Java. Сценариите и класовете на Groovy са означени с „[Groovy]", а класовете Java са означени с „[Java]." Това е видно и от генерираната от Groovydoc документация за Groovy API, където тези функции улесняват идентифицирането, че groovy.sql.Sql и AntBuilder са Java класове, докато JmxBuilder и SwingBuilder са Groovy класове.