Continuando con el tema del uso de Asciidoc (y otros formatos similares) para organizar procesos de documentación continua, quiero considerar el tema de la generación automática de documentación técnica.
La generación automática de documentación es un término común pero muy vago. Entiendo por este término extracción para presentación en una forma conveniente de información contenida en el código fuente y la configuración del programa documentado (sistema de información).
Esquema general de generación automática de documentación
, , — . .
- . , . , - , , , , . — , JSON/YAML XML;
- ( Asciidoc, DITA, Docbook, Markdown, reStructuredText).
, (html, docx, odt, pdf ..) ( ) . , ? , . .
:
, -. Asciidoc, (reStructuredText, Markdown), ( kroki, ).
. .
, , ( Javadoc, ReST ..) .
.
(, , ..), .
. — . , Junit xml report. , , , — Allure Framework.
, JSON-, Cucumber, , .
, , — . , .
(, xsd-, OpenAPI, DSL , ).
, , ( «flatten»).
( ) , .
—
, .
create table geo.Strana ( id int , naimenovaniye varchar(255) , primary key (id) ); create table geo.Gorod ( id int , naimenovaniye varchar(255) , strana_id int , constraint strana_gorod foreign key (strana_id) references geo.Strana(id) );
( PostgreSQL): JSON- .
drop table if exists fk; select x.table_schema as table_schema , x.table_name , y.table_schema as foreign_table_schema , y.table_name as foreign_table_name into temp fk from information_schema.referential_constraints rc join information_schema.key_column_usage x on x.constraint_name = rc.constraint_name join information_schema.key_column_usage y on y.ordinal_position = x.position_in_unique_constraint and y.constraint_name = rc.unique_constraint_name;
select json_agg(json_build_object( 'name', t.table_schema || '.' || t.table_name , 'columns' , (select json_agg(json_build_object ( 'name', column_name ,'type', data_type )) from information_schema.columns as c where c.table_name = t.table_name and c.table_schema = t.table_schema ) , 'fk' , (select json_agg(json_build_object ( 'fk_table' , fk.foreign_table_schema || '.' || fk.foreign_table_name )) from fk where fk.table_name = t.table_name and fk.table_schema = t.table_schema ) )) from information_schema.tables as t where table_schema = 'geo';
JSON-:
[{ "name": "geo.Strana", "columns": [{ "name": "id", "type": "integer" }, { "name": "naimenovaniye", "type": "character varying" } ], "fk": null }, { "name": "geo.Gorod", "columns": [{ "name": "id", "type": "integer" }, { "name": "naimenovaniye", "type": "character varying" }, { "name": "strana_id", "type": "integer" } ], "fk": [{ "fk_table": "geo.Strana" } ] } ]
, .
, , .
, , 2003 XSLT , , Ruby. 18 , , XSLT , .
Liquid JSON XSLT XML. Ruby, (1) Asciidoc — Asciidoctor — Ruby (2) Ruby- java javascript, .
JSON-
JSON-.
PlantUML:
{% assign bl = "\n" %} {%- for table in data -%} class {{ table.name }}{{ bl }} {%- for fk in table.fk -%} {{ table.name }} "*" -- "1" {{ fk.fk_table }}{{ bl }} {%- endfor -%} {%- endfor -%}
, . PlantUML class [ ]
. .
:
class geo.Strana class geo.Gorod geo.Gorod "*" -- "1" geo.Strana
Asciidoc:
{% assign bl = "\n" %}{% assign bbl = "\n\n" %} {%- for table in data -%} [[{{ table.name }}]]{{- bl -}} . {{ table.name }}{{- bl -}} [cols="1,3,3", options="header"]{{- bl -}} |==={{- bl -}} |№ | | {{ bl }} {%- for column in table.columns -%} |{counter:{{ table.name }}} |{{ column.name }} |{{ column.type }}{{- bl -}} {%- endfor -%} {%- if table.fk -%} 3+a| :{{- bbl -}} {%- for fk in table.fk -%} * <<{{fk.fk_table}}, {{fk.fk_table}}>>{{- bl -}} {%- endfor -%} {%- endif -%} |==={{- bbl -}} {%- endfor -%}
include:
= :lang: ru :figure-caption: :xrefstyle: short :sectnums: == (<<struktura>>). [[struktura]] . [plantuml, struktura, png, fitrect="170x240mm", srcdpi=300, width="50%"] .... skinparam dpi 300 left to right direction include::pu_sql.pu[] .... == include::adoc_sql.adoc[]
Asciidoc Asciidoc . Asciidoc . , . - ( , , ..).
,
XML-.
( xsd ) 3 — https://smev3.gosuslugi.ru/portal/inquirytype_one.jsp?id=41108&zone=fed. :
<ns1: =" 5087746429843" =" 5087746429843"> <ns1: ="5087746429843" ="2008-11-18"/> </ns1:> <ns1:> <ns1: ="77" ="770000000002990" ="7" ="6"> <fnst: ="" =""/> <fnst: ="" =" 2-"/> <fnst: ="5087746429843" ="2008-11-18"/> </ns1:> </ns1:>
, , xsd.
Asciidoc :
<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:ep="uri:asciidoc:doc:automation" extension-element-prefixes="ep"> <output method="text" /><strip-space elements="*"/> <template match="/"><apply-templates/></template> <template match="*[count(@*|*) > 0 and count(ancestor::*) > 0]"> <value-of select="'\n='"/> <for-each select="ancestor::*"><value-of select="'='"/></for-each> <value-of select="' '"/> <value-of select="concat('{',local-name(),'}')"/><text>\n\n</text> <text>|===\n</text> <for-each select="(@*)|(*[./text()])"> <text>|</text><value-of select="concat('{',local-name(),'}')"/> <text>|</text><value-of select="ep:iformat(current())"/> <text>\n</text> </for-each> <text>|===\n</text> <apply-templates/> </template> <template match="text()"/> </stylesheet>
. , . — . , Asciidoc |
.
XML- — Asciidoc. xsd- :
<?xml version="1.0" encoding="UTF-8"?> <stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <output method="text" /> <strip-space elements="*"/> <template match="*[@name]"> <value-of select="concat(':', @name, ': ')"/> <value-of select="normalize-space(xs:annotation/xs:documentation)"/> <text>\n</text> <apply-templates/> </template> <template match="*[not(@name)]"> <apply-templates/> </template> <template match="text()"></template> </stylesheet>
Asciidoc ( , .. xsd) :
:sectnums: include::adoc_egrul_xsd.adoc[] include::adoc_egrul_xsd2.adoc[] include::adoc_egrul.adoc[]
Microsoft Word :
, : , , .
, . , Asciidoc . , . . . XML JSON , . , , .
, , , , , .
, , — , , . <strip-space elements="*"/>
<text>\n</text>
. \n
. , - .
Liquid , bl
.
, .
. , XSLT apply-templates
. (template
) .
Asciidoc Asciidoc. , Open API «;
». . «;;
» Asciidoc , , , .
, , . — iformat
. (zero space) DD.MM.YYYY.
AsciidocDocAutomation = Class.new do def iformat(node) value = node.to_s re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/ vm = value.match(re) value = "#{vm[3]}.#{vm[2]}.#{vm[1]}" if !!(value =~ re) "​#​" end end
Para deshabilitar completamente la sintaxis de Asciidoc en los valores insertados, simplemente escapéelos.
conclusiones
- Se han desarrollado tecnologías para la generación automática de documentación y se pueden utilizar eficazmente en proyectos de TI de cualquier complejidad.
- El lenguaje de marcado Asciidoc está tecnológicamente avanzado para su uso en tareas de generación automática de documentación.
Y un anuncio: el próximo artículo estará dedicado a las cuestiones de garantía de calidad de la documentación en formato Asciidoc.