metanorma-plugin-lutaml 0.7.55.pre.alpha.2 → 0.7.55
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/lib/metanorma/plugin/lutaml/lutaml_data_preprocessor.rb +32 -7
- data/lib/metanorma/plugin/lutaml/version.rb +1 -1
- data/lib/metanorma/plugin/lutaml/xmi_context_builder.rb +113 -3
- data/lib/metanorma/plugin/lutaml/xmi_model_reference.rb +67 -0
- data/lib/metanorma/plugin/lutaml/xmi_renderer.rb +11 -5
- data/lib/metanorma/plugin/lutaml.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 608d678b19ef1c180dc4e244f98d329a8452d9c875469978b6d84247894c09d5
|
|
4
|
+
data.tar.gz: 64269c82a46666d5b50df626671d985917c6acb845708a8d527b6070a2da9e1e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9078948b7bd2f32a925871da5b6f51f54d6613a660c20dff4e0c19d1f72c77814f5877f02700926eb031b33dae6de3aae7bc09668a65581f8d9287db0d29a9ad
|
|
7
|
+
data.tar.gz: 10fa46ed3a240a00777df9f10621b1cdb5e5e35646958bf9b94b830ebacd8ca8b520dd367dcab4d9019a7042a19ec57270e8cea5d9cb63212ec957fdf0f8d6bf
|
|
@@ -18,6 +18,10 @@ module Metanorma
|
|
|
18
18
|
#
|
|
19
19
|
# [lutaml_data,<definitions.lml>,<context>]
|
|
20
20
|
#
|
|
21
|
+
# Options can be supplied as a JSON object:
|
|
22
|
+
#
|
|
23
|
+
# [lutaml_data,models.lml,store,options={"include_path":"templates"}]
|
|
24
|
+
#
|
|
21
25
|
class LutamlDataPreprocessor < BasePreprocessor
|
|
22
26
|
DATA_FILE_EXTENSIONS = %w[.lml .yaml .yml .json].freeze
|
|
23
27
|
DEFAULT_CONTEXT_NAME = "lutaml_data"
|
|
@@ -53,7 +57,7 @@ module Metanorma
|
|
|
53
57
|
return [line] unless block_header_match
|
|
54
58
|
|
|
55
59
|
definition_file, instance_file, context_name, options =
|
|
56
|
-
|
|
60
|
+
parse_header_logged(block_header_match)
|
|
57
61
|
|
|
58
62
|
end_mark = input_lines.next
|
|
59
63
|
lines = extract_block_lines(input_lines, end_mark)
|
|
@@ -68,10 +72,16 @@ module Metanorma
|
|
|
68
72
|
)
|
|
69
73
|
end
|
|
70
74
|
|
|
75
|
+
def parse_header_logged(match)
|
|
76
|
+
parse_header(match)
|
|
77
|
+
rescue StandardError => e
|
|
78
|
+
log_block_failure(e)
|
|
79
|
+
raise e
|
|
80
|
+
end
|
|
81
|
+
|
|
71
82
|
def parse_header(match)
|
|
72
83
|
definition_file = match[:definition_file].strip
|
|
73
|
-
segments = match[:rest]
|
|
74
|
-
options, segments = split_options(segments)
|
|
84
|
+
options, segments = split_options(match[:rest])
|
|
75
85
|
|
|
76
86
|
data_segments, name_segments = segments
|
|
77
87
|
.reject(&:empty?).partition { |segment| data_file?(segment) }
|
|
@@ -80,7 +90,18 @@ module Metanorma
|
|
|
80
90
|
name_segments.last || DEFAULT_CONTEXT_NAME, options]
|
|
81
91
|
end
|
|
82
92
|
|
|
83
|
-
def split_options(
|
|
93
|
+
def split_options(rest)
|
|
94
|
+
object_options = rest.match(/,\s*options=(\{.*\})\s*,?\s*\z/)
|
|
95
|
+
if object_options
|
|
96
|
+
require "json"
|
|
97
|
+
return [JSON.parse(object_options[1]),
|
|
98
|
+
rest[0...object_options.begin(0)].split(",").map(&:strip)]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
split_inline_options(rest.split(",").map(&:strip))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def split_inline_options(segments)
|
|
84
105
|
options_start = segments.index { |segment| segment.include?("=") }
|
|
85
106
|
return [{}, segments] unless options_start
|
|
86
107
|
|
|
@@ -105,12 +126,16 @@ module Metanorma
|
|
|
105
126
|
|
|
106
127
|
[render_liquid(lines, document, options, context_name, instances)]
|
|
107
128
|
rescue StandardError => e
|
|
129
|
+
log_block_failure(e)
|
|
130
|
+
raise e
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def log_block_failure(error)
|
|
108
134
|
::Metanorma::Util.log(
|
|
109
135
|
"[#{self.class.name}] Failed to parse LutaML data block: " \
|
|
110
|
-
"#{
|
|
136
|
+
"#{error.message}",
|
|
111
137
|
:error,
|
|
112
138
|
)
|
|
113
|
-
raise e
|
|
114
139
|
end
|
|
115
140
|
|
|
116
141
|
def render_liquid(lines, document, options, context_name, instances)
|
|
@@ -126,7 +151,7 @@ module Metanorma
|
|
|
126
151
|
case value
|
|
127
152
|
when Hash
|
|
128
153
|
value.transform_keys(&:to_s)
|
|
129
|
-
|
|
154
|
+
.transform_values { |v| deep_stringify(v) }
|
|
130
155
|
when Array
|
|
131
156
|
value.map { |v| deep_stringify(v) }
|
|
132
157
|
else
|
|
@@ -4,12 +4,16 @@ module Metanorma
|
|
|
4
4
|
module Plugin
|
|
5
5
|
module Lutaml
|
|
6
6
|
module XmiContextBuilder
|
|
7
|
+
MODEL_COLLECTIONS = %i[classes data_types].freeze
|
|
8
|
+
|
|
7
9
|
def create_context_object( # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
8
10
|
lutaml_document, additional_context, options,
|
|
9
11
|
context_name = "context"
|
|
10
12
|
)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
root_context = lutaml_document.to_liquid
|
|
14
|
+
root_package = package_level(
|
|
15
|
+
root_context, options.package_root_level || 1
|
|
16
|
+
)
|
|
13
17
|
contexts = create_default_context_object(
|
|
14
18
|
context_name, root_package, additional_context, options
|
|
15
19
|
)
|
|
@@ -17,6 +21,7 @@ module Metanorma
|
|
|
17
21
|
if options.packages.nil?
|
|
18
22
|
contexts[context_name]["render_nested_packages"] = true
|
|
19
23
|
contexts[context_name]["packages"] = root_package.packages
|
|
24
|
+
add_model_anchor_context(contexts[context_name], root_context)
|
|
20
25
|
|
|
21
26
|
return contexts
|
|
22
27
|
end
|
|
@@ -30,6 +35,7 @@ module Metanorma
|
|
|
30
35
|
"render_nested_packages" => !!options.render_nested_packages,
|
|
31
36
|
},
|
|
32
37
|
)
|
|
38
|
+
add_model_anchor_context(contexts[context_name], root_context)
|
|
33
39
|
|
|
34
40
|
contexts
|
|
35
41
|
end
|
|
@@ -42,13 +48,27 @@ module Metanorma
|
|
|
42
48
|
"name" => root_package.name,
|
|
43
49
|
"root_packages" => [root_package],
|
|
44
50
|
"additional_context" => additional_context
|
|
45
|
-
.merge(
|
|
51
|
+
.merge(
|
|
52
|
+
"external_classes" => external_model_targets(
|
|
53
|
+
options.external_classes,
|
|
54
|
+
),
|
|
55
|
+
),
|
|
46
56
|
"skip_unrecognized_connector" => !!options
|
|
47
57
|
.skip_unrecognized_connector,
|
|
48
58
|
}
|
|
49
59
|
contexts
|
|
50
60
|
end
|
|
51
61
|
|
|
62
|
+
def external_model_targets(external_classes)
|
|
63
|
+
external_classes&.transform_values do |target|
|
|
64
|
+
if target.is_a?(String) && target.match?(/\Asection-[^\s#]+\z/)
|
|
65
|
+
"##{target}"
|
|
66
|
+
else
|
|
67
|
+
target
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
52
72
|
def fill_in_entities_refs_attributes(document, # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
53
73
|
lutaml_document, _options)
|
|
54
74
|
all_children_packages = lutaml_document.packages
|
|
@@ -94,6 +114,96 @@ module Metanorma
|
|
|
94
114
|
.call(lutaml_document.packages)
|
|
95
115
|
.merge(children_pks_diags)
|
|
96
116
|
end
|
|
117
|
+
|
|
118
|
+
def add_model_anchor_context(context, root_package)
|
|
119
|
+
packages = model_packages(root_package)
|
|
120
|
+
context["additional_context"]["model_anchors"] =
|
|
121
|
+
model_reference_targets(packages)
|
|
122
|
+
.merge(model_anchors(packages))
|
|
123
|
+
.merge(model_stub_anchors(root_package))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Ids the XMI templates link to (attribute types, association ends),
|
|
127
|
+
# including ones the XMI never defines. Unnamed; anchors override.
|
|
128
|
+
def model_reference_targets(packages)
|
|
129
|
+
model_entities(packages).each_with_object({}) do |entity, targets|
|
|
130
|
+
Array(entity_list(entity, :attributes)).each do |attribute|
|
|
131
|
+
add_model_anchor(targets, object_xmi_id(attribute), nil)
|
|
132
|
+
end
|
|
133
|
+
Array(entity_list(entity, :associations)).each do |association|
|
|
134
|
+
add_model_anchor(targets, association_target(association), nil)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def model_entities(packages)
|
|
140
|
+
packages.flat_map do |package|
|
|
141
|
+
MODEL_COLLECTIONS.flat_map do |collection|
|
|
142
|
+
Array(entity_list(package, collection))
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def entity_list(object, collection)
|
|
148
|
+
object.public_send(collection) if object.respond_to?(collection)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def association_target(association)
|
|
152
|
+
return unless association.respond_to?(:member_end_xmi_id)
|
|
153
|
+
|
|
154
|
+
association.member_end_xmi_id
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def model_packages(root_package)
|
|
158
|
+
packages_tree(root_package)
|
|
159
|
+
.compact
|
|
160
|
+
.uniq { |package| package_key(package) }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def packages_tree(package)
|
|
164
|
+
[package, *child_packages(package).flat_map do |child|
|
|
165
|
+
packages_tree(child)
|
|
166
|
+
end]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def child_packages(package)
|
|
170
|
+
Array(package.packages).compact.uniq { |child| package_key(child) }
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def package_key(package)
|
|
174
|
+
xmi_id = package.xmi_id if package.respond_to?(:xmi_id)
|
|
175
|
+
xmi_id || package.name
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def model_anchors(packages)
|
|
179
|
+
collections = %w[classes enums data_types]
|
|
180
|
+
packages.each_with_object({}) do |package, anchors|
|
|
181
|
+
add_model_anchor(anchors, object_xmi_id(package), package.name)
|
|
182
|
+
collections.each do |collection|
|
|
183
|
+
next unless package.respond_to?(collection)
|
|
184
|
+
|
|
185
|
+
Array(package.public_send(collection)).each do |entity|
|
|
186
|
+
add_model_anchor(anchors, object_xmi_id(entity), entity.name)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def object_xmi_id(object)
|
|
193
|
+
object.xmi_id if object.respond_to?(:xmi_id)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def add_model_anchor(anchors, xmi_id, name)
|
|
197
|
+
anchors[xmi_id] = name if xmi_id.is_a?(String) && !xmi_id.empty?
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def model_stub_anchors(root_package)
|
|
201
|
+
xmi_root_model = root_package.instance_variable_get(:@xmi_root_model)
|
|
202
|
+
Array(xmi_root_model&.extension&.ea_stub)
|
|
203
|
+
.each_with_object({}) do |stub, anchors|
|
|
204
|
+
add_model_anchor(anchors, stub.id, stub.name)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
97
207
|
end
|
|
98
208
|
end
|
|
99
209
|
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "asciidoctor"
|
|
4
|
+
require "asciidoctor/extensions"
|
|
5
|
+
|
|
6
|
+
module Metanorma
|
|
7
|
+
module Plugin
|
|
8
|
+
module Lutaml
|
|
9
|
+
# Resolve model links after every macro has generated its actual anchors.
|
|
10
|
+
class XmiModelReference < ::Asciidoctor::Extensions::InlineMacroProcessor
|
|
11
|
+
use_dsl
|
|
12
|
+
named :lutaml_model_reference
|
|
13
|
+
parse_content_as :text
|
|
14
|
+
match(/(?<!\\)<<section-
|
|
15
|
+
(?<target>(?:(?!>>)[^,\s])+?)
|
|
16
|
+
(?:,(?<content>.*?))?>>/x)
|
|
17
|
+
|
|
18
|
+
INVENTORY_ATTRIBUTE = "lutaml_model_anchors"
|
|
19
|
+
|
|
20
|
+
def self.root_document(document)
|
|
21
|
+
document = document.parent_document while document.parent_document
|
|
22
|
+
document
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.register(document, anchors)
|
|
26
|
+
document = root_document(document)
|
|
27
|
+
unless document.attributes.key?(INVENTORY_ATTRIBUTE)
|
|
28
|
+
document.attributes[INVENTORY_ATTRIBUTE] = {}
|
|
29
|
+
document.extensions.inline_macro self
|
|
30
|
+
end
|
|
31
|
+
document.attributes[INVENTORY_ATTRIBUTE]
|
|
32
|
+
.merge!(anchors) { |_, old, new| new || old }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def process(parent, target, attrs)
|
|
36
|
+
document = self.class.root_document(parent.document)
|
|
37
|
+
inventory = document.attributes.fetch(INVENTORY_ATTRIBUTE, {})
|
|
38
|
+
name = inventory[target]
|
|
39
|
+
text = reference_text(parent, attrs["text"], target, name)
|
|
40
|
+
value = if inventory.key?(target) && !anchored?(document, target)
|
|
41
|
+
plain_text(parent, text, target)
|
|
42
|
+
else
|
|
43
|
+
label = text ? ",#{text}" : ""
|
|
44
|
+
"<<section-#{target}#{label}>>"
|
|
45
|
+
end
|
|
46
|
+
create_inline(parent, :quoted, value, type: :unquoted)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def anchored?(document, target)
|
|
52
|
+
document.catalog[:refs].key?("section-#{target}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def plain_text(parent, text, target)
|
|
56
|
+
text.to_s.strip.empty? ? parent.sub_specialchars(target) : text
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def reference_text(parent, text, target, name)
|
|
60
|
+
return text unless name && (text.to_s.strip.empty? || text == target)
|
|
61
|
+
|
|
62
|
+
parent.sub_specialchars(name)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -21,20 +21,26 @@ module Metanorma
|
|
|
21
21
|
}.freeze
|
|
22
22
|
RENDER_STYLE_ATTRIBUTE = "render_style"
|
|
23
23
|
|
|
24
|
-
def model_representation(
|
|
24
|
+
def model_representation( # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
25
|
+
lutaml_doc, document, add_context, options
|
|
26
|
+
)
|
|
25
27
|
fill_in_entities_refs_attributes(document, lutaml_doc, options)
|
|
28
|
+
contexts = create_context_object(lutaml_doc, add_context, options,
|
|
29
|
+
"context")
|
|
26
30
|
render_result, errors = Utils.render_liquid_string(
|
|
27
31
|
template_string: template(options.section_depth || 2,
|
|
28
32
|
options.render_style,
|
|
29
33
|
options.include_root),
|
|
30
|
-
contexts:
|
|
31
|
-
add_context,
|
|
32
|
-
options,
|
|
33
|
-
"context"),
|
|
34
|
+
contexts: contexts,
|
|
34
35
|
document: document,
|
|
35
36
|
include_path: template_path(document, options.template_path),
|
|
36
37
|
)
|
|
37
38
|
Utils.notify_render_errors(document, errors)
|
|
39
|
+
additional_context = contexts.fetch("context")
|
|
40
|
+
.fetch("additional_context", {})
|
|
41
|
+
XmiModelReference.register(
|
|
42
|
+
document, additional_context["model_anchors"] || {}
|
|
43
|
+
)
|
|
38
44
|
render_result.split("\n")
|
|
39
45
|
end
|
|
40
46
|
|
|
@@ -7,6 +7,7 @@ module Metanorma
|
|
|
7
7
|
autoload :XmiCache, "metanorma/plugin/lutaml/xmi_cache"
|
|
8
8
|
autoload :XmiConfig, "metanorma/plugin/lutaml/xmi_config"
|
|
9
9
|
autoload :XmiContextBuilder, "metanorma/plugin/lutaml/xmi_context_builder"
|
|
10
|
+
autoload :XmiModelReference, "metanorma/plugin/lutaml/xmi_model_reference"
|
|
10
11
|
autoload :XmiPackageFilter, "metanorma/plugin/lutaml/xmi_package_filter"
|
|
11
12
|
autoload :XmiRenderer, "metanorma/plugin/lutaml/xmi_renderer"
|
|
12
13
|
end
|
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.55
|
|
4
|
+
version: 0.7.55
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: asciidoctor
|
|
@@ -263,6 +263,7 @@ files:
|
|
|
263
263
|
- lib/metanorma/plugin/lutaml/xmi_cache.rb
|
|
264
264
|
- lib/metanorma/plugin/lutaml/xmi_config.rb
|
|
265
265
|
- lib/metanorma/plugin/lutaml/xmi_context_builder.rb
|
|
266
|
+
- lib/metanorma/plugin/lutaml/xmi_model_reference.rb
|
|
266
267
|
- lib/metanorma/plugin/lutaml/xmi_package_filter.rb
|
|
267
268
|
- lib/metanorma/plugin/lutaml/xmi_parse_cache.rb
|
|
268
269
|
- lib/metanorma/plugin/lutaml/xmi_partial_load_registry.rb
|