metanorma-standoc 3.1.3 → 3.1.5

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/metanorma/standoc/biblio-standoc.rng +0 -49
  3. data/lib/metanorma/standoc/biblio.rng +11 -1
  4. data/lib/metanorma/standoc/blocks_notes.rb +1 -0
  5. data/lib/metanorma/standoc/cleanup.rb +4 -1
  6. data/lib/metanorma/standoc/cleanup_bibdata.rb +16 -1
  7. data/lib/metanorma/standoc/cleanup_boilerplate.rb +62 -16
  8. data/lib/metanorma/standoc/cleanup_maths.rb +2 -1
  9. data/lib/metanorma/standoc/cleanup_section_names.rb +8 -8
  10. data/lib/metanorma/standoc/converter.rb +2 -3
  11. data/lib/metanorma/standoc/front.rb +0 -8
  12. data/lib/metanorma/standoc/front_committee.rb +140 -0
  13. data/lib/metanorma/standoc/front_contributor.rb +14 -3
  14. data/lib/metanorma/standoc/front_organisation.rb +24 -18
  15. data/lib/metanorma/standoc/isodoc.rng +24 -1
  16. data/lib/metanorma/standoc/macros.rb +1 -3
  17. data/lib/metanorma/standoc/ref_queue.rb +1 -1
  18. data/lib/metanorma/standoc/ref_utility.rb +27 -2
  19. data/lib/metanorma/standoc/regex.rb +6 -5
  20. data/lib/metanorma/standoc/sectiontype.rb +1 -0
  21. data/lib/metanorma/standoc/utils.rb +11 -1
  22. data/lib/metanorma/standoc/validate.rb +1 -1
  23. data/lib/metanorma/standoc/validate_section.rb +12 -2
  24. data/lib/metanorma/standoc/validate_table.rb +3 -1
  25. data/lib/metanorma/standoc/version.rb +1 -1
  26. data/metanorma-standoc.gemspec +1 -0
  27. metadata +17 -12
  28. data/lib/asciidoctor/standoc/datamodel/attributes_table_preprocessor.rb +0 -3
  29. data/lib/asciidoctor/standoc/datamodel/diagram_preprocessor.rb +0 -3
  30. data/lib/asciidoctor/standoc/datamodel/plantuml_renderer.rb +0 -8
  31. data/lib/asciidoctor/standoc/macros_plantuml.rb +0 -2
  32. data/lib/metanorma/standoc/datamodel/attributes_table_preprocessor.rb +0 -57
  33. data/lib/metanorma/standoc/datamodel/diagram_preprocessor.rb +0 -103
  34. data/lib/metanorma/standoc/datamodel/plantuml_renderer.rb +0 -409
  35. data/lib/metanorma/standoc/macros_plantuml.rb +0 -143
  36. data/lib/metanorma/standoc/views/datamodel/model_representation.adoc.erb +0 -30
  37. data/lib/metanorma/standoc/views/datamodel/plantuml_representation.adoc.erb +0 -20
@@ -1,409 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "erb"
4
-
5
- module Metanorma
6
- module Datamodel
7
- class PlantumlRenderer
8
- TEMPLATES_PATH = File.expand_path("../views/datamodel", __dir__).freeze
9
-
10
- attr_reader :yml, :plantuml_path
11
-
12
- def initialize(yml, plantuml_path)
13
- @yml = yml
14
- @plantuml_path = plantuml_path
15
- end
16
-
17
- def join_as_plantuml(*ary)
18
- ary.compact.join("\n").sub(/(?<!\s)\s+\Z/, "")
19
- end
20
-
21
- def render
22
- ERB.new(
23
- File.read(
24
- File.join(TEMPLATES_PATH, "plantuml_representation.adoc.erb"),
25
- ),
26
- ).result(binding)
27
- end
28
-
29
- def diagram_caption
30
- yml["caption"]
31
- end
32
-
33
- def imports_yml_to_plantuml
34
- return if empty?(yml, "imports")
35
-
36
- <<~TEMPLATE
37
- '******* IMPORTS ******************************************************
38
- !include #{plantuml_path}/style.uml.inc
39
- TEMPLATE
40
- end
41
-
42
- def class_defs_yml_to_plantuml
43
- return if empty?(yml, "classes") && empty?(yml, "enums")
44
-
45
- <<~TEMPLATE
46
- '******* CLASS DEFINITIONS ********************************************
47
- #{join_as_plantuml(
48
- classes_to_classes_plantuml(yml['classes']),
49
- enums_to_enums_plantuml(yml['enums']),
50
- )}
51
- TEMPLATE
52
- end
53
-
54
- def class_groups_yml_to_plantuml
55
- return if empty?(yml, "groups")
56
-
57
- <<~TEMPLATE
58
- '******* CLASS GROUPS *************************************************
59
- #{join_as_plantuml(
60
- groups_to_plantuml(yml['groups']),
61
- )}
62
- TEMPLATE
63
- end
64
-
65
- def class_relations_yml_to_plantuml
66
- return if empty?(yml, "classes") && empty?(yml, "relations")
67
-
68
- <<~TEMPLATE
69
- '******* CLASS RELATIONS **********************************************
70
- #{join_as_plantuml(
71
- classes_to_relations_plantuml(yml['classes']),
72
- relations_to_plantuml(nil, yml['relations']),
73
- )}
74
- TEMPLATE
75
- end
76
-
77
- def diagram_options_yml_to_plantuml
78
- return if empty?(yml, "diagram_options")
79
-
80
- <<~TEMPLATE
81
- '******* DIAGRAM SPECIFIC CONFIG **************************************
82
- #{join_as_plantuml(
83
- diagram_options_to_plantuml(yml['diagram_options']),
84
- )}
85
- TEMPLATE
86
- end
87
-
88
- def bottom_yml_to_plantuml
89
- return if empty?(yml, "bottom")
90
-
91
- <<~TEMPLATE
92
- '******* BOTTOM OVERRIDE CONFIG **************************************
93
- #{join_as_plantuml(bottom_to_plantuml(yml['bottom']))}
94
- TEMPLATE
95
- end
96
-
97
- def fidelity_yml_to_plantuml
98
- return if empty?(yml, "fidelity")
99
-
100
- <<~TEMPLATE
101
- '******* FIDELITY *****************************************************
102
- #{join_as_plantuml(fidelity_to_plantuml(yml['fidelity']))}
103
- TEMPLATE
104
- end
105
-
106
- def classes_to_classes_plantuml(classes)
107
- classes ||= {}
108
-
109
- return if classes.empty?
110
-
111
- classes.map do |(class_name, class_hash)|
112
- class_to_plantuml(class_name, class_hash)
113
- end.join("\n")
114
- end
115
-
116
- def class_to_plantuml(class_name, class_hash)
117
- return unless class_name
118
-
119
- class_hash ||= {}
120
-
121
- <<~TEMPLATE
122
- class #{class_name}#{model_stereotype_to_plantuml(class_hash['type'])} {
123
- #{join_as_plantuml(
124
- attributes_to_plantuml(class_hash['attributes']),
125
- constraints_to_plantuml(class_hash['constraints']),
126
- )}
127
- }
128
- TEMPLATE
129
- end
130
-
131
- def attributes_to_plantuml(attributes)
132
- return unless attributes
133
-
134
- attributes.map do |(attr_name, attr_hash)|
135
- attribute_to_plantuml(attr_name, attr_hash)
136
- end.join("").sub(/\n\Z/, "")
137
- end
138
-
139
- def attribute_to_plantuml(attr_name, attr_hash)
140
- <<~TEMPLATE
141
- +#{attr_name}: #{attr_hash['type']}#{attribute_cardinality_plantuml(attr_hash['cardinality'])}
142
- TEMPLATE
143
- end
144
-
145
- def attribute_cardinality_plantuml(cardinality, with_bracket = true)
146
- return "" if cardinality.nil? ||
147
- (cardinality["min"] == cardinality["max"] &&
148
- cardinality["min"] == 1)
149
-
150
- card = "#{cardinality['min']}..#{cardinality['max']}"
151
- return card unless with_bracket
152
-
153
- "[#{card}]"
154
- end
155
-
156
- def constraints_to_plantuml(constraints)
157
- constraints ||= []
158
-
159
- return if constraints.empty?
160
-
161
- constraints_output = constraints.map do |constraint|
162
- " {#{constraint}}"
163
- end
164
-
165
- <<~TEMPLATE
166
- __ constraints __
167
- #{join_as_plantuml(
168
- *constraints_output,
169
- )}
170
- TEMPLATE
171
- end
172
-
173
- def classes_to_relations_plantuml(classes)
174
- output_ary = classes.map do |(class_name, class_hash)|
175
- class_hash ||= {}
176
- relations = class_hash["relations"]
177
- relations_to_plantuml(class_name, relations)
178
- end
179
-
180
- join_as_plantuml(*output_ary)
181
- end
182
-
183
- def relations_to_plantuml(class_name, relations)
184
- return unless relations
185
-
186
- output_ary = relations.map do |relation|
187
- source = class_name || relation["source"]
188
- relation_to_plantuml(source,
189
- relation["target"],
190
- relation)
191
- end
192
-
193
- join_as_plantuml(*output_ary)
194
- end
195
-
196
- def relation_arrow(relationship, relation)
197
- [
198
- relationship_type_to_plantuml("source",
199
- relationship["source"]["type"]),
200
- (relation["direction"]).to_s,
201
- relationship_type_to_plantuml("target",
202
- relationship["target"]["type"]),
203
- ].compact.join("-")
204
- end
205
-
206
- def relation_label(action)
207
- return "" unless action
208
-
209
- case action["direction"]
210
- when "source"
211
- " : < #{action['verb']}"
212
- when "target"
213
- " : #{action['verb']} >"
214
- else
215
- ""
216
- end
217
- end
218
-
219
- def source_arrow_end(source, relationship)
220
- source_attribute = relationship_cardinality_to_plantuml(
221
- relationship["source"]["attribute"],
222
- )
223
- [source, source_attribute].join(" ")
224
- end
225
-
226
- def target_arrow_end(target, relationship, action)
227
- target_attribute = relationship_cardinality_to_plantuml(
228
- relationship["target"]["attribute"],
229
- )
230
- [
231
- [target_attribute, target].join(" "),
232
- relation_label(action),
233
- ].join
234
- end
235
-
236
- def relation_association(source, target, association)
237
- return unless association
238
-
239
- "\n(#{source}, #{target}) . #{association}"
240
- end
241
-
242
- def relation_to_plantuml(source, target, relation)
243
- relationship = relation["relationship"] || {}
244
- relationship["source"] ||= {}
245
- relationship["target"] ||= {}
246
- relation_output_lines(source, target, relation, relationship)
247
- end
248
-
249
- def relation_output_lines(source, target, relation, relationship)
250
- output_lines = [
251
- source_arrow_end(source, relationship),
252
- relation_arrow(relationship, relation),
253
- target_arrow_end(target, relationship, relation["action"]),
254
- relation_association(source, target, relationship["association"]),
255
- ].join(" ")
256
-
257
- join_as_plantuml(*output_lines)
258
- end
259
-
260
- def relationship_type_to_plantuml(relation_end, relationship_type)
261
- is_source = (relation_end == "source")
262
- mappings = {
263
- "direct" => is_source ? "<" : ">",
264
- "inheritance" => is_source ? "<|" : "|>",
265
- "composition" => "*",
266
- "aggregation" => "o",
267
- }
268
- mappings.fetch(relationship_type, "")
269
- end
270
-
271
- def relationship_cardinality_to_plantuml(attribute)
272
- attribute_name = (attribute || {}).keys.first
273
-
274
- return unless attribute_name
275
-
276
- attribute_hash = attribute[attribute_name] || {}
277
- card = attribute_cardinality(attribute_hash["cardinality"])
278
- "\"+#{attribute_name}#{card}\""
279
- end
280
-
281
- def attribute_cardinality(attribute_cardinality)
282
- cardinality = ""
283
- if attribute_cardinality
284
- cardinality = attribute_cardinality_plantuml(
285
- attribute_cardinality,
286
- false,
287
- )
288
- cardinality = " #{cardinality}"
289
- end
290
- cardinality
291
- end
292
-
293
- def enums_to_enums_plantuml(enums)
294
- enums ||= {}
295
-
296
- enums.map do |(enum_name, enum_hash)|
297
- enum_to_plantuml(enum_name, enum_hash)
298
- end.join("\n\n")
299
- end
300
-
301
- def enum_to_plantuml(enum_name, enum_hash)
302
- enum_hash ||= {}
303
-
304
- <<~TEMPLATE
305
- enum #{enum_name}#{model_stereotype_to_plantuml(enum_hash['type'])} {
306
- #{join_as_plantuml(enum_values_to_plantuml(enum_hash['values']))}
307
- }
308
- TEMPLATE
309
- end
310
-
311
- def model_stereotype_to_plantuml(model_stereotype)
312
- return "" unless model_stereotype
313
-
314
- " <<#{model_stereotype}>>"
315
- end
316
-
317
- def enum_values_to_plantuml(enum_values)
318
- output_ary = enum_values.map do |(enum_value, _enum_value_hash)|
319
- " #{enum_value}"
320
- end
321
-
322
- join_as_plantuml(*output_ary)
323
- end
324
-
325
- def groups_to_plantuml(groups)
326
- groups ||= []
327
- return if groups.empty?
328
-
329
- groups.reduce("") do |output, group|
330
- output += "\ntogether {\n"
331
- group.each do |class_name|
332
- output += "\nclass #{class_name}\n"
333
- end
334
- output += "\n}\n"
335
- output
336
- end
337
- end
338
-
339
- def diagram_options_to_plantuml(diagram_options)
340
- diagram_options ||= []
341
- return if diagram_options.empty?
342
-
343
- "#{diagram_options.join("\n")}\n"
344
- end
345
-
346
- def bottom_to_plantuml(bottom)
347
- bottom ||= []
348
- return if bottom.empty?
349
-
350
- "#{bottom.join("\n")}\n"
351
- end
352
-
353
- def format_hidden_class(accum, fidelity_classes, class_hash)
354
- return accum if class_hash["relations"].nil?
355
-
356
- class_hash["relations"].each_with_object(accum) do |relation, acc|
357
- format_source_target_relation(relation, fidelity_classes, acc)
358
- format_association_relation(relation, fidelity_classes, acc)
359
- end
360
- end
361
-
362
- def format_source_target_relation(relation, fidelity_classes, acc)
363
- %w[source target].each do |type|
364
- next unless relation[type] && !fidelity_classes.key?(relation[type])
365
-
366
- acc.merge!(relation[type] => true)
367
- end
368
- end
369
-
370
- def format_association_relation(relation, fidelity_classes, acc)
371
- return unless relation["relationship"] &&
372
- relation["relationship"]["association"]
373
-
374
- association = relation["relationship"]["association"]
375
- return unless association && !fidelity_classes.key?(association)
376
-
377
- acc.merge!(association => true)
378
- end
379
-
380
- def hide_other_classes(fidelity)
381
- return "" if fidelity.nil? || fidelity["classes"].nil?
382
-
383
- output = ""
384
- hidden_classes = fidelity["classes"]
385
- .reduce({}) do |acc, (_class_name, class_hash)|
386
- format_hidden_class(acc, fidelity["classes"], class_hash)
387
- end
388
-
389
- hidden_classes.each_key do |hidden_class_name|
390
- output += "\nhide #{hidden_class_name}\n"
391
- end
392
- output
393
- end
394
-
395
- def fidelity_to_plantuml(fidelity)
396
- return "" if fidelity.nil?
397
-
398
- output = ""
399
- output += hide_other_classes(fidelity) if fidelity["hideOtherClasses"]
400
- output += "\nhide members\n" if fidelity["hideMembers"]
401
- output
402
- end
403
-
404
- def empty?(yml, prop)
405
- yml[prop].nil? || yml[prop].length.zero?
406
- end
407
- end
408
- end
409
- end
@@ -1,143 +0,0 @@
1
- module Metanorma
2
- module Standoc
3
- class PlantUMLBlockMacroBackend
4
- def self.plantuml_installed?
5
- unless which("plantuml")
6
- raise "PlantUML not installed"
7
- end
8
- end
9
-
10
- def self.plantuml_bin
11
- if Gem.win_platform? || which("plantumlc")
12
- "plantumlc"
13
- else
14
- "plantuml"
15
- end
16
- end
17
-
18
- def self.run(umlfile, outfile, fmt)
19
- valid_formats = %w[png svg eps]
20
- unless valid_formats.include?(fmt)
21
- raise ArgumentError, "Invalid format: #{fmt}. Allowed formats are: #{valid_formats.join(', ')}"
22
- end
23
- system(plantuml_bin, umlfile.path, "-t#{fmt}") or
24
- (warn $? and return false)
25
- i = 0
26
- until !Gem.win_platform? || File.exist?(outfile) || i == 15
27
- sleep(1)
28
- i += 1
29
- end
30
- File.exist?(outfile)
31
- end
32
-
33
- # if no :imagesdir: leave image file in plantuml
34
- # sleep need for windows because dot works in separate process and
35
- # plantuml process may finish earlier then dot, as result png file
36
- # maybe not created yet after plantuml finish
37
- #
38
- # # Warning: metanorma/metanorma-standoc#187
39
- # Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
40
- # This is why we need to copy and then unlink.
41
- def self.generate_file(parent, reader)
42
- ldir, imagesdir, fmt = generate_file_prep(parent)
43
- umlfile, outfile = save_plantuml parent, reader, ldir, fmt
44
- run(umlfile, outfile, fmt) or
45
- raise "No image output from PlantUML (#{umlfile}, #{outfile})!"
46
- umlfile.unlink
47
- absolute_path, relative_path = path_prep(ldir, imagesdir)
48
- filename = File.basename(outfile.to_s)
49
- FileUtils.cp(outfile, absolute_path) and outfile.unlink
50
- #imagesdir ? filename : File.join(path, filename)
51
- File.join(relative_path, filename)
52
- end
53
-
54
- def self.generate_file_prep(parent)
55
- ldir = localdir(parent)
56
- imagesdir = parent.document.attr("imagesdir")
57
- fmt = parent.document.attr("plantuml-image-format")&.strip&.downcase ||
58
- "png"
59
- [ldir, imagesdir, fmt]
60
- end
61
-
62
- def self.localdir(parent)
63
- ret = Metanorma::Utils::localdir(parent.document)
64
- File.writable?(ret) or
65
- raise "Destination directory #{ret} not writable for PlantUML!"
66
- ret
67
- end
68
-
69
- def self.path_prep(localdir, imagesdir)
70
- #path = Pathname.new(localdir) + (imagesdir || "plantuml")
71
- path = Pathname.new(File.join(localdir, "_plantuml_images"))
72
- sourcepath = imagesdir ? File.join(localdir, imagesdir) : localdir
73
- path.mkpath
74
- File.writable?(path) or
75
- raise "Destination path #{path} not writable for PlantUML!"
76
- [path, Pathname.new(path).relative_path_from(Pathname.new(sourcepath)).to_s]
77
- end
78
-
79
- def self.save_plantuml(_parent, reader, _localdir, fmt)
80
- src = prep_source(reader)
81
- /^@startuml (?<fn>[^\n]+)\n/ =~ src
82
- Tempfile.open(["plantuml", ".pml"], encoding: "utf-8") do |f|
83
- f.write(src)
84
- [f, File.join(File.dirname(f.path),
85
- "#{fn || File.basename(f.path, '.pml')}.#{fmt}")]
86
- end
87
- end
88
-
89
- def self.prep_source(reader)
90
- src = reader.source
91
- reader.lines.first.sub(/(?<!\s)\s+$/, "").match /^@startuml($| )/ or
92
- src = "@startuml\n#{src}\n@enduml\n"
93
- %r{@enduml\s*$}m.match?(src) or
94
- raise "@startuml without matching @enduml in PlantUML!"
95
- src
96
- end
97
-
98
- def self.generate_attrs(attrs)
99
- %w(id align float title role width height alt)
100
- .inject({}) do |memo, key|
101
- memo[key] = attrs[key] if attrs.has_key? key
102
- memo
103
- end
104
- end
105
-
106
- # https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
107
- def self.which(cmd)
108
- exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
109
- ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
110
- exts.each do |ext|
111
- exe = File.join(path, "#{cmd}#{ext}")
112
- return exe if File.executable?(exe) && !File.directory?(exe)
113
- end
114
- end
115
- nil
116
- end
117
- end
118
-
119
- class PlantUMLBlockMacro < Asciidoctor::Extensions::BlockProcessor
120
- use_dsl
121
- named :plantuml
122
- on_context :literal
123
- parse_content_as :raw
124
-
125
- def abort(parent, reader, attrs, msg)
126
- warn msg
127
- attrs["language"] = "plantuml"
128
- create_listing_block parent, reader.source,
129
- (attrs.reject { |k, _v| k == 1 })
130
- end
131
-
132
- def process(parent, reader, attrs)
133
- PlantUMLBlockMacroBackend.plantuml_installed?
134
- filename = PlantUMLBlockMacroBackend.generate_file(parent, reader)
135
- through_attrs = PlantUMLBlockMacroBackend.generate_attrs attrs
136
- through_attrs["target"] = filename
137
- create_image_block parent, through_attrs
138
- rescue StandardError => e
139
- abort(parent, reader, attrs, e.message)
140
- end
141
- end
142
- end
143
- end
@@ -1,30 +0,0 @@
1
-
2
- [yaml2text,<%= model_path %>,definition]
3
- ----
4
-
5
- === {definition.name | default: "<%= file_name %>"}
6
- {definition.definition}
7
-
8
- {% if definition.attributes %}
9
- .{definition.name | default: "<%= file_name %>"} attributes
10
- |===
11
- |Name |Definition |Mandatory/ Optional/ Conditional |Max Occur |Data Type
12
-
13
- {definition.attributes.*,key,EOK}
14
- |{key} |{% if definition.attributes[key].definition %}{{ definition.attributes[key].definition }}{% else %}TODO: enum {{ key }}'s definition{% endif %} |{% if definition.attributes[key].cardinality.min == 0 %}O{% else %}M{% endif %} |{% if definition.attributes[key].cardinality.max == "*" %}N{% else %}1{% endif %} |{% if definition.attributes[key].origin %}<<{{ definition.attributes[key].origin }}>>{% endif %} `{definition.attributes[key].type}`
15
- {EOK}
16
- |===
17
- {% endif %}
18
-
19
- {% if definition['values'] %}
20
- .{definition.name | default: "<%= file_name %>"} values
21
- |===
22
- |Name |Definition
23
-
24
- {definition['values'].*,key,EOK}
25
- |{key} |{definition['values'][key].definition}
26
- {EOK}
27
- |===
28
- {% endif %}
29
-
30
- ----
@@ -1,20 +0,0 @@
1
- [plantuml,title=<%= diagram_caption %>]
2
- ....
3
- @startuml
4
-
5
- <%= diagram_options_yml_to_plantuml %>
6
-
7
- <%= class_groups_yml_to_plantuml %>
8
-
9
- <%= imports_yml_to_plantuml %>
10
-
11
- <%= class_defs_yml_to_plantuml %>
12
-
13
- <%= class_relations_yml_to_plantuml %>
14
-
15
- <%= fidelity_yml_to_plantuml %>
16
-
17
- <%= bottom_yml_to_plantuml %>
18
-
19
- @enduml
20
- ....