metanorma-plugin-lutaml 0.4.1 → 0.4.5
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/.github/workflows/rake.yml +3 -13
- data/.hound.yml +5 -0
- data/.rubocop.yml +10 -0
- data/README.adoc +301 -10
- data/lib/metanorma-plugin-lutaml.rb +2 -0
- data/lib/metanorma/plugin/lutaml/liquid/multiply_local_file_system.rb +53 -0
- data/lib/metanorma/plugin/lutaml/liquid_templates/_diagrams_block.liquid +11 -0
- data/lib/metanorma/plugin/lutaml/liquid_templates/_packages.liquid +119 -0
- data/lib/metanorma/plugin/lutaml/liquid_templates/_packages_class.liquid +81 -0
- data/lib/metanorma/plugin/lutaml/liquid_templates/_packages_data_type.liquid +63 -0
- data/lib/metanorma/plugin/lutaml/liquid_templates/_packages_enum.liquid +63 -0
- data/lib/metanorma/plugin/lutaml/lutaml_diagram_base.rb +81 -0
- data/lib/metanorma/plugin/lutaml/lutaml_diagram_block.rb +7 -62
- data/lib/metanorma/plugin/lutaml/lutaml_diagram_block_macro.rb +20 -0
- data/lib/metanorma/plugin/lutaml/lutaml_uml_attributes_table_preprocessor.rb +11 -10
- data/lib/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor.rb +156 -0
- data/lib/metanorma/plugin/lutaml/utils.rb +4 -2
- data/lib/metanorma/plugin/lutaml/version.rb +1 -1
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a510a5a23684dee91557fb6047267ca6ac87be25cbe11e0bcad8e4c0d0812b9
|
4
|
+
data.tar.gz: 5d6e5cfc99c66b42ed50b7f6c6b1346fd2887ee9bba4436cacd5fbc56121dde6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea2e863ee349fb9bc5e60c43802569b0592b3437f2aad58f5bdb5591ecd430388566aff7ad8ff6d6c187e64868df9f1f303917afa94e73396e5c6d6564d0571a
|
7
|
+
data.tar.gz: dc8d9a72fbea070f7aec02fa576c59859ce34abf6375e307ef18685cdbd70130edcc3958839e7b82e9e7155befc8b51657c685d5f42cef96e900de0fd4a50532
|
data/.github/workflows/rake.yml
CHANGED
@@ -4,7 +4,7 @@ name: rake
|
|
4
4
|
|
5
5
|
on:
|
6
6
|
push:
|
7
|
-
branches: [ master, main
|
7
|
+
branches: [ master, main ]
|
8
8
|
tags: [ v* ]
|
9
9
|
pull_request:
|
10
10
|
|
@@ -16,19 +16,9 @@ jobs:
|
|
16
16
|
strategy:
|
17
17
|
fail-fast: false
|
18
18
|
matrix:
|
19
|
-
ruby: [ '
|
19
|
+
ruby: [ '3.0', '2.7', '2.6', '2.5' ]
|
20
20
|
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
21
21
|
experimental: [ false ]
|
22
|
-
include:
|
23
|
-
- ruby: '3.0'
|
24
|
-
os: 'ubuntu-latest'
|
25
|
-
experimental: true
|
26
|
-
- ruby: '3.0'
|
27
|
-
os: 'windows-latest'
|
28
|
-
experimental: true
|
29
|
-
- ruby: '3.0'
|
30
|
-
os: 'macos-latest'
|
31
|
-
experimental: true
|
32
22
|
steps:
|
33
23
|
- uses: actions/checkout@master
|
34
24
|
|
@@ -49,5 +39,5 @@ jobs:
|
|
49
39
|
with:
|
50
40
|
token: ${{ secrets.METANORMA_CI_PAT_TOKEN || secrets.GITHUB_TOKEN }}
|
51
41
|
repository: ${{ github.repository }}
|
52
|
-
event-type:
|
42
|
+
event-type: tests-passed
|
53
43
|
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
|
data/.hound.yml
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Auto-generated by Cimas: Do not edit it manually!
|
2
|
+
# See https://github.com/metanorma/cimas
|
3
|
+
inherit_from:
|
4
|
+
- https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
|
5
|
+
|
6
|
+
# local repo-specific modifications
|
7
|
+
# ...
|
8
|
+
|
9
|
+
AllCops:
|
10
|
+
TargetRubyVersion: 2.5
|
data/README.adoc
CHANGED
@@ -64,9 +64,19 @@ Where:
|
|
64
64
|
|
65
65
|
* content within the block is called the "`template`";
|
66
66
|
|
67
|
-
* `{example.exp}` is the location of the
|
68
|
-
|
69
|
-
|
67
|
+
* `{example.exp}` is the location of the EXPRESS schema file (`*.exp`) that
|
68
|
+
contains data to be loaded. Location of the file is computed relative to the
|
69
|
+
source directory that `[lutaml]` is used (e.g., if
|
70
|
+
`[lutaml,example.exp,my_context]` is invoked in an `.adoc` file located at
|
71
|
+
`/foo/bar/doc.adoc`, the data file is expected to be found at
|
72
|
+
`/foo/bar/example.exp`);
|
73
|
+
|
74
|
+
* `{my_context}` is the name where the EXPRESS Repository read from the .exp
|
75
|
+
file can be accessed with. Context object is a serialized
|
76
|
+
`Expressir::Model::Repository` object with all variable names available. See
|
77
|
+
https://github.com/lutaml/expressir[Expressir] docs for reference.
|
78
|
+
`{my_context}` has `schemas` method to access Expressir
|
79
|
+
https://github.com/lutaml/expressir/blob/master/lib/expressir/model/schema.rb[schemas]
|
70
80
|
|
71
81
|
Will produce this output:
|
72
82
|
|
@@ -83,7 +93,11 @@ Will produce this output:
|
|
83
93
|
|
84
94
|
This macro also supports `.lutaml` files.
|
85
95
|
|
86
|
-
Instead of using the direct path to the file one can use `lutaml-express-index`
|
96
|
+
Instead of using the direct path to the file one can use `lutaml-express-index`
|
97
|
+
document attribute to supply directory with express files or YAML index file to
|
98
|
+
parse as well as the cache file location.
|
99
|
+
|
100
|
+
The syntax is as follows:
|
87
101
|
|
88
102
|
[source,adoc]
|
89
103
|
-----
|
@@ -92,13 +106,15 @@ Instead of using the direct path to the file one can use `lutaml-express-index`
|
|
92
106
|
|
93
107
|
Where:
|
94
108
|
|
95
|
-
* `my_custom_name` - is name of the parsed
|
109
|
+
* `my_custom_name` - is name of the parsed EXPRESS files context for the later
|
110
|
+
use with lutaml macro
|
96
111
|
|
97
|
-
* `dir_or_index_path` - location of directory with
|
112
|
+
* `dir_or_index_path` - location of directory with EXPRESS files or path to the
|
113
|
+
YAML index file to parse
|
98
114
|
|
99
|
-
* `cache_path` - optional, location of the
|
115
|
+
* `cache_path` - optional, location of the Expressir cache file to use
|
100
116
|
|
101
|
-
Example of
|
117
|
+
Example of usage:
|
102
118
|
|
103
119
|
[source,adoc]
|
104
120
|
-----
|
@@ -113,9 +129,75 @@ Author
|
|
113
129
|
----
|
114
130
|
-----
|
115
131
|
|
132
|
+
=== Usage, `lutaml_diagram` macro
|
133
|
+
|
134
|
+
This macro allows to quickly render lutaml file as diagram image.
|
135
|
+
Given `example.lutaml` file with the content:
|
136
|
+
|
137
|
+
[source,java]
|
138
|
+
----
|
139
|
+
diagram MyView {
|
140
|
+
title "my diagram"
|
141
|
+
|
142
|
+
enum AddressClassProfile {
|
143
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
144
|
+
definition
|
145
|
+
this is multiline with `ascidoc`
|
146
|
+
end definition
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
class AttributeProfile {
|
151
|
+
+addressClassProfile: CharacterString [0..1]
|
152
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
153
|
+
definition this is attribute definition
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}
|
157
|
+
----
|
158
|
+
|
159
|
+
And the `lutaml_diagram` macro:
|
160
|
+
|
161
|
+
[source,adoc]
|
162
|
+
-----
|
163
|
+
lutaml_diagram::example.lutaml[]
|
164
|
+
-----
|
165
|
+
|
166
|
+
Will add diagram image to the document
|
167
|
+
|
168
|
+
Also one can use `lutaml_diagram` block with embed lutaml code instead of block macro. Example:
|
169
|
+
|
170
|
+
[source,java]
|
171
|
+
----
|
172
|
+
[lutaml_diagram]
|
173
|
+
....
|
174
|
+
diagram MyView {
|
175
|
+
title "my diagram"
|
176
|
+
|
177
|
+
enum AddressClassProfile {
|
178
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
179
|
+
definition
|
180
|
+
this is multiline with `ascidoc`
|
181
|
+
end definition
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
class AttributeProfile {
|
186
|
+
+addressClassProfile: CharacterString [0..1]
|
187
|
+
imlicistAttributeProfile: CharacterString [0..1] {
|
188
|
+
definition this is attribute definition
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
....
|
193
|
+
----
|
194
|
+
|
195
|
+
|
116
196
|
=== Usage, `lutaml_uml_attributes_table` macro
|
117
197
|
|
118
|
-
This macro allows to quickly render
|
198
|
+
This macro allows to quickly render data model attributes/values tables.
|
199
|
+
|
200
|
+
Given `example.lutaml` file with the content:
|
119
201
|
|
120
202
|
[source,java]
|
121
203
|
----
|
@@ -187,6 +269,215 @@ Will produce this output:
|
|
187
269
|
|===
|
188
270
|
-----
|
189
271
|
|
272
|
+
=== Usage, `lutaml_uml_datamodel_description` macro
|
273
|
+
|
274
|
+
This macro allows to quickly render data model packages and its dependent
|
275
|
+
objects for supplied XMI file.
|
276
|
+
|
277
|
+
Given an Enterprise Architect `example.xmi` file with 2 packages:
|
278
|
+
|
279
|
+
* 'Another'
|
280
|
+
* 'CityGML'
|
281
|
+
|
282
|
+
The `lutaml_uml_datamodel_description` macro can be used:
|
283
|
+
|
284
|
+
[source,adoc]
|
285
|
+
-----
|
286
|
+
[lutaml_uml_datamodel_description, path/to/example.xmi]
|
287
|
+
--
|
288
|
+
[.before]
|
289
|
+
...
|
290
|
+
my text
|
291
|
+
...
|
292
|
+
|
293
|
+
[.diagram_include_block, base_path="requirements/"]
|
294
|
+
...
|
295
|
+
Diagram text
|
296
|
+
...
|
297
|
+
|
298
|
+
[.include_block, package="Another", base_path="spec/fixtures"]
|
299
|
+
...
|
300
|
+
my text
|
301
|
+
...
|
302
|
+
|
303
|
+
[.include_block, base_path="spec/fixtures"]
|
304
|
+
...
|
305
|
+
my text
|
306
|
+
...
|
307
|
+
|
308
|
+
[.before, package="Another"]
|
309
|
+
...
|
310
|
+
text before Another package
|
311
|
+
...
|
312
|
+
|
313
|
+
[.after, package="Another"]
|
314
|
+
...
|
315
|
+
text after Another package
|
316
|
+
...
|
317
|
+
|
318
|
+
[.after, package="CityGML"]
|
319
|
+
...
|
320
|
+
text after CityGML package
|
321
|
+
...
|
322
|
+
|
323
|
+
[.after]
|
324
|
+
...
|
325
|
+
footer text
|
326
|
+
...
|
327
|
+
--
|
328
|
+
--
|
329
|
+
-----
|
330
|
+
|
331
|
+
Where:
|
332
|
+
|
333
|
+
* `path/to/example.xmi` - required, path to the XMI file to render
|
334
|
+
|
335
|
+
* `[.before]` - macro to add additional text before the rendered output, can be used only once, additional occurrences of macro will overwrite text
|
336
|
+
|
337
|
+
* `[.after]` - macro to add additional text after the rendered output, can be used only once, additional occurrences of macro will overwrite text
|
338
|
+
|
339
|
+
* `[.after, package="Another"]` - macro with text to be inserted before(after in case of `.before` name) the package
|
340
|
+
|
341
|
+
* `[.diagram_include_block]` - macro to automatically include diagram images. Attribute `base_path` is a required attribute to supply path prefix where to look for a digram image. The logic is as follows:
|
342
|
+
[source,adoc]
|
343
|
+
-----
|
344
|
+
{% for diagram in package.diagrams %}
|
345
|
+
[[figure-{{ diagram.xmi_id }}]]
|
346
|
+
.{{ diagram.name }}
|
347
|
+
image::{{ image_base_path }}/{{ diagram.xmi_id }}.png[]
|
348
|
+
|
349
|
+
{% if diagram.definition %}
|
350
|
+
{{ diagram.definition | html2adoc }}
|
351
|
+
{% endif %}
|
352
|
+
{% endfor %}
|
353
|
+
-----
|
354
|
+
Eg: script will take package diagrams supplied in xmi file and will try to include `image` with the name equal to diagram' xmi_id attribute plus `.png`. Also one can add any text to the macro text, it will be added as paragraph before each image include.
|
355
|
+
|
356
|
+
* `[.diagram_include_block, package="Another"]` - same as above, but diagram will be included only for supplied package name
|
357
|
+
|
358
|
+
* `[.include_block, base_path="spec/fixtures"]` - macro to inlude files(adoc/liquid) for each package name. Attribute `base_path` is a required attribute to supply path prefix where to look for file to include. Macro will look for a file called `base_path` + `/` `_package_name`(downcase, replace : -> '', ' ' -> '_') + `.adoc`[`.liquid`], eg for package 'My Package name' and `base_path` eq to `my/path`, macro will look for the following file path: `my/path/_my_package_name.adoc`.
|
359
|
+
|
360
|
+
* `[.include_block, package="Another", base_path="spec/fixtures"]` - same as above, but include block will be included only for supplied package name
|
361
|
+
|
362
|
+
|
363
|
+
Will produce this output:
|
364
|
+
|
365
|
+
[source,adoc]
|
366
|
+
-----
|
367
|
+
my text
|
368
|
+
== CityGML package
|
369
|
+
=== CityGML overview
|
370
|
+
|
371
|
+
Diagram text
|
372
|
+
|
373
|
+
[[figure-EAID_ACBB5EE3_3428_40f5_9C7C_E41923419F29]]
|
374
|
+
.CityGML Package Diagram
|
375
|
+
image::requirements//EAID_ACBB5EE3_3428_40f5_9C7C_E41923419F29.png[]
|
376
|
+
|
377
|
+
BuildingFurnitureFunctionValue is a code list that enumerates the different purposes of a BuildingFurniture.
|
378
|
+
|
379
|
+
[[figure-EAID_938AE961_1C57_4052_B964_997D1894A58D]]
|
380
|
+
.Use of ISO and OASIS standards in CityGML
|
381
|
+
image::requirements//EAID_938AE961_1C57_4052_B964_997D1894A58D.png[]
|
382
|
+
|
383
|
+
The CityGML package is organized into
|
384
|
+
2 packages with 1 modules:
|
385
|
+
|
386
|
+
[arabic]
|
387
|
+
. Another package
|
388
|
+
. CityTML package
|
389
|
+
|
390
|
+
my text
|
391
|
+
|
392
|
+
Content for CityGML package
|
393
|
+
|
394
|
+
==== Defining tables
|
395
|
+
|
396
|
+
[arabic]
|
397
|
+
.<<tab-P-another-C-abstractatomictimeseries>> -- Elements of Another::AbstractAtomicTimeseries
|
398
|
+
|
399
|
+
[[tab-P-another-C-abstractatomictimeseries]]
|
400
|
+
.Elements of Another::AbstractAtomicTimeseries
|
401
|
+
[width="100%",cols="a,a,a,a,a,a,a,a"]
|
402
|
+
|===
|
403
|
+
h|Name: 7+| AbstractAtomicTimeseries
|
404
|
+
h|Definition: 7+|
|
405
|
+
h|Stereotype: 7+| interface
|
406
|
+
h|Abstract: 7+|
|
407
|
+
.1+h|Associations: 7+| (none)
|
408
|
+
.4+h|Public attributes:
|
409
|
+
| _Name_
|
410
|
+
2+| _Definition_
|
411
|
+
| _Derived_
|
412
|
+
| _Obligation_
|
413
|
+
| _Maximum occurrence_
|
414
|
+
| _Data type_
|
415
|
+
| adeOfAbstractAtomicTimeseries
|
416
|
+
2+|
|
417
|
+
|
|
418
|
+
| C
|
419
|
+
| *
|
420
|
+
| ADEOfAbstractAtomicTimeseries
|
421
|
+
| observationProperty
|
422
|
+
2+|
|
423
|
+
|
|
424
|
+
| M
|
425
|
+
| 1
|
426
|
+
| CharacterString
|
427
|
+
| uom
|
428
|
+
2+|
|
429
|
+
|
|
430
|
+
| C
|
431
|
+
| 1
|
432
|
+
| CharacterString
|
433
|
+
h|Constraints: 7+| (none)
|
434
|
+
|===
|
435
|
+
|
436
|
+
=== Additional Information
|
437
|
+
|
438
|
+
text after CityGML package
|
439
|
+
-----
|
440
|
+
|
441
|
+
In addition to just supplying XMI file, this macro also supports YAML
|
442
|
+
configuration file.
|
443
|
+
|
444
|
+
The format for using YAML is this:
|
445
|
+
|
446
|
+
[source,yaml]
|
447
|
+
-----
|
448
|
+
---
|
449
|
+
packages:
|
450
|
+
# includes these packages
|
451
|
+
- "Package *"
|
452
|
+
- two*
|
453
|
+
- three
|
454
|
+
# skips these packages
|
455
|
+
- skip: four
|
456
|
+
-----
|
457
|
+
|
458
|
+
Where:
|
459
|
+
|
460
|
+
* `packages` - required, root element with the list of strings or objects
|
461
|
+
|
462
|
+
* `Package *` - pattern matching, specifies lookup condition for packages to
|
463
|
+
render.
|
464
|
+
+
|
465
|
+
NOTE: In this example, it is equal to the following regular expression: `/^Package.*$/`
|
466
|
+
|
467
|
+
* `skip: four` - object with package name to skip
|
468
|
+
|
469
|
+
Usage with macro:
|
470
|
+
|
471
|
+
[source,adoc]
|
472
|
+
-----
|
473
|
+
[lutaml_uml_datamodel_description, path/to/example.xmi, path/to/config.yml]
|
474
|
+
....
|
475
|
+
-----
|
476
|
+
|
477
|
+
The macro processor will read supplied YAML file and arrange packages according
|
478
|
+
to the order supplied in the config file, also all packages supplied as `skip`
|
479
|
+
will be skipped during render
|
480
|
+
|
190
481
|
== Documentation
|
191
482
|
|
192
|
-
|
483
|
+
Please refer to https://www.metanorma.com.
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require "metanorma/plugin/lutaml/version"
|
2
2
|
require "metanorma/plugin/lutaml/lutaml_preprocessor"
|
3
3
|
require "metanorma/plugin/lutaml/lutaml_uml_attributes_table_preprocessor"
|
4
|
+
require "metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor"
|
4
5
|
require "metanorma/plugin/lutaml/lutaml_diagram_block"
|
6
|
+
require "metanorma/plugin/lutaml/lutaml_diagram_block_macro"
|
5
7
|
|
6
8
|
module Metanorma
|
7
9
|
module Plugin
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Metanorma
|
2
|
+
module Plugin
|
3
|
+
module Lutaml
|
4
|
+
module Liquid
|
5
|
+
class LocalFileSystem
|
6
|
+
attr_accessor :roots, :patterns
|
7
|
+
|
8
|
+
def initialize(roots, patterns = ["_%s.liquid"])
|
9
|
+
@roots = roots
|
10
|
+
@patterns = patterns
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_template_file(template_path)
|
14
|
+
full_path = full_path(template_path)
|
15
|
+
raise FileSystemError, "No such template '#{template_path}'" unless File.exist?(full_path)
|
16
|
+
|
17
|
+
File.read(full_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def full_path(template_path)
|
21
|
+
raise ::Liquid::FileSystemError, "Illegal template name '#{template_path}'" unless %r{\A[^./][a-zA-Z0-9_/]+\z}.match?(template_path)
|
22
|
+
|
23
|
+
result_path = if template_path.include?('/')
|
24
|
+
roots
|
25
|
+
.map do |root|
|
26
|
+
patterns.map do |pattern|
|
27
|
+
File.join(root, File.dirname(template_path), pattern % File.basename(template_path))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
.flatten
|
31
|
+
.find { |path| File.file?(path) }
|
32
|
+
else
|
33
|
+
roots
|
34
|
+
.map do |root|
|
35
|
+
patterns.map do |pattern|
|
36
|
+
File.join(root, pattern % template_path)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
.flatten
|
40
|
+
.find { |path| File.file?(path) }
|
41
|
+
end
|
42
|
+
|
43
|
+
unless roots.any? { |root| File.expand_path(result_path).start_with?(File.expand_path(root)) }
|
44
|
+
raise ::Liquid::FileSystemError, "Illegal template path '#{File.expand_path(result_path)}'"
|
45
|
+
end
|
46
|
+
|
47
|
+
result_path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
{{ text }}
|
2
|
+
|
3
|
+
{% for diagram in package.diagrams %}
|
4
|
+
[[figure-{{ diagram.xmi_id }}]]
|
5
|
+
.{{ diagram.name }}
|
6
|
+
image::{{ image_base_path }}/{{ diagram.xmi_id }}.png[]
|
7
|
+
|
8
|
+
{% if diagram.definition %}
|
9
|
+
{{ diagram.definition | html2adoc }}
|
10
|
+
{% endif %}
|
11
|
+
{% endfor %}
|
@@ -0,0 +1,119 @@
|
|
1
|
+
{% for package in context.packages %}
|
2
|
+
{% assign package_name = package.name | downcase | replace: ":", "" | replace: " ", "_" %}
|
3
|
+
{% if additional_context.before %}
|
4
|
+
{{ additional_context.before.text }}
|
5
|
+
{% endif %}
|
6
|
+
{% assign before_package_key = 'before;' | append: package.name %}
|
7
|
+
{% if additional_context[before_package_key] %}
|
8
|
+
{{ additional_context[before_package_key].text }}
|
9
|
+
{% endif %}
|
10
|
+
{% assign is_package_spare = package.name | slice: 0,5 %}
|
11
|
+
{% if is_package_spare == 'old: ' %}{% continue %}
|
12
|
+
{% elsif is_package_spare == 'Spare' %}{% continue %}
|
13
|
+
{% endif %}
|
14
|
+
|
15
|
+
{% capture equalsigns %}{% for count in (1..depth) %}={% endfor %}{% endcapture %}{{equalsigns}} {{ package.name }} package
|
16
|
+
{{equalsigns}}= {{ package.name }} overview
|
17
|
+
|
18
|
+
{% if additional_context.diagram_include_block %}
|
19
|
+
{% include "diagrams_block", package_name: package_name, image_base_path: additional_context.diagram_include_block.base_path, text: additional_context.diagram_include_block.text %}
|
20
|
+
{% endif %}
|
21
|
+
|
22
|
+
{% if package.packages.size > 0 %}The {{ package.name }} package is organized into
|
23
|
+
{{ package.packages.size }} packages{% assign modules_nested_size = 0 %}{% for module in package.packages %}{% assign modules_nested_size = modules_nested_size | plus: module.packages.size %}{% endfor %}{% if modules_nested_size > 0 %} with {{modules_nested_size}} modules{% endif %}:
|
24
|
+
{% endif %}
|
25
|
+
[arabic]
|
26
|
+
{% for module in package.packages %}
|
27
|
+
{% if module.packages.length > 0 %}
|
28
|
+
. {{ module.name }} package comprises:
|
29
|
+
{% for nested_module in module.packages %}
|
30
|
+
.. {{nested_module.name}} module
|
31
|
+
{% endfor %}
|
32
|
+
{% else %}
|
33
|
+
. {{ module.name }} package
|
34
|
+
{% endif %}
|
35
|
+
{% endfor %}
|
36
|
+
|
37
|
+
{% if package.classes.size > 0 or package.enums.size > 0 or package.data_types.size > 0 %}
|
38
|
+
|
39
|
+
{{equalsigns}}= Defining tables
|
40
|
+
|
41
|
+
[arabic]
|
42
|
+
{% for klass in package.classes %}
|
43
|
+
{% assign is_klass_spare = klass.name | slice: 0,5 %}
|
44
|
+
{% if is_klass_spare == 'old: ' %}{% continue %}
|
45
|
+
{% elsif is_klass_spare == 'Spare' %}{% continue %}
|
46
|
+
{% endif %}
|
47
|
+
{% assign klass_name = klass.name | downcase | replace: ':', '' | replace: ' ', '_' %}
|
48
|
+
.<<tab-P-{{ package_name }}-C-{{ klass_name }}>> -- Elements of {{ package.name }}::{{ klass.name }}
|
49
|
+
|
50
|
+
{% endfor %}
|
51
|
+
{% for enum in package.enums %}
|
52
|
+
{% assign is_enum_spare = enum.name | slice: 0,5 %}
|
53
|
+
{% if is_enum_spare == 'old: ' %}{% continue %}
|
54
|
+
{% elsif is_enum_spare == 'Spare' %}{% continue %}
|
55
|
+
{% endif %}
|
56
|
+
{% assign enum_name = enum.name | downcase | replace: ':', '' | replace: ' ', '_' %}
|
57
|
+
.<<tab-P-{{ package_name }}-E-{{ enum_name }}>> -- Elements of {{ package.name }}::{{ enum.name }}
|
58
|
+
|
59
|
+
{% endfor %}
|
60
|
+
{% for data_type in package.data_types %}
|
61
|
+
{% assign is_data_type_spare = data_type.name | slice: 0,5 %}
|
62
|
+
{% if is_data_type_spare == 'old: ' %}{% continue %}
|
63
|
+
{% elsif is_data_type_spare == 'Spare' %}{% continue %}
|
64
|
+
{% endif %}
|
65
|
+
{% assign data_type_name = data_type.name | downcase | replace: ':', '' | replace: ' ', '_' %}
|
66
|
+
.<<tab-P-{{ package_name }}-DT-{{ data_type_name }}>> -- Elements of {{ package.name }}::{{ data_type.name }}
|
67
|
+
|
68
|
+
{% endfor %}
|
69
|
+
|
70
|
+
{% for klass in package.classes %}
|
71
|
+
{% include "packages_class" %}
|
72
|
+
{% endfor %}
|
73
|
+
|
74
|
+
{% for klass in package.enums %}
|
75
|
+
{% include "packages_enum" %}
|
76
|
+
{% endfor %}
|
77
|
+
|
78
|
+
{% for klass in package.data_types %}
|
79
|
+
{% include "packages_data_type" %}
|
80
|
+
{% endfor %}
|
81
|
+
|
82
|
+
{% endif %}
|
83
|
+
|
84
|
+
{% if additional_context.include_block %}
|
85
|
+
{% assign block = additional_context.include_block %}
|
86
|
+
{% capture block_filename %}{{ block.base_path }}/{{ package_name }}{% endcapture %}
|
87
|
+
{% capture block_content %}{% include block_filename %}{% endcapture %}
|
88
|
+
{% unless block_content contains "Liquid error" %}
|
89
|
+
{% if block.text %}
|
90
|
+
{{ block.text }}
|
91
|
+
{% endif %}
|
92
|
+
{{ block_content }}
|
93
|
+
{% endunless %}
|
94
|
+
{% endif %}
|
95
|
+
|
96
|
+
{% assign include_block_package_key = 'include_block;' | append: package.name %}
|
97
|
+
{% assign block = additional_context[include_block_package_key] %}
|
98
|
+
{% if block %}
|
99
|
+
{% capture block_filename %}{{ block.base_path }}/{{ package_name }}{% endcapture %}
|
100
|
+
{% capture block_content %}{% include block_filename %}{% endcapture %}
|
101
|
+
{% unless block_content contains "Liquid error" %}
|
102
|
+
{% if block.text %}
|
103
|
+
{{ block.text }}
|
104
|
+
{% endif %}
|
105
|
+
{{ block_content }}
|
106
|
+
{% endunless %}
|
107
|
+
{% endif %}
|
108
|
+
|
109
|
+
{% assign after_package_key = 'after;' | append: package.name %}
|
110
|
+
{{equalsigns}}= Additional Information
|
111
|
+
{% if additional_context[after_package_key] %}
|
112
|
+
{{ additional_context[after_package_key].text }}
|
113
|
+
{% endif %}
|
114
|
+
{% if package.packages.size > 0 and render_nested_packages %}{% assign nested_depth = depth | plus: 1 %}{% include "packages", depth: nested_depth, context: package %}{% endif %}
|
115
|
+
{% endfor %}
|
116
|
+
|
117
|
+
{% if additional_context.after %}
|
118
|
+
{{ additional_context.after.text }}
|
119
|
+
{% endif %}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
{% assign is_klass_spare = klass.name | slice: 0,5 %}
|
2
|
+
{% if is_klass_spare == 'old: ' %}{% continue %}
|
3
|
+
{% elsif is_klass_spare == 'Spare' %}{% continue %}
|
4
|
+
{% endif %}
|
5
|
+
{% assign klass_name = klass.name | downcase | replace: ':', '' | replace: ' ', '_' %}
|
6
|
+
[[tab-P-{{ package_name }}-C-{{ klass_name }}]]
|
7
|
+
.Elements of {{ package.name }}::{{ klass.name }}
|
8
|
+
[width="100%",cols="a,a,a,a,a,a,a,a"]
|
9
|
+
|===
|
10
|
+
|
11
|
+
h|Name: 7+| {{ klass.name }}
|
12
|
+
|
13
|
+
h|Definition: 7+| {{ klass.definition | html2adoc }}
|
14
|
+
|
15
|
+
h|Stereotype: 7+| {{ klass.stereotype | default: 'interface' }}
|
16
|
+
|
17
|
+
{% assign inherited = klass.associations | where: "member_end_type", "inheritance" %}
|
18
|
+
{% if inherited.size > 0 %}
|
19
|
+
h|Inheritance from: 7+| {{ inherited | map: 'member_end' | join: ", " }}
|
20
|
+
{% endif %}
|
21
|
+
|
22
|
+
{% assign generalizations = klass.associations | where: "member_end_type", "generalization" %}
|
23
|
+
{% if generalizations.size > 0 %}
|
24
|
+
h|Generalization of: 7+| {{ generalizations | map: 'member_end' | join: ", " }}
|
25
|
+
{% endif %}
|
26
|
+
|
27
|
+
h|Abstract: 7+| {{ klass.is_abstract }}
|
28
|
+
{% assign aggregations = klass.associations | where: "member_end_type", "aggregation" %}
|
29
|
+
{% if aggregations.size > 0 %}
|
30
|
+
.{{aggregations.size | plus: 1}}+h|Associations:
|
31
|
+
4+|_Association with:_
|
32
|
+
|_Obligation_
|
33
|
+
| _Maximum occurrence_
|
34
|
+
|_Provides:_
|
35
|
+
|
36
|
+
{% for assoc in aggregations %}
|
37
|
+
4+| {{assoc.member_end}}
|
38
|
+
| {% if assoc.member_end_cardinality %}{{ assoc.member_end_cardinality.min }}{% endif %}
|
39
|
+
| {% if assoc.member_end_cardinality %}{{ assoc.member_end_cardinality.max }}{% endif %}
|
40
|
+
| {{ assoc.member_end_attribute_name }}
|
41
|
+
|
42
|
+
{% endfor %}
|
43
|
+
{% else %}
|
44
|
+
|
45
|
+
.1+h|Associations: 7+| (none)
|
46
|
+
{% endif %}
|
47
|
+
{% if klass.attributes.size > 0 %}
|
48
|
+
.{{klass.attributes.size | plus: 1}}+h|Public attributes:
|
49
|
+
| _Name_
|
50
|
+
2+| _Definition_
|
51
|
+
| _Derived_
|
52
|
+
| _Obligation_
|
53
|
+
| _Maximum occurrence_
|
54
|
+
| _Data type_
|
55
|
+
|
56
|
+
{% for attr in klass.attributes %}
|
57
|
+
| {{attr.name}}
|
58
|
+
2+| {{ attr.definition | html2adoc }}
|
59
|
+
| {{ attr.is_derived }}
|
60
|
+
| {{attr.cardinality.min}}
|
61
|
+
| {{attr.cardinality.max}}
|
62
|
+
| {{attr.type}}
|
63
|
+
|
64
|
+
{% endfor %}
|
65
|
+
{% else %}
|
66
|
+
.1+h|Public attributes:
|
67
|
+
7+| (none)
|
68
|
+
{% endif %}
|
69
|
+
|
70
|
+
{% if klass.constraints.size > 0 %}
|
71
|
+
.{{ klass.constraints.size }}+|*Constraints:*
|
72
|
+
{% for constr in klass.constraints %}
|
73
|
+
7+| `{{ constr.body }}`
|
74
|
+
|
75
|
+
{% endfor %}
|
76
|
+
{% else %}
|
77
|
+
|
78
|
+
h|Constraints: 7+| (none)
|
79
|
+
{% endif %}
|
80
|
+
|
81
|
+
|===
|
@@ -0,0 +1,63 @@
|
|
1
|
+
{% assign is_klass_spare = klass.name | slice: 0,5 %}
|
2
|
+
{% if is_klass_spare == 'old: ' %}{% continue %}
|
3
|
+
{% elsif is_klass_spare == 'Spare' %}{% continue %}
|
4
|
+
{% endif %}
|
5
|
+
{% assign klass_name = klass.name | downcase | replace: ':', '' | replace: ' ', '_' %}
|
6
|
+
[[tab-P-{{ package_name }}-DT-{{ klass_name }}]]
|
7
|
+
.Elements of {{ package.name }}::{{ klass.name }}
|
8
|
+
[width="100%",cols="a,a,a,a,a,a,a,a"]
|
9
|
+
|===
|
10
|
+
|
11
|
+
h|Name: 7+| {{ klass.name }}
|
12
|
+
|
13
|
+
h|Definition: 7+| {{ klass.definition | html2adoc }}
|
14
|
+
|
15
|
+
h|Stereotype: 7+| {{ klass.stereotype | default: 'interface' }}
|
16
|
+
|
17
|
+
{% assign inherited = klass.associations | where: "member_end_type", "inheritance" %}
|
18
|
+
{% if inherited.size > 0 %}
|
19
|
+
h|Inheritance from: 7+| {{ inherited | map: 'member_end' | join: ", " }}
|
20
|
+
{% endif %}
|
21
|
+
|
22
|
+
{% assign generalizations = klass.associations | where: "member_end_type", "generalization" %}
|
23
|
+
{% if generalizations.size > 0 %}
|
24
|
+
h|Generalization of: 7+| {{ generalizations | map: 'member_end' | join: ", " }}
|
25
|
+
{% endif %}
|
26
|
+
|
27
|
+
h|Abstract: 7+| {{ klass.is_abstract }}
|
28
|
+
{% assign aggregations = klass.associations | where: "member_end_type", "aggregation" %}
|
29
|
+
{% if aggregations.size > 0 %}
|
30
|
+
.{{aggregations.size | plus: 1}}+h|Associations:
|
31
|
+
4+|_Association with:_
|
32
|
+
|_Obligation_
|
33
|
+
| _Maximum occurrence_
|
34
|
+
|_Provides:_
|
35
|
+
|
36
|
+
{% for assoc in aggregations %}
|
37
|
+
4+| {{assoc.member_end}}
|
38
|
+
| {% if assoc.member_end_cardinality %}{{ assoc.member_end_cardinality.min }}{% endif %}
|
39
|
+
| {% if assoc.member_end_cardinality %}{{ assoc.member_end_cardinality.max }}{% endif %}
|
40
|
+
| {{ assoc.member_end_attribute_name }}
|
41
|
+
|
42
|
+
{% endfor %}
|
43
|
+
{% else %}
|
44
|
+
|
45
|
+
.1+h|Associations: 7+| (none)
|
46
|
+
{% endif %}
|
47
|
+
|
48
|
+
{% if klass.attributes.size > 0 %}
|
49
|
+
.{{klass.attributes.size | plus: 1}}+h|Values:
|
50
|
+
| _Name_
|
51
|
+
6+| _Definition_
|
52
|
+
|
53
|
+
{% for value in klass.attributes %}
|
54
|
+
| {{value.name}}
|
55
|
+
6+| {{ value.definition | html2adoc }}
|
56
|
+
|
57
|
+
{% endfor %}
|
58
|
+
{% else %}
|
59
|
+
.1+h|Values:
|
60
|
+
7+| (none)
|
61
|
+
{% endif %}
|
62
|
+
|
63
|
+
|===
|
@@ -0,0 +1,63 @@
|
|
1
|
+
{% assign is_klass_spare = klass.name | slice: 0,5 %}
|
2
|
+
{% if is_klass_spare == 'old: ' %}{% continue %}
|
3
|
+
{% elsif is_klass_spare == 'Spare' %}{% continue %}
|
4
|
+
{% endif %}
|
5
|
+
{% assign klass_name = klass.name | downcase | replace: ':', '' | replace: ' ', '_' %}
|
6
|
+
[[tab-P-{{ package_name }}-E-{{ klass_name }}]]
|
7
|
+
.Elements of {{ package.name }}::{{ klass.name }}
|
8
|
+
[width="100%",cols="a,a,a,a,a,a,a,a"]
|
9
|
+
|===
|
10
|
+
|
11
|
+
h|Name: 7+| {{ klass.name }}
|
12
|
+
|
13
|
+
h|Definition: 7+| {{ klass.definition | html2adoc }}
|
14
|
+
|
15
|
+
h|Stereotype: 7+| {{ klass.stereotype | default: 'interface' }}
|
16
|
+
|
17
|
+
{% assign inherited = klass.associations | where: "member_end_type", "inheritance" %}
|
18
|
+
{% if inherited.size > 0 %}
|
19
|
+
h|Inheritance from: 7+| {{ inherited | map: 'member_end' | join: ", " }}
|
20
|
+
{% endif %}
|
21
|
+
|
22
|
+
{% assign generalizations = klass.associations | where: "member_end_type", "generalization" %}
|
23
|
+
{% if generalizations.size > 0 %}
|
24
|
+
h|Generalization of: 7+| {{ generalizations | map: 'member_end' | join: ", " }}
|
25
|
+
{% endif %}
|
26
|
+
|
27
|
+
h|Abstract: 7+| {{ klass.is_abstract }}
|
28
|
+
{% assign aggregations = klass.associations | where: "member_end_type", "aggregation" %}
|
29
|
+
{% if aggregations.size > 0 %}
|
30
|
+
.{{aggregations.size | plus: 1}}+h|Associations:
|
31
|
+
4+|_Association with:_
|
32
|
+
|_Obligation_
|
33
|
+
| _Maximum occurrence_
|
34
|
+
|_Provides:_
|
35
|
+
|
36
|
+
{% for assoc in aggregations %}
|
37
|
+
4+| {{assoc.member_end}}
|
38
|
+
| {% if assoc.member_end_cardinality %}{{ assoc.member_end_cardinality.min }}{% endif %}
|
39
|
+
| {% if assoc.member_end_cardinality %}{{ assoc.member_end_cardinality.max }}{% endif %}
|
40
|
+
| {{ assoc.member_end_attribute_name }}
|
41
|
+
|
42
|
+
{% endfor %}
|
43
|
+
{% else %}
|
44
|
+
|
45
|
+
.1+h|Associations: 7+| (none)
|
46
|
+
{% endif %}
|
47
|
+
{% if klass.values.size > 0 %}
|
48
|
+
.{{klass.values.size | plus: 1}}+h|Values:
|
49
|
+
| _Name_
|
50
|
+
6+| _Definition_
|
51
|
+
|
52
|
+
{% for value in klass.values %}
|
53
|
+
| {{value.name}}
|
54
|
+
6+| {{ value.definition | html2adoc }}
|
55
|
+
|
56
|
+
{% endfor %}
|
57
|
+
{% else %}
|
58
|
+
.1+h|Values:
|
59
|
+
7+| (none)
|
60
|
+
{% endif %}
|
61
|
+
|
62
|
+
|
63
|
+
|===
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "liquid"
|
4
|
+
require "asciidoctor"
|
5
|
+
require "asciidoctor/reader"
|
6
|
+
require "lutaml"
|
7
|
+
require "lutaml/uml"
|
8
|
+
require "lutaml/formatter"
|
9
|
+
require "metanorma/plugin/lutaml/utils"
|
10
|
+
|
11
|
+
module Metanorma
|
12
|
+
module Plugin
|
13
|
+
module Lutaml
|
14
|
+
module LutamlDiagramBase
|
15
|
+
def process(parent, reader, attrs)
|
16
|
+
uml_document = ::Lutaml::Uml::Parsers::Dsl.parse(lutaml_file(parent.document, reader))
|
17
|
+
filename = generate_file(parent, reader, uml_document)
|
18
|
+
through_attrs = generate_attrs(attrs)
|
19
|
+
through_attrs["target"] = filename
|
20
|
+
through_attrs["title"] = uml_document.caption
|
21
|
+
create_image_block(parent, through_attrs)
|
22
|
+
rescue StandardError => e
|
23
|
+
abort(parent, reader, attrs, e.message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def lutaml_file(reader)
|
27
|
+
raise 'Implement me!'
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def abort(parent, reader, attrs, msg)
|
33
|
+
warn(msg)
|
34
|
+
attrs["language"] = "lutaml"
|
35
|
+
source = reader.respond_to?(:source) ? reader.source : reader
|
36
|
+
create_listing_block(
|
37
|
+
parent,
|
38
|
+
source,
|
39
|
+
attrs.reject { |k, _v| k == 1 }
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
# if no :imagesdir: leave image file in lutaml
|
44
|
+
def generate_file(parent, _reader, uml_document)
|
45
|
+
formatter = ::Lutaml::Formatter::Graphviz.new
|
46
|
+
formatter.type = :png
|
47
|
+
|
48
|
+
imagesdir = if parent.document.attr("imagesdir")
|
49
|
+
File.join(parent.document.attr("imagesdir"), "lutaml")
|
50
|
+
else
|
51
|
+
"lutaml"
|
52
|
+
end
|
53
|
+
result_path = Utils.relative_file_path(parent.document, imagesdir)
|
54
|
+
result_pathname = Pathname.new(result_path)
|
55
|
+
result_pathname.mkpath
|
56
|
+
File.writable?(result_pathname) || raise("Destination path #{result_path} not writable for Lutaml!")
|
57
|
+
|
58
|
+
outfile = Tempfile.new(["lutaml", ".png"])
|
59
|
+
outfile.binmode
|
60
|
+
outfile.puts(formatter.format(uml_document))
|
61
|
+
|
62
|
+
# Warning: metanorma/metanorma-standoc#187
|
63
|
+
# Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
|
64
|
+
# This is why we need to copy and then unlink.
|
65
|
+
filename = File.basename(outfile.path)
|
66
|
+
FileUtils.cp(outfile, result_pathname) && outfile.unlink
|
67
|
+
|
68
|
+
File.join(result_pathname, filename)
|
69
|
+
end
|
70
|
+
|
71
|
+
def generate_attrs(attrs)
|
72
|
+
%w(id align float title role width height alt)
|
73
|
+
.reduce({}) do |memo, key|
|
74
|
+
memo[key] = attrs[key] if attrs.has_key? key
|
75
|
+
memo
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,86 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
require "asciidoctor"
|
5
|
-
require "asciidoctor/reader"
|
6
|
-
require "lutaml"
|
7
|
-
require "lutaml/uml"
|
8
|
-
require "metanorma/plugin/lutaml/utils"
|
3
|
+
require "metanorma/plugin/lutaml/lutaml_diagram_base"
|
9
4
|
|
10
5
|
module Metanorma
|
11
6
|
module Plugin
|
12
7
|
module Lutaml
|
13
8
|
class LutamlDiagramBlock < Asciidoctor::Extensions::BlockProcessor
|
9
|
+
include LutamlDiagramBase
|
10
|
+
|
14
11
|
use_dsl
|
15
12
|
named :lutaml_diagram
|
16
13
|
on_context :literal
|
17
14
|
parse_content_as :raw
|
18
15
|
|
19
|
-
def
|
20
|
-
warn(msg)
|
21
|
-
attrs["language"] = "lutaml"
|
22
|
-
create_listing_block(
|
23
|
-
parent,
|
24
|
-
reader.source,
|
25
|
-
attrs.reject { |k, _v| k == 1 }
|
26
|
-
)
|
27
|
-
end
|
16
|
+
def lutaml_file(document, reader)
|
28
17
|
|
29
|
-
|
30
|
-
uml_document = ::Lutaml::Uml::Parsers::Dsl.parse(lutaml_temp(reader))
|
31
|
-
filename = generate_file(parent, reader, uml_document)
|
32
|
-
through_attrs = generate_attrs(attrs)
|
33
|
-
through_attrs["target"] = filename
|
34
|
-
through_attrs["title"] = uml_document.caption
|
35
|
-
create_image_block(parent, through_attrs)
|
36
|
-
rescue StandardError => e
|
37
|
-
abort(parent, reader, attrs, e.message)
|
18
|
+
lutaml_temp(document, reader)
|
38
19
|
end
|
39
20
|
|
40
21
|
private
|
41
22
|
|
42
|
-
def lutaml_temp(reader)
|
43
|
-
temp_file = Tempfile.new(["lutaml", ".lutaml"])
|
23
|
+
def lutaml_temp(document, reader)
|
24
|
+
temp_file = Tempfile.new(["lutaml", ".lutaml"], Utils.relative_file_path(document, ''))
|
44
25
|
temp_file.puts(reader.read)
|
45
26
|
temp_file.rewind
|
46
27
|
temp_file
|
47
28
|
end
|
48
|
-
|
49
|
-
# if no :imagesdir: leave image file in lutaml
|
50
|
-
def generate_file(parent, _reader, uml_document)
|
51
|
-
formatter = ::Lutaml::Uml::Formatter::Graphviz.new
|
52
|
-
formatter.type = :png
|
53
|
-
|
54
|
-
imagesdir = if parent.document.attr("imagesdir")
|
55
|
-
File.join(parent.document.attr("imagesdir"), "lutaml")
|
56
|
-
else
|
57
|
-
"lutaml"
|
58
|
-
end
|
59
|
-
result_path = Utils.relative_file_path(parent.document, imagesdir)
|
60
|
-
result_pathname = Pathname.new(result_path)
|
61
|
-
result_pathname.mkpath
|
62
|
-
File.writable?(result_pathname) || raise("Destination path #{result_path} not writable for Lutaml!")
|
63
|
-
|
64
|
-
outfile = Tempfile.new(["lutaml", ".png"])
|
65
|
-
outfile.binmode
|
66
|
-
outfile.puts(formatter.format(uml_document))
|
67
|
-
|
68
|
-
# Warning: metanorma/metanorma-standoc#187
|
69
|
-
# Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
|
70
|
-
# This is why we need to copy and then unlink.
|
71
|
-
filename = File.basename(outfile.path)
|
72
|
-
FileUtils.cp(outfile, result_pathname) && outfile.unlink
|
73
|
-
|
74
|
-
File.join(result_pathname, filename)
|
75
|
-
end
|
76
|
-
|
77
|
-
def generate_attrs(attrs)
|
78
|
-
%w(id align float title role width height alt)
|
79
|
-
.reduce({}) do |memo, key|
|
80
|
-
memo[key] = attrs[key] if attrs.has_key? key
|
81
|
-
memo
|
82
|
-
end
|
83
|
-
end
|
84
29
|
end
|
85
30
|
end
|
86
31
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "metanorma/plugin/lutaml/lutaml_diagram_base"
|
4
|
+
|
5
|
+
module Metanorma
|
6
|
+
module Plugin
|
7
|
+
module Lutaml
|
8
|
+
class LutamlDiagramBlockMacro < Asciidoctor::Extensions::BlockMacroProcessor
|
9
|
+
include LutamlDiagramBase
|
10
|
+
|
11
|
+
use_dsl
|
12
|
+
named :lutaml_diagram
|
13
|
+
|
14
|
+
def lutaml_file(document, file_path)
|
15
|
+
File.new(Utils.relative_file_path(document, file_path))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -14,7 +14,7 @@ module Metanorma
|
|
14
14
|
# @example [lutaml_uml_attributes_table,path/to/lutaml,EntityName]
|
15
15
|
class LutamlUmlAttributesTablePreprocessor < Asciidoctor::Extensions::Preprocessor
|
16
16
|
MARCO_REGEXP =
|
17
|
-
/\[lutaml_uml_attributes_table,([^,]+),?(
|
17
|
+
/\[lutaml_uml_attributes_table,([^,]+),?([^,]+),?(.+?)?\]/
|
18
18
|
# search document for block `datamodel_attributes_table`
|
19
19
|
# read include derectives that goes after that in block and transform
|
20
20
|
# into yaml2text blocks
|
@@ -36,15 +36,16 @@ module Metanorma
|
|
36
36
|
input_lines.each_with_object([]) do |line, result|
|
37
37
|
if match = line.match(MARCO_REGEXP)
|
38
38
|
lutaml_path = match[1]
|
39
|
-
entity_name = match[
|
40
|
-
|
39
|
+
entity_name = match[2]
|
40
|
+
skip_headers = match[3]
|
41
|
+
result.push(*parse_marco(lutaml_path, entity_name, document, skip_headers))
|
41
42
|
else
|
42
43
|
result.push(line)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
def parse_marco(lutaml_path, entity_name, document)
|
48
|
+
def parse_marco(lutaml_path, entity_name, document, skip_headers)
|
48
49
|
lutaml_document = lutaml_document_from_file(document, lutaml_path)
|
49
50
|
.serialized_document
|
50
51
|
entities = [lutaml_document["classes"], lutaml_document["enums"]]
|
@@ -53,12 +54,12 @@ module Metanorma
|
|
53
54
|
entity_definition = entities.detect do |klass|
|
54
55
|
klass["name"] == entity_name.strip
|
55
56
|
end
|
56
|
-
model_representation(entity_definition, document)
|
57
|
+
model_representation(entity_definition, document, skip_headers)
|
57
58
|
end
|
58
59
|
|
59
|
-
def model_representation(entity_definition, document)
|
60
|
+
def model_representation(entity_definition, document, skip_headers)
|
60
61
|
render_result, errors = Utils.render_liquid_string(
|
61
|
-
template_string: table_template,
|
62
|
+
template_string: table_template(skip_headers),
|
62
63
|
context_items: entity_definition,
|
63
64
|
context_name: "definition",
|
64
65
|
document: document
|
@@ -68,9 +69,9 @@ module Metanorma
|
|
68
69
|
end
|
69
70
|
|
70
71
|
# rubocop:disable Layout/IndentHeredoc
|
71
|
-
def table_template
|
72
|
+
def table_template(skip_headers)
|
72
73
|
<<~TEMPLATE
|
73
|
-
=== {{ definition.name }}
|
74
|
+
#{"=== {{ definition.name }}" unless skip_headers}
|
74
75
|
{{ definition.definition }}
|
75
76
|
|
76
77
|
{% if definition.attributes %}
|
@@ -89,7 +90,7 @@ module Metanorma
|
|
89
90
|
|Name |Definition |Mandatory/ Optional/ Conditional |Max Occur |Data Type
|
90
91
|
|
91
92
|
{% for item in definition.attributes %}
|
92
|
-
|{{ item.name }} |{% if item.definition %}{{ item.definition }}{%
|
93
|
+
|{{ item.name }} |{% if item.definition %}{{ item.definition }}{% endif %} |{% if item.cardinality.min == "0" %}O{% else %}M{% endif %} |{% if item.cardinality.max == "*" %}N{% else %}1{% endif %} |{% if item.origin %}<<{{ item.origin }}>>{% endif %} `{{ item.type }}`
|
93
94
|
{% endfor %}
|
94
95
|
|===
|
95
96
|
{% endif %}
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "liquid"
|
4
|
+
require "asciidoctor"
|
5
|
+
require "asciidoctor/reader"
|
6
|
+
require "lutaml"
|
7
|
+
require "lutaml/uml"
|
8
|
+
require "metanorma/plugin/lutaml/utils"
|
9
|
+
|
10
|
+
module Metanorma
|
11
|
+
module Plugin
|
12
|
+
module Lutaml
|
13
|
+
# Macro for quick rendering of datamodel attributes/values table
|
14
|
+
# @example [lutaml_uml_attributes_table,path/to/lutaml,EntityName]
|
15
|
+
class LutamlUmlDatamodelDescriptionPreprocessor <
|
16
|
+
Asciidoctor::Extensions::Preprocessor
|
17
|
+
MARCO_REGEXP =
|
18
|
+
/\[lutaml_uml_datamodel_description,([^,]+),?(.+)?\]/
|
19
|
+
LIQUID_INCLUDE_PATH = File.join(
|
20
|
+
Gem.loaded_specs["metanorma-plugin-lutaml"].full_gem_path,
|
21
|
+
"lib", "metanorma", "plugin", "lutaml", "liquid_templates"
|
22
|
+
)
|
23
|
+
# search document for block `lutaml_uml_datamodel_description`
|
24
|
+
# read include derectives that goes after that in block and transform
|
25
|
+
# into yaml2text blocks
|
26
|
+
def process(document, reader)
|
27
|
+
input_lines = reader.readlines.to_enum
|
28
|
+
Asciidoctor::Reader.new(processed_lines(document, input_lines))
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def lutaml_document_from_file(document, file_path)
|
34
|
+
::Lutaml::Parser
|
35
|
+
.parse(File.new(Utils.relative_file_path(document, file_path),
|
36
|
+
encoding: "UTF-8"))
|
37
|
+
.first
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_yaml_config_file(document, file_path)
|
41
|
+
return {} if file_path.nil?
|
42
|
+
|
43
|
+
relative_file_path = Utils.relative_file_path(document, file_path)
|
44
|
+
YAML.load(File.read(relative_file_path, encoding: "UTF-8"))
|
45
|
+
end
|
46
|
+
|
47
|
+
def processed_lines(document, input_lines)
|
48
|
+
result = []
|
49
|
+
loop do
|
50
|
+
result.push(*process_text_blocks(document, input_lines))
|
51
|
+
end
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
55
|
+
def process_text_blocks(document, input_lines)
|
56
|
+
line = input_lines.next
|
57
|
+
block_match = line.match(MARCO_REGEXP)
|
58
|
+
return [line] if block_match.nil?
|
59
|
+
|
60
|
+
model_representation(
|
61
|
+
lutaml_document_from_file(document, block_match[1]),
|
62
|
+
document,
|
63
|
+
collect_additional_context(input_lines, input_lines.next),
|
64
|
+
parse_yaml_config_file(document, block_match[2]))
|
65
|
+
end
|
66
|
+
|
67
|
+
def collect_additional_context(input_lines, end_mark)
|
68
|
+
additional_context = {}
|
69
|
+
block_lines = []
|
70
|
+
while (block_line = input_lines.next) != end_mark
|
71
|
+
block_lines.push(block_line)
|
72
|
+
end
|
73
|
+
block_document = (Asciidoctor::Document.new(block_lines, {})).parse
|
74
|
+
block_document.blocks.each do |block|
|
75
|
+
attrs = block.attributes
|
76
|
+
name = attrs.delete('role')
|
77
|
+
package = attrs.delete('package')
|
78
|
+
macro_keyword = [name, package].compact.join(";")
|
79
|
+
additional_context[macro_keyword] = { 'text' => block.lines[1..-2].join("\n") }.merge(attrs)
|
80
|
+
end
|
81
|
+
additional_context
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_context_object(lutaml_document, additional_context, options)
|
85
|
+
if options.length.zero?
|
86
|
+
return {
|
87
|
+
'render_nested_packages' => true,
|
88
|
+
"packages" => lutaml_document.to_liquid['packages'].first['packages'].first['packages'],
|
89
|
+
"additional_context" => additional_context
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
root_package = lutaml_document.to_liquid['packages'].first
|
94
|
+
all_packages = [root_package, *root_package['children_packages']]
|
95
|
+
{
|
96
|
+
"packages" => sort_and_filter_out_packages(all_packages, options),
|
97
|
+
"additional_context" => additional_context
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def sort_and_filter_out_packages(all_packages, options)
|
102
|
+
return all_packages if options['packages'].nil?
|
103
|
+
|
104
|
+
result = []
|
105
|
+
# Step one - filter out all skipped packages
|
106
|
+
options['packages']
|
107
|
+
.find_all { |entity| entity.is_a?(Hash) && entity['skip'] }
|
108
|
+
.each do |entity|
|
109
|
+
entity_regexp = config_entity_regexp(entity['skip'])
|
110
|
+
all_packages
|
111
|
+
.delete_if {|package| package['name'] =~ entity_regexp }
|
112
|
+
end
|
113
|
+
# Step two - select supplied packages by pattern
|
114
|
+
options['packages']
|
115
|
+
.find_all { |entity| entity.is_a?(String) }
|
116
|
+
.each do |entity|
|
117
|
+
entity_regexp = config_entity_regexp(entity)
|
118
|
+
all_packages.each.with_index do |package|
|
119
|
+
if package['name'] =~ entity_regexp
|
120
|
+
result.push(package)
|
121
|
+
all_packages
|
122
|
+
.delete_if {|nest_package| nest_package['name'] == package['name'] }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
result
|
127
|
+
end
|
128
|
+
|
129
|
+
def config_entity_regexp(entity)
|
130
|
+
additional_sym = '.*' if entity =~ /\*$/
|
131
|
+
%r{^#{Regexp.escape(entity.gsub('*', ''))}#{additional_sym}$}
|
132
|
+
end
|
133
|
+
|
134
|
+
def model_representation(lutaml_document, document, additional_context, options)
|
135
|
+
render_result, errors = Utils.render_liquid_string(
|
136
|
+
template_string: table_template,
|
137
|
+
context_items: create_context_object(lutaml_document,
|
138
|
+
additional_context,
|
139
|
+
options),
|
140
|
+
context_name: "context",
|
141
|
+
document: document,
|
142
|
+
include_path: LIQUID_INCLUDE_PATH
|
143
|
+
)
|
144
|
+
Utils.notify_render_errors(document, errors)
|
145
|
+
render_result.split("\n")
|
146
|
+
end
|
147
|
+
|
148
|
+
def table_template
|
149
|
+
<<~LIQUID
|
150
|
+
{% include "packages", depth: 2, context: context, additional_context: context.additional_context, render_nested_packages: context.render_nested_packages %}
|
151
|
+
LIQUID
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "expressir/express/cache"
|
2
2
|
require "metanorma/plugin/lutaml/liquid/custom_filters"
|
3
|
+
require "metanorma/plugin/lutaml/liquid/multiply_local_file_system"
|
3
4
|
|
4
5
|
::Liquid::Template.register_filter(Metanorma::Plugin::Lutaml::Liquid::CustomFilters)
|
5
6
|
|
@@ -22,10 +23,11 @@ module Metanorma
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def render_liquid_string(template_string:, context_items:,
|
25
|
-
context_name:, document:)
|
26
|
+
context_name:, document:, include_path: nil)
|
26
27
|
liquid_template = ::Liquid::Template.parse(template_string)
|
27
28
|
# Allow includes for the template
|
28
|
-
|
29
|
+
include_paths = [Utils.relative_file_path(document, ""), include_path].compact
|
30
|
+
liquid_template.registers[:file_system] = ::Metanorma::Plugin::Lutaml::Liquid::LocalFileSystem.new(include_paths, ["_%s.liquid", "_%s.adoc"])
|
29
31
|
rendered_string = liquid_template
|
30
32
|
.render(context_name => context_items,
|
31
33
|
strict_variables: true,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metanorma-plugin-lutaml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: liquid
|
@@ -243,6 +243,8 @@ extra_rdoc_files: []
|
|
243
243
|
files:
|
244
244
|
- ".github/workflows/rake.yml"
|
245
245
|
- ".gitignore"
|
246
|
+
- ".hound.yml"
|
247
|
+
- ".rubocop.yml"
|
246
248
|
- CODE_OF_CONDUCT.md
|
247
249
|
- Dockerfile
|
248
250
|
- Gemfile
|
@@ -255,9 +257,18 @@ files:
|
|
255
257
|
- lib/metanorma-plugin-lutaml.rb
|
256
258
|
- lib/metanorma/plugin/lutaml/express_remarks_decorator.rb
|
257
259
|
- lib/metanorma/plugin/lutaml/liquid/custom_filters.rb
|
260
|
+
- lib/metanorma/plugin/lutaml/liquid/multiply_local_file_system.rb
|
261
|
+
- lib/metanorma/plugin/lutaml/liquid_templates/_diagrams_block.liquid
|
262
|
+
- lib/metanorma/plugin/lutaml/liquid_templates/_packages.liquid
|
263
|
+
- lib/metanorma/plugin/lutaml/liquid_templates/_packages_class.liquid
|
264
|
+
- lib/metanorma/plugin/lutaml/liquid_templates/_packages_data_type.liquid
|
265
|
+
- lib/metanorma/plugin/lutaml/liquid_templates/_packages_enum.liquid
|
266
|
+
- lib/metanorma/plugin/lutaml/lutaml_diagram_base.rb
|
258
267
|
- lib/metanorma/plugin/lutaml/lutaml_diagram_block.rb
|
268
|
+
- lib/metanorma/plugin/lutaml/lutaml_diagram_block_macro.rb
|
259
269
|
- lib/metanorma/plugin/lutaml/lutaml_preprocessor.rb
|
260
270
|
- lib/metanorma/plugin/lutaml/lutaml_uml_attributes_table_preprocessor.rb
|
271
|
+
- lib/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor.rb
|
261
272
|
- lib/metanorma/plugin/lutaml/utils.rb
|
262
273
|
- lib/metanorma/plugin/lutaml/version.rb
|
263
274
|
- metanorma-plugin-lutaml.gemspec
|