metanorma 2.3.8 → 2.4.0

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: '097d46fb29b83f41c643c4985cf3083ef2f49c53c46cbcb5586b2497b502b81b'
4
- data.tar.gz: e020ab24c572c384ae47965fe1b84a71951d20745ff2385a4eec3aa4fb66c80a
3
+ metadata.gz: 2229a91588cc69992f7f3cba97a5856f84f9d6c4a6f5b97ef4ea031d6b4a279b
4
+ data.tar.gz: 9c4a785518d8ebbfe8246e8fca7dad9db726ea789f334cd6f13a3d985f2ca426
5
5
  SHA512:
6
- metadata.gz: e2447f50b14b180ec78b8e6ec17b4a5de8f059b0a9091aa6cdb2a5c43e0792a836afa283143ad26fb7caf60fe27212e7e939bf0cacb3b3d3f9bd263ec7787ea0
7
- data.tar.gz: 68ad569a5c042a89cc9caf4645a44d5f6e5c8511449eb60bce1563915535c639d7dc59fa737b1839bb9e6a9f85a156e8a9bccd8a91115bd192d6e2137c11c8fd
6
+ metadata.gz: 3631bba7d4064614b4a6926619bc5542d22b871fad3d10a9dd4e701d9d45433837fd4b2b43107fc32838f53f6d0a5e3181c81ab81b5ddbfed96381f973af2a21
7
+ data.tar.gz: d4877d254fb6182e6c24c71f20f89163ff72b5aa57b1df9b1acf0c419b665e727594bf7ea8f98d897e35812b0de6e4b99f379271c578da5301e2956e8612ced2
@@ -89,10 +89,41 @@ module Metanorma
89
89
  def to_xml
90
90
  c = ::Metanorma::Collection::Config::Config
91
91
  .new(directive: @directives, bibdata: @bibdata,
92
- manifest: @manifest.config, documents: @documents,
92
+ manifest: @manifest.config, documents: doc_containers,
93
93
  prefatory_content: @prefatory, final_content: @final)
94
94
  c.collection = self
95
- c.to_xml # .sub("<metanorma-collection", "<metanorma-collection xmlns='http://metanorma.org'")
95
+ c.to_xml
96
+ end
97
+
98
+ # Populate the Config#documents collection with DocContainer wrappers.
99
+ # Only emit them when the `documents-inline` directive is set; otherwise
100
+ # the manifest's <entry fileref=…> already references them externally.
101
+ def doc_containers
102
+ @directives.detect { |d| d.key == "documents-inline" } or return []
103
+ @documents.each_with_index.map do |(_, d), idx|
104
+ ::Metanorma::Collection::Config::DocContainer.new(
105
+ id: format("doc%<index>09d", index: idx),
106
+ content: doccontainer1_inner(d),
107
+ )
108
+ end
109
+ end
110
+
111
+ # Inner XML of a single <doc-container> as a string. The body is either
112
+ # the document's <metanorma> presentation root (with its own xmlns) or
113
+ # an attachment payload.
114
+ def doccontainer1_inner(doc)
115
+ b = Nokogiri::XML::Builder.new do |xml|
116
+ xml.send(:wrapper) do |w|
117
+ if doc.attachment
118
+ doc.bibitem and w << doc.bibitem.root.to_xml
119
+ w.attachment Vectory::Utils::datauri(doc.file)
120
+ else
121
+ doc.to_xml w
122
+ w.parent.children.first["flavor"] = Util::taste2flavor(flavor)
123
+ end
124
+ end
125
+ end
126
+ b.parent.elements.first.children.map(&:to_xml).join
96
127
  end
97
128
 
98
129
  def render(opts)
@@ -117,10 +148,40 @@ module Metanorma
117
148
  def content_to_xml(elm, builder)
118
149
  (cnt = send(elm)) or return
119
150
  @compile.load_flavor(Util::taste2flavor(flavor))
120
- out = prefatory_parse(Util::asciidoc_dummy_header + cnt.strip)
151
+ out = prefatory_parse(
152
+ Util::asciidoc_dummy_header(
153
+ docidentifier: dummy_header_docidentifier,
154
+ ) + cnt.strip,
155
+ )
121
156
  builder.send("#{elm}-content") { |b| b << out }
122
157
  end
123
158
 
159
+ # Pick a real docidentifier value for the prefatory dummy header so
160
+ # the flavor's metadata_id has something pubid-parseable to put into
161
+ # the bibdata, instead of falling back to its (Liquid-templated)
162
+ # docid_template (issue #558). Prefer the first manifest document's
163
+ # docidentifier (concrete, e.g. "IHO S-97"); fall back to the
164
+ # collection's own (which may carry suffixes like "(all parts)" that
165
+ # pubid won't parse). Both candidates are wrapped because the
166
+ # bibitem field may be a parsed Relaton BibliographicItem in some
167
+ # phases and a Nokogiri::XML::Document in others.
168
+ def dummy_header_docidentifier
169
+ first_doc_docid || @bibdata&.docidentifier&.first&.content
170
+ end
171
+
172
+ def first_doc_docid
173
+ bib = documents.values.first&.bibitem or return nil
174
+ if bib.respond_to?(:docidentifier)
175
+ bib.docidentifier&.first&.content
176
+ elsif bib.respond_to?(:at) # Nokogiri (rendering-phase shape)
177
+ # Use local-name() so the match works regardless of whether the
178
+ # rendering-phase document carries a default namespace, without
179
+ # tripping Nokogiri's "undefined namespace prefix" error when
180
+ # it doesn't.
181
+ bib.at("//*[local-name()='docidentifier']")&.text
182
+ end
183
+ end
184
+
124
185
  # @param cnt [String] prefatory/final content
125
186
  # @return [String] XML
126
187
  def prefatory_parse(cnt)
@@ -163,27 +224,6 @@ module Metanorma
163
224
  bibdata
164
225
  end
165
226
 
166
- # @param builder [Nokogiri::XML::Builder]
167
- def doccontainer(builder)
168
- @directives.detect { |d| d.key == "documents-inline" } or return
169
- documents.each_with_index do |(_, d), i|
170
- doccontainer1(builder, d, i)
171
- end
172
- end
173
-
174
- def doccontainer1(builder, doc, idx)
175
- id = format("doc%<index>09d", index: idx)
176
- builder.send(:"doc-container", id: id) do |b|
177
- if doc.attachment
178
- doc.bibitem and b << doc.bibitem.root.to_xml
179
- b.attachment Vectory::Utils::datauri(doc.file)
180
- else
181
- doc.to_xml b
182
- b.parent.children.first["flavor"] = Util::taste2flavor(flavor)
183
- end
184
- end
185
- end
186
-
187
227
  def flavor
188
228
  @flavor ||= fetch_flavor || "standoc"
189
229
  end
@@ -5,12 +5,14 @@ require_relative "converters"
5
5
  require_relative "bibdata"
6
6
  require_relative "directive"
7
7
  require_relative "manifest"
8
+ require_relative "namespaces"
9
+ require_relative "doc_container"
8
10
 
9
11
  module Metanorma
10
12
  class Collection
11
13
  module Config
12
14
  Lutaml::Model::Config.configure do |config|
13
- config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
15
+ config.xml_adapter_type = :nokogiri
14
16
  end
15
17
 
16
18
  class Config < ::Lutaml::Model::Serializable
@@ -26,7 +28,7 @@ module Metanorma
26
28
  attribute :compile, CompileOptions
27
29
  attribute :prefatory_content, :string, raw: true
28
30
  attribute :final_content, :string, raw: true
29
- attribute :documents, Bibdata, collection: true
31
+ attribute :documents, DocContainer, collection: true
30
32
  attribute :xmlns, :string, default: -> { "http://metanorma.org" }
31
33
 
32
34
  yaml do
@@ -48,9 +50,8 @@ module Metanorma
48
50
  end
49
51
 
50
52
  xml do
51
- root "metanorma-collection"
52
- # namespace "http://metanorma.org", "m"
53
- # map_attribute "xmlns", to: :xmlns
53
+ element "metanorma-collection"
54
+ namespace MetanormaCollectionNamespace
54
55
  map_element "bibdata", to: :bibdata, with: { from: :bibdata_from_xml,
55
56
  to: :bibdata_to_xml }
56
57
  map_element "directive", to: :directive
@@ -66,10 +67,7 @@ module Metanorma
66
67
  to: :prefatory_content,
67
68
  with: { from: :prefatory_from_xml,
68
69
  to: :prefatory_to_xml }
69
- map_element "doc-container",
70
- to: :documents,
71
- with: { from: :documents_from_xml,
72
- to: :documents_to_xml }
70
+ map_element "doc-container", to: :documents
73
71
  map_element "final-content",
74
72
  to: :final_content,
75
73
  with: { from: :final_from_xml,
@@ -80,9 +78,9 @@ module Metanorma
80
78
  model.manifest = Manifest.from_xml(node.to_xml)
81
79
  end
82
80
 
83
- def manifest_to_xml(model, parent, doc)
81
+ def manifest_to_xml(model, _parent, doc)
84
82
  model.collection&.manifest&.clean_manifest(model.manifest)
85
- doc.add_element(parent, model.manifest.to_xml)
83
+ add_raw_xml_element(doc, model.manifest.to_xml)
86
84
  end
87
85
 
88
86
  def prefatory_from_xml(model, node)
@@ -97,17 +95,17 @@ module Metanorma
97
95
  content_to_xml(model, parent, doc, "final")
98
96
  end
99
97
 
100
- def content_to_xml(model, parent, doc, type)
98
+ def content_to_xml(model, _parent, doc, type)
101
99
  x = model.send("#{type}_content") or return
102
100
  n = Nokogiri::XML(x)
103
- elem = if n.elements.size == 1
104
- "<#{type}-content>#{x}</#{type}-content>" # n.root
105
- else
106
- b = Nokogiri::XML::Builder.new
107
- model.collection.content_to_xml(type, b)
108
- b.parent.elements.first
109
- end
110
- 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)
111
109
  end
112
110
 
113
111
  def final_from_xml(model, node)
@@ -1,5 +1,6 @@
1
1
  require "relaton/bib"
2
2
  require "relaton/bib/hash_parser_v1"
3
+ require_relative "namespaces"
3
4
 
4
5
  module Metanorma
5
6
  class Collection
@@ -66,8 +67,8 @@ module Metanorma
66
67
 
67
68
  def bibdata_from_xml(model, node)
68
69
  node or return
69
- force_primary_docidentifier_xml(node.adapter_node)
70
- model.bibdata = Relaton::Cli.parse_xml(node.adapter_node)
70
+ force_primary_docidentifier_xml(node.adapter_node.native)
71
+ model.bibdata = Relaton::Cli.parse_xml(node.adapter_node.native)
71
72
  end
72
73
 
73
74
  def force_primary_docidentifier_xml(node)
@@ -76,38 +77,36 @@ module Metanorma
76
77
  d["primary"] = "true"
77
78
  end
78
79
 
79
- def bibdata_to_xml(model, parent, doc)
80
+ def bibdata_to_xml(model, _parent, doc)
80
81
  b = model.bibdata or return
81
- elem = b.to_xml(bibdata: true, date_format: :full)
82
- doc.add_element(parent, elem)
82
+ add_raw_xml_element(doc, b.to_xml(bibdata: true, date_format: :full))
83
83
  end
84
84
 
85
85
  def nop_to_yaml(model, doc); end
86
86
  def nop_to_xml(model, parent, doc); end
87
87
 
88
- def documents_from_xml(model, value)
89
- model.documents = value
90
- .each_with_object([]) do |b, m|
91
- m << b
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)
92
107
  end
93
- end
94
-
95
- def documents_to_xml(model, parent, doc)
96
- documents_to_xml?(doc) or return
97
- b = Nokogiri::XML::Builder.new do |xml|
98
- xml.document do |m|
99
- model.collection.doccontainer(m) or return
100
- end
101
- end
102
- b.parent.elements.first.elements
103
- .each { |x| doc.add_element(parent, x) }
104
- end
105
-
106
- def documents_to_xml?(doc)
107
- ret = doc.parent.elements.detect do |x|
108
- x.name == "doc-container"
109
- end
110
- !ret
108
+ el.raw_content = src.children.map(&:to_xml).join
109
+ el
111
110
  end
112
111
  end
113
112
  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
1
  require "lutaml/model"
2
- require "lutaml/model/xml_adapter/nokogiri_adapter"
3
2
  require_relative "../../array_monkeypatch"
4
3
  require_relative "converters"
5
4
  require_relative "bibdata"
@@ -8,7 +7,7 @@ module Metanorma
8
7
  class Collection
9
8
  module Config
10
9
  Lutaml::Model::Config.configure do |config|
11
- config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
10
+ config.xml_adapter_type = :nokogiri
12
11
  end
13
12
 
14
13
  class Manifest < ::Lutaml::Model::Serializable
@@ -86,10 +85,9 @@ module Metanorma
86
85
  model.entry = Manifest.from_xml(node.node.to_xml)
87
86
  end
88
87
 
89
- def entry_to_xml(model, parent, doc)
88
+ def entry_to_xml(model, _parent, doc)
90
89
  Array(model.entry).each do |e|
91
- elem = e.to_xml
92
- doc.add_element(parent, elem)
90
+ add_raw_xml_element(doc, e.to_xml)
93
91
  end
94
92
  end
95
93
 
@@ -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
@@ -36,7 +36,6 @@ module Metanorma
36
36
  def initialize(collection, folder, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
37
37
  check_options options
38
38
  @xml = Nokogiri::XML collection.to_xml # @xml is the collection manifest
39
- @xml.root.default_namespace = "http://metanorma.org"
40
39
  @lang = Array(collection.bibdata.language).first || "en"
41
40
  @script = Array(collection.bibdata.script).first || "Latn"
42
41
  @locale = @xml.at("//xmlns:bibdata/xmlns:locale")&.text
@@ -190,8 +189,6 @@ module Metanorma
190
189
  end
191
190
 
192
191
  def concatenate_presentation(xml)
193
- xml.sub!("<metanorma-collection>", "<metanorma-collection xmlns='http://metanorma.org'>")
194
- # TODO BEING FORCED TO DO THAT BECAUSE SHALE IS NOT DEALING WITH DEFAULT NAMESPACES
195
192
  @directives.detect { |d| d.key == "bilingual" } and
196
193
  xml = Metanorma::Collection::Multilingual
197
194
  .new({ align_cross_elements: %w(p note) }).to_bilingual(xml)
@@ -212,8 +212,8 @@ module Metanorma
212
212
  bibdata: YAML.safe_load(@bibdata.to_yaml,
213
213
  permitted_classes: [Date, Symbol]),
214
214
  docrefs: liquid_docrefs(@manifest),
215
- "prefatory-content": isodoc_builder(@xml.at("//prefatory-content")),
216
- "final-content": isodoc_builder(@xml.at("//final-content")),
215
+ "prefatory-content": isodoc_builder(@xml.at("//xmlns:prefatory-content")),
216
+ "final-content": isodoc_builder(@xml.at("//xmlns:final-content")),
217
217
  doctitle: Array(@bibdata.title).first&.content,
218
218
  docnumber: Array(@bibdata.docidentifier).first&.content }.each do |k, v|
219
219
  v and @isodoc.meta.set(k, v)
@@ -223,11 +223,9 @@ module Metanorma
223
223
  def isodoc_builder(node)
224
224
  node or return
225
225
 
226
- # Kludging namespace back in because of Shale brain damage
227
- doc = Nokogiri::XML(node.to_xml.sub(">", " xmlns='http://www.metanorma.org'>"))
228
226
  Nokogiri::HTML::Builder.new(encoding: "UTF-8") do |b|
229
227
  b.div do |div|
230
- doc.root.children&.each { |n| @isodoc.parse(n, div) }
228
+ node.children.each { |n| @isodoc.parse(n, div) }
231
229
  end
232
230
  end.doc.root.to_html
233
231
  end
@@ -156,23 +156,31 @@ module Metanorma
156
156
  # style.gsub(%r{url\(#([^()]+)\)}, "url(#\\1_#{document_suffix})")
157
157
  # end
158
158
 
159
+ # @deprecated Call Metanorma::Core::Isodoc.resolve_converter directly.
159
160
  def load_isodoc(flavor, presxml: false)
160
161
  Metanorma::Core::Isodoc.resolve_converter(flavor, presxml: presxml)
161
162
  end
162
163
 
164
+ # @deprecated Call Metanorma::Core::Isodoc.create directly. Kept as
165
+ # a back-compat shim so existing callers keep working unchanged
166
+ # while the wrapper is retired.
163
167
  def isodoc_create(flavor, lang, script, xml, presxml: false)
164
- conv = Metanorma::Core::Isodoc.resolve_converter(flavor,
165
- presxml: presxml)
166
- Metanorma::Core::Isodoc.init(conv, lang: lang, script: script,
167
- xml: xml)
168
- end
169
-
170
- def asciidoc_dummy_header
171
- <<~DUMMY
172
- = X
173
- A
174
-
175
- DUMMY
168
+ Metanorma::Core::Isodoc.create(flavor, lang: lang, script: script,
169
+ xml: xml, presxml: presxml)
170
+ end
171
+
172
+ # Dummy asciidoc header used by the prefatory-content parse so we
173
+ # can run Asciidoctor against arbitrary text. If +docidentifier+
174
+ # is supplied, it is injected as +:docidentifier:+ so the flavor
175
+ # processor's metadata_id has a real value to put into the
176
+ # bibdata, instead of falling back to its (Liquid-templated)
177
+ # docid_template. This prevents the prefatory parse from feeding
178
+ # an unresolved Liquid template through Relaton/pubid in flavors
179
+ # whose relaton-* gem eagerly parses the docidentifier in
180
+ # content= (issue #558).
181
+ def asciidoc_dummy_header(docidentifier: nil)
182
+ attrs = docidentifier ? ":docidentifier: #{docidentifier}\n" : ""
183
+ "= X\nA\n#{attrs}\n"
176
184
  end
177
185
 
178
186
  def nokogiri_to_temp(xml, filename, suffix)
@@ -1,5 +1,7 @@
1
1
  module Metanorma
2
2
  class Compile
3
+ NO_FONTIST_FORMATS = %i[xml presentation rxl].freeze
4
+
3
5
  # Generate presentation XML from semantic XML
4
6
  def generate_presentation_xml(source_file, xml, bibdata, output_paths, opt)
5
7
  process_ext(:presentation, source_file, xml, bibdata, output_paths, opt)
@@ -56,7 +58,8 @@ module Metanorma
56
58
 
57
59
  # isodoc is Raw Metanorma XML
58
60
  def gather_and_install_fonts(source_file, options, extensions)
59
- Util.sort_extensions_execution(extensions).each do |ext|
61
+ font_exts = extensions - NO_FONTIST_FORMATS
62
+ Util.sort_extensions_execution(font_exts).each do |ext|
60
63
  isodoc_options = get_isodoc_options(source_file, options, ext)
61
64
  font_install(isodoc_options.merge(options))
62
65
  end
@@ -1,3 +1,3 @@
1
1
  module Metanorma
2
- VERSION = "2.3.8".freeze
2
+ VERSION = "2.4.0".freeze
3
3
  end
data/metanorma.gemspec CHANGED
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_runtime_dependency "metanorma-standoc"
35
35
  spec.add_runtime_dependency "mn2pdf", "~> 2"
36
36
  spec.add_runtime_dependency "nokogiri"
37
- spec.add_development_dependency "canon"
37
+ spec.add_development_dependency "canon" #, "= 0.2.3"
38
38
  spec.add_development_dependency "metanorma-iho"
39
39
  spec.add_development_dependency "metanorma-iso"
40
40
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.8
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-27 00:00:00.000000000 Z
11
+ date: 2026-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -234,7 +234,9 @@ files:
234
234
  - lib/metanorma/collection/config/config.rb
235
235
  - lib/metanorma/collection/config/converters.rb
236
236
  - lib/metanorma/collection/config/directive.rb
237
+ - lib/metanorma/collection/config/doc_container.rb
237
238
  - lib/metanorma/collection/config/manifest.rb
239
+ - lib/metanorma/collection/config/namespaces.rb
238
240
  - lib/metanorma/collection/document/document.rb
239
241
  - lib/metanorma/collection/filelookup/base.rb
240
242
  - lib/metanorma/collection/filelookup/filelookup.rb