docbook 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CLAUDE.md +19 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/README.adoc +335 -0
- data/Rakefile +12 -0
- data/docs/.lycheeignore +33 -0
- data/docs/Gemfile +10 -0
- data/docs/INDEX.adoc +67 -0
- data/docs/_config.yml +186 -0
- data/docs/advanced/element-classes.adoc +185 -0
- data/docs/advanced/frontend-customization.adoc +193 -0
- data/docs/advanced/index.adoc +14 -0
- data/docs/advanced/templates.adoc +123 -0
- data/docs/features/element-coverage.adoc +373 -0
- data/docs/features/html-output/data-model.adoc +285 -0
- data/docs/features/html-output/directory-mode.adoc +180 -0
- data/docs/features/html-output/index.adoc +90 -0
- data/docs/features/html-output/single-file-mode.adoc +125 -0
- data/docs/features/index-generation.adoc +197 -0
- data/docs/features/index.adoc +63 -0
- data/docs/features/numbering.adoc +183 -0
- data/docs/features/toc-generation.adoc +150 -0
- data/docs/features/xinclude/fragid-schemes.adoc +287 -0
- data/docs/features/xinclude/index.adoc +119 -0
- data/docs/features/xinclude/text-includes.adoc +123 -0
- data/docs/features/xinclude/xml-includes.adoc +167 -0
- data/docs/getting-started/index.adoc +50 -0
- data/docs/getting-started/installation.adoc +113 -0
- data/docs/getting-started/quick-start.adoc +161 -0
- data/docs/guides/converting-article.adoc +188 -0
- data/docs/guides/converting-book.adoc +192 -0
- data/docs/guides/index.adoc +12 -0
- data/docs/guides/roundtrip-testing.adoc +129 -0
- data/docs/interfaces/cli/format-command.adoc +109 -0
- data/docs/interfaces/cli/index.adoc +73 -0
- data/docs/interfaces/cli/roundtrip-command.adoc +125 -0
- data/docs/interfaces/cli/to-html-command.adoc +178 -0
- data/docs/interfaces/cli/validate-command.adoc +104 -0
- data/docs/interfaces/index.adoc +101 -0
- data/docs/interfaces/ruby-api/html-output.adoc +186 -0
- data/docs/interfaces/ruby-api/index.adoc +111 -0
- data/docs/interfaces/ruby-api/parsing.adoc +202 -0
- data/docs/interfaces/ruby-api/xinclude.adoc +162 -0
- data/docs/interfaces/ruby-api/xref-resolution.adoc +156 -0
- data/docs/lychee.toml +42 -0
- data/docs/reference/cli-options.adoc +155 -0
- data/docs/reference/content-block-types.adoc +243 -0
- data/docs/reference/glossary.adoc +119 -0
- data/docs/reference/index.adoc +12 -0
- data/docs/reference/supported-elements.adoc +749 -0
- data/docs/understanding/architecture.adoc +145 -0
- data/docs/understanding/content-pipeline.adoc +102 -0
- data/docs/understanding/data-models.adoc +156 -0
- data/docs/understanding/index.adoc +34 -0
- data/exe/docbook +7 -0
- data/frontend/dist/app.css +1 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/package-lock.json +1445 -0
- data/frontend/package.json +22 -0
- data/frontend/src/App.vue +230 -0
- data/frontend/src/app.ts +8 -0
- data/frontend/src/components/AppSidebar.vue +116 -0
- data/frontend/src/components/AppendixSection.vue +39 -0
- data/frontend/src/components/BlockRenderer.vue +358 -0
- data/frontend/src/components/ChapterSection.vue +32 -0
- data/frontend/src/components/ContentRenderer.vue +13 -0
- data/frontend/src/components/EbookContainer.vue +147 -0
- data/frontend/src/components/EbookTopBar.vue +116 -0
- data/frontend/src/components/PartSection.vue +44 -0
- data/frontend/src/components/ReferenceEntry.vue +80 -0
- data/frontend/src/components/SearchModal.vue +286 -0
- data/frontend/src/components/SectionContent.vue +31 -0
- data/frontend/src/components/SettingsPanel.vue +236 -0
- data/frontend/src/components/TocTreeItem.vue +135 -0
- data/frontend/src/composables/useEbookStore.ts +191 -0
- data/frontend/src/composables/useSearch.ts +249 -0
- data/frontend/src/env.d.ts +7 -0
- data/frontend/src/stores/documentStore.ts +221 -0
- data/frontend/src/stores/uiStore.ts +98 -0
- data/frontend/src/styles.css +253 -0
- data/frontend/tsconfig.json +24 -0
- data/frontend/tsconfig.node.json +11 -0
- data/frontend/vite.config.ts +30 -0
- data/lib/docbook/cli.rb +123 -0
- data/lib/docbook/document.rb +67 -0
- data/lib/docbook/elements/abbrev.rb +16 -0
- data/lib/docbook/elements/acknowledgements.rb +22 -0
- data/lib/docbook/elements/address.rb +18 -0
- data/lib/docbook/elements/alt.rb +16 -0
- data/lib/docbook/elements/annotation.rb +18 -0
- data/lib/docbook/elements/appendix.rb +34 -0
- data/lib/docbook/elements/article.rb +31 -0
- data/lib/docbook/elements/att.rb +15 -0
- data/lib/docbook/elements/attribution.rb +20 -0
- data/lib/docbook/elements/audioobject.rb +18 -0
- data/lib/docbook/elements/author.rb +18 -0
- data/lib/docbook/elements/bibliography.rb +24 -0
- data/lib/docbook/elements/bibliomixed.rb +40 -0
- data/lib/docbook/elements/biblioref.rb +20 -0
- data/lib/docbook/elements/blockquote.rb +26 -0
- data/lib/docbook/elements/book.rb +36 -0
- data/lib/docbook/elements/buildtarget.rb +16 -0
- data/lib/docbook/elements/callout.rb +22 -0
- data/lib/docbook/elements/calloutlist.rb +22 -0
- data/lib/docbook/elements/caution.rb +30 -0
- data/lib/docbook/elements/chapter.rb +62 -0
- data/lib/docbook/elements/citation.rb +16 -0
- data/lib/docbook/elements/citerefentry.rb +26 -0
- data/lib/docbook/elements/citetitle.rb +20 -0
- data/lib/docbook/elements/classname.rb +16 -0
- data/lib/docbook/elements/code.rb +16 -0
- data/lib/docbook/elements/colophon.rb +22 -0
- data/lib/docbook/elements/computeroutput.rb +18 -0
- data/lib/docbook/elements/copyright.rb +17 -0
- data/lib/docbook/elements/danger.rb +28 -0
- data/lib/docbook/elements/date.rb +16 -0
- data/lib/docbook/elements/dedication.rb +22 -0
- data/lib/docbook/elements/dir.rb +16 -0
- data/lib/docbook/elements/emphasis.rb +18 -0
- data/lib/docbook/elements/entry.rb +30 -0
- data/lib/docbook/elements/entrytbl.rb +22 -0
- data/lib/docbook/elements/equation.rb +26 -0
- data/lib/docbook/elements/example.rb +30 -0
- data/lib/docbook/elements/fieldsynopsis.rb +21 -0
- data/lib/docbook/elements/figure.rb +28 -0
- data/lib/docbook/elements/filename.rb +16 -0
- data/lib/docbook/elements/firstname.rb +16 -0
- data/lib/docbook/elements/firstterm.rb +18 -0
- data/lib/docbook/elements/footnote.rb +22 -0
- data/lib/docbook/elements/footnoteref.rb +21 -0
- data/lib/docbook/elements/foreignphrase.rb +18 -0
- data/lib/docbook/elements/formalpara.rb +20 -0
- data/lib/docbook/elements/function.rb +16 -0
- data/lib/docbook/elements/glossary.rb +24 -0
- data/lib/docbook/elements/glossdef.rb +18 -0
- data/lib/docbook/elements/glossentry.rb +26 -0
- data/lib/docbook/elements/glosssee.rb +18 -0
- data/lib/docbook/elements/glossseealso.rb +18 -0
- data/lib/docbook/elements/glossterm.rb +18 -0
- data/lib/docbook/elements/holder.rb +16 -0
- data/lib/docbook/elements/honorific.rb +16 -0
- data/lib/docbook/elements/imagedata.rb +29 -0
- data/lib/docbook/elements/imageobject.rb +22 -0
- data/lib/docbook/elements/important.rb +30 -0
- data/lib/docbook/elements/index.rb +26 -0
- data/lib/docbook/elements/indexdiv.rb +22 -0
- data/lib/docbook/elements/indexentry.rb +22 -0
- data/lib/docbook/elements/indexterm.rb +34 -0
- data/lib/docbook/elements/info.rb +25 -0
- data/lib/docbook/elements/informalexample.rb +24 -0
- data/lib/docbook/elements/informalfigure.rb +20 -0
- data/lib/docbook/elements/inlinemediaobject.rb +22 -0
- data/lib/docbook/elements/itemizedlist.rb +21 -0
- data/lib/docbook/elements/legalnotice.rb +22 -0
- data/lib/docbook/elements/link.rb +26 -0
- data/lib/docbook/elements/listitem.rb +32 -0
- data/lib/docbook/elements/literal.rb +16 -0
- data/lib/docbook/elements/literallayout.rb +22 -0
- data/lib/docbook/elements/mediaobject.rb +26 -0
- data/lib/docbook/elements/msg.rb +20 -0
- data/lib/docbook/elements/msgexplan.rb +22 -0
- data/lib/docbook/elements/msgset.rb +22 -0
- data/lib/docbook/elements/note.rb +30 -0
- data/lib/docbook/elements/orderedlist.rb +23 -0
- data/lib/docbook/elements/orgname.rb +16 -0
- data/lib/docbook/elements/para.rb +61 -0
- data/lib/docbook/elements/paragraph_like.rb +18 -0
- data/lib/docbook/elements/parameter.rb +16 -0
- data/lib/docbook/elements/part.rb +38 -0
- data/lib/docbook/elements/personname.rb +22 -0
- data/lib/docbook/elements/phrase.rb +20 -0
- data/lib/docbook/elements/preface.rb +58 -0
- data/lib/docbook/elements/primary.rb +16 -0
- data/lib/docbook/elements/procedure.rb +24 -0
- data/lib/docbook/elements/productname.rb +18 -0
- data/lib/docbook/elements/productnumber.rb +16 -0
- data/lib/docbook/elements/programlisting.rb +28 -0
- data/lib/docbook/elements/property.rb +16 -0
- data/lib/docbook/elements/pubdate.rb +16 -0
- data/lib/docbook/elements/publishername.rb +16 -0
- data/lib/docbook/elements/quotation.rb +24 -0
- data/lib/docbook/elements/quote.rb +18 -0
- data/lib/docbook/elements/refentry.rb +32 -0
- data/lib/docbook/elements/refentrytitle.rb +16 -0
- data/lib/docbook/elements/reference.rb +26 -0
- data/lib/docbook/elements/refmeta.rb +29 -0
- data/lib/docbook/elements/refmiscinfo.rb +16 -0
- data/lib/docbook/elements/refname.rb +16 -0
- data/lib/docbook/elements/refnamediv.rb +22 -0
- data/lib/docbook/elements/refpurpose.rb +16 -0
- data/lib/docbook/elements/refsect1.rb +22 -0
- data/lib/docbook/elements/refsect2.rb +22 -0
- data/lib/docbook/elements/refsect3.rb +22 -0
- data/lib/docbook/elements/refsection.rb +32 -0
- data/lib/docbook/elements/releaseinfo.rb +16 -0
- data/lib/docbook/elements/remark.rb +20 -0
- data/lib/docbook/elements/replaceable.rb +16 -0
- data/lib/docbook/elements/row.rb +15 -0
- data/lib/docbook/elements/screen.rb +28 -0
- data/lib/docbook/elements/secondary.rb +16 -0
- data/lib/docbook/elements/sect1.rb +26 -0
- data/lib/docbook/elements/sect2.rb +24 -0
- data/lib/docbook/elements/sect3.rb +24 -0
- data/lib/docbook/elements/sect4.rb +24 -0
- data/lib/docbook/elements/sect5.rb +22 -0
- data/lib/docbook/elements/section.rb +66 -0
- data/lib/docbook/elements/see.rb +16 -0
- data/lib/docbook/elements/seealso.rb +18 -0
- data/lib/docbook/elements/set.rb +26 -0
- data/lib/docbook/elements/setindex.rb +24 -0
- data/lib/docbook/elements/sidebar.rb +22 -0
- data/lib/docbook/elements/simplesect.rb +32 -0
- data/lib/docbook/elements/step.rb +26 -0
- data/lib/docbook/elements/substeps.rb +18 -0
- data/lib/docbook/elements/subtitle.rb +16 -0
- data/lib/docbook/elements/surname.rb +16 -0
- data/lib/docbook/elements/table.rb +24 -0
- data/lib/docbook/elements/tag.rb +20 -0
- data/lib/docbook/elements/tbody.rb +15 -0
- data/lib/docbook/elements/term.rb +37 -0
- data/lib/docbook/elements/tertiary.rb +16 -0
- data/lib/docbook/elements/textobject.rb +18 -0
- data/lib/docbook/elements/tfoot.rb +15 -0
- data/lib/docbook/elements/tgroup.rb +21 -0
- data/lib/docbook/elements/thead.rb +15 -0
- data/lib/docbook/elements/tip.rb +30 -0
- data/lib/docbook/elements/title.rb +16 -0
- data/lib/docbook/elements/toc.rb +24 -0
- data/lib/docbook/elements/tocdiv.rb +22 -0
- data/lib/docbook/elements/tocentry.rb +20 -0
- data/lib/docbook/elements/topic.rb +26 -0
- data/lib/docbook/elements/type.rb +16 -0
- data/lib/docbook/elements/uri.rb +16 -0
- data/lib/docbook/elements/userinput.rb +18 -0
- data/lib/docbook/elements/variable.rb +16 -0
- data/lib/docbook/elements/variablelist.rb +19 -0
- data/lib/docbook/elements/varlistentry.rb +17 -0
- data/lib/docbook/elements/version.rb +16 -0
- data/lib/docbook/elements/videoobject.rb +18 -0
- data/lib/docbook/elements/warning.rb +30 -0
- data/lib/docbook/elements/xref.rb +18 -0
- data/lib/docbook/elements/year.rb +16 -0
- data/lib/docbook/elements.rb +204 -0
- data/lib/docbook/models/document_metadata.rb +30 -0
- data/lib/docbook/models/document_root.rb +33 -0
- data/lib/docbook/models/index_entry.rb +28 -0
- data/lib/docbook/models/reading_position.rb +22 -0
- data/lib/docbook/models/section_number.rb +18 -0
- data/lib/docbook/models/section_root.rb +29 -0
- data/lib/docbook/models/toc_node.rb +24 -0
- data/lib/docbook/models.rb +16 -0
- data/lib/docbook/output/data.rb +196 -0
- data/lib/docbook/output/html.rb +1450 -0
- data/lib/docbook/output/index_generator.rb +281 -0
- data/lib/docbook/output.rb +8 -0
- data/lib/docbook/services/document_builder.rb +258 -0
- data/lib/docbook/services/element_to_hash.rb +287 -0
- data/lib/docbook/services/index_generator.rb +134 -0
- data/lib/docbook/services/numbering_service.rb +186 -0
- data/lib/docbook/services/toc_generator.rb +138 -0
- data/lib/docbook/services.rb +14 -0
- data/lib/docbook/templates/document.html.liquid +20 -0
- data/lib/docbook/templates/partials/_head.liquid +39 -0
- data/lib/docbook/templates/partials/_scripts.liquid +10 -0
- data/lib/docbook/version.rb +5 -0
- data/lib/docbook/xinclude_resolver.rb +217 -0
- data/lib/docbook/xref_resolver.rb +78 -0
- data/lib/docbook.rb +17 -0
- data/sig/docbook.rbs +4 -0
- metadata +385 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Architecture
|
|
4
|
+
parent: Understanding
|
|
5
|
+
nav_order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Architecture
|
|
9
|
+
|
|
10
|
+
The Metanorma DocBook gem is organized into distinct modules, each
|
|
11
|
+
responsible for a specific phase of document processing. This page
|
|
12
|
+
describes the purpose of each module and how they interact.
|
|
13
|
+
|
|
14
|
+
== Module structure
|
|
15
|
+
|
|
16
|
+
=== Docbook::Document
|
|
17
|
+
|
|
18
|
+
The entry point for parsing. `Document.from_xml` accepts an XML string,
|
|
19
|
+
detects the root element name (such as `book` or `article`), looks up
|
|
20
|
+
the corresponding element class in `ROOT_ELEMENT_MAP`, and delegates
|
|
21
|
+
to that class's `from_xml` method.
|
|
22
|
+
|
|
23
|
+
=== Docbook::Elements
|
|
24
|
+
|
|
25
|
+
Contains 157 element classes, one per supported DocBook XML element.
|
|
26
|
+
Each class inherits from `Lutaml::Model::Serializable` and declares
|
|
27
|
+
typed attributes and XML mappings using the lutaml-model DSL. Classes
|
|
28
|
+
are autoloaded on demand from `lib/docbook/elements/`.
|
|
29
|
+
|
|
30
|
+
=== Docbook::XIncludeResolver
|
|
31
|
+
|
|
32
|
+
Resolves XInclude directives before model parsing. It iterates through
|
|
33
|
+
`xi:include` elements in a Nokogiri document, fetching referenced files
|
|
34
|
+
and replacing the include elements with the included content. Supports
|
|
35
|
+
both XML and text parse modes, plus `fragid` extensions for line-range
|
|
36
|
+
and search-based text extraction.
|
|
37
|
+
|
|
38
|
+
=== Docbook::XrefResolver
|
|
39
|
+
|
|
40
|
+
After parsing, builds an O(1) hash map of `xml:id` values to their
|
|
41
|
+
corresponding element objects. The resolver walks the element tree
|
|
42
|
+
recursively, collecting every element that has an `xml_id`. Callers
|
|
43
|
+
then use `title_for(linkend)` to resolve cross-reference text.
|
|
44
|
+
|
|
45
|
+
=== Docbook::Output::Html
|
|
46
|
+
|
|
47
|
+
The HTML generation engine. It takes a parsed element tree and an
|
|
48
|
+
optional `XrefResolver`, then walks the tree to produce structured
|
|
49
|
+
data (TOC, content blocks, numbering, index). The final output is
|
|
50
|
+
rendered through a Liquid template with inline or linked JSON data
|
|
51
|
+
for the Vue SPA frontend.
|
|
52
|
+
|
|
53
|
+
=== Docbook::Output::Data
|
|
54
|
+
|
|
55
|
+
Defines the data model classes used by the HTML generator:
|
|
56
|
+
`ContentBlock`, `SectionData`, `NumberingBuilder`, `DocbookOutput`,
|
|
57
|
+
and related types. These classes hold the intermediate representation
|
|
58
|
+
between the element tree and JSON serialization.
|
|
59
|
+
|
|
60
|
+
=== Docbook::Models
|
|
61
|
+
|
|
62
|
+
A parallel set of pre-computed models (`DocumentRoot`, `TocNode`,
|
|
63
|
+
`SectionRoot`, etc.) that represent the final JSON structure consumed
|
|
64
|
+
by the Vue SPA. These are separate from `Output::Data` because they
|
|
65
|
+
include additional metadata like reading position and generator version.
|
|
66
|
+
|
|
67
|
+
=== Docbook::CLI
|
|
68
|
+
|
|
69
|
+
A Thor-based command-line interface that provides the `format`,
|
|
70
|
+
`validate`, `to-html`, and `roundtrip` commands. It wires together
|
|
71
|
+
the parsing, resolution, and output modules based on user options.
|
|
72
|
+
|
|
73
|
+
== Data flow
|
|
74
|
+
|
|
75
|
+
The following diagram shows the complete data flow from XML input to
|
|
76
|
+
final HTML output:
|
|
77
|
+
|
|
78
|
+
....
|
|
79
|
+
+-----------------+
|
|
80
|
+
| XML Input |
|
|
81
|
+
| (string/file) |
|
|
82
|
+
+--------+--------+
|
|
83
|
+
|
|
|
84
|
+
v
|
|
85
|
+
+--------+--------+
|
|
86
|
+
| XIncludeResolver | (optional)
|
|
87
|
+
| resolve xi: |
|
|
88
|
+
| include refs |
|
|
89
|
+
+--------+--------+
|
|
90
|
+
|
|
|
91
|
+
v
|
|
92
|
+
+--------+--------+
|
|
93
|
+
| Document.from_xml|
|
|
94
|
+
| Nokogiri parse |
|
|
95
|
+
| -> Element model |
|
|
96
|
+
+--------+--------+
|
|
97
|
+
|
|
|
98
|
+
v
|
|
99
|
+
+--------+--------+
|
|
100
|
+
| XrefResolver |
|
|
101
|
+
| Build O(1) ID |
|
|
102
|
+
| lookup hash |
|
|
103
|
+
+--------+--------+
|
|
104
|
+
|
|
|
105
|
+
v
|
|
106
|
+
+--------+--------+
|
|
107
|
+
| Output::Html |
|
|
108
|
+
| Section tree |
|
|
109
|
+
| Numbering |
|
|
110
|
+
| Content blocks |
|
|
111
|
+
| Index collection|
|
|
112
|
+
+--------+--------+
|
|
113
|
+
|
|
|
114
|
+
v
|
|
115
|
+
+--------+--------+
|
|
116
|
+
| Liquid Template |
|
|
117
|
+
| Inline JSON |
|
|
118
|
+
| or linked JSON |
|
|
119
|
+
+--------+--------+
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
+--------+--------+
|
|
123
|
+
| HTML Output |
|
|
124
|
+
| (single file or |
|
|
125
|
+
| directory) |
|
|
126
|
+
+-----------------+
|
|
127
|
+
....
|
|
128
|
+
|
|
129
|
+
In single-file mode, all JSON data and CSS/JS assets are embedded
|
|
130
|
+
directly into the HTML. In directory mode, the JSON is written to
|
|
131
|
+
`docbook.data.json` and assets are copied to an `assets/` subdirectory.
|
|
132
|
+
|
|
133
|
+
== Element model system
|
|
134
|
+
|
|
135
|
+
Every DocBook element is represented by a Ruby class that inherits from
|
|
136
|
+
`Lutaml::Model::Serializable`. This provides:
|
|
137
|
+
|
|
138
|
+
* **Typed attributes** via the `attribute` DSL method
|
|
139
|
+
* **Mixed content** support via the `mixed_content` declaration
|
|
140
|
+
* **XML serialization** via `from_xml` / `to_xml`
|
|
141
|
+
* **JSON serialization** via `to_json`
|
|
142
|
+
|
|
143
|
+
The `each_mixed_content` method yields child nodes (both `String` and
|
|
144
|
+
element objects) in document order, which is essential for correct
|
|
145
|
+
inline content rendering.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Content Pipeline
|
|
4
|
+
parent: Understanding
|
|
5
|
+
nav_order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Content Pipeline
|
|
9
|
+
|
|
10
|
+
The content pipeline transforms a DocBook XML document into a rendered
|
|
11
|
+
HTML page with an embedded Vue SPA. Each step produces a well-defined
|
|
12
|
+
intermediate representation. Understanding this pipeline is essential
|
|
13
|
+
for debugging output or extending the gem.
|
|
14
|
+
|
|
15
|
+
== Step 1: XML input
|
|
16
|
+
|
|
17
|
+
The pipeline begins with raw XML, provided either as a file path or a
|
|
18
|
+
string. The CLI reads the file and passes the string to the parsing
|
|
19
|
+
layer. The XML may reference external files via XInclude or contain
|
|
20
|
+
`xml:id` attributes for cross-referencing.
|
|
21
|
+
|
|
22
|
+
== Step 2: XInclude resolution
|
|
23
|
+
|
|
24
|
+
If XInclude processing is enabled (the default for `to-html`), the
|
|
25
|
+
`XIncludeResolver` preprocesses the Nokogiri document. It iterates
|
|
26
|
+
through all `xi:include` elements, fetches the referenced files, and
|
|
27
|
+
replaces the include directives with the actual content. This step
|
|
28
|
+
handles nested includes by re-scanning after each resolution. Text
|
|
29
|
+
includes with `fragid` attributes support line-range extraction and
|
|
30
|
+
search-based filtering.
|
|
31
|
+
|
|
32
|
+
== Step 3: Parsing into element model
|
|
33
|
+
|
|
34
|
+
`Document.from_xml` parses the (possibly resolved) XML string. It
|
|
35
|
+
detects the root element name and delegates to the corresponding
|
|
36
|
+
element class (for example, `Elements::Book` for `<book>`). The
|
|
37
|
+
lutaml-model library maps each XML element to a typed Ruby object
|
|
38
|
+
with declared attributes, collections, and mixed content.
|
|
39
|
+
|
|
40
|
+
== Step 4: XRef resolution
|
|
41
|
+
|
|
42
|
+
The `XrefResolver` walks the parsed element tree and builds a hash
|
|
43
|
+
map of `xml:id` values to element objects. This provides O(1) lookup
|
|
44
|
+
for resolving `<xref linkend="...">` references. The resolver also
|
|
45
|
+
extracts the best available title text from each target element.
|
|
46
|
+
|
|
47
|
+
== Step 5: Section data collection
|
|
48
|
+
|
|
49
|
+
The HTML generator performs a recursive tree walk over the parsed
|
|
50
|
+
document, collecting `SectionData` objects. Each `SectionData` records
|
|
51
|
+
the section's ID, title, type (part, chapter, section, appendix, etc.),
|
|
52
|
+
and child sections. The resulting tree mirrors the document's logical
|
|
53
|
+
structure and is used for the table of contents.
|
|
54
|
+
|
|
55
|
+
== Step 6: Numbering
|
|
56
|
+
|
|
57
|
+
The `NumberingBuilder` assigns numbers to each section in the tree.
|
|
58
|
+
It uses different numbering schemes depending on the section type:
|
|
59
|
+
|
|
60
|
+
* **Parts** receive uppercase Roman numerals (I, II, III).
|
|
61
|
+
* **Chapters** receive Arabic numerals scoped to their parent part
|
|
62
|
+
(1, 2, 3).
|
|
63
|
+
* **Sections** receive hierarchical dotted numbers (1.1, 1.2.3).
|
|
64
|
+
* **Appendices** receive uppercase letters (A, B, C).
|
|
65
|
+
|
|
66
|
+
The numbering map is a simple hash of ID to formatted number string.
|
|
67
|
+
|
|
68
|
+
== Step 7: Content map building
|
|
69
|
+
|
|
70
|
+
For each section, the HTML generator calls `each_mixed_content` on the
|
|
71
|
+
corresponding element object. This yields `String` nodes and typed
|
|
72
|
+
element objects in document order. A `case` statement dispatches each
|
|
73
|
+
element to a builder method that produces `ContentBlock` instances.
|
|
74
|
+
Paragraphs become `paragraph` blocks with inline children; code
|
|
75
|
+
listings become `code` blocks with a `language` attribute; figures
|
|
76
|
+
become `image` blocks with `src` and `alt` attributes.
|
|
77
|
+
|
|
78
|
+
== Step 8: Index collection and generation
|
|
79
|
+
|
|
80
|
+
The `IndexCollector` traverses the element tree looking for
|
|
81
|
+
`<indexterm>` elements. It collects primary, secondary, and tertiary
|
|
82
|
+
terms along with their containing section IDs. The `IndexGenerator`
|
|
83
|
+
then groups collected terms by first letter, producing an `Index` model
|
|
84
|
+
with `IndexGroup` children, each containing sorted `IndexTerm` entries.
|
|
85
|
+
|
|
86
|
+
== Step 9: Template rendering
|
|
87
|
+
|
|
88
|
+
The assembled `DocbookOutput` model (containing title, TOC, content,
|
|
89
|
+
and index) is serialized to JSON and inserted into a Liquid template.
|
|
90
|
+
In single-file mode, the JSON replaces placeholder tokens
|
|
91
|
+
(`[[DOCBOOK_DATA]]`, `[[DOCBOOK_TOC]]`, `[[DOCBOOK_CONTENT]]`,
|
|
92
|
+
`[[DOCBOOK_INDEX]]`) directly in the HTML. In directory mode, the
|
|
93
|
+
placeholders are set to `null` and the JSON is written to a separate
|
|
94
|
+
file that the Vue SPA fetches at runtime.
|
|
95
|
+
|
|
96
|
+
== Step 10: Output
|
|
97
|
+
|
|
98
|
+
The final step writes the rendered HTML to disk. Single-file mode
|
|
99
|
+
produces one self-contained HTML file with all data inlined. Directory
|
|
100
|
+
mode creates an output directory with `index.html`, `docbook.data.json`,
|
|
101
|
+
and an `assets/` directory containing `app.css` and `app.iife.js` copied
|
|
102
|
+
from the frontend build.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Data Models
|
|
4
|
+
parent: Understanding
|
|
5
|
+
nav_order: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Data Models
|
|
9
|
+
|
|
10
|
+
The gem uses two separate model namespaces, each serving a distinct
|
|
11
|
+
purpose in the pipeline. All model classes inherit from
|
|
12
|
+
`Lutaml::Model::Serializable`, which provides XML and JSON
|
|
13
|
+
serialization through a declarative DSL.
|
|
14
|
+
|
|
15
|
+
== Output::Data models (`Docbook::Output`)
|
|
16
|
+
|
|
17
|
+
These classes are defined in `lib/docbook/output/data.rb` and are used
|
|
18
|
+
by the HTML generator to build the intermediate representation that
|
|
19
|
+
becomes the JSON payload for the Vue SPA.
|
|
20
|
+
|
|
21
|
+
=== DocbookOutput
|
|
22
|
+
|
|
23
|
+
The top-level output model. Contains `title`, `toc` (a `Toc` instance),
|
|
24
|
+
`content` (a `ContentData` instance), and `index` (an `Index`
|
|
25
|
+
instance). This single model encapsulates everything the Vue SPA needs
|
|
26
|
+
to render the document.
|
|
27
|
+
|
|
28
|
+
=== ContentBlock
|
|
29
|
+
|
|
30
|
+
A recursive tree node representing a single block of content. Each
|
|
31
|
+
block has a `type` (such as `:paragraph`, `:code`, `:image`), optional
|
|
32
|
+
text, source URL, alt text, title, language, and CSS class name. Block
|
|
33
|
+
types that can contain nested content use the `children` attribute,
|
|
34
|
+
which holds an array of child `ContentBlock` instances.
|
|
35
|
+
|
|
36
|
+
=== SectionData
|
|
37
|
+
|
|
38
|
+
Represents a section in the table of contents tree. Each entry has an
|
|
39
|
+
`id`, `title`, `type` (part, chapter, section, appendix, etc.), an
|
|
40
|
+
optional `number`, and `children` (an array of nested `SectionData`
|
|
41
|
+
instances).
|
|
42
|
+
|
|
43
|
+
=== NumberingEntry
|
|
44
|
+
|
|
45
|
+
A simple key-value pair mapping a section ID to its formatted number
|
|
46
|
+
string. These are collected into an array within the `Toc` model.
|
|
47
|
+
|
|
48
|
+
=== ContentData and ContentEntry
|
|
49
|
+
|
|
50
|
+
`ContentData` wraps a collection of `ContentEntry` objects. Each
|
|
51
|
+
`ContentEntry` maps a section ID (the `key`) to a `SectionContent`
|
|
52
|
+
instance containing the section's rendered content blocks. This
|
|
53
|
+
key-value structure serializes cleanly to JSON and provides O(1)
|
|
54
|
+
section lookup in the Vue SPA.
|
|
55
|
+
|
|
56
|
+
=== SectionContent
|
|
57
|
+
|
|
58
|
+
Holds the content for a single section: a `section_id` and an array
|
|
59
|
+
of `ContentBlock` instances representing the section's body.
|
|
60
|
+
|
|
61
|
+
=== Toc
|
|
62
|
+
|
|
63
|
+
Combines the `SectionData` tree (the recursive TOC structure) with the
|
|
64
|
+
`NumberingEntry` collection (the computed numbering map).
|
|
65
|
+
|
|
66
|
+
=== IndexTerm
|
|
67
|
+
|
|
68
|
+
Represents a single index entry with `primary`, `secondary`, and
|
|
69
|
+
`tertiary` term text, plus the `section_id` and `section_title` where
|
|
70
|
+
the term appears. Also holds `sees` and `see_alsos` arrays for
|
|
71
|
+
cross-references.
|
|
72
|
+
|
|
73
|
+
=== IndexGroup
|
|
74
|
+
|
|
75
|
+
Groups index terms under a `letter` heading. Contains an array of
|
|
76
|
+
`IndexTerm` entries.
|
|
77
|
+
|
|
78
|
+
=== Index
|
|
79
|
+
|
|
80
|
+
The top-level index model with a `title` and an array of `IndexGroup`
|
|
81
|
+
instances, one per letter of the alphabet.
|
|
82
|
+
|
|
83
|
+
=== DocumentData
|
|
84
|
+
|
|
85
|
+
A minimal model holding only the document `title`. Used in single-file
|
|
86
|
+
mode to initialize the `DOCBOOK_DATA` JavaScript object before merging
|
|
87
|
+
in the TOC, content, and index.
|
|
88
|
+
|
|
89
|
+
=== NumberingBuilder
|
|
90
|
+
|
|
91
|
+
A stateful builder that tracks counters for parts (Roman numerals),
|
|
92
|
+
chapters (Arabic, scoped per part), sections (hierarchical, scoped per
|
|
93
|
+
parent), and appendices (uppercase letters). It exposes methods like
|
|
94
|
+
`next_part`, `next_chapter`, `next_section`, and `next_appendix` that
|
|
95
|
+
increment the appropriate counter and return the formatted number. The
|
|
96
|
+
final `numbering` hash maps section IDs to their computed numbers.
|
|
97
|
+
|
|
98
|
+
== Models namespace (`Docbook::Models`)
|
|
99
|
+
|
|
100
|
+
Defined in `lib/docbook/models/`, these classes represent the full
|
|
101
|
+
document structure as consumed by the Vue SPA. They include additional
|
|
102
|
+
metadata beyond what the output data models carry.
|
|
103
|
+
|
|
104
|
+
=== DocumentRoot
|
|
105
|
+
|
|
106
|
+
The top-level model for the complete document. Contains `title`,
|
|
107
|
+
`metadata` (a `DocumentMetadata` instance), `toc` (an array of
|
|
108
|
+
`TocNode`), `sections` (an array of `SectionRoot`), `index` (an array
|
|
109
|
+
of `IndexEntry`), `numbering` (an array of `SectionNumber`), and a
|
|
110
|
+
`reading_position`. Also includes `generator` and `generated_at`
|
|
111
|
+
strings for debugging.
|
|
112
|
+
|
|
113
|
+
=== DocumentMetadata
|
|
114
|
+
|
|
115
|
+
Stores document-level information: `author`, `author_name`, `title`,
|
|
116
|
+
`subtitle`, `productname`, `version`, `pubdate`, `release_info`, and
|
|
117
|
+
`abstract`. All fields are strings.
|
|
118
|
+
|
|
119
|
+
=== TocNode
|
|
120
|
+
|
|
121
|
+
A recursive tree node for the table of contents. Each node has an `id`,
|
|
122
|
+
`title`, `type`, `number`, optional `page` (for PDF navigation), and
|
|
123
|
+
`children` (an array of nested `TocNode` instances).
|
|
124
|
+
|
|
125
|
+
=== SectionRoot
|
|
126
|
+
|
|
127
|
+
Represents a section in the pre-computed document structure. Contains
|
|
128
|
+
`id`, `type`, `title`, `number`, and `children` (nested `SectionRoot`
|
|
129
|
+
instances). For reference entries, it also holds a `refname` string.
|
|
130
|
+
|
|
131
|
+
=== SectionNumber
|
|
132
|
+
|
|
133
|
+
A simple mapping of section `id` to its computed `number` string and
|
|
134
|
+
`type`. Used by the Vue SPA to display section numbers in the UI.
|
|
135
|
+
|
|
136
|
+
=== IndexEntry
|
|
137
|
+
|
|
138
|
+
Represents an index term with `primary`, `secondary`, `tertiary`,
|
|
139
|
+
`see_also`, `section_id`, `section_title`, and a `sort_key` for
|
|
140
|
+
alphabetical ordering.
|
|
141
|
+
|
|
142
|
+
=== ReadingPosition
|
|
143
|
+
|
|
144
|
+
Tracks the user's reading progress. Contains `section_id`,
|
|
145
|
+
`percentage`, `last_read_at`, `reading_mode`, and `scroll_position`.
|
|
146
|
+
This data is persisted in the Vue SPA via localStorage.
|
|
147
|
+
|
|
148
|
+
== Serialization
|
|
149
|
+
|
|
150
|
+
Both model namespaces serialize to JSON for the Vue SPA. The output
|
|
151
|
+
data models (`Output::Data`) are what the Ruby HTML generator
|
|
152
|
+
constructs and serializes. The `Models` namespace provides the
|
|
153
|
+
TypeScript-compatible schema that the Vue SPA expects. In practice,
|
|
154
|
+
the HTML generator uses the `Output::Data` classes directly, and the
|
|
155
|
+
JSON they produce is consumed by the Pinia `documentStore` in the
|
|
156
|
+
frontend.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Understanding
|
|
4
|
+
nav_order: 5
|
|
5
|
+
has_children: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Understanding
|
|
9
|
+
|
|
10
|
+
This section explains how the Metanorma DocBook gem works internally.
|
|
11
|
+
It covers the architecture, data flow, and design decisions that shape
|
|
12
|
+
the library.
|
|
13
|
+
|
|
14
|
+
== When to read this section
|
|
15
|
+
|
|
16
|
+
Read these pages when you want to do any of the following:
|
|
17
|
+
|
|
18
|
+
* **Extend the gem** -- Adding new DocBook element types, custom output
|
|
19
|
+
formats, or additional template variables requires understanding the
|
|
20
|
+
module structure and element class design.
|
|
21
|
+
* **Debug output** -- If the HTML output does not match your expectations,
|
|
22
|
+
the content pipeline and data model pages will help you trace where a
|
|
23
|
+
particular element is transformed or lost.
|
|
24
|
+
* **Contribute** -- Contributors should understand the full pipeline from
|
|
25
|
+
XML input to rendered HTML before submitting patches.
|
|
26
|
+
|
|
27
|
+
== What is covered
|
|
28
|
+
|
|
29
|
+
* <<architecture, Architecture>> -- The major modules and how they fit
|
|
30
|
+
together.
|
|
31
|
+
* <<content-pipeline, Content Pipeline>> -- The step-by-step data flow
|
|
32
|
+
from XML input to final output.
|
|
33
|
+
* <<data-models, Data Models>> -- The Ruby classes that represent
|
|
34
|
+
parsed and computed document data.
|
data/exe/docbook
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.modal-enter-active[data-v-bc71f312],.modal-leave-active[data-v-bc71f312]{transition:opacity .2s ease}.modal-enter-from[data-v-bc71f312],.modal-leave-to[data-v-bc71f312]{opacity:0}.modal-enter-active>div[data-v-bc71f312]:last-child,.modal-leave-active>div[data-v-bc71f312]:last-child{transition:transform .2s ease,opacity .2s ease}.modal-enter-from>div[data-v-bc71f312]:last-child,.modal-leave-to>div[data-v-bc71f312]:last-child{transform:scale(.95);opacity:0}.reference-entry[data-v-3a1b793d]{font-family:Inter,system-ui,-apple-system,sans-serif}.reference-name[data-v-3a1b793d]{font-family:JetBrains Mono,Fira Code,ui-monospace,monospace}.reference-badge[data-v-3a1b793d]{letter-spacing:.05em}.ebook-topbar[data-v-b409adda]{position:fixed;top:0;left:0;right:0;z-index:30;background:#ffffffe6;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-bottom:1px solid rgba(0,0,0,.05);transition:left .2s ease}.dark{background:#111827e6;border-bottom-color:#ffffff1a}.ebook-topbar--with-sidebar[data-v-b409adda]{left:280px}@media (max-width: 1023px){.ebook-topbar--with-sidebar[data-v-b409adda]{left:0}}.ebook-topbar button[data-v-b409adda]{min-width:44px;min-height:44px}.theme-day{--ebook-bg: #ffffff;--ebook-bg-secondary: #f9fafb;--ebook-text: #1f2937;--ebook-text-muted: #6b7280;--ebook-accent: #0891b2;--ebook-accent-light: #ecfeff;--ebook-border: #e5e7eb;--ebook-code-bg: #1f2937;--ebook-code-text: #e5e7eb;--ebook-example-bg: #f0fdf4;--ebook-example-border: #22c55e;--ebook-reference-accent: #0891b2;--ebook-reference-bg: rgba(8, 145, 178, .03);--ebook-reference-name: #0e7490;--ebook-reference-badge-bg: #f3f4f6;--ebook-reference-badge-text: #6b7280}.theme-sepia{--ebook-bg: #fdf6e3;--ebook-bg-secondary: #f5efe0;--ebook-text: #5c5347;--ebook-text-muted: #8b7355;--ebook-accent: #b8860b;--ebook-accent-light: #fef9e7;--ebook-border: #e0d5c0;--ebook-code-bg: #3d3d3d;--ebook-code-text: #f5f5dc;--ebook-example-bg: #f0ead6;--ebook-example-border: #8b7355;--ebook-reference-accent: #b8860b;--ebook-reference-bg: rgba(184, 134, 11, .05);--ebook-reference-name: #8b6914;--ebook-reference-badge-bg: #e8dcc8;--ebook-reference-badge-text: #6b5d4d}.theme-sepia body,.theme-sepia{background-color:var(--ebook-bg);color:var(--ebook-text)}.theme-night{--ebook-bg: #111827;--ebook-bg-secondary: #1f2937;--ebook-text: #e5e7eb;--ebook-text-muted: #9ca3af;--ebook-accent: #22d3ee;--ebook-accent-light: rgba(34, 211, 238, .1);--ebook-border: #374151;--ebook-code-bg: #030712;--ebook-code-text: #e5e7eb;--ebook-example-bg: rgba(34, 197, 94, .1);--ebook-example-border: #22c55e;--ebook-reference-accent: #22d3ee;--ebook-reference-bg: rgba(34, 211, 238, .05);--ebook-reference-name: #22d3ee;--ebook-reference-badge-bg: #374151;--ebook-reference-badge-text: #9ca3af}.theme-night body,.theme-night{background-color:var(--ebook-bg);color:var(--ebook-text)}.theme-oled{--ebook-bg: #000000;--ebook-bg-secondary: #0a0a0a;--ebook-text: #ffffff;--ebook-text-muted: #a1a1aa;--ebook-accent: #60a5fa;--ebook-accent-light: rgba(96, 165, 250, .1);--ebook-border: #27272a;--ebook-code-bg: #18181b;--ebook-code-text: #fafafa;--ebook-example-bg: rgba(34, 197, 94, .05);--ebook-example-border: #22c55e;--ebook-reference-accent: #60a5fa;--ebook-reference-bg: rgba(96, 165, 250, .03);--ebook-reference-name: #60a5fa;--ebook-reference-badge-bg: #27272a;--ebook-reference-badge-text: #a1a1aa}.theme-oled body,.theme-oled{background-color:var(--ebook-bg);color:var(--ebook-text)}body{background-color:var(--ebook-bg, #ffffff);color:var(--ebook-text, #1f2937);transition:background-color .2s ease,color .2s ease}.ebook-container{font-size:var(--ebook-font-size, 18px);font-weight:var(--ebook-font-weight, 400);line-height:var(--ebook-line-height, 1.6)}.ebook-container.theme-day,.ebook-container.theme-sepia,.ebook-container.theme-night,.ebook-container.theme-oled{--bg: var(--ebook-bg);--text: var(--ebook-text)}*{box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:Inter,system-ui,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body.font-serif{font-family:Merriweather,Georgia,serif}body.font-sans{font-family:Inter,system-ui,sans-serif}.font-mono{font-family:JetBrains Mono,ui-monospace,monospace}.dark{color-scheme:dark}@media print{.lg\:translate-x-0,#sidebar,#sidebar-toggle,.sticky{display:none!important}}.toc-active{color:#2563eb;background:#dbeafe}.dark .toc-active{color:#60a5fa;background:#1e3a5f}.db-bibentry{padding-left:2em;text-indent:-2em;margin-bottom:.5em}.db-changelog-release{border-left:3px solid #60a5fa;padding-left:1rem;margin:1.5rem 0}.dark .db-changelog-release{border-left-color:#3b82f6}.db-appendix-wrap{border-left:4px solid #8b5cf6;padding-left:1rem;margin-left:-1rem}.dark .db-appendix-wrap{border-left-color:#a78bfa}.db-preface-wrap{border-left:4px solid #22c55e;padding-left:1rem;margin-left:-1rem}.dark .db-preface-wrap{border-left-color:#16a34a}mark.bg-yellow-200,mark.bg-yellow-600{padding:0 2px;border-radius:2px}[x-cloak]{display:none!important}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.dark ::-webkit-scrollbar-thumb{background:#4b5563}::-webkit-scrollbar-thumb:hover{background:#94a3b8}.dark ::-webkit-scrollbar-thumb:hover{background:#6b7280}.db-content h1{font-size:1.875rem;font-weight:700;margin-bottom:1rem}.db-content h2{font-size:1.25rem;font-weight:600;margin-top:2rem;margin-bottom:.75rem}.db-content h3{font-size:1.125rem;font-weight:600;margin-top:1.5rem;margin-bottom:.5rem}.db-content p{margin-bottom:1rem;line-height:1.75}.db-content ul,.db-content ol{margin-bottom:1rem;padding-left:1.5rem}.db-content li{margin-bottom:.25rem;line-height:1.6}.db-content code{font-family:JetBrains Mono,ui-monospace,monospace;font-size:.875em;background:#f1f5f9;padding:.125rem .375rem;border-radius:.25rem}.dark .db-content code{background:#374151}.db-content pre{font-family:JetBrains Mono,ui-monospace,monospace;font-size:.875rem;background:#f1f5f9;padding:1rem;border-radius:.5rem;overflow-x:auto;margin-bottom:1rem}.dark .db-content pre{background:#1f2937}.db-content a{color:#2563eb;text-decoration:underline}.dark .db-content a{color:#60a5fa}.db-content img{max-width:100%;height:auto;border-radius:.5rem;box-shadow:0 4px 6px -1px #0000001a}.dark .db-content img{box-shadow:0 4px 6px -1px #0000004d}.db-content blockquote{border-left:4px solid #d1d5db;padding-left:1rem;margin:1rem 0;font-style:italic;color:#6b7280}.dark .db-content blockquote{border-left-color:#4b5563;color:#9ca3af}.db-content table{width:100%;border-collapse:collapse;margin-bottom:1rem}.db-content th,.db-content td{border:1px solid #e5e7eb;padding:.5rem;text-align:left}.dark .db-content th,.dark .db-content td{border-color:#374151}.db-content th{background:#f9fafb;font-weight:600}.dark .db-content th{background:#1f2937}
|