Continuing the topic of using Asciidoc (and other similar formats) to organize continuous documentation processes, I want to consider the topic of automatic generation of technical documentation.
Automatic documentation generation is a common but very vague term. I understand by this term extraction for presentation in a convenient form of information contained in the source code and settings of the documented program (information system).
General scheme of automatic documentation generation
, , βββ . .
- . , . , - , , , , . βββ , 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
To completely disable Asciidoc syntax in inserted values, simply escaping them.
conclusions
- Technologies for automatic generation of documentation have been developed and they can be effectively used in IT projects of any complexity.
- The Asciidoc markup language is technologically advanced for use in tasks of automatic generation of documentation.
And an announcement: the next article will be devoted to the issues of quality assurance of documentation in the Asciidoc format.