metanorma-plugin-lutaml 0.7.10 → 0.7.11

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.
@@ -4,11 +4,11 @@ require "liquid"
4
4
  require "asciidoctor"
5
5
  require "asciidoctor/reader"
6
6
  require "lutaml"
7
- # require "lutaml/uml"
8
7
  require "xmi"
9
8
  require "lutaml/xmi"
10
9
  require "metanorma/plugin/lutaml/utils"
11
10
  require "metanorma/plugin/lutaml/asciidoctor/preprocessor"
11
+ require "metanorma/plugin/lutaml/lutaml_ea_xmi_base"
12
12
 
13
13
  module Metanorma
14
14
  module Plugin
@@ -17,292 +17,17 @@ module Metanorma
17
17
  # from liquid drop object
18
18
  class LutamlEaXmiPreprocessor <
19
19
  ::Asciidoctor::Extensions::Preprocessor
20
+ include LutamlEaXmiBase
21
+
20
22
  MACRO_REGEXP =
21
23
  /\[lutaml_ea_xmi,([^,]+),?(.+)?\]/.freeze
22
- LIQUID_INCLUDE_PATH = File.join(
23
- Gem.loaded_specs["metanorma-plugin-lutaml"].full_gem_path,
24
- "lib", "metanorma", "plugin", "lutaml", "liquid_templates"
25
- )
26
- DEFAULT_RENDER_INCLUDE = "packages"
27
- RENDER_STYLES_INCLUDES = {
28
- "default" => "packages",
29
- "entity_list" => "packages_entity_list",
30
- "entity_list_class" => "packages_entity_list_class",
31
- "data_dictionary" => "packages_data_dictionary",
32
- }.freeze
33
- RENDER_STYLE_ATTRIBUTE = "render_style"
34
- SUPPORTED_NESTED_MACRO = %w[
35
- before diagram_include_block after include_block package_text
36
- ].freeze
37
- # search document for block `lutaml_ea_xmi`
38
- # read include derectives that goes after that in block and transform
39
- # into yaml2text blocks
40
- def process(document, reader)
41
- r = Asciidoctor::PreprocessorNoIfdefsReader.new document, reader.lines
42
- input_lines = r.readlines.to_enum
43
- Asciidoctor::PreprocessorNoIfdefsReader
44
- .new(document, processed_lines(document, input_lines))
45
- end
46
24
 
47
25
  private
48
26
 
49
- def lutaml_document_from_file_or_cache(document, file_path, yaml_config)
50
- full_path = Utils.relative_file_path(document, file_path)
51
- if document.attributes["lutaml_xmi_cache"] &&
52
- document.attributes["lutaml_xmi_cache"][full_path]
53
- return document.attributes["lutaml_xmi_cache"][full_path]
54
- end
55
-
56
- yaml_config["ea_extension"]&.each do |ea_extension_path|
57
- ea_extension_full_path = File.expand_path(
58
- ea_extension_path, File.dirname(file_path)
59
- )
60
- Xmi::EaRoot.load_extension(ea_extension_full_path)
61
- end
62
-
63
- result_document = ::Lutaml::XMI::Parsers::XML.serialize_xmi_to_liquid(
64
- File.new(
65
- full_path, encoding: "UTF-8"
66
- ),
67
- )
68
-
69
- document.attributes["lutaml_xmi_cache"] ||= {}
70
- document.attributes["lutaml_xmi_cache"][full_path] = result_document
71
- result_document
72
- end
73
-
74
- def parse_yaml_config_file(document, file_path)
75
- return {} if file_path.nil?
76
-
77
- relative_file_path = Utils.relative_file_path(document, file_path)
78
- YAML.safe_load(File.read(relative_file_path, encoding: "UTF-8"))
79
- end
80
-
81
- def processed_lines(document, input_lines)
82
- result = []
83
- loop do
84
- result.push(*process_text_blocks(document, input_lines))
85
- end
86
- result
87
- end
88
-
89
- def process_text_blocks(document, input_lines)
90
- line = input_lines.next
91
- block_match = line.match(MACRO_REGEXP)
92
-
93
- return [line] if block_match.nil?
94
-
95
- yaml_config = parse_yaml_config_file(document, block_match[2])
96
- lutaml_document = lutaml_document_from_file_or_cache(document,
97
- block_match[1],
98
- yaml_config)
99
- fill_in_diagrams_attributes(document, lutaml_document)
100
- model_representation(
101
- lutaml_document, document,
102
- collect_additional_context(document, input_lines, input_lines.next),
103
- yaml_config
104
- )
105
- end
106
-
107
- def fill_in_entities_refs_attributes(document,
108
- lutaml_document, options)
109
- # render_style = options.fetch(RENDER_STYLE_ATTRIBUTE, "default")
110
- all_children_packages = lutaml_document.packages
111
- .map(&:children_packages).flatten
112
- package_flat_packages = lambda do |pks|
113
- pks.each_with_object({}) do |package, res|
114
- res[package.name] = package.xmi_id
115
- end
116
- end
117
- children_pks = package_flat_packages.call(all_children_packages)
118
- ref_dictionary = package_flat_packages.call(lutaml_document.packages)
119
- .merge(children_pks)
120
- %w[class enum data_type].each do |type|
121
- package_flat = lambda do |pks|
122
- pks.each_with_object({}) do |package, res|
123
- plural = type == "class" ? "classes" : "#{type}s"
124
- package.send(plural).map do |entity|
125
- res["#{type}:#{package.name}:#{entity.name}"] = entity.xmi_id
126
- end
127
- end
128
- end
129
- children_pks_diags = package_flat.call(all_children_packages)
130
- ref_dictionary = ref_dictionary
131
- .merge(package_flat.call(lutaml_document.packages)
132
- .merge(children_pks_diags))
133
- end
134
- document.attributes["lutaml_entity_id"] = ref_dictionary
135
- end
136
-
137
- def fill_in_diagrams_attributes(document, lutaml_document)
138
- package_flat_diagrams = lambda do |pks|
139
- pks.each_with_object({}) do |package, res|
140
- package.diagrams.map do |diag|
141
- res["#{package.name}:#{diag.name}"] = diag.xmi_id
142
- end
143
- end
144
- end
145
- children_pks_diags = package_flat_diagrams.call(
146
- lutaml_document.packages.map(&:children_packages).flatten,
147
- )
148
-
149
- document.attributes["lutaml_figure_id"] = package_flat_diagrams
150
- .call(lutaml_document.packages)
151
- .merge(children_pks_diags)
152
- end
153
-
154
- def collect_additional_context(document, input_lines, end_mark)
155
- additional_context = Hash.new { |hash, key| hash[key] = [] }
156
- additional_context["all_macros"] = []
157
- block_lines = []
158
- while (block_line = input_lines.next) != end_mark
159
- block_lines.push(block_line)
160
- end
161
- processed_lines =
162
- process(document, ::Asciidoctor::PreprocessorReader.new(
163
- document, block_lines
164
- ))
165
- .read_lines
166
- block_document = ::Asciidoctor::Document
167
- .new(processed_lines, {}).parse
168
- block_document.blocks.each do |block|
169
- next unless SUPPORTED_NESTED_MACRO.include?(block.attributes["role"])
170
-
171
- attrs = block.attributes
172
- name = attrs.delete("role")
173
- package = attrs.delete("package")
174
- macro_keyword = [name, package].compact.join(";")
175
- block_text = block.lines.length.positive? ? block.lines.join("\n") : ""
176
- additional_context[macro_keyword]
177
- .push({ "text" => block_text }.merge(attrs))
178
- additional_context["all_macros"]
179
- .push({ "text" => block_text,
180
- "type" => name, "package" => package }.merge(attrs))
181
- end
182
- additional_context
183
- end
184
-
185
- def package_level(lutaml_document, level)
186
- return lutaml_document if level <= 0
187
-
188
- package_level(lutaml_document["packages"].first, level - 1)
189
- end
190
-
191
- def create_context_object(lutaml_document, additional_context, options)
192
- root_package = package_level(lutaml_document.to_liquid,
193
- options["package_root_level"] || 1)
194
- if options.empty? || options["packages"].nil?
195
- return {
196
- "render_nested_packages" => true,
197
- "packages" => root_package["packages"],
198
- "root_packages" => [root_package],
199
- "additional_context" => additional_context
200
- .merge("external_classes" => options["external_classes"]),
201
- "name" => root_package["name"],
202
- }
203
- end
204
-
205
- all_packages = [root_package, *root_package["children_packages"]]
206
- {
207
- "packages" => sort_and_filter_out_packages(all_packages, options),
208
- "package_entities" => package_entities(options),
209
- "package_skip_sections" => package_skip_sections(options),
210
- "additional_context" => additional_context
211
- .merge("external_classes" => options["external_classes"]),
212
- "root_packages" => [root_package],
213
- "render_nested_packages" => options["render_nested_packages"] ||
214
- false,
215
- "name" => root_package["name"],
216
- }
217
- end
218
-
219
- def package_entities(options)
220
- return {} unless options["packages"]
221
-
222
- options["packages"]
223
- .find_all { |entity| entity.is_a?(Hash) && entity.values.first["render_entities"] }
224
- .map do |entity|
225
- [entity.keys.first,
226
- entity.values.first["render_entities"].map { |n| [n, true] }.to_h]
227
- end.to_h
228
- end
229
-
230
- def package_skip_sections(options)
231
- return {} unless options["packages"]
232
-
233
- options["packages"]
234
- .find_all { |entity| entity.is_a?(Hash) && entity.values.first["skip_tables"] }
235
- .map do |entity|
236
- [entity.keys.first, entity.values.first["skip_tables"].map do |n|
237
- [n, true]
238
- end.to_h]
239
- end.to_h
240
- end
241
-
242
- def sort_and_filter_out_packages(all_packages, options)
243
- return all_packages if options["packages"].nil?
244
-
245
- result = []
246
- # Step one - filter out all skipped packages
247
- options["packages"]
248
- .find_all { |entity| entity.is_a?(Hash) && entity["skip"] }
249
- .each do |entity|
250
- entity_regexp = config_entity_regexp(entity["skip"])
251
- all_packages
252
- .delete_if { |package| package["name"] =~ entity_regexp }
253
- end
254
- # Step two - select supplied packages by pattern
255
- options["packages"]
256
- .find_all { |entity| entity.is_a?(String) || (entity.is_a?(Hash) && !entity["skip"]) }
257
- .each do |entity_obj|
258
- entity = entity_obj.is_a?(String) ? entity_obj : entity_obj.keys.first
259
- entity_regexp = config_entity_regexp(entity)
260
- all_packages.each do |package|
261
- if package["name"]&.match?(entity_regexp)
262
- result.push(package)
263
- all_packages
264
- .delete_if { |nest_package| nest_package["name"] == package["name"] }
265
- end
266
- end
267
- end
268
- result
269
- end
270
-
271
- def config_entity_regexp(entity)
272
- additional_sym = ".*" if /\*$/.match?(entity)
273
- %r{^#{Regexp.escape(entity.delete('*'))}#{additional_sym}$}
274
- end
275
-
276
- def model_representation(lutaml_document, document, additional_context,
277
- options)
278
- fill_in_entities_refs_attributes(document, lutaml_document, options)
279
- render_result, errors = Utils.render_liquid_string(
280
- template_string: template(options["section_depth"] || 2,
281
- options["render_style"],
282
- options["include_root"]),
283
- context_items: create_context_object(lutaml_document,
284
- additional_context,
285
- options),
286
- context_name: "context",
287
- document: document,
288
- include_path: LIQUID_INCLUDE_PATH,
27
+ def parse_result_document(full_path)
28
+ ::Lutaml::XMI::Parsers::XML.serialize_xmi_to_liquid(
29
+ File.new(full_path, encoding: "UTF-8"),
289
30
  )
290
- Utils.notify_render_errors(document, errors)
291
- render_result.split("\n")
292
- end
293
-
294
- def template(section_depth, render_style, include_root)
295
- include_name = RENDER_STYLES_INCLUDES.fetch(render_style,
296
- DEFAULT_RENDER_INCLUDE)
297
- result = ""
298
- if include_root
299
- result += <<~LIQUID
300
- {% include "#{include_name}", package_skip_sections: context.package_skip_sections, package_entities: context.package_entities, context: context.root_packages, additional_context: context.additional_context, render_nested_packages: false %}
301
- LIQUID
302
- end
303
- result + <<~LIQUID
304
- {% include "#{include_name}", depth: #{section_depth}, package_skip_sections: context.package_skip_sections, package_entities: context.package_entities, context: context, additional_context: context.additional_context, render_nested_packages: context.render_nested_packages %}
305
- LIQUID
306
31
  end
307
32
  end
308
33
  end
@@ -1,17 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "liquid_drops/klass_table_drop"
4
- require_relative "liquid_drops/klass_table_attribute_drop"
5
- require_relative "liquid_drops/klass_table_general_drop"
6
- require_relative "liquid_drops/klass_table_content_drop"
7
-
8
3
  module Metanorma
9
4
  module Plugin
10
5
  module Lutaml
11
6
  class LutamlKlassTableBlockMacro < ::Asciidoctor::Extensions::BlockMacroProcessor
12
- DEFAULT_TEMPLATE_PATH = File.join(
7
+ DEFAULT_TEMPLATE = File.join(
13
8
  Gem::Specification.find_by_name("metanorma-plugin-lutaml").gem_dir,
14
- "lib", "metanorma", "plugin", "lutaml", "liquid_templates"
9
+ "lib", "metanorma", "plugin", "lutaml", "liquid_templates",
10
+ "_klass_table.liquid"
15
11
  )
16
12
 
17
13
  use_dsl
@@ -20,154 +16,46 @@ module Metanorma
20
16
  def process(parent, target, attrs)
21
17
  xmi_path = Utils.relative_file_path(parent.document, target)
22
18
 
23
- if attrs["tmpl_folder"]
24
- attrs["tmpl_folder"] = Utils.relative_file_path(
25
- parent.document, attrs["tmpl_folder"]
19
+ if attrs["template"]
20
+ attrs["template"] = Utils.relative_file_path(
21
+ parent.document, attrs["template"]
26
22
  )
27
23
  end
28
24
 
29
- gen = ::Lutaml::XMI::Parsers::XML.serialize_generalization_by_name(
25
+ klass = ::Lutaml::XMI::Parsers::XML.serialize_generalization_by_name(
30
26
  xmi_path, attrs["name"]
31
27
  )
32
28
 
33
- render(gen, parent, attrs)
29
+ render(klass, parent, attrs)
34
30
  end
35
31
 
36
32
  private
37
33
 
38
- def render(gen, parent, attrs) # rubocop:disable Metrics/MethodLength
39
- content = {}
40
-
41
- content[:owned_props] = render_owned_props(
42
- gen, parent, attrs
43
- )
44
- content[:assoc_props] = render_assoc_props(
45
- gen, parent, attrs
46
- )
47
- content[:inherited_props] = render_inherited_props(
48
- gen, parent, attrs
49
- )
50
- content[:inherited_assoc_props] = render_inherited_assoc_props(
51
- gen, parent, attrs
52
- )
53
-
54
- rendered_table = render_table(gen, parent, attrs, content)
34
+ def render(klass, parent, attrs)
35
+ rendered_table = render_table(klass, parent, attrs)
55
36
 
56
37
  block = create_open_block(parent, "", attrs)
57
38
  parse_content(block, rendered_table, attrs)
58
39
  end
59
40
 
60
- def render_table(gen, parent, attrs, content)
61
- table_tmpl = get_template(
62
- parent.document, attrs, :table
63
- )
64
-
65
- table_tmpl.assigns["root"] = KlassTableDrop.new(gen)
66
- table_tmpl.assigns["content"] =
67
- KlassTableContentDrop.new(content)
68
-
41
+ def render_table(klass, parent, attrs)
42
+ table_tmpl = get_template(parent.document, attrs)
43
+ table_tmpl.assigns["klass"] = klass
69
44
  table_tmpl.render
70
45
  end
71
46
 
72
- def render_general_rows(general_item, parent, attrs)
73
- row_tmpl = get_template(
74
- parent.document, attrs, :row
75
- )
76
- render_rows(general_item, row_tmpl)
77
- end
78
-
79
- def render_assoc_rows(general_item, parent, attrs)
80
- row_tmpl = get_template(
81
- parent.document, attrs, :assoc_row
82
- )
83
- render_rows(general_item, row_tmpl)
84
- end
85
-
86
- def render_rows(general_item, row_tmpl)
87
- gen_attrs = general_item[:general_attributes]
88
- upper_klass = general_item[:general_upper_klass]
89
- gen_name = general_item[:general_name]
90
- attributes = prepare_row_drops(gen_attrs, upper_klass, gen_name)
91
- row_tmpl.assigns["attributes"] = attributes
92
- row_tmpl.render
93
- end
94
-
95
- def prepare_row_drops(gen_attrs, upper_klass, gen_name) # rubocop:disable Metrics/MethodLength
96
- gen_attrs.map do |attr|
97
- attr[:gen_name] = gen_name
98
- attr[:upper_klass] = upper_klass
99
- attr[:name_ns] = case attr[:type_ns]
100
- when "core", "gml"
101
- upper_klass
102
- else
103
- attr[:type_ns]
104
- end
105
-
106
- attr[:name_ns] = upper_klass if attr[:name_ns].nil?
107
-
108
- KlassTableAttributeDrop.new(attr)
47
+ def get_template(document, attrs)
48
+ template = DEFAULT_TEMPLATE
49
+ if attrs["template"]
50
+ template = attrs["template"]
109
51
  end
110
- end
111
-
112
- def render_owned_props(gen, parent, attrs)
113
- render_general_rows(gen, parent, attrs)
114
- end
115
-
116
- def render_assoc_props(gen, parent, attrs)
117
- render_assoc_rows(gen, parent, attrs)
118
- end
119
-
120
- def render_inherited_props(gen, parent, attrs)
121
- render_inherited_rows(gen, parent, attrs, assoc: false)
122
- end
123
-
124
- def render_inherited_assoc_props(gen, parent, attrs)
125
- render_inherited_rows(gen, parent, attrs, assoc: true)
126
- end
127
-
128
- def render_inherited_rows(gen, parent, attrs, assoc: false) # rubocop:disable Metrics/MethodLength
129
- rendered_rows = ""
130
- general_item = gen[:general]
131
-
132
- while general_item && !general_item.empty?
133
- rendered_rows += if assoc
134
- render_assoc_rows(general_item, parent, attrs)
135
- else
136
- render_general_rows(general_item, parent, attrs)
137
- end
138
- general_item = general_item[:general]
139
- end
140
-
141
- rendered_rows
142
- end
143
-
144
- def get_template(document, attrs, tmpl_type)
145
- tmpl_folder = DEFAULT_TEMPLATE_PATH
146
- if attrs["tmpl_folder"]
147
- tmpl_folder = attrs["tmpl_folder"]
148
- end
149
- template_path = get_default_template_path(tmpl_type, tmpl_folder,
150
- attrs)
151
52
 
152
53
  rel_tmpl_path = Utils.relative_file_path(
153
- document, template_path
54
+ document, template
154
55
  )
155
56
 
156
57
  ::Liquid::Template.parse(File.read(rel_tmpl_path))
157
58
  end
158
-
159
- def get_default_template_path(tmpl_type, tmpl_folder, attrs)
160
- liquid_template = case tmpl_type
161
- when :row
162
- attrs["row_tmpl_name"] || "_klass_row"
163
- when :assoc_row
164
- attrs["assoc_row_tmpl_name"] || "_klass_assoc_row"
165
- else
166
- attrs["table_tmpl_name"] || "_klass_table"
167
- end
168
-
169
- File.join(tmpl_folder, "#{liquid_template}.liquid")
170
- end
171
59
  end
172
60
  end
173
61
  end
@@ -167,18 +167,21 @@ module Metanorma
167
167
  options
168
168
  end
169
169
 
170
- def decorate_schema_object(schema:, document:, indexes:, index_names:, selected:, options:)
170
+ def decorate_schema_object(schema:, document:, indexes:, index_names:, # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/ParameterLists
171
+ selected:, options:)
171
172
  # Mark a schema as "selected" with `.selected`
172
173
  schema["selected"] = true if selected
173
174
 
174
175
  # Provide pretty-formatted code under `.formatted`
175
- index_found_key, index_found_value = indexes.detect do |k,v|
176
- found = v[:wrapper].original_document.schemas.detect do |s|
176
+ _index_found_key, index_found_value = indexes.detect do |k, v|
177
+ _found = get_original_document(v[:wrapper]).schemas.detect do |s|
177
178
  s.id == schema["id"]
178
179
  end
179
180
  end
180
181
 
181
- schema["formatted"] = index_found_value[:wrapper].original_document.schemas.detect do |s|
182
+ schema["formatted"] = get_original_document(
183
+ index_found_value[:wrapper],
184
+ ).schemas.detect do |s|
182
185
  s.id == schema["id"]
183
186
  end.to_s(no_remarks: true)
184
187
 
@@ -193,8 +196,15 @@ module Metanorma
193
196
  ) || {}
194
197
  end
195
198
 
196
- def render_template(document:, lines:, context_name:, index_names:, options:, indexes:)
199
+ def get_original_document(wrapper)
200
+ doc = wrapper
201
+ return doc if doc.instance_of?(::Lutaml::XMI::RootDrop)
197
202
 
203
+ doc.original_document
204
+ end
205
+
206
+ def render_template(document:, lines:, context_name:, index_names:, # rubocop:disable Metrics/ParameterLists,Metrics/AbcSize,Metrics/MethodLength
207
+ options:, indexes:)
198
208
  config_yaml_path = options.delete("config_yaml")
199
209
  config = read_config_yaml_file(document, config_yaml_path)
200
210
  selected_schemas = config["selected_schemas"]
@@ -204,21 +214,19 @@ module Metanorma
204
214
  document: document,
205
215
  indexes: indexes,
206
216
  ).map do |items|
207
-
208
217
  serialized_hash = items[:serialized_hash]
209
218
 
210
- if serialized_hash["schemas"]
211
- serialized_hash["schemas"].map! do |schema|
212
-
213
- decorate_schema_object(
214
- schema: schema,
215
- document: document,
216
- index_names: index_names,
217
- indexes: indexes,
218
- selected: selected_schemas && selected_schemas.include?(schema["id"]),
219
- options: options
220
- )
221
- end
219
+ serialized_hash["schemas"]&.map! do |schema|
220
+ decorate_schema_object(
221
+ schema: schema,
222
+ document: document,
223
+ index_names: index_names,
224
+ indexes: indexes,
225
+ selected: selected_schemas && selected_schemas.include?(
226
+ schema["id"],
227
+ ),
228
+ options: options,
229
+ )
222
230
  end
223
231
 
224
232
  render_block(