metanorma-plugin-lutaml 0.7.10 → 0.7.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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(