metanorma-standoc 3.1.4 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07dd4263f3139ccb6d6f742e52e7ccf48a4d5f585dc442fd4fe22019f68a2173
4
- data.tar.gz: 013cd7e28bb591c2b09b138de8debe85831bc30460ed6192aae2dd9d4434de89
3
+ metadata.gz: 101539eb3a9ab93dced9b971970f3ff3901d7d85de59be1cac19e92495b0e9d3
4
+ data.tar.gz: f628e233575e4b13e92aeb6980fd42c9b97e05256f8e859f892fbb71643247f1
5
5
  SHA512:
6
- metadata.gz: 4a856c20cb73fb75982c1e473d5d959e1aeed8cd1354eb53af33d612d8769e03b4f85229bfd93adaafb0840e1a4071eca7c99e49e4ed5ae0dd2211e55d6ec8fa
7
- data.tar.gz: bcc1fa89fe0ddd3b33c0bf9d0bd7696a1668a9d123b289aff61d4f82969bcdc54c7dc0088e7debb3d3256221eefbc5609b8e0ac3d074c62dfcbde826e5b995e7
6
+ metadata.gz: '0943020cfe1c2a7a7947f470c0443ff200cbb5ca894b8268dda16e66e8e310f810cb0c0dc64d495b544c987d14995bd5e57d69ac25e61432a3b563bf1b8f2036'
7
+ data.tar.gz: 1d68cd3ebd13e2681442a65975201ce39bdcf84eaa6f7b299345716e0b091129babaea12eb6faf5af207415af45a5d4a39cf59c362659156fcdf635f8f9b86f4
@@ -51,9 +51,9 @@ module Metanorma
51
51
  NO_SYMABBR = "[.//definitions[not(@type)]]".freeze
52
52
  SYMABBR = "[.//definitions[@type = 'symbols']]" \
53
53
  "[.//definitions[@type = 'abbreviated_terms']]".freeze
54
- SYMnoABBR = "[.//definitions[@type = 'symbols']]" \
54
+ SYM_NO_ABBR = "[.//definitions[@type = 'symbols']]" \
55
55
  "[not(.//definitions[@type = 'abbreviated_terms'])]".freeze
56
- ABBRnoSYM = "[.//definitions[@type = 'abbreviated_terms']]" \
56
+ ABBR_NO_SYM = "[.//definitions[@type = 'abbreviated_terms']]" \
57
57
  "[not(.//definitions[@type = 'symbols'])]".freeze
58
58
 
59
59
  def section_names_terms_cleanup(xml)
@@ -81,9 +81,9 @@ module Metanorma
81
81
 
82
82
  def section_names_terms1_cleanup(xml)
83
83
  auto_name_terms(xml) or return
84
- replace_title(xml, "//terms#{SYMnoABBR} | //clause[@type = 'terms']#{SYMnoABBR}",
84
+ replace_title(xml, "//terms#{SYM_NO_ABBR} | //clause[@type = 'terms']#{SYM_NO_ABBR}",
85
85
  @i18n&.termsdefsymbols, true)
86
- replace_title(xml, "//terms#{ABBRnoSYM} | //clause[@type = 'terms']#{ABBRnoSYM}",
86
+ replace_title(xml, "//terms#{ABBR_NO_SYM} | //clause[@type = 'terms']#{ABBR_NO_SYM}",
87
87
  @i18n&.termsdefabbrev, true)
88
88
  replace_title(xml, "//terms#{SYMABBR} | //clause[@type = 'terms']#{SYMABBR}",
89
89
  @i18n&.termsdefsymbolsabbrev, true)
@@ -124,12 +124,12 @@ module Metanorma
124
124
  unless acc[:parent] == :term # don't count Term > Term twice
125
125
  :term
126
126
  end
127
- elsif hasterm && node.at("./self::*#{SYMnoABBR}") then :tsna
128
- elsif hasterm && node.at("./self::*#{ABBRnoSYM}") then :tans
127
+ elsif hasterm && node.at("./self::*#{SYM_NO_ABBR}") then :tsna
128
+ elsif hasterm && node.at("./self::*#{ABBR_NO_SYM}") then :tans
129
129
  elsif hasterm && node.at("./self::*#{SYMABBR}") then :tsa
130
130
  elsif hasterm && node.at("./self::*#{NO_SYMABBR}") then :tnsa
131
- elsif node.at("./self::*#{SYMnoABBR}") then :sna
132
- elsif node.at("./self::*#{ABBRnoSYM}") then :ans
131
+ elsif node.at("./self::*#{SYM_NO_ABBR}") then :sna
132
+ elsif node.at("./self::*#{ABBR_NO_SYM}") then :ans
133
133
  elsif node.at("./self::*#{SYMABBR}") then :sa
134
134
  elsif node.at("./self::*#{NO_SYMABBR}") then :nsa
135
135
  elsif node.name == "definitions" # ignore
@@ -34,11 +34,10 @@ module Metanorma
34
34
  block Metanorma::Plugin::Lutaml::LutamlGmlDictionaryBlock
35
35
  block_macro Metanorma::Plugin::Lutaml::LutamlKlassTableBlockMacro
36
36
  block_macro Metanorma::Plugin::Lutaml::LutamlEnumTableBlockMacro
37
+ block Metanorma::Plugin::Plantuml::BlockProcessor
37
38
  preprocessor Metanorma::Standoc::EmbedIncludeProcessor
38
39
  preprocessor Metanorma::Standoc::LinkProtectPreprocessor
39
40
  preprocessor Metanorma::Standoc::PassProtectPreprocessor
40
- preprocessor Metanorma::Standoc::Datamodel::AttributesTablePreprocessor
41
- preprocessor Metanorma::Standoc::Datamodel::DiagramPreprocessor
42
41
  preprocessor Metanorma::Plugin::Lutaml::Json2TextPreprocessor
43
42
  preprocessor Metanorma::Plugin::Lutaml::Yaml2TextPreprocessor
44
43
  preprocessor Metanorma::Plugin::Lutaml::Data2TextPreprocessor
@@ -81,7 +80,6 @@ module Metanorma
81
80
  inline_macro Metanorma::Standoc::SourceIncludeInlineMacro
82
81
  block Metanorma::Standoc::ToDoAdmonitionBlock
83
82
  block Metanorma::Standoc::EditorAdmonitionBlock
84
- block Metanorma::Standoc::PlantUMLBlockMacro
85
83
  block Metanorma::Standoc::PseudocodeBlockMacro
86
84
  block_macro Metanorma::Standoc::ColumnBreakBlockMacro
87
85
  end
@@ -90,11 +90,13 @@ module Metanorma
90
90
  agency_abbrev: opts.dig(:agency_abbrev, i), abbr: opts[:abbr],
91
91
  committee: opts[:committee], default_org: opts[:default_org])
92
92
  end
93
- org_attrs_add_committees(node, ret, opts, opts_orig)
93
+ if !opts_orig[:groups] || opts_orig[:groups].empty? then ret.first
94
+ else org_attrs_add_committees(node, ret, opts, opts_orig)
95
+ end
94
96
  end
95
97
 
96
98
  def org_attrs_add_committees(node, ret, opts, opts_orig)
97
- opts_orig[:groups]&.each_with_index do |g, i|
99
+ opts_orig[:groups].each_with_index do |g, i|
98
100
  i.zero? and next
99
101
  contributors_committees_pad_multiples(ret.first, node, g)
100
102
  opts = committee_contrib_org_prep(node, g, nil, opts_orig)
@@ -5,13 +5,22 @@ module Metanorma
5
5
  module Standoc
6
6
  module Front
7
7
  def metadata_author(node, xml)
8
- org_contributor(node, xml,
9
- { source: ["publisher", "pub"], role: "author",
10
- default: default_publisher })
8
+ org_author(node, xml)
11
9
  personal_author(node, xml)
12
10
  committee_contributors(node, xml, default_publisher, {})
13
11
  end
14
12
 
13
+ def org_author(node, xml)
14
+ if node.attr("corporate-author")
15
+ org_contributor(node, xml, { source: ["corporate-author"],
16
+ role: "author" })
17
+ else
18
+ org_contributor(node, xml,
19
+ { source: ["publisher", "pub"], role: "author",
20
+ default: default_publisher })
21
+ end
22
+ end
23
+
15
24
  def personal_author(node, xml)
16
25
  (node.attr("fullname") || node.attr("surname")) and
17
26
  personal_author1(node, xml, "")
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
3
- <!-- VERSION v2.1.1 -->
3
+ <!-- VERSION v2.1.2 -->
4
4
 
5
5
  <!--
6
6
  ALERT: cannot have root comments, because of https://github.com/metanorma/metanorma/issues/437
@@ -123,6 +123,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
123
123
  <a:documentation>Notes whose scope is the current block</a:documentation>
124
124
  </ref>
125
125
  </zeroOrMore>
126
+ <ref name="BlockSource">
127
+ <a:documentation>Bibliographic source for the information in the list.
128
+ Sources are currently only rendered in metanorma-plateau</a:documentation>
129
+ </ref>
126
130
  </define>
127
131
  <define name="OlBody">
128
132
  <optional>
@@ -140,6 +144,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
140
144
  <a:documentation>Notes whose scope is the current block</a:documentation>
141
145
  </ref>
142
146
  </zeroOrMore>
147
+ <ref name="BlockSource">
148
+ <a:documentation>Bibliographic source for the information in the list.
149
+ Sources are currently only rendered in metanorma-plateau</a:documentation>
150
+ </ref>
143
151
  </define>
144
152
  <define name="OlAttributes">
145
153
  <a:documentation>NOTE: `start` attribute is not included by default, because of problems it raises with DOC output</a:documentation>
@@ -188,6 +196,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
188
196
  <a:documentation>Notes whose scope is the current block</a:documentation>
189
197
  </ref>
190
198
  </zeroOrMore>
199
+ <ref name="BlockSource">
200
+ <a:documentation>Bibliographic source for the information in the list.
201
+ Sources are currently only rendered in metanorma-plateau</a:documentation>
202
+ </ref>
191
203
  </define>
192
204
  <define name="dt">
193
205
  <element name="dt">
@@ -871,6 +883,12 @@ titlecase, or lowercase</a:documentation>
871
883
  </oneOrMore>
872
884
  </element>
873
885
  </define>
886
+ <define name="ParagraphFnBody" combine="interleave">
887
+ <ref name="BlockSource">
888
+ <a:documentation>Bibliographic source for the information in the paragraph
889
+ parargaph sources are currently only rendered in metanorma-plateau</a:documentation>
890
+ </ref>
891
+ </define>
874
892
  <define name="BasicBlock" combine="choice">
875
893
  <ref name="columnbreak"/>
876
894
  </define>
@@ -2,16 +2,14 @@ require "uuidtools"
2
2
  require "yaml"
3
3
  require "csv"
4
4
  require_relative "macros_inline"
5
- require_relative "macros_plantuml"
6
5
  require_relative "macros_terms"
7
6
  require_relative "macros_form"
8
7
  require_relative "macros_note"
9
8
  require_relative "macros_embed"
10
9
  require_relative "macros_link"
11
- require_relative "datamodel/attributes_table_preprocessor"
12
- require_relative "datamodel/diagram_preprocessor"
13
10
  require "metanorma-plugin-glossarist"
14
11
  require "metanorma-plugin-lutaml"
12
+ require "metanorma-plugin-plantuml"
15
13
 
16
14
  module Metanorma
17
15
  module Standoc
@@ -154,7 +154,17 @@ module Metanorma
154
154
  #{text}
155
155
  ADOC
156
156
  c = Asciidoctor.convert(doc, backend: flavour, header_footer: true)
157
- Nokogiri::XML(c).at("//xmlns:sections")
157
+ ret = Nokogiri::XML(c).at("//xmlns:sections")
158
+ separate_numbering_footnotes(ret)
159
+ end
160
+
161
+ # separate numbering of externally sourced footnotes
162
+ # from that of current doc
163
+ def separate_numbering_footnotes(docxml)
164
+ docxml.xpath("//xmlns:fn").each do |f|
165
+ f["reference"] = "_#{UUIDTools::UUID.random_create}_#{f['reference']}"
166
+ end
167
+ docxml
158
168
  end
159
169
 
160
170
  def asciimath_key(sym)
@@ -28,7 +28,7 @@ module Metanorma
28
28
  math_validate(doc)
29
29
  fatalerrors = @log.abort_messages
30
30
  fatalerrors.empty? or
31
- clean_abort("\n\nFATAL ERRROS:\n\n#{fatalerrors.join("\n\n")}", doc)
31
+ clean_abort("\n\nFATAL ERRORS:\n\n#{fatalerrors.join("\n\n")}", doc)
32
32
  end
33
33
 
34
34
  MATHML_NS = "http://www.w3.org/1998/Math/MathML".freeze
@@ -31,11 +31,21 @@ module Metanorma
31
31
  @log.add("Style", node, w)
32
32
  end
33
33
 
34
+ def reject_metanorma_extension
35
+ ->(node) {
36
+ node.ancestors.detect do |x|
37
+ x.name == "metanorma-extension"
38
+ end
39
+ }
40
+ end
41
+
34
42
  def asset_title_style(root)
35
- root.xpath("//figure[image][not(name)]").each do |node|
43
+ root.xpath("//figure[image][not(name)]")
44
+ .reject(&reject_metanorma_extension).each do |node|
36
45
  style_warning(node, "Figure should have title", nil)
37
46
  end
38
- root.xpath("//table[not(name)]").each do |node|
47
+ root.xpath("//table[not(name)]")
48
+ .reject(&reject_metanorma_extension).each do |node|
39
49
  style_warning(node, "Table should have title", nil)
40
50
  end
41
51
  end
@@ -15,7 +15,8 @@ module Metanorma
15
15
  end
16
16
 
17
17
  def empty_table_validate(doc)
18
- doc.xpath("//table[not(.//tr)]").each do |t|
18
+ doc.xpath("//table[not(.//tr)]").reject(&reject_metanorma_extension)
19
+ .each do |t|
19
20
  @log.add("Table", t, "Empty table", severity: 0)
20
21
  end
21
22
  end
@@ -30,6 +31,7 @@ module Metanorma
30
31
  end
31
32
 
32
33
  def maxrowcols_validate(table, maxcols, mode: "row_cols")
34
+ reject_metanorma_extension.call(table) and return
33
35
  case mode
34
36
  when "row_cols"
35
37
  maxrowcols_validate0(table, maxcols, "*", mode)
@@ -19,6 +19,6 @@ module Metanorma
19
19
  end
20
20
 
21
21
  module Standoc
22
- VERSION = "3.1.4".freeze
22
+ VERSION = "3.1.5".freeze
23
23
  end
24
24
  end
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency "metanorma", ">= 1.6.0"
37
37
  spec.add_dependency "metanorma-plugin-glossarist", "~> 0.2.3"
38
38
  spec.add_dependency "metanorma-plugin-lutaml", "~> 0.7.31"
39
+ spec.add_dependency "metanorma-plugin-plantuml", "~> 1.0.0"
39
40
  spec.add_dependency "metanorma-utils", "~> 1.11.0"
40
41
  spec.add_dependency "ruby-jing"
41
42
  # relaton-cli not just relaton, to avoid circular reference in metanorma
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-standoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.4
4
+ version: 3.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-18 00:00:00.000000000 Z
11
+ date: 2025-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.7.31
125
+ - !ruby/object:Gem::Dependency
126
+ name: metanorma-plugin-plantuml
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 1.0.0
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 1.0.0
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: metanorma-utils
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -447,9 +461,6 @@ files:
447
461
  - lib/asciidoctor/standoc/cleanup_toc.rb
448
462
  - lib/asciidoctor/standoc/cleanup_xref.rb
449
463
  - lib/asciidoctor/standoc/converter.rb
450
- - lib/asciidoctor/standoc/datamodel/attributes_table_preprocessor.rb
451
- - lib/asciidoctor/standoc/datamodel/diagram_preprocessor.rb
452
- - lib/asciidoctor/standoc/datamodel/plantuml_renderer.rb
453
464
  - lib/asciidoctor/standoc/deprecated.rb
454
465
  - lib/asciidoctor/standoc/front.rb
455
466
  - lib/asciidoctor/standoc/front_contributor.rb
@@ -458,7 +469,6 @@ files:
458
469
  - lib/asciidoctor/standoc/macros.rb
459
470
  - lib/asciidoctor/standoc/macros_form.rb
460
471
  - lib/asciidoctor/standoc/macros_note.rb
461
- - lib/asciidoctor/standoc/macros_plantuml.rb
462
472
  - lib/asciidoctor/standoc/macros_terms.rb
463
473
  - lib/asciidoctor/standoc/ref.rb
464
474
  - lib/asciidoctor/standoc/ref_sect.rb
@@ -515,9 +525,6 @@ files:
515
525
  - lib/metanorma/standoc/cleanup_toc.rb
516
526
  - lib/metanorma/standoc/cleanup_xref.rb
517
527
  - lib/metanorma/standoc/converter.rb
518
- - lib/metanorma/standoc/datamodel/attributes_table_preprocessor.rb
519
- - lib/metanorma/standoc/datamodel/diagram_preprocessor.rb
520
- - lib/metanorma/standoc/datamodel/plantuml_renderer.rb
521
528
  - lib/metanorma/standoc/front.rb
522
529
  - lib/metanorma/standoc/front_committee.rb
523
530
  - lib/metanorma/standoc/front_contributor.rb
@@ -534,7 +541,6 @@ files:
534
541
  - lib/metanorma/standoc/macros_inline.rb
535
542
  - lib/metanorma/standoc/macros_link.rb
536
543
  - lib/metanorma/standoc/macros_note.rb
537
- - lib/metanorma/standoc/macros_plantuml.rb
538
544
  - lib/metanorma/standoc/macros_terms.rb
539
545
  - lib/metanorma/standoc/merge_bibitems.rb
540
546
  - lib/metanorma/standoc/processor.rb
@@ -561,8 +567,6 @@ files:
561
567
  - lib/metanorma/standoc/validate_table.rb
562
568
  - lib/metanorma/standoc/validate_term.rb
563
569
  - lib/metanorma/standoc/version.rb
564
- - lib/metanorma/standoc/views/datamodel/model_representation.adoc.erb
565
- - lib/metanorma/standoc/views/datamodel/plantuml_representation.adoc.erb
566
570
  - metanorma-standoc.gemspec
567
571
  homepage: https://github.com/metanorma/metanorma-standoc
568
572
  licenses:
@@ -1,3 +0,0 @@
1
- require "asciidoctor/standoc/deprecated"
2
- require "metanorma/standoc/datamodel/attributes_table_preprocessor"
3
-
@@ -1,3 +0,0 @@
1
- require "asciidoctor/standoc/deprecated"
2
- require "metanorma/standoc/datamodel/diagram_preprocessor"
3
-
@@ -1,8 +0,0 @@
1
- require "asciidoctor/standoc/deprecated"
2
- require "metanorma/standoc/datamodel/plantuml_renderer"
3
-
4
- module Asciidoctor
5
- module Datamodel
6
- PlantumlRenderer = Metanorma::Datamodel::PlantumlRenderer
7
- end
8
- end
@@ -1,2 +0,0 @@
1
- require "asciidoctor/standoc/deprecated"
2
- require "metanorma/standoc/macros_plantuml"
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "erb"
4
-
5
- module Metanorma
6
- module Standoc
7
- module Datamodel
8
- class AttributesTablePreprocessor < Asciidoctor::Extensions::Preprocessor
9
- BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}/
10
- BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z/
11
- MARCO_REGEXP = /\[datamodel_attributes_table,([^,]+),?(.+)?\]/
12
- TEMPLATES_PATH = File.expand_path("../views/datamodel", __dir__).freeze
13
- # search document for block `datamodel_attributes_table`
14
- # read include derectives that goes after that in block and transform
15
- # into yaml2text blocks
16
- def process(document, reader)
17
- input_lines = reader.lines.to_enum
18
- Asciidoctor::PreprocessorReader.new(document, processed_lines(document, input_lines))
19
- end
20
-
21
- private
22
-
23
- def processed_lines(document, input_lines)
24
- input_lines.each_with_object([]) do |line, result|
25
- if match = line.match(MARCO_REGEXP)
26
- yaml_path = match[1]
27
- result.push(*parse_marco(yaml_path, document))
28
- else
29
- result.push(line)
30
- end
31
- end
32
- end
33
-
34
- def parse_marco(yaml_path, document)
35
- model_representation(yaml_relative_path(yaml_path, document))
36
- .split("\n")
37
- end
38
-
39
- def model_representation(model_path)
40
- template = File.read(File.join(
41
- TEMPLATES_PATH,
42
- "model_representation.adoc.erb",
43
- ))
44
- file_name = File.basename(model_path).gsub(/\.ya?ml/, "")
45
- ERB
46
- .new(template)
47
- .result(binding)
48
- end
49
-
50
- def yaml_relative_path(file_path, document)
51
- directory = File.dirname(document.attributes["docfile"] || ".")
52
- document.path_resolver.system_path(file_path, directory)
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "erb"
4
- require "metanorma/standoc/datamodel/plantuml_renderer"
5
-
6
- module Metanorma
7
- module Standoc
8
- module Datamodel
9
- class DiagramPreprocessor < Asciidoctor::Extensions::Preprocessor
10
- BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}/.freeze
11
- BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z/.freeze
12
- MARCO_REGEXP = /\[datamodel_diagram,([^,]+),?(.+)?\]/.freeze
13
- TEMPLATES_PATH = File.expand_path("../views/datamodel", __dir__).freeze
14
- # search document for block `datamodel_diagram`
15
- # read include derectives that goes after that in block and transform
16
- # into plantuml block
17
- def process(document, reader)
18
- input_lines = reader.lines.to_enum
19
- Asciidoctor::PreprocessorReader.new(document, processed_lines(document, input_lines))
20
- end
21
-
22
- private
23
-
24
- def processed_lines(document, input_lines)
25
- input_lines.each_with_object([]) do |line, result|
26
- if match = line.match(MARCO_REGEXP)
27
- result
28
- .push(*parse_datamodel_marco(match[1], match[2], document))
29
- else
30
- result.push(line)
31
- end
32
- end
33
- end
34
-
35
- def parse_datamodel_marco(yaml_path, include_path, document)
36
- include_path ||= File.join(File.dirname(yaml_path), "..", "models")
37
- include_path = yaml_relative_path(include_path, document)
38
- yaml_relative_to_doc_path = yaml_relative_path(yaml_path, document)
39
- view_hash = YAML.safe_load(File.read(yaml_relative_to_doc_path))
40
- plantuml_representations(view_hash,
41
- yaml_relative_to_doc_path,
42
- include_path)
43
- end
44
-
45
- def yaml_relative_path(file_path, document)
46
- docfile = document.attributes["docfile"] || "."
47
- docfile_directory = File.dirname(docfile)
48
- document.path_resolver.system_path(file_path, docfile_directory)
49
- end
50
-
51
- def import_format(include_path, import_name, values)
52
- include_content = File.read(File.join(
53
- include_path,
54
- "#{import_name}.yml",
55
- ))
56
- content = YAML.safe_load(include_content)
57
- if values
58
- content["skipSection"] = values["skipSection"]
59
- end
60
- content
61
- end
62
-
63
- def format_import_directives(imports, include_path)
64
- imports
65
- .each_with_object({}) do |(import_name, values), res|
66
- full_model_name = import_name.split("/").join
67
- content = import_format(include_path, import_name, values)
68
- res[content["name"] || full_model_name] = content
69
- end.compact
70
- end
71
-
72
- def prepare_view_hash(view_hash, all_imports)
73
- view_hash.merge!(
74
- "classes" => model_type(all_imports, "class"),
75
- "enums" => model_type(all_imports, "enum"),
76
- "relations" => view_hash["relations"] || [],
77
- "fidelity" => (view_hash["fidelity"] || {})
78
- .merge!("classes" => model_type(all_imports,
79
- "class")),
80
- )
81
- end
82
-
83
- def model_type(imports, type)
84
- imports
85
- .select do |_name, elem|
86
- elem["modelType"] == type
87
- end
88
- end
89
-
90
- def plantuml_representations(view_hash, view_path, include_path)
91
- yaml_directory = File.dirname(view_path)
92
- all_imports = format_import_directives(view_hash["imports"],
93
- include_path)
94
- prepare_view_hash(view_hash, all_imports)
95
- Metanorma::Datamodel::PlantumlRenderer
96
- .new(view_hash, File.join(yaml_directory, ".."))
97
- .render
98
- .split("\n")
99
- end
100
- end
101
- end
102
- end
103
- end
@@ -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
- ....