metanorma-plugin-lutaml 0.7.38 → 0.7.39
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 +5 -1
- data/.gitignore +2 -1
- data/.rubocop.yml +11 -2
- data/.rubocop_todo.yml +238 -0
- data/CLAUDE.md +78 -0
- data/Gemfile +8 -12
- data/Gemfile.devel +2 -0
- data/Rakefile +17 -0
- data/docs/usages/express.adoc +55 -0
- data/lib/metanorma/plugin/lutaml/base_structured_text_preprocessor.rb +5 -5
- data/lib/metanorma/plugin/lutaml/data2_text_preprocessor.rb +1 -0
- data/lib/metanorma/plugin/lutaml/express_remarks_decorator.rb +1 -1
- data/lib/metanorma/plugin/lutaml/json2_text_preprocessor.rb +1 -0
- data/lib/metanorma/plugin/lutaml/liquid/multiply_local_file_system.rb +2 -2
- data/lib/metanorma/plugin/lutaml/liquid_templates/_klass_table.liquid +6 -6
- data/lib/metanorma/plugin/lutaml/lutaml_ea_diagram_block_macro.rb +1 -1
- data/lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb +125 -15
- data/lib/metanorma/plugin/lutaml/lutaml_ea_xmi_preprocessor.rb +2 -2
- data/lib/metanorma/plugin/lutaml/lutaml_enum_table_block_macro.rb +1 -3
- data/lib/metanorma/plugin/lutaml/lutaml_gml_dictionary_block.rb +1 -0
- data/lib/metanorma/plugin/lutaml/lutaml_klass_table_block_macro.rb +2 -3
- data/lib/metanorma/plugin/lutaml/lutaml_preprocessor.rb +4 -5
- data/lib/metanorma/plugin/lutaml/lutaml_table_inline_macro.rb +1 -0
- data/lib/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor.rb +2 -2
- data/lib/metanorma/plugin/lutaml/lutaml_xmi_uml_preprocessor.rb +2 -2
- data/lib/metanorma/plugin/lutaml/source_extractor.rb +3 -3
- data/lib/metanorma/plugin/lutaml/utils.rb +52 -8
- data/lib/metanorma/plugin/lutaml/version.rb +1 -1
- data/lib/metanorma/plugin/lutaml/yaml2_text_preprocessor.rb +1 -0
- data/metanorma-plugin-lutaml.gemspec +4 -4
- metadata +25 -11
- data/.hound.yml +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ad20f1059d6fb886f3b8f8e209a5eb2053bd84cf197b4df0a6f28c32ceb05b76
|
|
4
|
+
data.tar.gz: ea0e8cdfdc56ada195921fbefff8256f67e702dba4685a001ba22051031bfc74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a72753313c88e6a7bc1388c764fe2f315faf4ff7447690dd5d6a8851f5fe15285d1b53629ff6c79db3ba53ec20e7cdb9d05242e1ef2b5e9fe7b6e69076cedad0
|
|
7
|
+
data.tar.gz: 98fbf0c36b867aae35acf710853ea74506bfc7835e8ed4368e9acedc84fe9f622f2cc44aae66fabc75bd49c9242487027d94239cec948972cf0e6117941e1ac2
|
data/.github/workflows/rake.yml
CHANGED
|
@@ -10,6 +10,10 @@ on:
|
|
|
10
10
|
|
|
11
11
|
jobs:
|
|
12
12
|
rake:
|
|
13
|
-
uses: metanorma/ci/.github/workflows/
|
|
13
|
+
uses: metanorma/ci/.github/workflows/generic-rake.yml@main
|
|
14
|
+
with:
|
|
15
|
+
setup-tools: graphviz
|
|
16
|
+
submodules: true
|
|
17
|
+
choco-cache: true
|
|
14
18
|
secrets:
|
|
15
19
|
pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
# Auto-generated by Cimas: Do not edit it manually!
|
|
2
2
|
# See https://github.com/metanorma/cimas
|
|
3
3
|
inherit_from:
|
|
4
|
-
- https://raw.githubusercontent.com/riboseinc/oss-guides/
|
|
4
|
+
- https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml
|
|
5
|
+
- .rubocop_todo.yml
|
|
6
|
+
|
|
7
|
+
inherit_mode:
|
|
8
|
+
merge:
|
|
9
|
+
- Exclude
|
|
5
10
|
|
|
6
11
|
# local repo-specific modifications
|
|
7
12
|
# ...
|
|
13
|
+
plugins:
|
|
14
|
+
- rubocop-rspec
|
|
15
|
+
- rubocop-performance
|
|
16
|
+
- rubocop-rake
|
|
8
17
|
|
|
9
18
|
AllCops:
|
|
10
|
-
TargetRubyVersion:
|
|
19
|
+
TargetRubyVersion: 3.0
|
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2026-05-12 14:53:16 UTC using RuboCop version 1.86.1.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 1
|
|
10
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
11
|
+
Gemspec/RequireMFA:
|
|
12
|
+
Exclude:
|
|
13
|
+
- 'metanorma-plugin-lutaml.gemspec'
|
|
14
|
+
|
|
15
|
+
# Offense count: 1
|
|
16
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
17
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
18
|
+
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
19
|
+
Layout/ArgumentAlignment:
|
|
20
|
+
Exclude:
|
|
21
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_klass_table_block_macro.rb'
|
|
22
|
+
|
|
23
|
+
# Offense count: 1
|
|
24
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
25
|
+
Layout/ClosingParenthesisIndentation:
|
|
26
|
+
Exclude:
|
|
27
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
28
|
+
|
|
29
|
+
# Offense count: 1
|
|
30
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
31
|
+
Layout/EmptyLines:
|
|
32
|
+
Exclude:
|
|
33
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
34
|
+
|
|
35
|
+
# Offense count: 1
|
|
36
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
37
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
38
|
+
# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses
|
|
39
|
+
Layout/FirstArgumentIndentation:
|
|
40
|
+
Exclude:
|
|
41
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
42
|
+
|
|
43
|
+
# Offense count: 9
|
|
44
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
45
|
+
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
46
|
+
# URISchemes: http, https
|
|
47
|
+
Layout/LineLength:
|
|
48
|
+
Exclude:
|
|
49
|
+
- 'Rakefile'
|
|
50
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
51
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_klass_table_block_macro.rb'
|
|
52
|
+
- 'lib/metanorma/plugin/lutaml/utils.rb'
|
|
53
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor_spec.rb'
|
|
54
|
+
- 'spec/support/shared_examples/structured_data_2_text_preprocessor.rb'
|
|
55
|
+
|
|
56
|
+
# Offense count: 1
|
|
57
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
58
|
+
# Configuration parameters: EnforcedStyle.
|
|
59
|
+
# SupportedStyles: symmetrical, new_line, same_line
|
|
60
|
+
Layout/MultilineMethodCallBraceLayout:
|
|
61
|
+
Exclude:
|
|
62
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
63
|
+
|
|
64
|
+
# Offense count: 3
|
|
65
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
66
|
+
# Configuration parameters: AllowInHeredoc.
|
|
67
|
+
Layout/TrailingWhitespace:
|
|
68
|
+
Exclude:
|
|
69
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
70
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_klass_table_block_macro.rb'
|
|
71
|
+
|
|
72
|
+
# Offense count: 1
|
|
73
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
74
|
+
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
|
|
75
|
+
# NotImplementedExceptions: NotImplementedError
|
|
76
|
+
Lint/UnusedMethodArgument:
|
|
77
|
+
Exclude:
|
|
78
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
79
|
+
|
|
80
|
+
# Offense count: 1
|
|
81
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
82
|
+
Metrics/AbcSize:
|
|
83
|
+
Exclude:
|
|
84
|
+
- 'lib/metanorma/plugin/lutaml/utils.rb'
|
|
85
|
+
|
|
86
|
+
# Offense count: 2
|
|
87
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
88
|
+
Metrics/CyclomaticComplexity:
|
|
89
|
+
Exclude:
|
|
90
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
91
|
+
- 'lib/metanorma/plugin/lutaml/utils.rb'
|
|
92
|
+
|
|
93
|
+
# Offense count: 3
|
|
94
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
95
|
+
Metrics/MethodLength:
|
|
96
|
+
Max: 19
|
|
97
|
+
|
|
98
|
+
# Offense count: 2
|
|
99
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
100
|
+
Metrics/PerceivedComplexity:
|
|
101
|
+
Exclude:
|
|
102
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
103
|
+
- 'lib/metanorma/plugin/lutaml/utils.rb'
|
|
104
|
+
|
|
105
|
+
# Offense count: 1
|
|
106
|
+
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
|
107
|
+
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
|
|
108
|
+
Naming/MethodParameterName:
|
|
109
|
+
Exclude:
|
|
110
|
+
- 'lib/metanorma/plugin/lutaml/utils.rb'
|
|
111
|
+
|
|
112
|
+
# Offense count: 2
|
|
113
|
+
# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
|
|
114
|
+
# AllowedMethods: call
|
|
115
|
+
# WaywardPredicates: infinite?, nonzero?
|
|
116
|
+
Naming/PredicateMethod:
|
|
117
|
+
Exclude:
|
|
118
|
+
- 'lib/metanorma/plugin/lutaml/asciidoctor/preprocessor.rb'
|
|
119
|
+
- 'lib/metanorma/plugin/lutaml/liquid/custom_filters/file_exist.rb'
|
|
120
|
+
|
|
121
|
+
# Offense count: 1
|
|
122
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
123
|
+
Performance/AncestorsInclude:
|
|
124
|
+
Exclude:
|
|
125
|
+
- 'lib/metanorma/plugin/lutaml/utils.rb'
|
|
126
|
+
|
|
127
|
+
# Offense count: 86
|
|
128
|
+
# Configuration parameters: Prefixes, AllowedPatterns.
|
|
129
|
+
# Prefixes: when, with, without
|
|
130
|
+
RSpec/ContextWording:
|
|
131
|
+
Exclude:
|
|
132
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_ea_xmi_preprocessor_spec.rb'
|
|
133
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_enum_table_block_macro_spec.rb'
|
|
134
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_gml_dictionary_block_macro_spec.rb'
|
|
135
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_gml_dictionary_block_spec.rb'
|
|
136
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_klass_table_block_macro_spec.rb'
|
|
137
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_text_preprocessor_spec.rb'
|
|
138
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor_spec.rb'
|
|
139
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_xmi_index_spec.rb'
|
|
140
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_xmi_uml_preprocessor_spec.rb'
|
|
141
|
+
- 'spec/metanorma/plugin/lutaml/macros_data2text_spec.rb'
|
|
142
|
+
- 'spec/support/shared_examples/structured_data_2_text_preprocessor.rb'
|
|
143
|
+
|
|
144
|
+
# Offense count: 37
|
|
145
|
+
# Configuration parameters: CountAsOne.
|
|
146
|
+
RSpec/ExampleLength:
|
|
147
|
+
Max: 26
|
|
148
|
+
|
|
149
|
+
# Offense count: 13
|
|
150
|
+
# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns.
|
|
151
|
+
RSpec/IndexedLet:
|
|
152
|
+
Exclude:
|
|
153
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_ea_diagram_block_macro_spec.rb'
|
|
154
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_xmi_index_spec.rb'
|
|
155
|
+
- 'spec/metanorma/plugin/lutaml/macros_data2text_spec.rb'
|
|
156
|
+
- 'spec/support/shared_examples/structured_data_2_text_preprocessor.rb'
|
|
157
|
+
|
|
158
|
+
# Offense count: 4
|
|
159
|
+
# Configuration parameters: AssignmentOnly.
|
|
160
|
+
RSpec/InstanceVariable:
|
|
161
|
+
Exclude:
|
|
162
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_express_preprocessor_spec.rb'
|
|
163
|
+
|
|
164
|
+
# Offense count: 44
|
|
165
|
+
RSpec/LeakyLocalVariable:
|
|
166
|
+
Exclude:
|
|
167
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_ea_xmi_preprocessor_spec.rb'
|
|
168
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_klass_table_block_macro_spec.rb'
|
|
169
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor_spec.rb'
|
|
170
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_xmi_index_spec.rb'
|
|
171
|
+
|
|
172
|
+
# Offense count: 14
|
|
173
|
+
RSpec/MultipleExpectations:
|
|
174
|
+
Max: 6
|
|
175
|
+
|
|
176
|
+
# Offense count: 9
|
|
177
|
+
# Configuration parameters: AllowSubject.
|
|
178
|
+
RSpec/MultipleMemoizedHelpers:
|
|
179
|
+
Max: 8
|
|
180
|
+
|
|
181
|
+
# Offense count: 41
|
|
182
|
+
# Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
|
|
183
|
+
# SupportedStyles: always, named_only
|
|
184
|
+
RSpec/NamedSubject:
|
|
185
|
+
Exclude:
|
|
186
|
+
- 'spec/metanorma/plugin/lutaml/config/root_spec.rb'
|
|
187
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_ea_xmi_preprocessor_spec.rb'
|
|
188
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_gml_dictionary_block_macro_spec.rb'
|
|
189
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_gml_dictionary_block_spec.rb'
|
|
190
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor_spec.rb'
|
|
191
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_xmi_index_spec.rb'
|
|
192
|
+
- 'spec/metanorma/plugin/lutaml/source_extractor_spec.rb'
|
|
193
|
+
|
|
194
|
+
# Offense count: 117
|
|
195
|
+
# Configuration parameters: AllowedGroups.
|
|
196
|
+
RSpec/NestedGroups:
|
|
197
|
+
Max: 7
|
|
198
|
+
|
|
199
|
+
# Offense count: 1
|
|
200
|
+
RSpec/PendingWithoutReason:
|
|
201
|
+
Exclude:
|
|
202
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_text_preprocessor_spec.rb'
|
|
203
|
+
|
|
204
|
+
# Offense count: 2
|
|
205
|
+
RSpec/RepeatedExampleGroupBody:
|
|
206
|
+
Exclude:
|
|
207
|
+
- 'spec/metanorma/plugin/lutaml/macros_yaml2text_spec.rb'
|
|
208
|
+
|
|
209
|
+
# Offense count: 6
|
|
210
|
+
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
|
|
211
|
+
# SupportedInflectors: default, active_support
|
|
212
|
+
RSpec/SpecFilePathFormat:
|
|
213
|
+
Exclude:
|
|
214
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_express_preprocessor_spec.rb'
|
|
215
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_text_preprocessor_spec.rb'
|
|
216
|
+
- 'spec/metanorma/plugin/lutaml/lutaml_xmi_index_spec.rb'
|
|
217
|
+
- 'spec/metanorma/plugin/lutaml/macros_data2text_spec.rb'
|
|
218
|
+
- 'spec/metanorma/plugin/lutaml/macros_json2text_spec.rb'
|
|
219
|
+
- 'spec/metanorma/plugin/lutaml/macros_yaml2text_spec.rb'
|
|
220
|
+
|
|
221
|
+
# Offense count: 1
|
|
222
|
+
RSpec/SubjectDeclaration:
|
|
223
|
+
Exclude:
|
|
224
|
+
- 'spec/metanorma/plugin/lutaml/config/root_spec.rb'
|
|
225
|
+
|
|
226
|
+
# Offense count: 1
|
|
227
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
228
|
+
Style/RedundantBegin:
|
|
229
|
+
Exclude:
|
|
230
|
+
- 'lib/metanorma/plugin/lutaml/lutaml_ea_xmi_base.rb'
|
|
231
|
+
|
|
232
|
+
# Offense count: 1
|
|
233
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
234
|
+
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
|
|
235
|
+
# SupportedStyles: single_quotes, double_quotes
|
|
236
|
+
Style/StringLiterals:
|
|
237
|
+
Exclude:
|
|
238
|
+
- 'metanorma-plugin-lutaml.gemspec'
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
`metanorma-plugin-lutaml` is a Ruby gem that provides Asciidoctor extensions for the Metanorma document publishing system. It enables Metanorma documents to embed and render data models from multiple formats: EXPRESS (ISO 10303), LutaML DSL, Enterprise Architect XMI, GML Dictionaries, JSON, and YAML.
|
|
8
|
+
|
|
9
|
+
## Build and Test Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install dependencies
|
|
13
|
+
bundle install
|
|
14
|
+
|
|
15
|
+
# Run all tests
|
|
16
|
+
bundle exec rake
|
|
17
|
+
|
|
18
|
+
# Run a single test file
|
|
19
|
+
bundle exec rspec spec/metanorma/plugin/lutaml/lutaml_preprocessor_spec.rb
|
|
20
|
+
|
|
21
|
+
# Run a specific test by line number
|
|
22
|
+
bundle exec rspec spec/metanorma/plugin/lutaml/lutaml_preprocessor_spec.rb:42
|
|
23
|
+
|
|
24
|
+
# Lint
|
|
25
|
+
bundle exec rubocop
|
|
26
|
+
|
|
27
|
+
# Build the gem
|
|
28
|
+
bundle exec rake build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Architecture
|
|
32
|
+
|
|
33
|
+
### Asciidoctor Extension Pattern
|
|
34
|
+
|
|
35
|
+
All extensions follow the Asciidoctor extension API. The two main extension types are:
|
|
36
|
+
|
|
37
|
+
- **Preprocessors** (`::Asciidoctor::Extensions::Preprocessor`): Process document text before rendering. They read input lines, detect custom block syntax (e.g., `[lutaml_express, ...]`), and replace them with rendered content via Liquid templates.
|
|
38
|
+
- **Block/BlockMacro/InlineMacro extensions**: Handle `lutaml_diagram`, `lutaml_ea_diagram`, `lutaml_gml_dictionary`, `lutaml_klass_table`, `lutaml_enum_table`, `lutaml_table`, `lutaml_figure` — these are Asciidoctor block-level or inline macros.
|
|
39
|
+
|
|
40
|
+
### Preprocessor Inheritance Hierarchy
|
|
41
|
+
|
|
42
|
+
- `LutamlPreprocessor` — handles `[lutaml]`, `[lutaml_express]`, `[lutaml_express_liquid]` blocks. Parses EXPRESS files via the `lutaml`/`expressir` gems, builds Liquid contexts, and renders templates.
|
|
43
|
+
- `LutamlUmlDatamodelDescriptionPreprocessor` and `LutamlEaXmiPreprocessor` — both include `LutamlEaXmiBase`, which handles XMI parsing via `lutaml` gem and renders using bundled Liquid templates.
|
|
44
|
+
- `LutamlXmiUmlPreprocessor` — another XMI-based preprocessor with its own macro regex.
|
|
45
|
+
- `BaseStructuredTextPreprocessor` — base for `[yaml2text]`, `[json2text]`, `[data2text]` blocks. Its subclasses (`Yaml2TextPreprocessor`, `Json2TextPreprocessor`, `Data2TextPreprocessor`) differ only in how they load content (YAML vs JSON vs auto-detect). The `Content` module provides the actual parsing logic.
|
|
46
|
+
|
|
47
|
+
### Key Shared Modules
|
|
48
|
+
|
|
49
|
+
- `Utils` (`lib/metanorma/plugin/lutaml/utils.rb`) — shared helpers: file path resolution relative to document, Liquid template rendering with custom environment/filters, EXPRESS repository loading with caching, parsing document-level `:lutaml-express-index:` attributes.
|
|
50
|
+
- `LutamlEaXmiBase` — shared logic for XMI-based preprocessors: XMI document loading/caching, config YAML parsing, package filtering/sorting, Liquid context building, nested macro collection.
|
|
51
|
+
- `LutamlDiagramBase` — shared logic for diagram block extensions: parses LutaML DSL, renders via Graphviz to PNG.
|
|
52
|
+
- `LutamlGmlDictionaryBase` — parses GML XML via `ogc-gml` gem, renders via Liquid.
|
|
53
|
+
- `SourceExtractor` — scans document lines for anchors (`[[id]]`, `[#id]`, `[id=...]`) and `include::`/`embed::` directives, extracting source blocks into `document.attributes["source_blocks"]` for later use by preprocessors.
|
|
54
|
+
|
|
55
|
+
### Liquid Template System
|
|
56
|
+
|
|
57
|
+
The plugin uses Liquid templates extensively. Key components:
|
|
58
|
+
|
|
59
|
+
- **Custom Liquid environment** with registered `keyiterator` tag and custom filters (`html2adoc`, `values`, `replace_regex`, `loadfile`, `file_exist`).
|
|
60
|
+
- **`Liquid::LocalFileSystem`** subclass (`multiply_local_file_system.rb`) resolves includes from multiple paths.
|
|
61
|
+
- **Liquid Drops** (`liquid_drops/`) wrap domain objects (e.g., `GmlDictionaryDrop`) for template rendering.
|
|
62
|
+
- **Bundled templates** live in `lib/metanorma/plugin/lutaml/liquid_templates/` for XMI/EA rendering.
|
|
63
|
+
|
|
64
|
+
### Config System
|
|
65
|
+
|
|
66
|
+
The `Config` module (`lib/metanorma/plugin/lutaml/config.rb`) uses `lutaml/model` (LutaML::Model) to define config schemas: `Root`, `Package`, `Guidance`, `GuidanceKlass`, `GuidanceAttribute`. Config YAML files control which packages/entities to render from XMI documents.
|
|
67
|
+
|
|
68
|
+
### Testing Pattern
|
|
69
|
+
|
|
70
|
+
Tests use `metanorma-standoc` as the backend. The spec helper registers all extensions with Asciidoctor, then tests call `metanorma_convert(input)` which converts AsciiDoc input through the pipeline and compares XML output. Test fixtures (`.exp`, `.lutaml`, `.xmi`, `.xml`, `.liquid` files) are in `spec/fixtures/lutaml/` and `spec/assets/lutaml/`.
|
|
71
|
+
|
|
72
|
+
## Key Dependencies
|
|
73
|
+
|
|
74
|
+
- `lutaml` — core LutaML parser/model library (EXPRESS, UML, XMI formats)
|
|
75
|
+
- `expressir` — EXPRESS schema parser
|
|
76
|
+
- `ogc-gml` — OGC GML dictionary parser
|
|
77
|
+
- `liquid` — template rendering engine
|
|
78
|
+
- `asciidoctor` — document processing framework
|
data/Gemfile
CHANGED
|
@@ -12,18 +12,14 @@ rescue StandardError
|
|
|
12
12
|
nil
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
gem "byebug"
|
|
16
|
-
gem "debug"
|
|
17
|
-
gem "equivalent-xml"
|
|
18
|
-
gem "metanorma"
|
|
19
|
-
gem "metanorma-standoc", "~> 3.0.8"
|
|
20
15
|
gem "rake", "~> 13"
|
|
21
|
-
gem "rspec"
|
|
16
|
+
gem "rspec"
|
|
22
17
|
gem "rspec-html-matchers"
|
|
23
|
-
gem "rubocop"
|
|
24
|
-
gem "rubocop-performance"
|
|
25
|
-
gem "
|
|
26
|
-
gem "
|
|
27
|
-
gem "
|
|
18
|
+
gem "rubocop"
|
|
19
|
+
gem "rubocop-performance"
|
|
20
|
+
gem "rubocop-rake"
|
|
21
|
+
gem "rubocop-rspec"
|
|
22
|
+
gem "simplecov"
|
|
23
|
+
gem "timecop"
|
|
24
|
+
gem "vcr"
|
|
28
25
|
gem "webmock"
|
|
29
|
-
gem "xml-c14n"
|
data/Gemfile.devel
ADDED
data/Rakefile
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
2
|
require "rspec/core/rake_task"
|
|
3
3
|
|
|
4
|
+
XMI_SPEC_FILES = %w[
|
|
5
|
+
spec/metanorma/plugin/lutaml/lutaml_ea_xmi_preprocessor_spec.rb
|
|
6
|
+
spec/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor_spec.rb
|
|
7
|
+
spec/metanorma/plugin/lutaml/lutaml_klass_table_block_macro_spec.rb
|
|
8
|
+
spec/metanorma/plugin/lutaml/lutaml_enum_table_block_macro_spec.rb
|
|
9
|
+
spec/metanorma/plugin/lutaml/lutaml_xmi_index_spec.rb
|
|
10
|
+
spec/metanorma/plugin/lutaml/lutaml_xmi_uml_preprocessor_spec.rb
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
4
13
|
RSpec::Core::RakeTask.new(:spec)
|
|
5
14
|
|
|
15
|
+
RSpec::Core::RakeTask.new(:fast) do |t|
|
|
16
|
+
t.pattern = FileList["spec/**/*_spec.rb"] - XMI_SPEC_FILES
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
RSpec::Core::RakeTask.new(:xmi) do |t|
|
|
20
|
+
t.pattern = FileList[XMI_SPEC_FILES]
|
|
21
|
+
end
|
|
22
|
+
|
|
6
23
|
task default: :spec
|
data/docs/usages/express.adoc
CHANGED
|
@@ -297,3 +297,58 @@ from the paths other than the location of the document.
|
|
|
297
297
|
The resulting block adds the `include_path` to the Liquid renderer. The path is
|
|
298
298
|
resolved based on the location of the document. You can add multiple paths by
|
|
299
299
|
separating them with commas.
|
|
300
|
+
|
|
301
|
+
=== Using `loadfile` filter in templates
|
|
302
|
+
|
|
303
|
+
This functionality allows `[lutaml_express_liquid]` blocks to load data from
|
|
304
|
+
a yaml or json file from the specified path.
|
|
305
|
+
|
|
306
|
+
For example, you have the Metanorma document file (.adoc) with the following
|
|
307
|
+
content:
|
|
308
|
+
|
|
309
|
+
[source,adoc]
|
|
310
|
+
-----
|
|
311
|
+
:lutaml-express-index: all_schemas; ../schemas_all.yaml;
|
|
312
|
+
|
|
313
|
+
[lutaml_express_liquid,all_schemas,context,config_yaml=schemas.yaml,include_path=../templates]
|
|
314
|
+
---
|
|
315
|
+
{% assign all_schemas = repo.schemas %}
|
|
316
|
+
{% render "templates/resources/schema" for ordered_schemas as schema %}
|
|
317
|
+
...
|
|
318
|
+
----
|
|
319
|
+
-----
|
|
320
|
+
|
|
321
|
+
You have the liquid template file
|
|
322
|
+
`templates/resources/schema.liquid` with the following content:
|
|
323
|
+
|
|
324
|
+
[source,liquid]
|
|
325
|
+
-----
|
|
326
|
+
{% assign data = "my_file.yaml" | loadfile: "." %}
|
|
327
|
+
This is {{ data.shape }} with color {{ data.color }}.
|
|
328
|
+
-----
|
|
329
|
+
|
|
330
|
+
Where:
|
|
331
|
+
|
|
332
|
+
* `loadfile:` is a liquid filter that loads the file content based on the path
|
|
333
|
+
`my_file.yaml` with argument `.`.
|
|
334
|
+
The argument is the path of the parent folder, which is the
|
|
335
|
+
current directory of the Metanorma document.
|
|
336
|
+
|
|
337
|
+
And the content of the yaml file `my_file.yaml` looks like:
|
|
338
|
+
|
|
339
|
+
[source,yaml]
|
|
340
|
+
----
|
|
341
|
+
---
|
|
342
|
+
shape: square
|
|
343
|
+
color: blue
|
|
344
|
+
corners: 4
|
|
345
|
+
----
|
|
346
|
+
|
|
347
|
+
Will render as:
|
|
348
|
+
[source,asciidoc]
|
|
349
|
+
----
|
|
350
|
+
...
|
|
351
|
+
This is square with color blue.
|
|
352
|
+
...
|
|
353
|
+
----
|
|
354
|
+
|
|
@@ -15,9 +15,9 @@ module Metanorma
|
|
|
15
15
|
::Asciidoctor::Extensions::Preprocessor
|
|
16
16
|
include Utils
|
|
17
17
|
|
|
18
|
-
BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}
|
|
19
|
-
BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z
|
|
20
|
-
LOAD_FILE_REGEXP = /{% assign (.*) = (.*) \| load_file %}
|
|
18
|
+
BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}/
|
|
19
|
+
BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z/
|
|
20
|
+
LOAD_FILE_REGEXP = /{% assign (.*) = (.*) \| load_file %}/
|
|
21
21
|
INCLUDE_PATH_OPTION = "include_path"
|
|
22
22
|
TEMPLATE_OPTION = "template"
|
|
23
23
|
|
|
@@ -63,7 +63,7 @@ module Metanorma
|
|
|
63
63
|
load_file_match = block_line.match(LOAD_FILE_REGEXP)
|
|
64
64
|
|
|
65
65
|
# Add parent folder as argument to loadfile filter
|
|
66
|
-
block_line = "{% assign #{load_file_match[1]} = "\
|
|
66
|
+
block_line = "{% assign #{load_file_match[1]} = " \
|
|
67
67
|
"#{load_file_match[2]} | loadfile: " \
|
|
68
68
|
"\"#{document.attributes['docdir']}\" %}"
|
|
69
69
|
end
|
|
@@ -106,7 +106,7 @@ module Metanorma
|
|
|
106
106
|
)
|
|
107
107
|
multiple_contexts
|
|
108
108
|
elsif block_match[1].start_with?("#")
|
|
109
|
-
anchor = block_match[1].split(",").first.strip[1
|
|
109
|
+
anchor = block_match[1].split(",").first.strip[1..]
|
|
110
110
|
{
|
|
111
111
|
block_match[1].split(",").last.strip =>
|
|
112
112
|
content_from_anchor(document, anchor),
|
|
@@ -52,8 +52,8 @@ module Metanorma
|
|
|
52
52
|
|
|
53
53
|
if result_path.nil?
|
|
54
54
|
raise ::Liquid::FileSystemError,
|
|
55
|
-
"No documents in template path:
|
|
56
|
-
"
|
|
55
|
+
"No documents in template path: " \
|
|
56
|
+
"#{File.expand_path(template_path)}"
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
unless roots.any? do |root|
|
|
@@ -22,7 +22,7 @@ h| Stereotype 2+| {{ stereotype }}
|
|
|
22
22
|
3+h| Inherited Properties
|
|
23
23
|
h| Property Name h| Property Type and Multiplicity h| Definition
|
|
24
24
|
{% for attr in root.inherited_props %}
|
|
25
|
-
{%-
|
|
25
|
+
{%- unless attr.has_association? -%}
|
|
26
26
|
{%- capture name_col -%}
|
|
27
27
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
28
28
|
({{ attr.gen_name }})
|
|
@@ -30,13 +30,13 @@ h| Property Name h| Property Type and Multiplicity h| Definition
|
|
|
30
30
|
| {{ name_col | newline_to_br }}
|
|
31
31
|
| {{ attr.type }} [{{ attr.cardinality.min }}..{{ attr.cardinality.max }}]
|
|
32
32
|
| {{ attr.definition }}
|
|
33
|
-
{%-
|
|
33
|
+
{%- endunless -%}
|
|
34
34
|
{% endfor %}
|
|
35
35
|
|
|
36
36
|
3+h| Self-defined Properties
|
|
37
37
|
h| Property Name h| Property Type and Multiplicity h| Definition
|
|
38
38
|
{% for attr in root.owned_props %}
|
|
39
|
-
{%-
|
|
39
|
+
{%- unless attr.has_association? -%}
|
|
40
40
|
{%- capture name_col -%}
|
|
41
41
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
42
42
|
({{ attr.gen_name }})
|
|
@@ -44,13 +44,13 @@ h| Property Name h| Property Type and Multiplicity h| Definition
|
|
|
44
44
|
| {{ name_col | newline_to_br }}
|
|
45
45
|
| {{ attr.type }} [{{ attr.cardinality.min }}..{{ attr.cardinality.max }}]
|
|
46
46
|
| {{ attr.definition }}
|
|
47
|
-
{%-
|
|
47
|
+
{%- endunless -%}
|
|
48
48
|
{% endfor %}
|
|
49
49
|
|
|
50
50
|
3+h| Properties Inherited from Association
|
|
51
51
|
h| Property Name h| Property Type and Multiplicity h| Definition
|
|
52
52
|
{% for attr in root.inherited_assoc_props %}
|
|
53
|
-
{%- if attr.has_association?
|
|
53
|
+
{%- if attr.has_association? -%}
|
|
54
54
|
{%- capture name_col -%}
|
|
55
55
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
56
56
|
({{ attr.gen_name }})
|
|
@@ -64,7 +64,7 @@ h| Property Name h| Property Type and Multiplicity h| Definition
|
|
|
64
64
|
3+h| Properties Defined in Association
|
|
65
65
|
h| Property Name h| Property Type and Multiplicity h| Definition
|
|
66
66
|
{% for attr in root.assoc_props %}
|
|
67
|
-
{%- if attr.has_association?
|
|
67
|
+
{%- if attr.has_association? -%}
|
|
68
68
|
{%- capture name_col -%}
|
|
69
69
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
70
70
|
({{ attr.gen_name }})
|
|
@@ -40,7 +40,7 @@ module Metanorma
|
|
|
40
40
|
(?<config_path>.+) # Capture config path
|
|
41
41
|
)? # End of optional group
|
|
42
42
|
$ # End of the pattern
|
|
43
|
-
}x
|
|
43
|
+
}x
|
|
44
44
|
|
|
45
45
|
# search document for block `lutaml_ea_xmi`
|
|
46
46
|
# or `lutaml_uml_datamodel_description`
|
|
@@ -63,16 +63,22 @@ module Metanorma
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
yaml_config.ea_extension&.each do |ea_extension_path|
|
|
66
|
-
# resolve paths of ea extensions based on the location of
|
|
67
|
-
# config yaml file
|
|
68
66
|
ea_extension_full_path = File.expand_path(
|
|
69
67
|
ea_extension_path, File.dirname(yaml_config_path)
|
|
70
68
|
)
|
|
71
|
-
Xmi::EaRoot.
|
|
69
|
+
unless Xmi::EaRoot.loaded_extensions.value?(ea_extension_full_path)
|
|
70
|
+
Xmi::EaRoot.load_extension(ea_extension_full_path)
|
|
71
|
+
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
guidance = get_guidance(document, yaml_config.guidance)
|
|
75
|
-
|
|
75
|
+
cache_key = [full_path, guidance]
|
|
76
|
+
|
|
77
|
+
@@lutaml_doc_cache ||= {}
|
|
78
|
+
result_document = @@lutaml_doc_cache[cache_key] ||= parse_result_document(
|
|
79
|
+
full_path, guidance
|
|
80
|
+
)
|
|
81
|
+
|
|
76
82
|
document.attributes["lutaml_xmi_cache"] ||= {}
|
|
77
83
|
document.attributes["lutaml_xmi_cache"][full_path] = result_document
|
|
78
84
|
result_document
|
|
@@ -203,8 +209,8 @@ module Metanorma
|
|
|
203
209
|
all_children_packages = lutaml_document.packages
|
|
204
210
|
.map(&:children_packages).flatten
|
|
205
211
|
package_flat_packages = lambda do |pks|
|
|
206
|
-
pks.
|
|
207
|
-
|
|
212
|
+
pks.to_h do |package|
|
|
213
|
+
[package.name, package.xmi_id]
|
|
208
214
|
end
|
|
209
215
|
end
|
|
210
216
|
children_pks = package_flat_packages.call(all_children_packages)
|
|
@@ -292,7 +298,7 @@ module Metanorma
|
|
|
292
298
|
def package_level(lutaml_document, level)
|
|
293
299
|
return lutaml_document if level <= 0
|
|
294
300
|
|
|
295
|
-
package_level(lutaml_document
|
|
301
|
+
package_level(lutaml_document.packages.first, level - 1)
|
|
296
302
|
end
|
|
297
303
|
|
|
298
304
|
def create_context_object( # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
@@ -307,12 +313,12 @@ module Metanorma
|
|
|
307
313
|
|
|
308
314
|
if options.packages.nil?
|
|
309
315
|
contexts[context_name]["render_nested_packages"] = true
|
|
310
|
-
contexts[context_name]["packages"] = root_package
|
|
316
|
+
contexts[context_name]["packages"] = root_package.packages
|
|
311
317
|
|
|
312
318
|
return contexts
|
|
313
319
|
end
|
|
314
320
|
|
|
315
|
-
all_packages = [root_package, *root_package
|
|
321
|
+
all_packages = [root_package, *root_package.children_packages]
|
|
316
322
|
contexts[context_name].merge!(
|
|
317
323
|
{
|
|
318
324
|
"packages" => sort_and_filter_out_packages(all_packages, options),
|
|
@@ -330,7 +336,7 @@ module Metanorma
|
|
|
330
336
|
)
|
|
331
337
|
contexts = {}
|
|
332
338
|
contexts[context_name] = {
|
|
333
|
-
"name" => root_package
|
|
339
|
+
"name" => root_package.name,
|
|
334
340
|
"root_packages" => [root_package],
|
|
335
341
|
"additional_context" => additional_context
|
|
336
342
|
.merge("external_classes" => options.external_classes),
|
|
@@ -346,7 +352,7 @@ module Metanorma
|
|
|
346
352
|
result = {}
|
|
347
353
|
packages = options.packages.reject { |p| p.send(key.to_sym).nil? }
|
|
348
354
|
packages.each do |p|
|
|
349
|
-
result[p.name] = p.send(key.to_sym).
|
|
355
|
+
result[p.name] = p.send(key.to_sym).to_h { |n| [n, true] }
|
|
350
356
|
end
|
|
351
357
|
result
|
|
352
358
|
end
|
|
@@ -370,7 +376,7 @@ module Metanorma
|
|
|
370
376
|
options.skip.each do |skip_package|
|
|
371
377
|
entity_regexp = config_entity_regexp(skip_package)
|
|
372
378
|
all_packages.delete_if do |package|
|
|
373
|
-
package
|
|
379
|
+
package.name =~ entity_regexp
|
|
374
380
|
end
|
|
375
381
|
end
|
|
376
382
|
|
|
@@ -381,10 +387,10 @@ module Metanorma
|
|
|
381
387
|
options.packages.each do |package|
|
|
382
388
|
entity_regexp = config_entity_regexp(package.name)
|
|
383
389
|
all_packages.each do |p|
|
|
384
|
-
if p
|
|
390
|
+
if p.name&.match?(entity_regexp)
|
|
385
391
|
result.push(p)
|
|
386
392
|
all_packages.delete_if do |nest_package|
|
|
387
|
-
nest_package
|
|
393
|
+
nest_package.name == p.name
|
|
388
394
|
end
|
|
389
395
|
end
|
|
390
396
|
end
|
|
@@ -473,6 +479,110 @@ module Metanorma
|
|
|
473
479
|
|
|
474
480
|
attrs["name"]
|
|
475
481
|
end
|
|
482
|
+
|
|
483
|
+
# The class methods `serialize_generalization_by_name` and
|
|
484
|
+
# `serialize_enumeration_by_name` were removed from
|
|
485
|
+
# `Lutaml::Xmi::Parsers::Xml` in lutaml 0.10. The replacements below
|
|
486
|
+
# rebuild the same shape of result by reusing the still-available
|
|
487
|
+
# path-aware finders on the parser instance and the new
|
|
488
|
+
# `XmiLookupService`, then bridging to the UML object that the
|
|
489
|
+
# current `KlassDrop` / `EnumDrop` constructors expect.
|
|
490
|
+
def serialize_klass_drop_by_name(xmi_path, name, document = nil,
|
|
491
|
+
guidance = nil)
|
|
492
|
+
parser, uml_doc = build_uml_document(xmi_path, document)
|
|
493
|
+
raw_klass = find_packaged_klass(parser.xmi_index, name)
|
|
494
|
+
warn "Class not found for name: #{name}" if raw_klass.nil?
|
|
495
|
+
klass = raw_klass && find_uml_node_by_xmi_id(
|
|
496
|
+
uml_doc, raw_klass.id, :classes
|
|
497
|
+
)
|
|
498
|
+
::Lutaml::Xmi::LiquidDrops::KlassDrop.new(
|
|
499
|
+
klass, guidance, build_drop_options(parser)
|
|
500
|
+
)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def serialize_enum_drop_by_name(xmi_path, name, document = nil)
|
|
504
|
+
parser, uml_doc = build_uml_document(xmi_path, document)
|
|
505
|
+
raw_enum = find_packaged_enum(parser.xmi_index, name)
|
|
506
|
+
warn "Enumeration not found for name: #{name}" if raw_enum.nil?
|
|
507
|
+
enum = raw_enum && find_uml_node_by_xmi_id(
|
|
508
|
+
uml_doc, raw_enum.id, :enums
|
|
509
|
+
)
|
|
510
|
+
::Lutaml::Xmi::LiquidDrops::EnumDrop.new(
|
|
511
|
+
enum, build_drop_options(parser)
|
|
512
|
+
)
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def build_uml_document(xmi_path, _document = nil)
|
|
516
|
+
@@uml_doc_cache ||= {}
|
|
517
|
+
if @@uml_doc_cache[xmi_path]
|
|
518
|
+
return @@uml_doc_cache[xmi_path]
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
xmi_model = ::Xmi::Sparx::Root.parse_xml(File.read(xmi_path))
|
|
522
|
+
parser = ::Lutaml::Xmi::Parsers::Xml.new
|
|
523
|
+
result = [parser, parser.parse(xmi_model)]
|
|
524
|
+
@@uml_doc_cache[xmi_path] = result
|
|
525
|
+
result
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def build_drop_options(parser)
|
|
529
|
+
lookup = ::Lutaml::Xmi::XmiLookupService.new(
|
|
530
|
+
parser.xmi_root_model, parser.id_name_mapping
|
|
531
|
+
)
|
|
532
|
+
{
|
|
533
|
+
xmi_root_model: parser.xmi_root_model,
|
|
534
|
+
id_name_mapping: parser.id_name_mapping,
|
|
535
|
+
lookup: lookup,
|
|
536
|
+
with_gen: true,
|
|
537
|
+
with_absolute_path: true,
|
|
538
|
+
}
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
def find_uml_node_by_xmi_id(container, xmi_id, collection)
|
|
542
|
+
found = container.public_send(collection)
|
|
543
|
+
.find { |node| node.xmi_id == xmi_id }
|
|
544
|
+
return found if found
|
|
545
|
+
|
|
546
|
+
container.packages.each do |pkg|
|
|
547
|
+
nested = find_uml_node_by_xmi_id(pkg, xmi_id, collection)
|
|
548
|
+
return nested if nested
|
|
549
|
+
end
|
|
550
|
+
nil
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
def find_packaged_klass(index, path)
|
|
554
|
+
segments = path.split("::")
|
|
555
|
+
if segments.one?
|
|
556
|
+
index.find_packaged_by_name_and_types(
|
|
557
|
+
path, ["uml:Class", "uml:AssociationClass"]
|
|
558
|
+
)
|
|
559
|
+
else
|
|
560
|
+
find_packaged_klass_by_path(index, segments)
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def find_packaged_klass_by_path(index, segments)
|
|
565
|
+
klass_name = segments.pop
|
|
566
|
+
klass = index.find_packaged_by_name_and_types(
|
|
567
|
+
klass_name, ["uml:Class", "uml:AssociationClass"]
|
|
568
|
+
)
|
|
569
|
+
return unless klass
|
|
570
|
+
|
|
571
|
+
# Verify the path by walking up the parent chain
|
|
572
|
+
current = klass
|
|
573
|
+
segments.reverse_each do |pkg_name|
|
|
574
|
+
parent = index.find_parent(current.id)
|
|
575
|
+
return unless parent && parent.name == pkg_name
|
|
576
|
+
|
|
577
|
+
current = parent
|
|
578
|
+
end
|
|
579
|
+
klass
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def find_packaged_enum(index, name)
|
|
583
|
+
index.packaged_elements_of_type("uml:Enumeration")
|
|
584
|
+
.find { |e| e.name == name }
|
|
585
|
+
end
|
|
476
586
|
end
|
|
477
587
|
end
|
|
478
588
|
end
|
|
@@ -20,12 +20,12 @@ module Metanorma
|
|
|
20
20
|
include LutamlEaXmiBase
|
|
21
21
|
|
|
22
22
|
MACRO_REGEXP =
|
|
23
|
-
/\[lutaml_ea_xmi,([^,]+),?(.+)?\]
|
|
23
|
+
/\[lutaml_ea_xmi,([^,]+),?(.+)?\]/
|
|
24
24
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
27
|
def parse_result_document(full_path, guidance)
|
|
28
|
-
::Lutaml::
|
|
28
|
+
::Lutaml::Xmi::Parsers::Xml.serialize_xmi_to_liquid(
|
|
29
29
|
File.new(full_path, encoding: "UTF-8"),
|
|
30
30
|
guidance,
|
|
31
31
|
)
|
|
@@ -23,9 +23,7 @@ module Metanorma
|
|
|
23
23
|
xmi_path = get_xmi_path(parent, target, attrs)
|
|
24
24
|
path = get_name_path(attrs)
|
|
25
25
|
|
|
26
|
-
enum =
|
|
27
|
-
xmi_path, path
|
|
28
|
-
)
|
|
26
|
+
enum = serialize_enum_drop_by_name(xmi_path, path, parent.document)
|
|
29
27
|
|
|
30
28
|
render_table(enum, CONTEXT_NAME, parent, attrs)
|
|
31
29
|
end
|
|
@@ -28,9 +28,8 @@ module Metanorma
|
|
|
28
28
|
guidance = get_guidance(parent.document, attrs["guidance"])
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
klass =
|
|
32
|
-
|
|
33
|
-
)
|
|
31
|
+
klass = serialize_klass_drop_by_name(xmi_path, path, parent.document,
|
|
32
|
+
guidance)
|
|
34
33
|
|
|
35
34
|
render_table(klass, CONTEXT_NAME, parent, attrs)
|
|
36
35
|
end
|
|
@@ -28,7 +28,7 @@ module Metanorma
|
|
|
28
28
|
(?<context_name>[^,]+)? # Optional context name
|
|
29
29
|
(?<options>,.*)? # Optional options
|
|
30
30
|
\] # Closing bracket
|
|
31
|
-
}x
|
|
31
|
+
}x
|
|
32
32
|
|
|
33
33
|
def process(document, reader) # rubocop:disable Metrics/MethodLength
|
|
34
34
|
r = Asciidoctor::PreprocessorNoIfdefsReader.new(document,
|
|
@@ -85,8 +85,8 @@ module Metanorma
|
|
|
85
85
|
index_names = block_header_match[:index_names].split(";").map(&:strip)
|
|
86
86
|
context_name = block_header_match[:context_name].strip
|
|
87
87
|
|
|
88
|
-
options = block_header_match[:options] &&
|
|
89
|
-
parse_options(block_header_match[:options].to_s.strip) || {}
|
|
88
|
+
options = (block_header_match[:options] &&
|
|
89
|
+
parse_options(block_header_match[:options].to_s.strip)) || {}
|
|
90
90
|
|
|
91
91
|
end_mark = input_lines.next
|
|
92
92
|
|
|
@@ -277,8 +277,7 @@ module Metanorma
|
|
|
277
277
|
options_string
|
|
278
278
|
.to_s
|
|
279
279
|
.scan(/,\s*([^=]+?)=(\s*[^,]+)/)
|
|
280
|
-
.
|
|
281
|
-
.to_h
|
|
280
|
+
.to_h { |elem| elem.map(&:strip) }
|
|
282
281
|
end
|
|
283
282
|
end
|
|
284
283
|
end
|
|
@@ -20,12 +20,12 @@ module Metanorma
|
|
|
20
20
|
include LutamlEaXmiBase
|
|
21
21
|
|
|
22
22
|
MACRO_REGEXP =
|
|
23
|
-
/\[lutaml_uml_datamodel_description,([^,]+),?(.+)?\]
|
|
23
|
+
/\[lutaml_uml_datamodel_description,([^,]+),?(.+)?\]/
|
|
24
24
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
27
|
def parse_result_document(full_path, guidance)
|
|
28
|
-
::Lutaml::
|
|
28
|
+
::Lutaml::Xmi::Parsers::Xml.serialize_xmi_to_liquid(
|
|
29
29
|
File.new(full_path, encoding: "UTF-8"),
|
|
30
30
|
guidance,
|
|
31
31
|
)
|
|
@@ -15,12 +15,12 @@ module Metanorma
|
|
|
15
15
|
class LutamlXmiUmlPreprocessor < ::Asciidoctor::Extensions::Preprocessor
|
|
16
16
|
include LutamlEaXmiBase
|
|
17
17
|
|
|
18
|
-
MACRO_REGEXP = /\[lutaml_xmi_uml,([^,]+),?(.+)?\]
|
|
18
|
+
MACRO_REGEXP = /\[lutaml_xmi_uml,([^,]+),?(.+)?\]/
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
22
|
def parse_result_document(full_path, _guidance)
|
|
23
|
-
::Lutaml::
|
|
23
|
+
::Lutaml::Xmi::Parsers::Xml.parse(
|
|
24
24
|
File.new(full_path, encoding: "UTF-8"),
|
|
25
25
|
)
|
|
26
26
|
end
|
|
@@ -10,17 +10,17 @@ module Metanorma
|
|
|
10
10
|
|
|
11
11
|
# example:
|
|
12
12
|
# - [[abc]]
|
|
13
|
-
ANCHOR_REGEX_1 = /^\[\[(?<id>[^\]]*)\]\]\s
|
|
13
|
+
ANCHOR_REGEX_1 = /^\[\[(?<id>[^\]]*)\]\]\s*$/
|
|
14
14
|
|
|
15
15
|
# examples:
|
|
16
16
|
# - [#abc]
|
|
17
17
|
# - [source#abc,ruby]
|
|
18
|
-
ANCHOR_REGEX_2 = /^\[[^#,]*#(?<id>[^,\]]*)[,\]]
|
|
18
|
+
ANCHOR_REGEX_2 = /^\[[^#,]*#(?<id>[^,\]]*)[,\]]/
|
|
19
19
|
|
|
20
20
|
# examples:
|
|
21
21
|
# - [id=abc]
|
|
22
22
|
# - [source,id="abc"]
|
|
23
|
-
ANCHOR_REGEX_3 = /^\[(?:.+,)?id=['"]?(?<id>[^,\]'"]*)['"]?[,\]]
|
|
23
|
+
ANCHOR_REGEX_3 = /^\[(?:.+,)?id=['"]?(?<id>[^,\]'"]*)['"]?[,\]]/
|
|
24
24
|
|
|
25
25
|
def initialize(document, input_lines)
|
|
26
26
|
@document = document
|
|
@@ -14,6 +14,24 @@ module Metanorma
|
|
|
14
14
|
module Lutaml
|
|
15
15
|
# Helpers for lutaml macros
|
|
16
16
|
module Utils
|
|
17
|
+
# Prepended to Liquid::Context to preserve the original exception
|
|
18
|
+
# that Liquid 5.x wraps as InternalError (discarding the cause).
|
|
19
|
+
module LiquidErrorCapturer
|
|
20
|
+
def handle_error(e, line_number = nil)
|
|
21
|
+
if e.is_a?(::Liquid::Error)
|
|
22
|
+
super
|
|
23
|
+
else
|
|
24
|
+
ie = ::Liquid::InternalError.new("internal")
|
|
25
|
+
ie.set_backtrace(e.backtrace)
|
|
26
|
+
ie.define_singleton_method(:original_error) { e }
|
|
27
|
+
ie.template_name ||= template_name
|
|
28
|
+
ie.line_number ||= line_number
|
|
29
|
+
errors.push(ie)
|
|
30
|
+
exception_renderer.call(ie).to_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
17
35
|
LUTAML_EXP_IDX_TAG = %r{
|
|
18
36
|
^:lutaml-express-index: # Start of the pattern
|
|
19
37
|
(?<index_name>.+?) # Capture index name
|
|
@@ -25,7 +43,7 @@ module Metanorma
|
|
|
25
43
|
(?<cache_path>.+) # Capture cache path
|
|
26
44
|
)? # End of optional group
|
|
27
45
|
$ # End of the pattern
|
|
28
|
-
}x
|
|
46
|
+
}x
|
|
29
47
|
|
|
30
48
|
module_function
|
|
31
49
|
|
|
@@ -42,6 +60,10 @@ module Metanorma
|
|
|
42
60
|
contexts:, document:,
|
|
43
61
|
template_string: nil, include_path: nil, template_path: nil
|
|
44
62
|
)
|
|
63
|
+
unless ::Liquid::Context <= LiquidErrorCapturer
|
|
64
|
+
::Liquid::Context.prepend(LiquidErrorCapturer)
|
|
65
|
+
end
|
|
66
|
+
|
|
45
67
|
# Allow includes for the template
|
|
46
68
|
include_paths = [
|
|
47
69
|
Utils.relative_file_path(document, ""),
|
|
@@ -85,16 +107,38 @@ module Metanorma
|
|
|
85
107
|
result
|
|
86
108
|
end
|
|
87
109
|
|
|
110
|
+
@seen_liquid_errors = Set.new
|
|
111
|
+
|
|
88
112
|
def notify_render_errors(_document, errors)
|
|
89
|
-
errors.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
113
|
+
return if errors.empty?
|
|
114
|
+
|
|
115
|
+
grouped = errors.group_by { |e| error_signature(e) }
|
|
116
|
+
grouped.each do |sig, errs|
|
|
117
|
+
total = errs.size
|
|
118
|
+
already_seen = @seen_liquid_errors.include?(sig)
|
|
119
|
+
@seen_liquid_errors << sig
|
|
120
|
+
next if already_seen
|
|
121
|
+
|
|
122
|
+
err = errs.first
|
|
123
|
+
backtrace = err.backtrace&.first(3)&.join("\n").to_s
|
|
124
|
+
count_label = total > 1 ? " (#{total}x)" : ""
|
|
125
|
+
parts = ["[metanorma-plugin-lutaml] Liquid render error#{count_label}:"]
|
|
126
|
+
parts << " #{err.class}: #{err.message}"
|
|
127
|
+
if err.respond_to?(:original_error) && err.original_error
|
|
128
|
+
orig = err.original_error
|
|
129
|
+
parts << " Caused by: #{orig.class}: #{orig.message}"
|
|
130
|
+
end
|
|
131
|
+
parts << backtrace unless backtrace.empty?
|
|
132
|
+
::Metanorma::Util.log(parts.join("\n"), :error)
|
|
95
133
|
end
|
|
96
134
|
end
|
|
97
135
|
|
|
136
|
+
def error_signature(err)
|
|
137
|
+
orig = err.respond_to?(:original_error) && err.original_error
|
|
138
|
+
base = "#{err.class}: #{err.message}"
|
|
139
|
+
orig ? "#{base} (#{orig.class}: #{orig.message})" : base
|
|
140
|
+
end
|
|
141
|
+
|
|
98
142
|
def load_express_repositories( # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
99
143
|
path:, cache_path:, document:, force_read: false
|
|
100
144
|
)
|
|
@@ -166,7 +210,7 @@ module Metanorma
|
|
|
166
210
|
|
|
167
211
|
# TODO: Refactor this using Suma::SchemaConfig
|
|
168
212
|
def load_express_from_index(_document, path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
169
|
-
yaml_content = YAML.
|
|
213
|
+
yaml_content = YAML.safe_load_file(path)
|
|
170
214
|
schema_yaml_base_path = Pathname.new(File.dirname(path))
|
|
171
215
|
|
|
172
216
|
# If there is a global root path set, all subsequent paths are
|
|
@@ -29,12 +29,12 @@ Gem::Specification.new do |spec|
|
|
|
29
29
|
spec.required_ruby_version = ">= 2.7.0" # rubocop:disable Gemspec/RequiredRubyVersion
|
|
30
30
|
|
|
31
31
|
spec.add_dependency "asciidoctor"
|
|
32
|
-
spec.add_dependency "coradoc", "~> 1.1"
|
|
33
|
-
spec.add_dependency "expressir", "~> 2.
|
|
32
|
+
spec.add_dependency "coradoc", "~> 1.1.8"
|
|
33
|
+
spec.add_dependency "expressir", "~> 2.3", ">= 2.3.4"
|
|
34
34
|
spec.add_dependency "isodoc"
|
|
35
35
|
spec.add_dependency "liquid"
|
|
36
|
-
spec.add_dependency "lutaml", "~> 0.
|
|
37
|
-
spec.add_dependency "ogc-gml", "~>1.
|
|
36
|
+
spec.add_dependency "lutaml", "~> 0.10", ">= 0.10.12"
|
|
37
|
+
spec.add_dependency "ogc-gml", "~> 1.1"
|
|
38
38
|
spec.add_dependency "relaton-cli"
|
|
39
39
|
|
|
40
40
|
spec.metadata["rubygems_mfa_required"] = "false"
|
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.7.
|
|
4
|
+
version: 0.7.39
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: asciidoctor
|
|
@@ -30,28 +30,34 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 1.1.8
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 1.1.8
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: expressir
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '2.
|
|
47
|
+
version: '2.3'
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: 2.3.4
|
|
48
51
|
type: :runtime
|
|
49
52
|
prerelease: false
|
|
50
53
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
54
|
requirements:
|
|
52
55
|
- - "~>"
|
|
53
56
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '2.
|
|
57
|
+
version: '2.3'
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 2.3.4
|
|
55
61
|
- !ruby/object:Gem::Dependency
|
|
56
62
|
name: isodoc
|
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,28 +92,34 @@ dependencies:
|
|
|
86
92
|
requirements:
|
|
87
93
|
- - "~>"
|
|
88
94
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '0.
|
|
95
|
+
version: '0.10'
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: 0.10.12
|
|
90
99
|
type: :runtime
|
|
91
100
|
prerelease: false
|
|
92
101
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
102
|
requirements:
|
|
94
103
|
- - "~>"
|
|
95
104
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '0.
|
|
105
|
+
version: '0.10'
|
|
106
|
+
- - ">="
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: 0.10.12
|
|
97
109
|
- !ruby/object:Gem::Dependency
|
|
98
110
|
name: ogc-gml
|
|
99
111
|
requirement: !ruby/object:Gem::Requirement
|
|
100
112
|
requirements:
|
|
101
113
|
- - "~>"
|
|
102
114
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: 1.
|
|
115
|
+
version: '1.1'
|
|
104
116
|
type: :runtime
|
|
105
117
|
prerelease: false
|
|
106
118
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
119
|
requirements:
|
|
108
120
|
- - "~>"
|
|
109
121
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: 1.
|
|
122
|
+
version: '1.1'
|
|
111
123
|
- !ruby/object:Gem::Dependency
|
|
112
124
|
name: relaton-cli
|
|
113
125
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -132,10 +144,12 @@ files:
|
|
|
132
144
|
- ".github/workflows/rake.yml"
|
|
133
145
|
- ".github/workflows/release.yml"
|
|
134
146
|
- ".gitignore"
|
|
135
|
-
- ".hound.yml"
|
|
136
147
|
- ".rubocop.yml"
|
|
148
|
+
- ".rubocop_todo.yml"
|
|
149
|
+
- CLAUDE.md
|
|
137
150
|
- CODE_OF_CONDUCT.md
|
|
138
151
|
- Gemfile
|
|
152
|
+
- Gemfile.devel
|
|
139
153
|
- LICENSE
|
|
140
154
|
- README.adoc
|
|
141
155
|
- Rakefile
|