metanorma-plugin-plantuml 1.0.1 → 1.0.2
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 +4 -4
- data/Gemfile +1 -1
- data/README.adoc +151 -1
- data/lib/metanorma/plugin/plantuml/backend.rb +24 -11
- data/lib/metanorma/plugin/plantuml/block_processor.rb +11 -44
- data/lib/metanorma/plugin/plantuml/block_processor_base.rb +99 -0
- data/lib/metanorma/plugin/plantuml/image_block_macro_processor.rb +57 -0
- data/lib/metanorma/plugin/plantuml/version.rb +1 -1
- data/lib/metanorma/plugin/plantuml/wrapper.rb +95 -36
- data/lib/metanorma-plugin-plantuml.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b3797ddb4db236753261836d74b360e635c58ea3b0eeaa09dfc1c7192dacf78
|
4
|
+
data.tar.gz: 0aba3ecbbc90f15570887ea206fa0a0c19b6d3179a7d9481438fb480df305873
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed86eced1c2c434f17ce4f3f5307c88f349c30275ad2051ea7c6063feb1d13ca6c64480ea406951221a75ceececd035ffed82519d9dfe169205f55a160996c9b
|
7
|
+
data.tar.gz: 2e1488a69e6c3ff71a88c17778fead0b264ed2b6c8d2a7d8803e04441d0d7f1b4b2cebdd102050e6428500a477a876d6f3cc8cdf9882da007a7bc6900a19cffa
|
data/Gemfile
CHANGED
data/README.adoc
CHANGED
@@ -65,6 +65,8 @@ the `plantuml` block directive.
|
|
65
65
|
|
66
66
|
=== Basic syntax
|
67
67
|
|
68
|
+
==== Block style
|
69
|
+
|
68
70
|
[source,asciidoc]
|
69
71
|
----
|
70
72
|
[plantuml]
|
@@ -76,6 +78,25 @@ Bob -> Alice: Hi there
|
|
76
78
|
....
|
77
79
|
----
|
78
80
|
|
81
|
+
==== Block macro style
|
82
|
+
|
83
|
+
[source,asciidoc]
|
84
|
+
----
|
85
|
+
plantuml_image::path/to/my-plantuml.puml[]
|
86
|
+
----
|
87
|
+
|
88
|
+
The file `path/to/my-plantuml.puml` looks like:
|
89
|
+
[source,plantuml]
|
90
|
+
----
|
91
|
+
@startuml
|
92
|
+
Alice -> Bob: Hello
|
93
|
+
Bob -> Alice: Hi there
|
94
|
+
@enduml
|
95
|
+
----
|
96
|
+
|
97
|
+
`plantuml_image` block macro performs the same function as the `plantuml` block,
|
98
|
+
but the PlantUML diagram is defined in a separate file instead of a block.
|
99
|
+
|
79
100
|
=== Supported diagram types
|
80
101
|
|
81
102
|
PlantUML supports various diagram types, each with its own `@start` and `@end` directives:
|
@@ -183,6 +204,132 @@ Alice -> Bob: Hello
|
|
183
204
|
....
|
184
205
|
----
|
185
206
|
|
207
|
+
==== Document-level includedirs configuration
|
208
|
+
|
209
|
+
When using `!include` or `!includesub` in your PlantUML diagrams, you can set
|
210
|
+
the default include directories (separated by semicolons) by
|
211
|
+
`plantuml-includedirs` to search for files for all PlantUML diagrams in your
|
212
|
+
document:
|
213
|
+
|
214
|
+
[source,asciidoc]
|
215
|
+
----
|
216
|
+
:plantuml-includedirs: path/to/plantuml/include-1;path/to/plantuml/include-2
|
217
|
+
|
218
|
+
[plantuml]
|
219
|
+
....
|
220
|
+
@startuml
|
221
|
+
!include sequences.puml!1
|
222
|
+
@enduml
|
223
|
+
....
|
224
|
+
|
225
|
+
[plantuml]
|
226
|
+
....
|
227
|
+
@startuml
|
228
|
+
!include components.puml!FRONTEND
|
229
|
+
!include components.puml!BACKEND
|
230
|
+
|
231
|
+
WebApp --> APIGateway
|
232
|
+
MobileApp --> APIGateway
|
233
|
+
APIGateway --> DB
|
234
|
+
@enduml
|
235
|
+
....
|
236
|
+
|
237
|
+
[plantuml]
|
238
|
+
....
|
239
|
+
@startuml
|
240
|
+
title this contains only B and D
|
241
|
+
!includesub subpart.puml!BASIC
|
242
|
+
@enduml
|
243
|
+
....
|
244
|
+
----
|
245
|
+
|
246
|
+
This plugin will search the include directories specified by
|
247
|
+
`includedirs` options (i.e. `path/to/plantuml/include-1` and
|
248
|
+
`path/to/plantuml/include-2`) for the files referenced in `!include` or
|
249
|
+
`!includesub` directives (i.e. `sequences.puml`, `components.puml` and
|
250
|
+
`subpart.puml`).
|
251
|
+
|
252
|
+
You can also use `plantuml_image` to include external PlantUML files as images:
|
253
|
+
|
254
|
+
[source,asciidoc]
|
255
|
+
----
|
256
|
+
:plantuml-includedirs: path/to/plantuml/include-1;path/to/plantuml/include-2
|
257
|
+
|
258
|
+
plantuml_image::path/to/my-plantuml-1.puml[]
|
259
|
+
|
260
|
+
plantuml_image::path/to/my-plantuml-2.puml[]
|
261
|
+
----
|
262
|
+
|
263
|
+
The file `path/to/my-plantuml-1.puml` looks like:
|
264
|
+
[source,plantuml]
|
265
|
+
----
|
266
|
+
@startuml
|
267
|
+
!include sequences.puml!1
|
268
|
+
@enduml
|
269
|
+
----
|
270
|
+
|
271
|
+
The file `path/to/my-plantuml-2.puml` looks like:
|
272
|
+
[source,plantuml]
|
273
|
+
----
|
274
|
+
@startuml
|
275
|
+
!include components.puml!FRONTEND
|
276
|
+
!include components.puml!BACKEND
|
277
|
+
|
278
|
+
WebApp --> APIGateway
|
279
|
+
MobileApp --> APIGateway
|
280
|
+
APIGateway --> DB
|
281
|
+
@enduml
|
282
|
+
----
|
283
|
+
|
284
|
+
When using `plantuml_image`, the path of the directory of the PlantUML file will
|
285
|
+
also be added into the `includedirs`. (i.e. `path/to` will be added to
|
286
|
+
`includedirs`)
|
287
|
+
|
288
|
+
==== Block-level includedirs configuration
|
289
|
+
|
290
|
+
When using `!include` or `!includesub` in your PlantUML diagrams, you can set
|
291
|
+
the default include directories (separated by semicolons) by `includedirs` in
|
292
|
+
block-level to search for files for the PlantUML diagram defined in your block:
|
293
|
+
|
294
|
+
[source,asciidoc]
|
295
|
+
----
|
296
|
+
[plantuml,includedirs="path/to/plantuml/include-1"]
|
297
|
+
....
|
298
|
+
@startuml
|
299
|
+
!include sequences.puml!1
|
300
|
+
@enduml
|
301
|
+
....
|
302
|
+
|
303
|
+
[plantuml,includedirs="path/to/plantuml/include-2"]
|
304
|
+
....
|
305
|
+
@startuml
|
306
|
+
!include components.puml!FRONTEND
|
307
|
+
!include components.puml!BACKEND
|
308
|
+
|
309
|
+
WebApp --> APIGateway
|
310
|
+
MobileApp --> APIGateway
|
311
|
+
APIGateway --> DB
|
312
|
+
@enduml
|
313
|
+
....
|
314
|
+
----
|
315
|
+
|
316
|
+
This plugin will search `sequences.puml` in `path/to/plantuml/include-1` and
|
317
|
+
`components.puml` in `path/to/plantuml/include-2`.
|
318
|
+
|
319
|
+
The block-level `includedirs` configuration can be used together with the
|
320
|
+
document-level configuration to provide more granular control over include
|
321
|
+
paths. You can set multiple paths by separating them with semicolons.
|
322
|
+
|
323
|
+
You can also use `plantuml_image` to set `includedirs` option to include
|
324
|
+
external PlantUML files:
|
325
|
+
|
326
|
+
[source,asciidoc]
|
327
|
+
----
|
328
|
+
plantuml_image::path/to/my-plantuml-1.puml[includedirs=path/to/plantuml/include-1]
|
329
|
+
|
330
|
+
plantuml_image::path/to/my-plantuml-2.puml[includedirs=path/to/plantuml/include-2]
|
331
|
+
----
|
332
|
+
|
186
333
|
=== Image attributes
|
187
334
|
|
188
335
|
Standard AsciiDoc image attributes are supported:
|
@@ -272,7 +419,7 @@ Metanorma integration and PlantUML execution:
|
|
272
419
|
----
|
273
420
|
Metanorma Document
|
274
421
|
↓
|
275
|
-
BlockProcessor ← (processes [plantuml] blocks)
|
422
|
+
BlockProcessor ← (processes [plantuml] blocks) or ImageBlockMacroProcessor ← (processes plantuml_image::{path}[{options}] macros)
|
276
423
|
↓
|
277
424
|
Backend ← (Metanorma integration, paths, validation)
|
278
425
|
↓
|
@@ -284,6 +431,9 @@ PlantUML JAR ← (diagram generation)
|
|
284
431
|
`BlockProcessor`:: Processes `[plantuml]` blocks in Metanorma documents and
|
285
432
|
integrates with the Metanorma rendering pipeline.
|
286
433
|
|
434
|
+
`ImageBlockMacroProcessor`:: Processes `plantuml_image::{path}[{options}]` macros
|
435
|
+
in Metanorma documents and integrates with the Metanorma rendering pipeline.
|
436
|
+
|
287
437
|
`Backend`:: Handles Metanorma-specific logic including document paths, PlantUML
|
288
438
|
source validation, filename extraction, and attribute mapping.
|
289
439
|
|
@@ -14,19 +14,21 @@ module Metanorma
|
|
14
14
|
class Backend
|
15
15
|
class << self
|
16
16
|
def plantuml_installed?
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
return true if plantuml_available?
|
18
|
+
|
19
|
+
raise "PlantUML not installed"
|
20
20
|
end
|
21
21
|
|
22
22
|
def plantuml_available?
|
23
23
|
Wrapper.available?
|
24
24
|
end
|
25
25
|
|
26
|
-
def generate_file(
|
26
|
+
def generate_file( # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
27
|
+
parent, reader, format_override: nil, options: {}
|
28
|
+
)
|
27
29
|
ldir, imagesdir, fmt = generate_file_prep(parent)
|
28
30
|
fmt = format_override if format_override
|
29
|
-
plantuml_content = prep_source(reader)
|
31
|
+
plantuml_content = prep_source(parent, reader)
|
30
32
|
|
31
33
|
# Extract filename from PlantUML source if specified
|
32
34
|
filename = generate_unique_filename(fmt)
|
@@ -43,6 +45,7 @@ module Metanorma
|
|
43
45
|
plantuml_content,
|
44
46
|
format: fmt,
|
45
47
|
output_file: output_file,
|
48
|
+
includedirs: options[:includedirs],
|
46
49
|
)
|
47
50
|
|
48
51
|
unless result[:success]
|
@@ -52,10 +55,12 @@ module Metanorma
|
|
52
55
|
File.join(relative_path, filename)
|
53
56
|
end
|
54
57
|
|
55
|
-
def generate_multiple_files(
|
58
|
+
def generate_multiple_files(
|
59
|
+
parent, reader, formats, attrs, options: {}
|
60
|
+
)
|
56
61
|
# Generate files for each format
|
57
62
|
filenames = formats.map do |format|
|
58
|
-
generate_file(parent, reader, format)
|
63
|
+
generate_file(parent, reader, format, options: options)
|
59
64
|
end
|
60
65
|
|
61
66
|
# Return data for BlockProcessor to create image block
|
@@ -72,8 +77,9 @@ module Metanorma
|
|
72
77
|
def generate_file_prep(parent)
|
73
78
|
ldir = localdir(parent)
|
74
79
|
imagesdir = parent.document.attr("imagesdir")
|
75
|
-
fmt = parent
|
76
|
-
.
|
80
|
+
fmt = parent.document
|
81
|
+
.attr("plantuml-image-format")&.strip&.downcase ||
|
82
|
+
Wrapper::DEFAULT_FORMAT
|
77
83
|
[ldir, imagesdir, fmt]
|
78
84
|
end
|
79
85
|
|
@@ -100,8 +106,15 @@ module Metanorma
|
|
100
106
|
]
|
101
107
|
end
|
102
108
|
|
103
|
-
def prep_source(reader)
|
104
|
-
src = reader.source
|
109
|
+
def prep_source(parent, reader) # rubocop:disable Metrics/MethodLength
|
110
|
+
src = if reader.respond_to?(:source)
|
111
|
+
# get content from BlockProcessor
|
112
|
+
reader.source
|
113
|
+
else
|
114
|
+
# get content from ImageBlockMacroProcessor
|
115
|
+
docdir = parent.document.attributes["docdir"]
|
116
|
+
File.read(File.join(docdir, reader))
|
117
|
+
end
|
105
118
|
|
106
119
|
# Validate that we have matching start/end pairs
|
107
120
|
validate_plantuml_delimiters(src)
|
@@ -1,29 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "asciidoctor"
|
4
|
-
require "asciidoctor/extensions"
|
5
|
-
require_relative "backend"
|
6
|
-
|
7
3
|
module Metanorma
|
8
4
|
module Plugin
|
9
5
|
module Plantuml
|
10
6
|
# PlantUML block processor for Asciidoctor
|
11
7
|
class BlockProcessor < ::Asciidoctor::Extensions::BlockProcessor
|
8
|
+
include ::Metanorma::Plugin::Plantuml::BlockProcessorBase
|
12
9
|
use_dsl
|
13
10
|
named :plantuml
|
14
11
|
on_context :literal
|
15
12
|
parse_content_as :raw
|
16
13
|
|
17
|
-
def abort(parent, reader, attrs, msg)
|
18
|
-
warn msg
|
19
|
-
attrs["language"] = "plantuml"
|
20
|
-
create_listing_block(
|
21
|
-
parent,
|
22
|
-
reader.source,
|
23
|
-
attrs.reject { |k, _v| k.to_s.match?(/^\d+$/) },
|
24
|
-
)
|
25
|
-
end
|
26
|
-
|
27
14
|
def process(parent, reader, attrs) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
28
15
|
# Check for document-level disable flag
|
29
16
|
if parent.document.attr("plantuml-disabled")
|
@@ -37,45 +24,25 @@ module Metanorma
|
|
37
24
|
|
38
25
|
# Parse format specifications
|
39
26
|
formats = parse_formats(attrs, parent.document)
|
27
|
+
options = parse_options(parent, reader, attrs)
|
40
28
|
|
41
|
-
|
42
|
-
# Single format - original behavior
|
43
|
-
filename = Backend.generate_file(parent, reader, formats.first)
|
44
|
-
through_attrs = Backend.generate_attrs(attrs)
|
45
|
-
through_attrs["target"] = filename
|
46
|
-
else
|
47
|
-
# Multiple formats - generate multiple files
|
48
|
-
through_attrs = Backend
|
49
|
-
.generate_multiple_files(parent, reader, formats, attrs)
|
50
|
-
end
|
51
|
-
create_image_block parent, through_attrs
|
29
|
+
process_image_block(parent, reader, attrs, formats, options)
|
52
30
|
rescue StandardError => e
|
53
31
|
abort(parent, reader, attrs, e.message)
|
54
32
|
end
|
55
33
|
|
56
34
|
private
|
57
35
|
|
58
|
-
def
|
59
|
-
|
60
|
-
if attrs["formats"]
|
61
|
-
formats = attrs["formats"].split(",").map(&:strip).map(&:downcase)
|
62
|
-
return formats.select { |f| valid_format?(f) }
|
63
|
-
end
|
64
|
-
|
65
|
-
# Check for format attribute (single format override)
|
66
|
-
if attrs["format"]
|
67
|
-
format = attrs["format"].strip.downcase
|
68
|
-
return [format] if valid_format?(format)
|
69
|
-
end
|
36
|
+
def parse_options(parent, _reader, attrs)
|
37
|
+
options = {}
|
70
38
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
39
|
+
# Parse include directory
|
40
|
+
options[:includedirs] = parse_doc_includedirs(parent.document)
|
41
|
+
options[:includedirs] = add_attrs_to_includedirs(
|
42
|
+
parent.document, attrs, options[:includedirs]
|
43
|
+
)
|
76
44
|
|
77
|
-
|
78
|
-
%w[png svg pdf txt eps].include?(format)
|
45
|
+
options
|
79
46
|
end
|
80
47
|
end
|
81
48
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "asciidoctor"
|
4
|
+
require "asciidoctor/extensions"
|
5
|
+
require_relative "backend"
|
6
|
+
|
7
|
+
module Metanorma
|
8
|
+
module Plugin
|
9
|
+
module Plantuml
|
10
|
+
module BlockProcessorBase
|
11
|
+
def abort(parent, reader, attrs, msg)
|
12
|
+
warn msg
|
13
|
+
attrs["language"] = "plantuml"
|
14
|
+
create_listing_block(
|
15
|
+
parent,
|
16
|
+
reader.respond_to?(:source) ? reader.source : reader,
|
17
|
+
attrs.reject { |k, _v| k.to_s.match?(/^\d+$/) },
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def parse_doc_includedirs(document)
|
24
|
+
docdir = document.attributes["docdir"]
|
25
|
+
includedirs = document.attr("plantuml-includedirs")&.split(";") || []
|
26
|
+
|
27
|
+
includedirs.map! do |includedir|
|
28
|
+
if Pathname.new(includedir).relative?
|
29
|
+
Pathname.new(docdir).join(includedir).to_s
|
30
|
+
else
|
31
|
+
includedir
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
includedirs.compact.uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_formats(attrs, document) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/MethodLength
|
39
|
+
# Check for formats attribute (multiple formats)
|
40
|
+
if attrs["formats"]
|
41
|
+
formats = attrs["formats"].split(",").map(&:strip).map(&:downcase)
|
42
|
+
return formats.select { |f| valid_format?(f) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Check for format attribute (single format override)
|
46
|
+
if attrs["format"]
|
47
|
+
format = attrs["format"].strip.downcase
|
48
|
+
return [format] if valid_format?(format)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Fall back to document attribute or default
|
52
|
+
default_format = document
|
53
|
+
.attr("plantuml-image-format")&.strip&.downcase ||
|
54
|
+
Wrapper::DEFAULT_FORMAT
|
55
|
+
|
56
|
+
[default_format]
|
57
|
+
end
|
58
|
+
|
59
|
+
def valid_format?(format)
|
60
|
+
Wrapper::SUPPORTED_FORMATS.include?(format)
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_attrs_to_includedirs(document, attrs, includedirs)
|
64
|
+
docdir = document.attributes["docdir"]
|
65
|
+
attrs_includedirs = attrs["includedirs"]&.split(";") || []
|
66
|
+
|
67
|
+
attrs_includedirs.each do |attrs_includedir|
|
68
|
+
includedirs << if Pathname.new(attrs_includedir).relative?
|
69
|
+
Pathname.new(docdir).join(attrs_includedir).to_s
|
70
|
+
else
|
71
|
+
attrs_includedir
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
includedirs.compact.uniq
|
76
|
+
end
|
77
|
+
|
78
|
+
def process_image_block(parent, reader, attrs, formats, options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
79
|
+
if formats.length == 1
|
80
|
+
# Single format - original behavior
|
81
|
+
filename = Backend.generate_file(
|
82
|
+
parent, reader, format_override: formats.first, options: options
|
83
|
+
)
|
84
|
+
through_attrs = Backend.generate_attrs(attrs)
|
85
|
+
through_attrs["target"] = filename
|
86
|
+
else
|
87
|
+
# Multiple formats - generate multiple files
|
88
|
+
through_attrs = Backend
|
89
|
+
.generate_multiple_files(
|
90
|
+
parent, reader, formats, attrs, options: options
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
create_image_block parent, through_attrs
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Metanorma
|
4
|
+
module Plugin
|
5
|
+
module Plantuml
|
6
|
+
# PlantUML block processor for Asciidoctor
|
7
|
+
class ImageBlockMacroProcessor < ::Asciidoctor::Extensions::BlockMacroProcessor
|
8
|
+
include ::Metanorma::Plugin::Plantuml::BlockProcessorBase
|
9
|
+
use_dsl
|
10
|
+
named :plantuml_image
|
11
|
+
|
12
|
+
def process(parent, reader, attrs) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
13
|
+
# Check for document-level disable flag
|
14
|
+
if parent.document.attr("plantuml-disabled")
|
15
|
+
return abort(parent, reader, attrs, "PlantUML processing disabled")
|
16
|
+
end
|
17
|
+
|
18
|
+
# Check PlantUML availability explicitly
|
19
|
+
unless Backend.plantuml_available?
|
20
|
+
return abort(parent, reader, attrs, "PlantUML not installed")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Parse format specifications
|
24
|
+
formats = parse_formats(attrs, parent.document)
|
25
|
+
options = parse_options(parent, reader, attrs)
|
26
|
+
|
27
|
+
process_image_block(parent, reader, attrs, formats, options)
|
28
|
+
rescue StandardError => e
|
29
|
+
abort(parent, reader, attrs, e.message)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def add_image_path_to_includedirs(document, image_path, includedirs)
|
35
|
+
docdir = document.attributes["docdir"]
|
36
|
+
includedirs << File.dirname(File.join(docdir, image_path))
|
37
|
+
includedirs.compact.uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_options(parent, reader, attrs)
|
41
|
+
options = {}
|
42
|
+
|
43
|
+
# Parse include directory
|
44
|
+
options[:includedirs] = parse_doc_includedirs(parent.document)
|
45
|
+
options[:includedirs] = add_attrs_to_includedirs(
|
46
|
+
parent.document, attrs, options[:includedirs]
|
47
|
+
)
|
48
|
+
options[:includedirs] = add_image_path_to_includedirs(
|
49
|
+
parent.document, reader, options[:includedirs]
|
50
|
+
)
|
51
|
+
|
52
|
+
options
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -32,7 +32,7 @@ module Metanorma
|
|
32
32
|
options
|
33
33
|
end
|
34
34
|
|
35
|
-
def generate( # rubocop:disable Metrics/MethodLength
|
35
|
+
def generate( # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
36
36
|
content,
|
37
37
|
format: DEFAULT_FORMAT,
|
38
38
|
output_file: nil,
|
@@ -42,6 +42,9 @@ module Metanorma
|
|
42
42
|
ensure_jar_available!
|
43
43
|
ensure_java_available!
|
44
44
|
|
45
|
+
include_files = get_include_files(content, options)
|
46
|
+
options[:include_files] = include_files unless include_files.empty?
|
47
|
+
|
45
48
|
result = if output_file
|
46
49
|
generate_to_file(content, format, output_file, options)
|
47
50
|
elsif base64
|
@@ -55,6 +58,24 @@ module Metanorma
|
|
55
58
|
{ success: false, error: e }
|
56
59
|
end
|
57
60
|
|
61
|
+
def get_include_files(content, _options) # rubocop:disable Metrics/MethodLength
|
62
|
+
include_files = []
|
63
|
+
content.each_line do |line|
|
64
|
+
case line
|
65
|
+
when /(!include|!includesub)\s(.+){1}/
|
66
|
+
found_file = $2.split("!").first
|
67
|
+
|
68
|
+
# skip web links and standard libraries
|
69
|
+
if found_file.start_with?("<", "http")
|
70
|
+
found_file = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
include_files << found_file
|
74
|
+
end
|
75
|
+
end
|
76
|
+
include_files.compact.uniq
|
77
|
+
end
|
78
|
+
|
58
79
|
def generate_from_file(
|
59
80
|
input_file, format: DEFAULT_FORMAT,
|
60
81
|
output_file: nil, base64: false, **options
|
@@ -169,47 +190,85 @@ module Metanorma
|
|
169
190
|
end
|
170
191
|
|
171
192
|
def execute_plantuml(content, format, output_file, options) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/MethodLength
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
#
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
end
|
190
|
-
raise GenerationError.new(error_message, error)
|
193
|
+
# PlantUML generates output files based on filename specified in
|
194
|
+
# @start... line
|
195
|
+
# We need to use a temp directory and then move the file
|
196
|
+
Dir.mktmpdir do |temp_dir| # rubocop:disable Metrics/BlockLength
|
197
|
+
# create input file
|
198
|
+
File.open("#{temp_dir}/plantuml_input.puml", "w") do |f|
|
199
|
+
f.write(content)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Handle include files
|
203
|
+
if options[:include_files] && !options[:include_files].empty?
|
204
|
+
if options[:includedirs].empty?
|
205
|
+
# raise error when include files are found but includedirs
|
206
|
+
# is nil
|
207
|
+
raise PlantumlError.new(
|
208
|
+
"includedirs is required when include files are specified",
|
209
|
+
)
|
191
210
|
end
|
192
211
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
212
|
+
options[:include_files].each do |include_file|
|
213
|
+
# find local include file in includedirs
|
214
|
+
found_include_file = nil
|
215
|
+
options[:includedirs].each do |includedir|
|
216
|
+
include_file_path = File.join(includedir, include_file)
|
217
|
+
if File.exist?(include_file_path)
|
218
|
+
found_include_file = include_file_path
|
219
|
+
break
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
if found_include_file
|
224
|
+
# create include file in temp directory
|
225
|
+
temp_include_file = File.join(temp_dir, include_file)
|
226
|
+
FileUtils.mkdir_p(File.dirname(temp_include_file))
|
227
|
+
|
228
|
+
File.open(temp_include_file, "w") do |f|
|
229
|
+
f.write(File.read(found_include_file))
|
230
|
+
end
|
208
231
|
end
|
209
232
|
end
|
233
|
+
end
|
234
|
+
|
235
|
+
cmd = build_command(
|
236
|
+
"#{temp_dir}/plantuml_input.puml",
|
237
|
+
format,
|
238
|
+
temp_dir,
|
239
|
+
options,
|
240
|
+
)
|
210
241
|
|
211
|
-
|
242
|
+
output, error, status = Open3.capture3(*cmd)
|
243
|
+
|
244
|
+
unless status.success?
|
245
|
+
error_message = if error.empty?
|
246
|
+
"Unknown PlantUML error"
|
247
|
+
else
|
248
|
+
error.strip
|
249
|
+
end
|
250
|
+
raise GenerationError.new(error_message, error)
|
212
251
|
end
|
252
|
+
|
253
|
+
# Find the generated file and move it to the desired location
|
254
|
+
if output_file
|
255
|
+
generated_file = find_generated_file(temp_dir, content,
|
256
|
+
format)
|
257
|
+
if generated_file && File.exist?(generated_file)
|
258
|
+
FileUtils.mv(generated_file, output_file)
|
259
|
+
else
|
260
|
+
# Debug: List what files were actually generated
|
261
|
+
generated_files = Dir.glob(File.join(temp_dir, "*"))
|
262
|
+
error_msg = "Generated file not found in temp directory. "
|
263
|
+
error_msg += "Expected: #{generated_file}. "
|
264
|
+
error_msg += "Found files: #{generated_files.map do |f|
|
265
|
+
File.basename(f)
|
266
|
+
end.join(', ')}"
|
267
|
+
raise GenerationError.new(error_msg)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
output
|
213
272
|
end
|
214
273
|
end
|
215
274
|
|
@@ -11,4 +11,6 @@ require "metanorma/plugin/plantuml/config"
|
|
11
11
|
require "metanorma/plugin/plantuml/wrapper"
|
12
12
|
require "metanorma/plugin/plantuml/utils"
|
13
13
|
require "metanorma/plugin/plantuml/backend"
|
14
|
+
require "metanorma/plugin/plantuml/block_processor_base"
|
14
15
|
require "metanorma/plugin/plantuml/block_processor"
|
16
|
+
require "metanorma/plugin/plantuml/image_block_macro_processor"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metanorma-plugin-plantuml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: asciidoctor
|
@@ -58,8 +58,10 @@ files:
|
|
58
58
|
- lib/metanorma-plugin-plantuml.rb
|
59
59
|
- lib/metanorma/plugin/plantuml/backend.rb
|
60
60
|
- lib/metanorma/plugin/plantuml/block_processor.rb
|
61
|
+
- lib/metanorma/plugin/plantuml/block_processor_base.rb
|
61
62
|
- lib/metanorma/plugin/plantuml/config.rb
|
62
63
|
- lib/metanorma/plugin/plantuml/errors.rb
|
64
|
+
- lib/metanorma/plugin/plantuml/image_block_macro_processor.rb
|
63
65
|
- lib/metanorma/plugin/plantuml/utils.rb
|
64
66
|
- lib/metanorma/plugin/plantuml/version.rb
|
65
67
|
- lib/metanorma/plugin/plantuml/wrapper.rb
|