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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4df1a140d2fef0dd3a780ac070ab6f6a3906191da12c6d72b30883ad0217bf25
4
- data.tar.gz: ad4569a0ce6ae86577978e7358de9e98c1955227755efa699a7182aca38ab428
3
+ metadata.gz: 608d678b19ef1c180dc4e244f98d329a8452d9c875469978b6d84247894c09d5
4
+ data.tar.gz: 64269c82a46666d5b50df626671d985917c6acb845708a8d527b6070a2da9e1e
5
5
  SHA512:
6
- metadata.gz: 31f6b4b4a670f661412cedc5cdb5538b074054f6618a35e06bfa15080af2dedba5075b237f163e4ba63e8ab99d95b92dc1ed8b99c35111a8abf588616fa62c37
7
- data.tar.gz: 3edd282734af0e5b2d3f6e74daa6e5b9a310e51b022baca413ab00aac448a186a76d22ffb8e5924bacdf0c9c4aaf08b50ce877e2f63a22f83c3d536b68219258
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
- parse_header(block_header_match)
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].split(",").map(&:strip)
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(segments)
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
- "#{e.message}",
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
- .transform_values { |v| deep_stringify(v) }
154
+ .transform_values { |v| deep_stringify(v) }
130
155
  when Array
131
156
  value.map { |v| deep_stringify(v) }
132
157
  else
@@ -1,7 +1,7 @@
1
1
  module Metanorma
2
2
  module Plugin
3
3
  module Lutaml
4
- VERSION = "0.7.55.pre.alpha.2".freeze
4
+ VERSION = "0.7.55".freeze
5
5
  end
6
6
  end
7
7
  end
@@ -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
- root_package = package_level(lutaml_document.to_liquid,
12
- options.package_root_level || 1)
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("external_classes" => options.external_classes),
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(/(?<!\\)&lt;&lt;section-
15
+ (?<target>(?:(?!&gt;&gt;)[^,\s])+?)
16
+ (?:,(?<content>.*?))?&gt;&gt;/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
+ "&lt;&lt;section-#{target}#{label}&gt;&gt;"
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(lutaml_doc, document, add_context, options) # rubocop:disable Metrics/MethodLength
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: create_context_object(lutaml_doc,
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.pre.alpha.2
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-27 00:00:00.000000000 Z
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