metanorma 2.0.0 → 2.5.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +23 -2
  5. data/Gemfile +17 -0
  6. data/Gemfile.devel +10 -0
  7. data/README.adoc +1 -1
  8. data/lib/metanorma/collection/artifact_store.rb +142 -0
  9. data/lib/metanorma/collection/collection.rb +160 -122
  10. data/lib/metanorma/collection/config/bibdata.rb +3 -3
  11. data/lib/metanorma/collection/config/compile_options.rb +4 -5
  12. data/lib/metanorma/collection/config/config.rb +81 -83
  13. data/lib/metanorma/collection/config/converters.rb +92 -8
  14. data/lib/metanorma/collection/config/directive.rb +9 -3
  15. data/lib/metanorma/collection/config/doc_container.rb +28 -0
  16. data/lib/metanorma/collection/config/manifest.rb +59 -29
  17. data/lib/metanorma/collection/config/namespaces.rb +12 -0
  18. data/lib/metanorma/collection/document/document.rb +56 -15
  19. data/lib/metanorma/collection/filelookup/filelookup.rb +185 -77
  20. data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +99 -27
  21. data/lib/metanorma/collection/filelookup/utils.rb +52 -0
  22. data/lib/metanorma/collection/helpers.rb +82 -0
  23. data/lib/metanorma/collection/log.rb +24 -0
  24. data/lib/metanorma/collection/manifest/manifest.rb +45 -14
  25. data/lib/metanorma/collection/multilingual/multilingual.rb +660 -0
  26. data/lib/metanorma/collection/renderer/filelocation.rb +166 -0
  27. data/lib/metanorma/collection/renderer/fileparse.rb +135 -115
  28. data/lib/metanorma/collection/renderer/fileprocess.rb +185 -37
  29. data/lib/metanorma/collection/renderer/navigation.rb +21 -7
  30. data/lib/metanorma/collection/renderer/render_word.rb +15 -10
  31. data/lib/metanorma/collection/renderer/renderer.rb +236 -32
  32. data/lib/metanorma/collection/renderer/svg.rb +107 -0
  33. data/lib/metanorma/collection/renderer/utils.rb +110 -39
  34. data/lib/metanorma/collection/sectionsplit/collection.rb +110 -0
  35. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +198 -115
  36. data/lib/metanorma/collection/util/disambig_files.rb +4 -5
  37. data/lib/metanorma/collection/util/util.rb +155 -18
  38. data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +47 -33
  39. data/lib/metanorma/compile/assets/icc-boilerplate.adoc +41 -0
  40. data/lib/metanorma/compile/compile.rb +181 -156
  41. data/lib/metanorma/compile/compile_options.rb +186 -79
  42. data/lib/metanorma/compile/extract.rb +75 -58
  43. data/lib/metanorma/compile/flavor.rb +28 -0
  44. data/lib/metanorma/compile/output_filename.rb +75 -0
  45. data/lib/metanorma/compile/output_filename_config.rb +27 -0
  46. data/lib/metanorma/compile/relaton_drop.rb +56 -0
  47. data/lib/metanorma/compile/render.rb +226 -0
  48. data/lib/metanorma/compile/validator.rb +26 -0
  49. data/lib/metanorma/compile/writeable.rb +12 -0
  50. data/lib/metanorma/util/fontist_helper.rb +35 -19
  51. data/lib/metanorma/version.rb +1 -1
  52. data/lib/metanorma.rb +1 -7
  53. data/metanorma.gemspec +15 -21
  54. data/plans/collection-rendering-architecture-review.md +516 -0
  55. metadata +65 -113
  56. data/.hound.yml +0 -5
  57. data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +0 -22
  58. data/lib/metanorma/asciidoctor_extensions.rb +0 -1
  59. data/lib/metanorma/compile/compile_validate.rb +0 -68
  60. data/lib/metanorma/config/config.rb +0 -23
  61. data/lib/metanorma/input/asciidoc.rb +0 -107
  62. data/lib/metanorma/input/base.rb +0 -9
  63. data/lib/metanorma/input.rb +0 -7
  64. data/lib/metanorma/processor/processor.rb +0 -54
  65. data/lib/metanorma/processor.rb +0 -6
  66. data/lib/metanorma/registry/registry.rb +0 -63
  67. data/lib/metanorma/shale_monkeypatch.rb +0 -15
  68. data/lib/metanorma/util/util.rb +0 -45
@@ -1,84 +1,90 @@
1
- require "shale"
2
- require_relative "../../shale_monkeypatch"
1
+ require "lutaml/model"
3
2
  require_relative "../../array_monkeypatch"
4
3
  require_relative "compile_options"
5
4
  require_relative "converters"
6
5
  require_relative "bibdata"
7
6
  require_relative "directive"
8
7
  require_relative "manifest"
8
+ require_relative "namespaces"
9
+ require_relative "doc_container"
9
10
 
10
11
  module Metanorma
11
12
  class Collection
12
13
  module Config
13
- require "shale/adapter/nokogiri"
14
- ::Shale.xml_adapter = ::Shale::Adapter::Nokogiri
14
+ Lutaml::Model::Config.configure do |config|
15
+ config.xml_adapter_type = :nokogiri
16
+ end
15
17
 
16
- class Config < ::Shale::Mapper
18
+ class Config < ::Lutaml::Model::Serializable
17
19
  attr_accessor :path, :collection, :from_xml
18
20
 
19
21
  attribute :bibdata, Bibdata
20
22
  attribute :directive, Directive, collection: true
21
23
  attribute :manifest, Manifest
22
- attribute :format, ::Shale::Type::String, collection: true,
23
- default: -> { [:html] }
24
- attribute :output_folder, ::Shale::Type::String
25
- attribute :coverpage, ::Shale::Type::String, default: -> {
26
- "cover.html"
27
- }
24
+ attribute :coverpage, :string, default: -> { "cover.html" }
25
+ attribute :coverpage_pdf_portfolio, :string
26
+ attribute :format, :string, collection: true, default: -> { [:html] }
27
+ attribute :output_folder, :string
28
28
  attribute :compile, CompileOptions
29
- attribute :prefatory_content, ::Shale::Type::String
30
- attribute :final_content, ::Shale::Type::String
31
- attribute :documents, Bibdata, collection: true
32
- attribute :xmlns, ::Shale::Type::String, default: -> { "http://metanorma.org" }
29
+ attribute :prefatory_content, :string, raw: true
30
+ attribute :final_content, :string, raw: true
31
+ attribute :documents, DocContainer, collection: true
32
+ attribute :xmlns, :string, default: -> { "http://metanorma.org" }
33
33
 
34
34
  yaml do
35
- map "directives", using: { from: :directives_from_yaml,
36
- to: :directives_to_yaml }
37
- map "bibdata", using: { from: :bibdata_from_yaml,
38
- to: :bibdata_to_yaml }
35
+ map "directives", to: :directive, with: { from: :directives_from_yaml,
36
+ to: :directives_to_yaml }
37
+ map "bibdata", to: :bibdata, with: { from: :bibdata_from_yaml,
38
+ to: :bibdata_to_yaml }
39
39
  map "manifest", to: :manifest
40
- map "format", to: :format
40
+ map "entry", to: :manifest, with: { from: :entry_from_yaml,
41
+ to: :nop_to_yaml }
42
+ map "format", to: :format, render_default: true
41
43
  map "output_folder", to: :output_folder
42
- map "coverpage", to: :coverpage
43
- map "compile", to: :compile
44
+ map "coverpage", to: :coverpage, render_default: true
45
+ map "coverpage-pdf-portfolio", to: :coverpage_pdf_portfolio,
46
+ render_default: true
47
+ map "compile", to: :compile, render_default: true
44
48
  map "prefatory-content", to: :prefatory_content
45
49
  map "final-content", to: :final_content
46
50
  end
47
51
 
48
52
  xml do
49
- root "metanorma-collection"
50
- # namespace "http://metanorma.org", "m"
51
- # map_attribute "xmlns", to: :xmlns
52
- map_element "bibdata", using: { from: :bibdata_from_xml,
53
- to: :bibdata_to_xml }
54
- map_element "directive", using: { from: :directive_from_xml,
55
- to: :directive_to_xml }
56
- map_element "entry", using: { from: :manifest_from_xml,
57
- to: :manifest_to_xml }
58
- map_element "format", to: :format
53
+ element "metanorma-collection"
54
+ namespace MetanormaCollectionNamespace
55
+ map_element "bibdata", to: :bibdata, with: { from: :bibdata_from_xml,
56
+ to: :bibdata_to_xml }
57
+ map_element "directive", to: :directive
58
+ map_element "entry", to: :manifest, with: { from: :manifest_from_xml,
59
+ to: :manifest_to_xml }
60
+ map_element "format", to: :format, render_default: true
59
61
  map_element "output_folder", to: :output_folder
60
- map_element "coverpage", to: :coverpage
61
- map_element "compile", to: :compile
62
- map_element "prefatory-content", using: { from: :prefatory_from_xml,
63
- to: :prefatory_to_xml }
64
- map_element "doc-container",
65
- using: { from: :documents_from_xml,
66
- to: :documents_to_xml }
67
- map_element "final-content", using: { from: :final_from_xml,
68
- to: :final_to_xml }
62
+ map_element "coverpage", to: :coverpage, render_default: true
63
+ map_element "coverpage-pdf-portfolio", to: :coverpage_pdf_portfolio,
64
+ render_default: true
65
+ map_element "compile", to: :compile, render_default: true
66
+ map_element "prefatory-content",
67
+ to: :prefatory_content,
68
+ with: { from: :prefatory_from_xml,
69
+ to: :prefatory_to_xml }
70
+ map_element "doc-container", to: :documents
71
+ map_element "final-content",
72
+ to: :final_content,
73
+ with: { from: :final_from_xml,
74
+ to: :final_to_xml }
69
75
  end
70
76
 
71
77
  def manifest_from_xml(model, node)
72
78
  model.manifest = Manifest.from_xml(node.to_xml)
73
79
  end
74
80
 
75
- def manifest_to_xml(model, parent, doc)
81
+ def manifest_to_xml(model, _parent, doc)
76
82
  model.collection&.manifest&.clean_manifest(model.manifest)
77
- doc.add_element(parent, model.manifest.to_xml)
83
+ add_raw_xml_element(doc, model.manifest.to_xml)
78
84
  end
79
85
 
80
86
  def prefatory_from_xml(model, node)
81
- model.prefatory_content = node.to_xml
87
+ model.prefatory_content = node.children.map(&:to_xml).join
82
88
  end
83
89
 
84
90
  def prefatory_to_xml(model, parent, doc)
@@ -89,32 +95,21 @@ module Metanorma
89
95
  content_to_xml(model, parent, doc, "final")
90
96
  end
91
97
 
92
- def content_to_xml(model, parent, doc, type)
98
+ def content_to_xml(model, _parent, doc, type)
93
99
  x = model.send("#{type}_content") or return
94
100
  n = Nokogiri::XML(x)
95
- elem = if n.elements.size == 1 then n.root
96
- else
97
- b = Nokogiri::XML::Builder.new
98
- model.collection.content_to_xml(type, b)
99
- b.parent.elements.first
100
- end
101
- doc.add_element(parent, elem)
101
+ raw = if n.elements.size == 1
102
+ "<#{type}-content>#{x}</#{type}-content>"
103
+ else
104
+ b = Nokogiri::XML::Builder.new
105
+ model.collection.content_to_xml(type, b)
106
+ b.parent.elements.first.to_xml
107
+ end
108
+ add_raw_xml_element(doc, raw)
102
109
  end
103
110
 
104
111
  def final_from_xml(model, node)
105
- model.final_content = node.to_xml
106
- end
107
-
108
- def directive_from_xml(model, node)
109
- model.directive ||= []
110
- model.directive << Directive.from_xml(node.to_xml)
111
- end
112
-
113
- def directive_to_xml(model, parent, doc)
114
- Array(model.directive).each do |e|
115
- elem = e.to_xml
116
- doc.add_element(parent, elem)
117
- end
112
+ model.final_content = node.children.map(&:to_xml).join
118
113
  end
119
114
 
120
115
  def directives_from_yaml(model, value)
@@ -134,26 +129,29 @@ module Metanorma
134
129
  end
135
130
  end
136
131
 
137
- def documents_from_xml(model, value)
138
- x = if value.is_a?(Shale::Adapter::Nokogiri::Node)
139
- value.content
140
- else Nokogiri::XML(value)
141
- end
142
- model.documents = x.xpath(".//bibdata")
143
- .each_with_object([]) do |b, m|
144
- m << Bibdata.from_xml(b.to_xml)
145
- end
132
+ def entry_from_yaml(model, value)
133
+ model.manifest ||= value
146
134
  end
147
135
 
148
- def documents_to_xml(model, parent, doc)
149
- b = Nokogiri::XML::Builder.new do |xml|
150
- xml.document do |m|
151
- model.collection.doccontainer(m) or return
152
- end
153
- end
154
- b.parent.elements.first.elements.each do |x|
155
- doc.add_element(parent, x)
156
- end
136
+ def self.flavor_from_yaml(yaml)
137
+ yaml["directives"]&.detect do |x|
138
+ x.is_a?(Hash) && x.has_key?("flavor")
139
+ end&.dig("flavor")&.upcase
140
+ end
141
+
142
+ def self.flavor_to_bibdata(file)
143
+ # propagate flavor from directives to bibdata
144
+ yaml = YAML.safe_load(file)
145
+ flavor = flavor_from_yaml(yaml) or return file
146
+ yaml["bibdata"] or return file
147
+ yaml["bibdata"]["ext"] ||= {}
148
+ yaml["bibdata"]["ext"]["flavor"] ||= flavor
149
+ yaml.to_yaml
150
+ end
151
+
152
+ def self.from_yaml(file)
153
+ file = flavor_to_bibdata(file)
154
+ super
157
155
  end
158
156
 
159
157
  include Converters
@@ -1,29 +1,113 @@
1
- require "relaton-cli"
1
+ require "relaton/bib"
2
+ require "relaton/bib/hash_parser_v1"
3
+ require_relative "namespaces"
2
4
 
3
5
  module Metanorma
4
6
  class Collection
5
7
  module Config
6
8
  module Converters
9
+ # Keys present only in 1.x YAML format — unambiguously absent in 2.x
10
+ V1_BIBDATA_KEYS = %w[docid link biblionote].freeze
11
+
7
12
  def bibdata_from_yaml(model, value)
8
- value and !value.empty? or return
9
- model.bibdata = Relaton::Cli::YAMLConvertor.convert_single_file(value)
13
+ (value and !value.empty?) or return
14
+ if value.is_a?(String)
15
+ value = YAML.safe_load_file(value,
16
+ permitted_classes: [Date,
17
+ Symbol])
18
+ end
19
+ force_primary_docidentifier_yaml(value)
20
+ model.bibdata = if bibdata_yaml_v1_format?(value)
21
+ # 1.x YAML format (docid:/link:/biblionote: present) —
22
+ # bridge via HashParserV1 for backward compatibility
23
+ h = Relaton::Bib::HashParserV1.hash_to_bib(value)
24
+ Relaton::Bib::ItemData.new(**h)
25
+ else
26
+ # 2.x YAML format (docidentifier:/uri:/note: keys) —
27
+ # parse directly with lutaml-model
28
+ Relaton::Bib::Item.from_yaml(value.to_yaml)
29
+ end
30
+ end
31
+
32
+ # Recursively detect 1.x-format YAML by presence of renamed keys.
33
+ # Checks the entire nested structure so relation:/contributor: entries
34
+ # are also detected.
35
+ def bibdata_yaml_v1_format?(obj)
36
+ case obj
37
+ when Hash
38
+ return true if (obj.keys.map(&:to_s) & V1_BIBDATA_KEYS).any?
39
+
40
+ obj.values.any? { |v| bibdata_yaml_v1_format?(v) }
41
+ when Array
42
+ obj.any? { |v| bibdata_yaml_v1_format?(v) }
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ def force_primary_docidentifier_yaml(value)
49
+ case value["docid"]
50
+ when Array
51
+ value["docid"].empty? ||
52
+ value["docid"].none? do |x|
53
+ x["primary"] == "true"
54
+ end or
55
+ value["docid"].first["primary"] = "true"
56
+ when Hash
57
+ value["docid"]["primary"] ||= "true"
58
+ end
10
59
  end
11
60
 
12
61
  def bibdata_to_yaml(model, doc)
13
- doc["bibdata"] = model.bibdata&.to_hash
62
+ return unless model.bibdata
63
+
64
+ doc["bibdata"] = YAML.safe_load(model.bibdata.to_yaml,
65
+ permitted_classes: [Date, Symbol])
14
66
  end
15
67
 
16
68
  def bibdata_from_xml(model, node)
17
- model.bibdata = Relaton::Cli.parse_xml(node.content)
69
+ node or return
70
+ force_primary_docidentifier_xml(node.adapter_node.native)
71
+ model.bibdata = Relaton::Cli.parse_xml(node.adapter_node.native)
72
+ end
73
+
74
+ def force_primary_docidentifier_xml(node)
75
+ node.at("//docidentifier[@primary = 'true']") and return node
76
+ d = node.at("//docidentifier") or return node
77
+ d["primary"] = "true"
18
78
  end
19
79
 
20
- def bibdata_to_xml(model, parent, doc)
80
+ def bibdata_to_xml(model, _parent, doc)
21
81
  b = model.bibdata or return
22
- elem = b.to_xml(bibdata: true, date_format: :full)
23
- doc.add_element(parent, elem)
82
+ add_raw_xml_element(doc, b.to_xml(bibdata: true, date_format: :full))
24
83
  end
25
84
 
26
85
  def nop_to_yaml(model, doc); end
86
+ def nop_to_xml(model, parent, doc); end
87
+
88
+ # Add a single-element XML string as a child of the wrapper's current
89
+ # context, preserving inline namespace declarations.
90
+ #
91
+ # The custom-method `doc.add_element(parent, str)` path under
92
+ # lutaml-model 0.8 routes through Moxml fragment parsing, whose graft
93
+ # (`CustomMethodWrapper#add_fragment_children_to_parent`) discards
94
+ # xmlns declarations on grafted DataModel children. The
95
+ # `create_and_add_element` + `raw_content` path goes through the XML
96
+ # adapter's raw-content handler, which uses Nokogiri's native fragment
97
+ # parser and keeps xmlns intact. Tag the new element with
98
+ # MetanormaCollectionNamespace so it inherits cleanly from the
99
+ # metanorma-collection root rather than emitting `xmlns=""`.
100
+ def add_raw_xml_element(doc, raw)
101
+ parsed = Nokogiri::XML.fragment(raw)
102
+ src = parsed.elements.first or return
103
+ el = doc.create_and_add_element(src.name)
104
+ el.namespace_class = MetanormaCollectionNamespace
105
+ src.attribute_nodes.each do |a|
106
+ doc.add_attribute(el, a.name, a.value)
107
+ end
108
+ el.raw_content = src.children.map(&:to_xml).join
109
+ el
110
+ end
27
111
  end
28
112
  end
29
113
  end
@@ -1,9 +1,15 @@
1
1
  module Metanorma
2
2
  class Collection
3
3
  module Config
4
- class Directive < ::Shale::Mapper
5
- attribute :key, ::Shale::Type::String
6
- attribute :value, ::Shale::Type::String
4
+ class Directive < ::Lutaml::Model::Serializable
5
+ attribute :key, :string
6
+ attribute :value, :string
7
+
8
+ xml do
9
+ root "directive"
10
+ map_element "key", to: :key
11
+ map_element "value", to: :value
12
+ end
7
13
  end
8
14
  end
9
15
  end
@@ -0,0 +1,28 @@
1
+ require "lutaml/model"
2
+ require_relative "namespaces"
3
+
4
+ module Metanorma
5
+ class Collection
6
+ module Config
7
+ # Serializable wrapper for a single <doc-container> element.
8
+ #
9
+ # The body of a doc-container is the inline presentation XML of an
10
+ # individual document, in a flavour-specific namespace (e.g. bipm,
11
+ # iso, ietf). We round-trip it as an opaque string via `map_all`
12
+ # so the underlying XML adapter (Nokogiri) preserves the inner xmlns
13
+ # declaration; if we let lutaml-model parse the body into typed
14
+ # children it would lose the inner namespace.
15
+ class DocContainer < ::Lutaml::Model::Serializable
16
+ attribute :id, :string
17
+ attribute :content, :string
18
+
19
+ xml do
20
+ element "doc-container"
21
+ namespace MetanormaCollectionNamespace
22
+ map_attribute "id", to: :id
23
+ map_all to: :content
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,4 @@
1
- require "shale"
2
- require_relative "../../shale_monkeypatch"
1
+ require "lutaml/model"
3
2
  require_relative "../../array_monkeypatch"
4
3
  require_relative "converters"
5
4
  require_relative "bibdata"
@@ -7,68 +6,99 @@ require_relative "bibdata"
7
6
  module Metanorma
8
7
  class Collection
9
8
  module Config
10
- require "shale/adapter/nokogiri"
11
- ::Shale.xml_adapter = ::Shale::Adapter::Nokogiri
9
+ Lutaml::Model::Config.configure do |config|
10
+ config.xml_adapter_type = :nokogiri
11
+ end
12
12
 
13
- class Manifest < ::Shale::Mapper
14
- attribute :identifier, ::Shale::Type::String,
13
+ class Manifest < ::Lutaml::Model::Serializable
14
+ attribute :identifier, :string,
15
15
  default: -> { UUIDTools::UUID.random_create.to_s }
16
- attribute :id, ::Shale::Type::String
16
+ attribute :id, :string
17
17
  attribute :bibdata, Bibdata
18
- attribute :type, ::Shale::Type::String
19
- attribute :title, ::Shale::Type::String
20
- attribute :url, ::Shale::Type::String
21
- attribute :attachment, ::Shale::Type::Boolean
22
- attribute :sectionsplit, ::Shale::Type::Boolean
23
- attribute :index, ::Shale::Type::Boolean, default: -> { true }
18
+ attribute :type, :string
19
+ attribute :title, :string
20
+ attribute :url, :string
21
+ attribute :level, :string
22
+ attribute :attachment, :boolean
23
+ attribute :sectionsplit, :boolean
24
+ attribute :index, :boolean, default: -> { true }
24
25
  attribute :entry, Manifest, collection: true
25
- attribute :file, ::Shale::Type::String
26
+ attribute :file, :string
27
+ attribute :pdffile, :string
28
+ attribute :format, :string, collection: true
29
+ attribute :output_filename, :string
30
+ attribute :sectionsplit_filename, :string
31
+ attribute :sectionsplit_output, :boolean
26
32
 
27
33
  yaml do
28
34
  map "identifier", to: :identifier
35
+ map "pdf-file", to: :pdffile
29
36
  map "type", to: :type
30
- map "level", using: { from: :level_from_yaml, to: :nop_to_yaml }
37
+ map "level", to: :level,
38
+ with: { from: :level_from_yaml, to: :nop_to_yaml }
31
39
  map "title", to: :title
32
40
  map "url", to: :url
33
41
  map "attachment", to: :attachment
34
42
  map "sectionsplit", to: :sectionsplit
35
43
  map "index", to: :index
36
44
  map "file", to: :file
37
- map "fileref", using: { from: :fileref_from_yaml, to: :nop_to_yaml }
45
+ map "fileref", to: :file,
46
+ with: { from: :fileref_from_yaml, to: :nop_to_yaml }
38
47
  map "entry", to: :entry
39
- map "docref", using: { from: :docref_from_yaml, to: :nop_to_yaml }
40
- map "manifest", using: { from: :docref_from_yaml, to: :nop_to_yaml }
41
- map "bibdata", using: { from: :bibdata_from_yaml,
42
- to: :bibdata_to_yaml }
48
+ map "docref", to: :entry,
49
+ with: { from: :docref_from_yaml, to: :nop_to_yaml }
50
+ map "manifest", to: :entry,
51
+ with: { from: :docref_from_yaml, to: :nop_to_yaml }
52
+ map "bibdata", to: :bibdata, with: { from: :bibdata_from_yaml,
53
+ to: :bibdata_to_yaml }
54
+ map "format", to: :format, render_default: true
55
+ map "output-filename", to: :output_filename
56
+ map "sectionsplit-filename", to: :sectionsplit_filename
57
+ map "sectionsplit-output", to: :sectionsplit_output
43
58
  end
44
59
 
45
60
  xml do
46
61
  root "entry"
47
- map_attribute "id", to: :id
62
+ map_attribute "target", to: :id
48
63
  map_attribute "attachment", to: :attachment
49
64
  map_attribute "sectionsplit", to: :sectionsplit
50
65
  map_attribute "index", to: :index
51
66
  map_attribute "url", to: :url
67
+ map_attribute "pdf-file", to: :pdffile
52
68
  map_attribute "fileref", to: :file
53
- map_element "identifier", to: :identifier
69
+ map_element "identifier", to: :identifier, render_default: true
54
70
  map_element "type", to: :type
55
71
  map_element "title", to: :title
56
- map_element "bibdata", using: { from: :bibdata_from_xml,
57
- to: :bibdata_to_xml }
58
- map_element "entry", to: :entry # using: { from: :entry_from_xml, to: :entry_to_xml }
72
+ map_element "format", to: :format
73
+ map_element "bibdata", to: :bibdata, with: { from: :bibdata_from_xml,
74
+ to: :bibdata_to_xml }
75
+ map_element "entry", to: :entry
76
+ map_element "output-filename", to: :output_filename,
77
+ with: { from: :output_filename_from_xml,
78
+ to: :nop_to_xml }
79
+ map_element "sectionsplit-filename", to: :sectionsplit_filename,
80
+ with: { from: :sectionsplit_filename_from_xml,
81
+ to: :nop_to_xml }
59
82
  end
60
83
 
61
84
  def entry_from_xml(model, node)
62
- model.entry = Manifest.from_xml(node.content)
85
+ model.entry = Manifest.from_xml(node.node.to_xml)
63
86
  end
64
87
 
65
- def entry_to_xml(model, parent, doc)
88
+ def entry_to_xml(model, _parent, doc)
66
89
  Array(model.entry).each do |e|
67
- elem = e.to_xml
68
- doc.add_element(parent, elem)
90
+ add_raw_xml_element(doc, e.to_xml)
69
91
  end
70
92
  end
71
93
 
94
+ def output_filename_from_xml(model, node)
95
+ model.output_filename ||= node.content
96
+ end
97
+
98
+ def sectionsplit_filename_from_xml(model, node)
99
+ model.sectionsplit_filename ||= node.content
100
+ end
101
+
72
102
  def level_from_yaml(model, value)
73
103
  model.type ||= value
74
104
  end
@@ -0,0 +1,12 @@
1
+ require "lutaml/xml"
2
+
3
+ module Metanorma
4
+ class Collection
5
+ module Config
6
+ class MetanormaCollectionNamespace < ::Lutaml::Xml::Namespace
7
+ uri "http://metanorma.org"
8
+ element_form_default :qualified
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,5 @@
1
+ require "relaton-cli"
2
+
1
3
  module Metanorma
2
4
  class Collection
3
5
  class Document
@@ -46,26 +48,65 @@ module Metanorma
46
48
 
47
49
  private
48
50
 
51
+ # Select the appropriate Relaton parser class for the given flavor tag.
52
+ # Uses the flavor's Item class (not Bibdata) because in Relaton 2.x,
53
+ # Bibdata.from_xml produces a generic Relaton::Bib::ItemData instance
54
+ # (due to BibdataShared resolving `model ItemData` in the definer's scope),
55
+ # while Item.from_xml correctly produces a flavor-specific ItemData instance.
56
+ # @param tag [String, nil] flavor tag (e.g. "iso", "ietf")
57
+ # @return [Class] Relaton Item parser class
49
58
  def mn2relaton_parser(tag)
50
- case tag.sub(/-standard/, "")
51
- when "bipm" then RelatonBipm::XMLParser
52
- when "bsi" then RelatonBsi::XMLParser
53
- when "ietf" then RelatonIetf::XMLParser
54
- when "iho" then RelatonIho::XMLParser
55
- when "itu" then RelatonItu::XMLParser
56
- when "iec" then RelatonIec::XMLParser
57
- when "iso" then RelatonIsoBib::XMLParser
58
- when "nist" then RelatonNist::XMLParser
59
- when "ogc" then RelatonOgc::XMLParser
60
- else RelatonBib::XMLParser
59
+ case tag
60
+ when "bipm"
61
+ require "relaton/bipm" unless defined?(::Relaton::Bipm::Item)
62
+ ::Relaton::Bipm::Item
63
+ when "bsi"
64
+ require "relaton/bsi" unless defined?(::Relaton::Bsi::Item)
65
+ ::Relaton::Bsi::Item
66
+ when "ietf"
67
+ require "relaton/ietf" unless defined?(::Relaton::Ietf::Item)
68
+ ::Relaton::Ietf::Item
69
+ when "iho"
70
+ require "relaton/iho" unless defined?(::Relaton::Iho::Item)
71
+ ::Relaton::Iho::Item
72
+ when "itu"
73
+ require "relaton/itu" unless defined?(::Relaton::Itu::Item)
74
+ ::Relaton::Itu::Item
75
+ when "iec"
76
+ require "relaton/iec" unless defined?(::Relaton::Iec::Item)
77
+ ::Relaton::Iec::Item
78
+ when "iso"
79
+ require "relaton/iso" unless defined?(::Relaton::Iso::Item)
80
+ ::Relaton::Iso::Item
81
+ when "nist"
82
+ require "relaton/nist" unless defined?(::Relaton::Nist::Item)
83
+ ::Relaton::Nist::Item
84
+ when "ogc"
85
+ require "relaton/ogc" unless defined?(::Relaton::Ogc::Item)
86
+ ::Relaton::Ogc::Item
87
+ else
88
+ ::Relaton::Bib::Item
61
89
  end
90
+ rescue LoadError => e
91
+ warn "Warning: Failed to load relaton gem for '#{tag}': #{e.message}. Falling back to Relaton::Bib::Item"
92
+ ::Relaton::Bib::Item
62
93
  end
63
94
 
64
95
  # #param xml [Nokogiri::XML::Document, Nokogiri::XML::Element]
65
96
  # @return [RelatonBib::BibliographicItem,RelatonIso::IsoBibliographicItem]
66
97
  def from_xml(xml)
67
98
  b = xml.at("//xmlns:bibitem|//xmlns:bibdata")
68
- r = mn2relaton_parser(xml.root.name)
99
+ # <bibitem> elements are always flavor-independent: use the base
100
+ # Relaton::Bib::Bibitem regardless of collection flavor.
101
+ # <bibdata> elements carry flavor-specific metadata (<ext> etc.) and
102
+ # must be parsed with the appropriate flavor Bibdata class.
103
+ r = if b.name == "bibitem"
104
+ ::Relaton::Bib::Bibitem
105
+ else
106
+ mn2relaton_parser(xml.root["flavor"])
107
+ end
108
+ # Relaton doesn't understand Pres XML tags
109
+ b.xpath("//xmlns:fmt-identifier").each(&:remove)
69
110
  r.from_xml(b.to_xml)
70
111
  end
71
112
 
@@ -111,9 +152,9 @@ module Metanorma
111
152
 
112
153
  # @return [String]
113
154
  def type
114
- first = @bibitem.docidentifier.first
155
+ first = @bibitem.docidentifier&.first
115
156
  @type ||= (first&.type&.downcase ||
116
- first&.id&.match(/^[^\s]+/)&.to_s)&.downcase ||
157
+ first&.content&.match(/^[^\s]+/)&.to_s)&.downcase ||
117
158
  "standoc"
118
159
  end
119
160
 
@@ -123,7 +164,7 @@ module Metanorma
123
164
  if @raw
124
165
  builder.parent.add_child(@bibitem.root)
125
166
  else
126
- builder.send("#{type}-standard") do |b|
167
+ builder.send(:metanorma) do |b|
127
168
  b << @bibitem.to_xml(bibdata: true)
128
169
  end
129
170
  end