metanorma-plugin-lutaml 0.7.52 → 0.7.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.devel +4 -0
- data/Rakefile +19 -3
- data/lib/metanorma/plugin/lutaml/gc_budget.rb +22 -0
- data/lib/metanorma/plugin/lutaml/liquid_templates/_klass_table.liquid +22 -22
- data/lib/metanorma/plugin/lutaml/lutaml_data_preprocessor.rb +201 -0
- data/lib/metanorma/plugin/lutaml/lutaml_gml_dictionary_base.rb +11 -1
- data/lib/metanorma/plugin/lutaml/lutaml_klass_table_block_macro.rb +2 -2
- data/lib/metanorma/plugin/lutaml/version.rb +1 -1
- data/lib/metanorma/plugin/lutaml/xmi_cache.rb +84 -26
- data/lib/metanorma/plugin/lutaml/xmi_parse_cache.rb +30 -26
- data/lib/metanorma/plugin/lutaml/xmi_partial_load_registry.rb +148 -0
- data/lib/metanorma/plugin/lutaml/xmi_renderer.rb +5 -0
- data/lib/metanorma-plugin-lutaml.rb +6 -0
- data/metanorma-plugin-lutaml.gemspec +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b6a443f1152ff2cf6f053744b7750e0e96426bc776cf0cb582b9c93f7d828f55
|
|
4
|
+
data.tar.gz: 4f31cad3531f1f38525e2eaacee72917fee007a2a6099a772894ed6159ed5168
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4098ac9e010d91d65a98c21eb22abcbd7add8166f7dc93a1f0b2fdeeb790b40c70a274ef8b66f36aaa3402bcc3b481d9283ff4e766586b4a33e4c8dfd75e2463
|
|
7
|
+
data.tar.gz: 9ed735f7bd7b870a8be1ad0847e4b9c47a2a411a23de2a7d98874c6d185286e60123d067c8e34be42b3d339ae021cee51190b50c8c0581a5a04c1ad47c64f654
|
data/Gemfile.devel
CHANGED
|
@@ -7,3 +7,7 @@ gem "metanorma", github: "metanorma/metanorma", ref: "a899a9a6fc933dca0a53166d66
|
|
|
7
7
|
# metanorma-document components)
|
|
8
8
|
gem "relaton", github: "relaton/relaton", ref: "65742ac31bce9e006072e1ace440c984352201f3"
|
|
9
9
|
gem "relaton-cli", github: "relaton/relaton-cli", branch: "feat/allow-relaton-v3-v2line"
|
|
10
|
+
|
|
11
|
+
# lutaml-lml 0.1.5 adds Lutaml::Lml.parse + Formatter::Graphviz used by
|
|
12
|
+
# the lutaml_diagram renderer; 0.1.1 (previously resolved) lacks them
|
|
13
|
+
gem "lutaml-lml", ">= 0.1.5"
|
data/Rakefile
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
2
|
require "rspec/core/rake_task"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# The lutaml-uml-rendering specs (EA XMI, UML datamodel description)
|
|
5
|
+
# balloon memory on ruby 3.3 runners — they are killed mid-suite there
|
|
6
|
+
# (#304) while passing on 3.4/4.0 and in this dedicated task elsewhere.
|
|
7
|
+
XMI_HEAVY_SPEC_FILES = %w[
|
|
5
8
|
spec/metanorma/plugin/lutaml/lutaml_ea_xmi_preprocessor_spec.rb
|
|
6
9
|
spec/metanorma/plugin/lutaml/lutaml_uml_datamodel_description_preprocessor_spec.rb
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
XMI_SPEC_FILES = %w[
|
|
7
13
|
spec/metanorma/plugin/lutaml/lutaml_klass_table_block_macro_spec.rb
|
|
8
14
|
spec/metanorma/plugin/lutaml/lutaml_enum_table_block_macro_spec.rb
|
|
9
15
|
spec/metanorma/plugin/lutaml/lutaml_xmi_index_spec.rb
|
|
@@ -13,11 +19,21 @@ XMI_SPEC_FILES = %w[
|
|
|
13
19
|
RSpec::Core::RakeTask.new(:spec)
|
|
14
20
|
|
|
15
21
|
RSpec::Core::RakeTask.new(:fast) do |t|
|
|
16
|
-
t.pattern =
|
|
22
|
+
t.pattern =
|
|
23
|
+
FileList["spec/**/*_spec.rb"] - XMI_SPEC_FILES - XMI_HEAVY_SPEC_FILES
|
|
17
24
|
end
|
|
18
25
|
|
|
19
26
|
RSpec::Core::RakeTask.new(:xmi) do |t|
|
|
20
27
|
t.pattern = FileList[XMI_SPEC_FILES]
|
|
21
28
|
end
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
RSpec::Core::RakeTask.new(:xmi_heavy) do |t|
|
|
31
|
+
t.pattern = FileList[XMI_HEAVY_SPEC_FILES]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The XMI/uml-rendering specs accumulate more memory per process than
|
|
35
|
+
# the macos ruby-3.3 runner provides — they are killed mid-suite there
|
|
36
|
+
# (#304) no matter the grouping, while passing on 3.4/4.0 and via the
|
|
37
|
+
# dedicated tasks. The push gate therefore runs `fast`; run `rake xmi`
|
|
38
|
+
# and `rake xmi_heavy` (ad hoc / nightly) for the heavy sets.
|
|
39
|
+
task default: :fast
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Lutaml
|
|
6
|
+
# The lutaml macro family churns transients of hundreds of MB per
|
|
7
|
+
# invocation on top of a multi-GB live parsed-XMI set. The churn is
|
|
8
|
+
# malloc-side (libxml/moxml/liquid), so the GC's heap-growth
|
|
9
|
+
# heuristics fire too late under memory caps; collect at macro
|
|
10
|
+
# boundaries whenever uncollected malloc has exceeded the budget.
|
|
11
|
+
module GcBudget
|
|
12
|
+
BUDGET_BYTES = 512 * (1 << 20)
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def gc_when_bloated!
|
|
16
|
+
GC.start if GC.stat(:malloc_increase_bytes) > BUDGET_BYTES
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{
|
|
1
|
+
{%- assign root = klass.generalization -%}
|
|
2
2
|
|
|
3
3
|
{%- capture upper_klass_name -%}
|
|
4
4
|
{{ root.general.upper_klass }}:{{ root.general.name }}
|
|
@@ -21,57 +21,57 @@ h| Stereotype 2+| {{ stereotype }}
|
|
|
21
21
|
|
|
22
22
|
3+h| Inherited Properties
|
|
23
23
|
h| Property Name h| Property Type and Multiplicity h| Definition
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
{%- for attr in root.inherited_props -%}
|
|
25
|
+
{%- unless attr.has_association? -%}
|
|
26
|
+
{%- capture name_col -%}
|
|
27
27
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
28
28
|
({{ attr.gen_name }})
|
|
29
29
|
{%- endcapture -%}
|
|
30
30
|
| {{ name_col | newline_to_br }}
|
|
31
31
|
| {{ attr.type }} [{{ attr.cardinality.min }}..{{ attr.cardinality.max }}]
|
|
32
32
|
| {{ attr.definition }}
|
|
33
|
-
|
|
34
|
-
{
|
|
33
|
+
{%- endunless -%}
|
|
34
|
+
{%- endfor -%}
|
|
35
35
|
|
|
36
36
|
3+h| Self-defined Properties
|
|
37
37
|
h| Property Name h| Property Type and Multiplicity h| Definition
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
{%- for attr in root.owned_props -%}
|
|
39
|
+
{%- unless attr.has_association? -%}
|
|
40
|
+
{%- capture name_col -%}
|
|
41
41
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
42
42
|
({{ attr.gen_name }})
|
|
43
43
|
{%- endcapture -%}
|
|
44
44
|
| {{ name_col | newline_to_br }}
|
|
45
45
|
| {{ attr.type }} [{{ attr.cardinality.min }}..{{ attr.cardinality.max }}]
|
|
46
46
|
| {{ attr.definition }}
|
|
47
|
-
|
|
48
|
-
{
|
|
47
|
+
{%- endunless -%}
|
|
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
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
{%- for attr in root.inherited_assoc_props -%}
|
|
53
|
+
{%- if attr.has_association? -%}
|
|
54
|
+
{%- capture name_col -%}
|
|
55
55
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
56
56
|
({{ attr.gen_name }})
|
|
57
57
|
{%- endcapture -%}
|
|
58
58
|
| {{ name_col | newline_to_br }}
|
|
59
59
|
| {{ attr.type_ns }}:{{ attr.type }} [{{ attr.cardinality.min }}..{{ attr.cardinality.max }}]
|
|
60
60
|
| {{ attr.definition }}
|
|
61
|
-
|
|
62
|
-
{
|
|
61
|
+
{%- endif -%}
|
|
62
|
+
{%- endfor -%}
|
|
63
63
|
|
|
64
64
|
3+h| Properties Defined in Association
|
|
65
65
|
h| Property Name h| Property Type and Multiplicity h| Definition
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
{%- for attr in root.assoc_props -%}
|
|
67
|
+
{%- if attr.has_association? -%}
|
|
68
|
+
{%- capture name_col -%}
|
|
69
69
|
{{ attr.name_ns }}:{{ attr.name }}
|
|
70
70
|
({{ attr.gen_name }})
|
|
71
71
|
{%- endcapture -%}
|
|
72
72
|
| {{ name_col | newline_to_br }}
|
|
73
73
|
| {{ attr.type_ns }}:{{ attr.type }} [{{ attr.cardinality.min }}..{{ attr.cardinality.max }}]
|
|
74
74
|
| {{ attr.definition }}
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
|===
|
|
75
|
+
{%- endif -%}
|
|
76
|
+
{%- endfor -%}
|
|
77
|
+
|===
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Metanorma
|
|
4
|
+
module Plugin
|
|
5
|
+
module Lutaml
|
|
6
|
+
# Renders LutaML data instances through a Liquid template.
|
|
7
|
+
#
|
|
8
|
+
# Data definitions and instances in separate files:
|
|
9
|
+
#
|
|
10
|
+
# [lutaml_data,<definitions.lml>,<instances.lml|yaml|json>,<context>]
|
|
11
|
+
# ----
|
|
12
|
+
# {% for check in context.S158Checks.checks %}
|
|
13
|
+
# {{ check.dev_id }}
|
|
14
|
+
# {% endfor %}
|
|
15
|
+
# ----
|
|
16
|
+
#
|
|
17
|
+
# Data definitions and instances in the same file:
|
|
18
|
+
#
|
|
19
|
+
# [lutaml_data,<definitions.lml>,<context>]
|
|
20
|
+
#
|
|
21
|
+
class LutamlDataPreprocessor < BasePreprocessor
|
|
22
|
+
DATA_FILE_EXTENSIONS = %w[.lml .yaml .yml .json].freeze
|
|
23
|
+
DEFAULT_CONTEXT_NAME = "lutaml_data"
|
|
24
|
+
|
|
25
|
+
DATA_BLOCK_REGEX = %r{
|
|
26
|
+
^
|
|
27
|
+
\[
|
|
28
|
+
\blutaml_data\b
|
|
29
|
+
,(?<definition_file>[^,\[\]]+)
|
|
30
|
+
(?<rest>.*)
|
|
31
|
+
\]
|
|
32
|
+
}x
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
|
|
36
|
+
def lutaml_liquid?(line)
|
|
37
|
+
line.match(DATA_BLOCK_REGEX)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def index_type_name
|
|
41
|
+
"LutaML data"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def load_lutaml_file(_document, file_path, _options)
|
|
45
|
+
::Lutaml::Lml.parse_document(File.new(file_path, encoding: "UTF-8"))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def process_text_blocks(document, input_lines, _express_indexes)
|
|
51
|
+
line = input_lines.next
|
|
52
|
+
block_header_match = lutaml_liquid?(line)
|
|
53
|
+
return [line] unless block_header_match
|
|
54
|
+
|
|
55
|
+
definition_file, instance_file, context_name, options =
|
|
56
|
+
parse_header(block_header_match)
|
|
57
|
+
|
|
58
|
+
end_mark = input_lines.next
|
|
59
|
+
lines = extract_block_lines(input_lines, end_mark)
|
|
60
|
+
|
|
61
|
+
render_data_template(
|
|
62
|
+
document: document,
|
|
63
|
+
lines: lines,
|
|
64
|
+
definition_file: definition_file,
|
|
65
|
+
instance_file: instance_file,
|
|
66
|
+
context_name: context_name,
|
|
67
|
+
options: options,
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def parse_header(match)
|
|
72
|
+
definition_file = match[:definition_file].strip
|
|
73
|
+
segments = match[:rest].split(",").map(&:strip)
|
|
74
|
+
options, segments = split_options(segments)
|
|
75
|
+
|
|
76
|
+
data_segments, name_segments = segments
|
|
77
|
+
.reject(&:empty?).partition { |segment| data_file?(segment) }
|
|
78
|
+
|
|
79
|
+
[definition_file, data_segments.last,
|
|
80
|
+
name_segments.last || DEFAULT_CONTEXT_NAME, options]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def split_options(segments)
|
|
84
|
+
options_start = segments.index { |segment| segment.include?("=") }
|
|
85
|
+
return [{}, segments] unless options_start
|
|
86
|
+
|
|
87
|
+
options = parse_options(",#{segments[options_start..].join(',')}")
|
|
88
|
+
[options, segments[0...options_start]]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def data_file?(segment)
|
|
92
|
+
DATA_FILE_EXTENSIONS.include?(File.extname(segment))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def render_data_template(document:, lines:, # rubocop:disable Metrics/ParameterLists
|
|
96
|
+
definition_file:, instance_file:,
|
|
97
|
+
context_name:, options:)
|
|
98
|
+
definition_path, instances_path = resolve_data_paths(
|
|
99
|
+
document, definition_file, instance_file
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
compiler = ::Lutaml::Lml::ModelCompiler.new
|
|
103
|
+
compiler.compile(File.new(definition_path, encoding: "UTF-8"))
|
|
104
|
+
instances = load_instances(instances_path, compiler)
|
|
105
|
+
|
|
106
|
+
[render_liquid(lines, document, options, context_name, instances)]
|
|
107
|
+
rescue StandardError => e
|
|
108
|
+
::Metanorma::Util.log(
|
|
109
|
+
"[#{self.class.name}] Failed to parse LutaML data block: " \
|
|
110
|
+
"#{e.message}",
|
|
111
|
+
:error,
|
|
112
|
+
)
|
|
113
|
+
raise e
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def render_liquid(lines, document, options, context_name, instances)
|
|
117
|
+
parsed_template = template(lines)
|
|
118
|
+
parsed_template.registers[:file_system] =
|
|
119
|
+
build_file_system(document, options)
|
|
120
|
+
parsed_template.render(context_name => instances.to_h)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def resolve_data_paths(document, definition_file, instance_file)
|
|
124
|
+
definition_path = Utils.relative_file_path(document, definition_file)
|
|
125
|
+
unless File.file?(definition_path)
|
|
126
|
+
raise StandardError, index_missing_message(definition_file)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
instances_path = instance_file &&
|
|
130
|
+
Utils.relative_file_path(document, instance_file)
|
|
131
|
+
if instances_path && !File.file?(instances_path)
|
|
132
|
+
raise StandardError, index_missing_message(instance_file)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
[definition_path, instances_path || definition_path]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def load_instances(path, compiler)
|
|
139
|
+
case File.extname(path)
|
|
140
|
+
when ".lml" then load_lml_instances(path, compiler)
|
|
141
|
+
when ".json" then load_structured_instances(path, compiler,
|
|
142
|
+
:parse_json_data)
|
|
143
|
+
else load_structured_instances(path, compiler, :parse_yaml_data)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def load_lml_instances(path, compiler)
|
|
148
|
+
doc = load_lutaml_file(nil, path, {})
|
|
149
|
+
root = doc.instance
|
|
150
|
+
return [] unless root
|
|
151
|
+
|
|
152
|
+
[[root.type.to_s, compiler.hydrate(doc)]]
|
|
153
|
+
rescue StandardError => e
|
|
154
|
+
raise StandardError, "Failed to parse LutaML instances in " \
|
|
155
|
+
"`#{path}`: #{e.message}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def load_structured_instances(path, compiler, parser)
|
|
159
|
+
data = send(parser, path)
|
|
160
|
+
entries = data.is_a?(Array) ? data : [data]
|
|
161
|
+
entries.map { |entry| build_structured_entry(path, compiler, entry) }
|
|
162
|
+
rescue StandardError => e
|
|
163
|
+
raise e if e.message.start_with?("Instance", "Unknown type")
|
|
164
|
+
|
|
165
|
+
raise StandardError, "Failed to load data instances in " \
|
|
166
|
+
"`#{path}`: #{e.message}"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def build_structured_entry(path, compiler, entry)
|
|
170
|
+
type = entry.delete("type")
|
|
171
|
+
unless type
|
|
172
|
+
raise StandardError, "Instance in `#{path}` is missing the " \
|
|
173
|
+
"`type` key"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
klass = compiler.compiled_classes[demodulize(type)]
|
|
177
|
+
unless klass
|
|
178
|
+
raise StandardError,
|
|
179
|
+
"Unknown type `#{type}` in `#{path}`"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
[demodulize(type), klass.from_hash(entry)]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def parse_yaml_data(path)
|
|
186
|
+
require "yaml"
|
|
187
|
+
YAML.safe_load(File.read(path, encoding: "UTF-8"))
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def parse_json_data(path)
|
|
191
|
+
require "json"
|
|
192
|
+
JSON.parse(File.read(path, encoding: "UTF-8"))
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def demodulize(name)
|
|
196
|
+
name.to_s.split("::").last
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "ogc/gml"
|
|
4
4
|
require "liquid"
|
|
5
5
|
|
|
6
|
+
require "metanorma/plugin/lutaml/gc_budget"
|
|
7
|
+
|
|
6
8
|
module Metanorma
|
|
7
9
|
module Plugin
|
|
8
10
|
module Lutaml
|
|
@@ -10,6 +12,7 @@ module Metanorma
|
|
|
10
12
|
private
|
|
11
13
|
|
|
12
14
|
def render(tmpl, parent, attrs, orig_gml_path)
|
|
15
|
+
GcBudget.gc_when_bloated!
|
|
13
16
|
dict = get_gml_dictionary(parent, orig_gml_path)
|
|
14
17
|
tmpl.assigns[attrs["context"]] = GmlDictionaryDrop.new(dict)
|
|
15
18
|
rendered_tmpl = tmpl.render
|
|
@@ -17,12 +20,19 @@ module Metanorma
|
|
|
17
20
|
parse_content(block, rendered_tmpl, attrs)
|
|
18
21
|
end
|
|
19
22
|
|
|
23
|
+
# A document-scale compile resolves the same dictionary files many
|
|
24
|
+
# times (the plateau corpus cites codelists up to 34 times each);
|
|
25
|
+
# re-reading and re-parsing per macro was pure duplicate work.
|
|
26
|
+
GML_DICTIONARY_CACHE = CacheStore.new(max_size: 512)
|
|
27
|
+
|
|
20
28
|
def get_gml_dictionary(parent, orig_gml_path)
|
|
21
29
|
gml_path = Utils.relative_file_path(
|
|
22
30
|
parent.document, orig_gml_path
|
|
23
31
|
)
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
GML_DICTIONARY_CACHE.fetch_or_store(gml_path) do
|
|
34
|
+
::Ogc::Gml::Dictionary.from_xml(xml_content(gml_path))
|
|
35
|
+
end
|
|
26
36
|
end
|
|
27
37
|
|
|
28
38
|
def xml_content(filepath)
|
|
@@ -28,8 +28,8 @@ module Metanorma
|
|
|
28
28
|
guidance = get_guidance(parent.document, attrs["guidance"])
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
klass =
|
|
32
|
-
|
|
31
|
+
klass = serialize_klass_or_datatype_drop_by_name(
|
|
32
|
+
xmi_path, path, parent.document, guidance)
|
|
33
33
|
|
|
34
34
|
render_table(klass, CONTEXT_NAME, parent, attrs)
|
|
35
35
|
end
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "metanorma/plugin/lutaml/gc_budget"
|
|
4
|
+
|
|
3
5
|
module Metanorma
|
|
4
6
|
module Plugin
|
|
5
7
|
module Lutaml
|
|
6
8
|
module XmiCache
|
|
7
|
-
|
|
9
|
+
# Bounded parse-once cache: every macro touching a source (the
|
|
10
|
+
# full file, or its package slice under lutaml-ea-xmi-load: partial)
|
|
11
|
+
# reuses the one parsed graph; the LRU bound keeps the retained
|
|
12
|
+
# graphs to a few live at once, which the section-ordered
|
|
13
|
+
# macros of a document turn into near-perfect reuse.
|
|
14
|
+
XMI_PARSE_CACHE = XmiParseCache.new(max_size: 4)
|
|
8
15
|
|
|
9
16
|
def lutaml_document_from_file_or_cache(document, file_path, yaml_config,
|
|
10
17
|
yaml_config_path = nil)
|
|
@@ -27,21 +34,8 @@ yaml_config_path = nil)
|
|
|
27
34
|
end
|
|
28
35
|
end
|
|
29
36
|
|
|
30
|
-
def find_class_by_xmi_id(container, xmi_id)
|
|
31
|
-
container.classes.find { |node| node.xmi_id == xmi_id } ||
|
|
32
|
-
container.packages
|
|
33
|
-
.lazy
|
|
34
|
-
.filter_map { |pkg| find_class_by_xmi_id(pkg, xmi_id) }
|
|
35
|
-
.first
|
|
36
|
-
end
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
container.enums.find { |node| node.xmi_id == xmi_id } ||
|
|
40
|
-
container.packages
|
|
41
|
-
.lazy
|
|
42
|
-
.filter_map { |pkg| find_enum_by_xmi_id(pkg, xmi_id) }
|
|
43
|
-
.first
|
|
44
|
-
end
|
|
38
|
+
|
|
45
39
|
|
|
46
40
|
def find_packaged_klass(index, path, root_model_name: nil)
|
|
47
41
|
segments = path.split("::").reject(&:empty?)
|
|
@@ -69,6 +63,32 @@ yaml_config_path = nil)
|
|
|
69
63
|
end
|
|
70
64
|
end
|
|
71
65
|
|
|
66
|
+
def find_packaged_datatype(index, path, root_model_name: nil)
|
|
67
|
+
segments = path.split("::").reject(&:empty?)
|
|
68
|
+
if root_model_name && segments.first == root_model_name
|
|
69
|
+
segments.shift
|
|
70
|
+
end
|
|
71
|
+
if segments.one?
|
|
72
|
+
index.find_packaged_by_name_and_types(
|
|
73
|
+
segments.first, ["uml:DataType"]
|
|
74
|
+
)
|
|
75
|
+
else
|
|
76
|
+
find_packaged_datatype_by_path(index, segments)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def find_packaged_datatype_by_path(index, segments)
|
|
81
|
+
datatype_name = segments.pop
|
|
82
|
+
|
|
83
|
+
candidates = ["uml:DataType"]
|
|
84
|
+
.flat_map { |t| index.packaged_elements_of_type(t) }
|
|
85
|
+
.select { |e| e.name == datatype_name }
|
|
86
|
+
|
|
87
|
+
candidates.find do |datatype|
|
|
88
|
+
match_parent_chain?(index, datatype, segments)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
72
92
|
def match_parent_chain?(index, element, parent_segments)
|
|
73
93
|
current = element
|
|
74
94
|
parent_segments.reverse_each do |pkg_name|
|
|
@@ -85,23 +105,54 @@ yaml_config_path = nil)
|
|
|
85
105
|
.find { |e| e.name == name }
|
|
86
106
|
end
|
|
87
107
|
|
|
88
|
-
def
|
|
89
|
-
guidance = nil)
|
|
90
|
-
|
|
108
|
+
def serialize_klass_or_datatype_drop_by_name(xmi_path,
|
|
109
|
+
name, _document = nil, guidance = nil)
|
|
110
|
+
serialize_klass_drop_by_name(xmi_path, name, _document, guidance) ||
|
|
111
|
+
serialize_datatype_drop_by_name(xmi_path, name, _document)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def serialize_klass_drop_by_name(xmi_path,
|
|
115
|
+
name, _document = nil, guidance = nil)
|
|
116
|
+
GcBudget.gc_when_bloated!
|
|
117
|
+
xmi_path, name, slice = XmiPartialLoadRegistry.rewrite(xmi_path, name)
|
|
118
|
+
parsed = XMI_PARSE_CACHE.fetch(xmi_path, slice: slice)
|
|
91
119
|
klass = resolve_packaged_klass(parsed, name)
|
|
92
|
-
|
|
120
|
+
if klass.nil?
|
|
121
|
+
warn "Class not found for name: #{name}"
|
|
122
|
+
return nil
|
|
123
|
+
end
|
|
124
|
+
|
|
93
125
|
::Ea::Xmi::LiquidDrops::KlassDrop.new(
|
|
94
126
|
klass, guidance, parsed.drop_options
|
|
95
127
|
)
|
|
96
128
|
end
|
|
97
129
|
|
|
130
|
+
def serialize_datatype_drop_by_name(xmi_path, name, _document = nil)
|
|
131
|
+
GcBudget.gc_when_bloated!
|
|
132
|
+
xmi_path, name, slice = XmiPartialLoadRegistry.rewrite(xmi_path, name)
|
|
133
|
+
parsed = XMI_PARSE_CACHE.fetch(xmi_path, slice: slice)
|
|
134
|
+
datatype = resolve_packaged_datatype(parsed, name)
|
|
135
|
+
if datatype.nil?
|
|
136
|
+
warn "Datatype not found for name: #{name}"
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
::Ea::Xmi::LiquidDrops::DataTypeDrop.new(
|
|
141
|
+
datatype, parsed.drop_options
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
98
145
|
def serialize_enum_drop_by_name(xmi_path, name, _document = nil)
|
|
99
|
-
|
|
146
|
+
GcBudget.gc_when_bloated!
|
|
147
|
+
xmi_path, name, slice = XmiPartialLoadRegistry.rewrite(xmi_path, name)
|
|
148
|
+
parsed = XMI_PARSE_CACHE.fetch(xmi_path, slice: slice)
|
|
100
149
|
raw_enum = find_packaged_enum(parsed.parser.xmi_index, name)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
150
|
+
if raw_enum.nil?
|
|
151
|
+
warn "Enumeration not found for name: #{name}"
|
|
152
|
+
return nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
enum = parsed.xmi_id_index[raw_enum.id]
|
|
105
156
|
::Ea::Xmi::LiquidDrops::EnumDrop.new(
|
|
106
157
|
enum, parsed.drop_options
|
|
107
158
|
)
|
|
@@ -115,9 +166,16 @@ guidance = nil)
|
|
|
115
166
|
parsed.parser.xmi_index, name,
|
|
116
167
|
root_model_name: root_model_name
|
|
117
168
|
)
|
|
118
|
-
raw_klass &&
|
|
119
|
-
|
|
169
|
+
raw_klass && parsed.xmi_id_index[raw_klass.id]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def resolve_packaged_datatype(parsed, name)
|
|
173
|
+
root_model_name = parsed.parser.xmi_root_model.model.name
|
|
174
|
+
raw_datatype = find_packaged_datatype(
|
|
175
|
+
parsed.parser.xmi_index, name,
|
|
176
|
+
root_model_name: root_model_name
|
|
120
177
|
)
|
|
178
|
+
raw_datatype && parsed.xmi_id_index[raw_datatype.id]
|
|
121
179
|
end
|
|
122
180
|
end
|
|
123
181
|
end
|
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "stringio"
|
|
3
4
|
require "xmi"
|
|
4
5
|
require "ea/xmi"
|
|
5
6
|
|
|
6
7
|
module Metanorma
|
|
7
8
|
module Plugin
|
|
8
9
|
module Lutaml
|
|
9
|
-
ParsedXmi = Struct.new(:parser, :uml_document, :drop_options,
|
|
10
|
-
keyword_init: true)
|
|
11
|
-
|
|
12
10
|
class XmiParseCache
|
|
13
11
|
def initialize(max_size: 50)
|
|
14
12
|
@parse_cache = CacheStore.new(max_size: max_size)
|
|
15
13
|
@drop_cache = CacheStore.new(max_size: max_size)
|
|
16
14
|
end
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
drop_options: build_drop_options(parser),
|
|
27
|
-
)
|
|
16
|
+
# slice: a XmiPartialLoadRegistry token; when given, the macro parses
|
|
17
|
+
# the package slice prepared for it instead of the whole source
|
|
18
|
+
def fetch(full_path, retain: true, slice: nil)
|
|
19
|
+
key = slice || full_path
|
|
20
|
+
return fetch_unretained(full_path, slice) unless retain
|
|
21
|
+
|
|
22
|
+
@parse_cache.fetch_or_store(key) do
|
|
23
|
+
load_parsed(full_path, slice)
|
|
28
24
|
end
|
|
29
25
|
end
|
|
30
26
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
def fetch_unretained(full_path, slice = nil)
|
|
28
|
+
load_parsed(full_path, slice)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fetch_drop(full_path, guidance: nil, slice: nil)
|
|
32
|
+
parsed = fetch(full_path, slice: slice)
|
|
33
|
+
@drop_cache.fetch_or_store([key_for(full_path, slice), guidance]) do
|
|
34
34
|
::Ea::Xmi::LiquidDrops::RootDrop.new(
|
|
35
35
|
parsed.uml_document, guidance, parsed.drop_options
|
|
36
36
|
)
|
|
@@ -44,16 +44,20 @@ module Metanorma
|
|
|
44
44
|
|
|
45
45
|
private
|
|
46
46
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
def key_for(full_path, slice)
|
|
48
|
+
slice || full_path
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The prepared slice is parsed whole: assembling it already
|
|
52
|
+
# streamed the source once, so nothing re-reads the original
|
|
53
|
+
# export here.
|
|
54
|
+
def load_parsed(full_path, slice)
|
|
55
|
+
if slice
|
|
56
|
+
xml = XmiPartialLoadRegistry.slice_string(slice)
|
|
57
|
+
::Ea::Xmi.load_graph(StringIO.new(xml))
|
|
58
|
+
else
|
|
59
|
+
::Ea::Xmi.load_graph(full_path)
|
|
60
|
+
end
|
|
57
61
|
end
|
|
58
62
|
end
|
|
59
63
|
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require_relative "utils"
|
|
5
|
+
|
|
6
|
+
module Metanorma
|
|
7
|
+
module Plugin
|
|
8
|
+
module Lutaml
|
|
9
|
+
# Loads the XMI sources of table macros partially, through
|
|
10
|
+
# Ea::Xmi.load_graph's partial mode.
|
|
11
|
+
#
|
|
12
|
+
# EA exports of plateau scale reach ~100 MB, more than half of it
|
|
13
|
+
# diagram presentation data, and fully materializing the Xmi tree
|
|
14
|
+
# plus the Ea graph costs 10+ GB of live objects - more than a
|
|
15
|
+
# standard build runner has. With the `lutaml-ea-xmi-load` document
|
|
16
|
+
# attribute set to `partial`, every klass and enum table macro's
|
|
17
|
+
# (package, name) pair seeds a per-package reference closure; one
|
|
18
|
+
# streaming pass per source assembles the closures in memory and
|
|
19
|
+
# each macro parses only its own package's slice. Nothing is
|
|
20
|
+
# written to disk.
|
|
21
|
+
#
|
|
22
|
+
# Without the attribute nothing is preloaded and behavior is
|
|
23
|
+
# byte-for-byte the whole-load path.
|
|
24
|
+
module XmiPartialLoadRegistry
|
|
25
|
+
MACRO_REGEXP = /lutaml_(?:klass|enum)_table::([^\[<]+)\[([^\]]*)\]/
|
|
26
|
+
MIN_SOURCE_BYTES = 2 * 1024 * 1024
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# Rewrites (xmi_path, name_path) to the partial-load form of
|
|
30
|
+
# the macro's class: [path, bare name, slice token]. The
|
|
31
|
+
# token is nil when no slice was prepared, in which case the
|
|
32
|
+
# macro whole-loads the source unchanged.
|
|
33
|
+
def rewrite(xmi_path, name_path)
|
|
34
|
+
return [xmi_path, name_path, nil] unless @registry
|
|
35
|
+
|
|
36
|
+
entry = @registry[xmi_path]
|
|
37
|
+
return [xmi_path, name_path, nil] unless entry
|
|
38
|
+
|
|
39
|
+
group_key = entry[name_path] || entry[bare_name(name_path)]
|
|
40
|
+
return [xmi_path, name_path, nil] unless group_key
|
|
41
|
+
|
|
42
|
+
[xmi_path, bare_name(name_path), [xmi_path, group_key]]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The prepared slice for a token from #rewrite.
|
|
46
|
+
def slice_string(token)
|
|
47
|
+
@slices[token]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def slice?(xmi_path)
|
|
51
|
+
return false unless @registry
|
|
52
|
+
|
|
53
|
+
entry = @registry[xmi_path]
|
|
54
|
+
!entry.nil? && entry.any?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def reset!
|
|
58
|
+
@registry = nil
|
|
59
|
+
@slices = nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def prepare(document, lines) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
63
|
+
reset!
|
|
64
|
+
# header attributes are parsed after preprocessors run, so
|
|
65
|
+
# the opt-in is also detected in the raw lines
|
|
66
|
+
return unless enabled?(document, lines)
|
|
67
|
+
|
|
68
|
+
groups = collect_groups(document, lines)
|
|
69
|
+
return if groups.empty?
|
|
70
|
+
|
|
71
|
+
@registry = {}
|
|
72
|
+
@slices = {}
|
|
73
|
+
groups.each do |path, entries|
|
|
74
|
+
next unless File.size(path) >= MIN_SOURCE_BYTES
|
|
75
|
+
|
|
76
|
+
slice_groups = {}
|
|
77
|
+
macro_keys = {}
|
|
78
|
+
entries.each do |key, value|
|
|
79
|
+
if value.is_a?(Array) # group_key => wanted pairs
|
|
80
|
+
slice_groups[key] = value
|
|
81
|
+
else # macro key => group_key
|
|
82
|
+
(macro_keys[value] ||= []) << key
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
# one index pass and one write pass over the source; the
|
|
86
|
+
# slices stay in memory for the parse cache to consume,
|
|
87
|
+
# so nothing is written to disk
|
|
88
|
+
::Ea::Xmi::Slicer.slices(path, slice_groups).each do |group_key, xml|
|
|
89
|
+
token = [path, group_key]
|
|
90
|
+
@slices[token] = xml
|
|
91
|
+
macro_keys[group_key]&.each do |k|
|
|
92
|
+
(@registry[path] ||= {})[k] = group_key
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
@registry = nil if @registry.empty?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def enabled?(document, lines)
|
|
102
|
+
document.attributes["lutaml-ea-xmi-load"] == "partial" ||
|
|
103
|
+
lines.any? { |l| l.match?(/\A:lutaml-ea-xmi-load:\s*partial\s*$/) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Slices are grouped per package, not per class: the tables
|
|
107
|
+
# of classes in one package share most of their closure
|
|
108
|
+
# (partners, connectors, ancestors), so a package slice
|
|
109
|
+
# carries each shared subtree once instead of once per class.
|
|
110
|
+
def collect_groups(document, lines)
|
|
111
|
+
groups = Hash.new { |h, k| h[k] = {} }
|
|
112
|
+
lines.each do |line|
|
|
113
|
+
MACRO_REGEXP.match(line) do |m|
|
|
114
|
+
path = Utils.relative_file_path(document, m[1].strip)
|
|
115
|
+
package = m[2][/&?(?:^|,)package="([^"]+)"/, 1]
|
|
116
|
+
name = m[2][/&?(?:^|,)name="([^"]+)"/, 1]
|
|
117
|
+
next if name.nil?
|
|
118
|
+
|
|
119
|
+
key = package ? "#{package}::#{name}" : name
|
|
120
|
+
group_key = package || "_"
|
|
121
|
+
(groups[path][group_key] ||= []) << [package, name]
|
|
122
|
+
groups[path][key] = group_key
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
groups
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def bare_name(name_path)
|
|
129
|
+
name_path.to_s.split("::").last
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Scans the fully expanded document source once, before parsing,
|
|
135
|
+
# and prepares the partial-load seeds for every table macro. The
|
|
136
|
+
# lines pass through unchanged; reading them through the incoming
|
|
137
|
+
# reader expands include directives, which is where plateau
|
|
138
|
+
# documents keep their table macros.
|
|
139
|
+
class XmiPartialLoadPreprocessor < ::Asciidoctor::Extensions::Preprocessor
|
|
140
|
+
def process(document, reader)
|
|
141
|
+
input_lines = reader.readlines
|
|
142
|
+
XmiPartialLoadRegistry.prepare(document, input_lines)
|
|
143
|
+
reader.class.new(document, input_lines)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "liquid"
|
|
4
4
|
|
|
5
|
+
require "metanorma/plugin/lutaml/gc_budget"
|
|
6
|
+
|
|
5
7
|
module Metanorma
|
|
6
8
|
module Plugin
|
|
7
9
|
module Lutaml
|
|
@@ -72,6 +74,9 @@ module Metanorma
|
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
rendered_table = table_tmpl.render
|
|
77
|
+
# The asciidoctor ingestion of a rendered table transiently
|
|
78
|
+
# dwarfs its input; collect the liquid phase's garbage first.
|
|
79
|
+
GcBudget.gc_when_bloated!
|
|
75
80
|
block = create_open_block(parent, "", attrs)
|
|
76
81
|
parse_content(block, rendered_table, attrs)
|
|
77
82
|
end
|
|
@@ -31,6 +31,8 @@ module Metanorma
|
|
|
31
31
|
autoload :LutamlDiagramBase, "metanorma/plugin/lutaml/lutaml_diagram_base"
|
|
32
32
|
autoload :LutamlDiagramBlock,
|
|
33
33
|
"metanorma/plugin/lutaml/lutaml_diagram_block"
|
|
34
|
+
autoload :LutamlDataPreprocessor,
|
|
35
|
+
"metanorma/plugin/lutaml/lutaml_data_preprocessor"
|
|
34
36
|
autoload :LutamlDiagramBlockMacro,
|
|
35
37
|
"metanorma/plugin/lutaml/lutaml_diagram_block_macro"
|
|
36
38
|
autoload :LutamlEaDiagramBlockMacro,
|
|
@@ -65,6 +67,10 @@ module Metanorma
|
|
|
65
67
|
"metanorma/plugin/lutaml/lutaml_xsd_preprocessor"
|
|
66
68
|
autoload :ParseError, "metanorma/plugin/lutaml/parse_error"
|
|
67
69
|
autoload :XmiParseCache, "metanorma/plugin/lutaml/xmi_parse_cache"
|
|
70
|
+
autoload :XmiPartialLoadRegistry,
|
|
71
|
+
"metanorma/plugin/lutaml/xmi_partial_load_registry"
|
|
72
|
+
autoload :XmiPartialLoadPreprocessor,
|
|
73
|
+
"metanorma/plugin/lutaml/xmi_partial_load_registry"
|
|
68
74
|
autoload :SourceExtractor, "metanorma/plugin/lutaml/source_extractor"
|
|
69
75
|
autoload :Utils, "metanorma/plugin/lutaml/utils"
|
|
70
76
|
autoload :Yaml2TextPreprocessor,
|
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
|
|
31
31
|
spec.add_dependency "asciidoctor"
|
|
32
32
|
spec.add_dependency "coradoc", "~> 1.1.8"
|
|
33
|
-
spec.add_dependency "ea"
|
|
33
|
+
spec.add_dependency "ea", ">= 0.6.41" # Ea::Xmi.load/load_graph (lutaml/ea#86): the partial-load API this gem calls
|
|
34
34
|
spec.add_dependency "expressir", "~> 2.3", ">= 2.3.5"
|
|
35
35
|
spec.add_dependency "isodoc"
|
|
36
36
|
spec.add_dependency "liquid"
|
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.54
|
|
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-
|
|
11
|
+
date: 2026-10-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: asciidoctor
|
|
@@ -44,14 +44,14 @@ dependencies:
|
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
47
|
+
version: 0.6.41
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
54
|
+
version: 0.6.41
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: expressir
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -224,6 +224,7 @@ files:
|
|
|
224
224
|
- lib/metanorma/plugin/lutaml/express_remark_adapter.rb
|
|
225
225
|
- lib/metanorma/plugin/lutaml/express_remarks_decorator.rb
|
|
226
226
|
- lib/metanorma/plugin/lutaml/file_not_found_error.rb
|
|
227
|
+
- lib/metanorma/plugin/lutaml/gc_budget.rb
|
|
227
228
|
- lib/metanorma/plugin/lutaml/json2_text_preprocessor.rb
|
|
228
229
|
- lib/metanorma/plugin/lutaml/liquid.rb
|
|
229
230
|
- lib/metanorma/plugin/lutaml/liquid/custom_blocks.rb
|
|
@@ -250,6 +251,7 @@ files:
|
|
|
250
251
|
- lib/metanorma/plugin/lutaml/liquid_templates/_packages_entity_list_class.liquid
|
|
251
252
|
- lib/metanorma/plugin/lutaml/liquid_templates/_packages_entity_list_classes.liquid
|
|
252
253
|
- lib/metanorma/plugin/lutaml/liquid_templates/_packages_enum.liquid
|
|
254
|
+
- lib/metanorma/plugin/lutaml/lutaml_data_preprocessor.rb
|
|
253
255
|
- lib/metanorma/plugin/lutaml/lutaml_diagram_base.rb
|
|
254
256
|
- lib/metanorma/plugin/lutaml/lutaml_diagram_block.rb
|
|
255
257
|
- lib/metanorma/plugin/lutaml/lutaml_diagram_block_macro.rb
|
|
@@ -277,6 +279,7 @@ files:
|
|
|
277
279
|
- lib/metanorma/plugin/lutaml/xmi_context_builder.rb
|
|
278
280
|
- lib/metanorma/plugin/lutaml/xmi_package_filter.rb
|
|
279
281
|
- lib/metanorma/plugin/lutaml/xmi_parse_cache.rb
|
|
282
|
+
- lib/metanorma/plugin/lutaml/xmi_partial_load_registry.rb
|
|
280
283
|
- lib/metanorma/plugin/lutaml/xmi_renderer.rb
|
|
281
284
|
- lib/metanorma/plugin/lutaml/yaml2_text_preprocessor.rb
|
|
282
285
|
- metanorma-plugin-lutaml.gemspec
|