metanorma-document 0.2.1 → 0.2.3

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.
@@ -8,22 +8,6 @@ module Metanorma
8
8
  # Extends StandardRenderer with ISO-specific cover page, boilerplate,
9
9
  # foreword, introduction, annex formatting, and ISO term entries.
10
10
  class IsoRenderer < StandardRenderer
11
- class << self
12
- def doc_types
13
- @doc_types ||= []
14
- if superclass <= IsoRenderer && superclass != IsoRenderer
15
- superclass.doc_types + @doc_types
16
- else
17
- @doc_types.dup
18
- end
19
- end
20
-
21
- def registers_doc_type(klass)
22
- @doc_types ||= []
23
- @doc_types << klass
24
- end
25
- end
26
-
27
11
  # --- Public hooks for flavor customization ---
28
12
 
29
13
  def flavor_publishers(_doc_id)
@@ -67,45 +51,20 @@ module Metanorma
67
51
  end
68
52
  end
69
53
 
70
- def render(node, **)
71
- case node
72
- when Metanorma::IsoDocument::Root
73
- render_document(node, **)
74
- when Metanorma::IsoDocument::Sections::IsoPreface
75
- render_preface(node, **)
76
- when Metanorma::IsoDocument::Sections::IsoSections
77
- render_sections(node, **)
78
- when Metanorma::IsoDocument::Sections::IsoClauseSection
79
- render_clause(node, **)
80
- when Metanorma::IsoDocument::Sections::IsoAnnexSection
81
- render_annex(node, **)
82
- when Metanorma::IsoDocument::Sections::IsoTermsSection
83
- render_terms_section(node, **)
84
- when Metanorma::IsoDocument::Sections::IsoForewordSection
85
- render_foreword(node, **)
86
- when Metanorma::IsoDocument::Sections::IsoAbstractSection
87
- render_abstract(node, **)
88
- when Metanorma::IsoDocument::Terms::IsoTerm
89
- render_term(node, **)
90
- when Metanorma::IsoDocument::Terms::TermNote
91
- render_term_note(node, **)
92
- when Metanorma::IsoDocument::Terms::TermExample
93
- render_term_example(node, **)
94
- when Metanorma::IsoDocument::Boilerplate
95
- render_boilerplate(node, **)
96
- else
97
- super
98
- end
99
- end
54
+ register_render Metanorma::IsoDocument::Root, :render_document
55
+ register_render Metanorma::IsoDocument::Sections::IsoPreface, :render_preface
56
+ register_render Metanorma::IsoDocument::Sections::IsoSections, :render_sections
57
+ register_render Metanorma::IsoDocument::Sections::IsoClauseSection, :render_clause
58
+ register_render Metanorma::IsoDocument::Sections::IsoAnnexSection, :render_annex
59
+ register_render Metanorma::IsoDocument::Sections::IsoTermsSection, :render_terms_section
60
+ register_render Metanorma::IsoDocument::Sections::IsoForewordSection, :render_foreword
61
+ register_render Metanorma::IsoDocument::Sections::IsoAbstractSection, :render_abstract
62
+ register_render Metanorma::IsoDocument::Terms::IsoTerm, :render_term
63
+ register_render Metanorma::IsoDocument::Terms::TermNote, :render_term_note
64
+ register_render Metanorma::IsoDocument::Terms::TermExample, :render_term_example
65
+ register_render Metanorma::IsoDocument::Boilerplate, :render_boilerplate
100
66
 
101
- def render_inline_element(element)
102
- case element
103
- when Metanorma::IsoDocument::Terms::TermOrigin
104
- render_term_origin(element)
105
- else
106
- super
107
- end
108
- end
67
+ register_inline_render Metanorma::IsoDocument::Terms::TermOrigin, :render_term_origin
109
68
 
110
69
  def render_term_origin(element)
111
70
  text = extract_text_value(element)
@@ -585,32 +544,6 @@ module Metanorma
585
544
  end
586
545
  end
587
546
 
588
- def render_term_note(note)
589
- attrs = element_attrs(id: safe_attr(note, :id), class: "note-block")
590
- tag("div", attrs) do
591
- label = extract_termnote_label(note)
592
- @output << "<p><span class=\"term-note-label\">#{escape_html(label)}: </span>"
593
- note.p&.each { |para| render_mixed_inline(para) }
594
- @output << "</p>"
595
- note.ul&.each { |ul| render_unordered_list(ul) }
596
- note.ol&.each { |ol| render_ordered_list(ol) }
597
- note.dl&.then { |dl| render_definition_list(dl) }
598
- end
599
- end
600
-
601
- def render_term_example(example)
602
- attrs = element_attrs(id: safe_attr(example, :id), class: "example")
603
- tag("div", attrs) do
604
- label = extract_block_label(example, "EXAMPLE")
605
- @output << "<p><span class=\"example-label\">#{escape_html(label)}</span>&nbsp;"
606
- example.p&.each { |para| render_mixed_inline(para) }
607
- @output << "</p>"
608
- example.ul&.each { |ul| render_unordered_list(ul) }
609
- example.ol&.each { |ol| render_ordered_list(ol) }
610
- example.dl&.then { |dl| render_definition_list(dl) }
611
- end
612
- end
613
-
614
547
  # --- Boilerplate rendering ---
615
548
 
616
549
  def render_boilerplate(boilerplate, **_opts)
@@ -685,45 +618,6 @@ module Metanorma
685
618
  @output << "</#{h}>"
686
619
  end
687
620
 
688
- # Collect all renderable children from a section, sorted by displayorder.
689
- # Uses element_order directly because each_mixed_content returns early
690
- # when the model class doesn't declare mixed_content (mixed? is false).
691
- def collect_ordered_children(section)
692
- children = gather_element_order_children(section)
693
-
694
- # Also gather typed attributes that may not appear in element_order
695
- %i[terms definitions].each do |attr|
696
- val = safe_attr(section, attr)
697
- next if val.nil?
698
-
699
- Array(val).each do |v|
700
- children << v unless children.include?(v)
701
- end
702
- end
703
-
704
- # Sort by displayorder (non-nil first, then nil at end)
705
- children.compact!
706
- children.sort_by do |node|
707
- order = begin
708
- node.displayorder
709
- rescue StandardError
710
- nil
711
- end
712
- order &&= order.to_i
713
- order || Float::INFINITY
714
- end
715
- end
716
-
717
- def render_ordered_content(section, level = 1)
718
- children = collect_ordered_children(section)
719
- children.each do |node|
720
- next if node.is_a?(String)
721
- next if is_title_element?(node, section)
722
-
723
- render(node, level: level + 1)
724
- end
725
- end
726
-
727
621
  # Collect all document-level children (sections, normative refs, annexes,
728
622
  # bibliography) sorted by displayorder for correct document order.
729
623
  # Top-level paragraphs in sections (title paragraphs) are excluded —
@@ -731,87 +625,19 @@ module Metanorma
731
625
  def collect_document_children(doc)
732
626
  items = []
733
627
 
734
- # Main sections children (clauses, terms, etc.)
735
628
  if doc.sections
736
- section_children = gather_element_order_children(doc.sections)
737
- # Title paragraphs are ParagraphBlock objects at the top level of
738
- # sections. They are rendered by render_doc_title, so skip them here.
629
+ section_children = collect_ordered_children(doc.sections)
739
630
  section_children.reject! do |node|
740
631
  node.is_a?(Metanorma::Document::Components::Paragraphs::ParagraphBlock)
741
632
  end
742
633
  items.concat(section_children)
743
- # Also add typed attributes that may not be in element_order
744
- %i[terms definitions].each do |attr|
745
- val = safe_attr(doc.sections, attr)
746
- next if val.nil?
747
-
748
- Array(val).each { |v| items << v unless items.include?(v) }
749
- end
750
634
  end
751
635
 
752
- # Normative references from bibliography (may have displayorder)
753
636
  doc.bibliography&.references&.each { |r| items << r }
754
-
755
- # Annexes
756
637
  doc.annex&.each { |a| items << a }
757
638
 
758
- # Non-normative bibliography (no displayorder = goes at end)
759
- if doc.bibliography&.references
760
- # Already included above; filter normative vs non-normative below
761
- end
762
-
763
639
  items.compact!
764
- items.sort_by do |node|
765
- order = begin
766
- node.displayorder
767
- rescue StandardError
768
- nil
769
- end
770
- order &&= order.to_i
771
- order || Float::INFINITY
772
- end
773
- end
774
-
775
- # Iterate element_order directly, bypassing each_mixed_content which
776
- # requires mixed?/ordered? to be true (many section models aren't).
777
- def gather_element_order_children(node)
778
- children = []
779
- return children unless node.is_a?(Lutaml::Model::Serializable)
780
- return children unless node.element_order && !node.element_order.empty?
781
-
782
- xml_mapping = node.class.mappings_for(:xml, node.lutaml_register)
783
- return children unless xml_mapping
784
-
785
- element_to_attr = {}
786
- xml_mapping.mapping_elements_hash.each_value do |rule_or_array|
787
- Array(rule_or_array).each do |rule|
788
- element_to_attr[rule.name] = rule.to
789
- element_to_attr[rule.name.to_s] = rule.to if rule.name.is_a?(Symbol)
790
- end
791
- end
792
-
793
- indices = Hash.new(0)
794
-
795
- node.element_order.each do |el|
796
- if el.text?
797
- children << el.text_content if el.text_content
798
- elsif el.element?
799
- attr_name = element_to_attr[el.name]
800
- next unless attr_name
801
-
802
- coll = node.send(attr_name)
803
- obj = if coll.is_a?(Array)
804
- idx = indices[attr_name]
805
- indices[attr_name] += 1
806
- coll[idx]
807
- else
808
- coll
809
- end
810
- children << obj if obj
811
- end
812
- end
813
-
814
- children
640
+ sort_by_displayorder(items)
815
641
  end
816
642
 
817
643
  def publisher_logos_html(_doc)
@@ -4,7 +4,6 @@ module Metanorma
4
4
  module Html
5
5
  # ITU brand: #0e99d5 blue from logo
6
6
  class ItuRenderer < IsoRenderer
7
- registers_doc_type Metanorma::ItuDocument::Root
8
7
 
9
8
  def flavor_publishers(_doc_id)
10
9
  ["ITU"]
@@ -5,7 +5,6 @@ module Metanorma
5
5
  # Renders OgcDocument components to HTML.
6
6
  # Extends IsoRenderer with OGC-specific branding (geospatial, OGC cyan-blue #00b1ff).
7
7
  class OgcRenderer < IsoRenderer
8
- registers_doc_type Metanorma::OgcDocument::Root
9
8
 
10
9
  def flavor_publishers(_doc_id)
11
10
  ["OGC"]
@@ -5,7 +5,6 @@ module Metanorma
5
5
  # Renders OimlDocument (OIML) components to HTML.
6
6
  # Extends IsoRenderer with OIML branding.
7
7
  class OimlRenderer < IsoRenderer
8
- registers_doc_type Metanorma::OimlDocument::Root
9
8
 
10
9
  def flavor_publishers(_doc_id)
11
10
  ["OIML"]
@@ -5,7 +5,6 @@ module Metanorma
5
5
  # Renders PDF Association (PDFA) taste documents to HTML.
6
6
  # PDFA brand: #cf9c1d gold + #d03544 red + #4992b2 steel blue from logo
7
7
  class PdfaRenderer < IsoRenderer
8
- registers_doc_type Metanorma::RiboseDocument::Root
9
8
 
10
9
  def flavor_publishers(_doc_id)
11
10
  ["PDF Association"]
@@ -5,7 +5,6 @@ module Metanorma
5
5
  # Renders RiboseDocument components to HTML.
6
6
  # Extends IsoRenderer with Ribose branding.
7
7
  class RiboseRenderer < IsoRenderer
8
- registers_doc_type Metanorma::RiboseDocument::Root
9
8
 
10
9
  def flavor_publishers(_doc_id)
11
10
  ["Ribose"]
@@ -5,38 +5,19 @@ module Metanorma
5
5
  # Renders StandardDocument components to HTML.
6
6
  # Extends BaseRenderer with terms, bibliography, and standard sections.
7
7
  class StandardRenderer < BaseRenderer
8
- def render(node, **)
9
- case node
10
- when Metanorma::StandardDocument::Root
11
- render_standard_document(node, **)
12
- when Metanorma::StandardDocument::Terms::Term
13
- render_term(node, **)
14
- when Metanorma::StandardDocument::Sections::TermsSection
15
- render_terms_section(node, **)
16
- when Metanorma::StandardDocument::Sections::StandardReferencesSection
17
- render_references_section(node, **)
18
- when Metanorma::StandardDocument::Sections::BibliographySection
19
- render_bibliography(node, **)
20
- when Metanorma::StandardDocument::Sections::ClauseSection
21
- render_clause_section(node, **)
22
- when Metanorma::StandardDocument::Sections::AnnexSection
23
- render_annex_section(node, **)
24
- when Metanorma::StandardDocument::Sections::StandardSection
25
- render_standard_section(node, **)
26
- when Metanorma::StandardDocument::Sections::Abstract
27
- render_abstract_section(node, **)
28
- when Metanorma::StandardDocument::Sections::Foreword
29
- render_foreword_section(node, **)
30
- when Metanorma::StandardDocument::Sections::Introduction
31
- render_introduction_section(node, **)
32
- when Metanorma::StandardDocument::Sections::FloatingTitle
33
- render_floating_title(node, **)
34
- when Metanorma::StandardDocument::Blocks::AmendBlock
35
- render_amend_block(node, **)
36
- else
37
- super
38
- end
39
- end
8
+ register_render Metanorma::StandardDocument::Root, :render_standard_document
9
+ register_render Metanorma::StandardDocument::Terms::Term, :render_term
10
+ register_render Metanorma::StandardDocument::Sections::TermsSection, :render_terms_section
11
+ register_render Metanorma::StandardDocument::Sections::StandardReferencesSection, :render_references_section
12
+ register_render Metanorma::StandardDocument::Sections::BibliographySection, :render_bibliography
13
+ register_render Metanorma::StandardDocument::Sections::ClauseSection, :render_clause_section
14
+ register_render Metanorma::StandardDocument::Sections::AnnexSection, :render_annex_section
15
+ register_render Metanorma::StandardDocument::Sections::StandardSection, :render_standard_section
16
+ register_render Metanorma::StandardDocument::Sections::Abstract, :render_abstract_section
17
+ register_render Metanorma::StandardDocument::Sections::Foreword, :render_foreword_section
18
+ register_render Metanorma::StandardDocument::Sections::Introduction, :render_introduction_section
19
+ register_render Metanorma::StandardDocument::Sections::FloatingTitle, :render_floating_title
20
+ register_render Metanorma::StandardDocument::Blocks::AmendBlock, :render_amend_block
40
21
 
41
22
  private
42
23
 
@@ -138,63 +119,46 @@ module Metanorma
138
119
 
139
120
  # --- Section rendering ---
140
121
 
141
- def render_clause_section(clause, level: 1, **_opts)
142
- attrs = element_attrs(id: safe_attr(clause, :id))
122
+ def render_section(section, level: 1, title_class: nil, with_subsections: false, with_terms: false)
123
+ attrs = element_attrs(id: safe_attr(section, :id))
143
124
  tag("div", attrs) do
144
- render_standard_title(clause, level)
145
- render_standard_section_blocks(clause, level)
146
- render_subsections(clause, level)
125
+ if title_class
126
+ render_standard_title(section, level, default_class: title_class)
127
+ else
128
+ render_standard_title(section, level)
129
+ end
130
+ render_standard_section_blocks(section, level)
131
+ render_subsections(section, level) if with_subsections
132
+ section.terms&.each { |term| render_term(term) } if with_terms
147
133
  end
148
134
  end
149
135
 
150
- def render_annex_section(annex, level: 1, **_opts)
151
- attrs = element_attrs(id: safe_attr(annex, :id))
152
- tag("div", attrs) do
153
- render_standard_title(annex, level)
154
- render_standard_section_blocks(annex, level)
155
- render_subsections(annex, level)
156
- end
136
+ def render_clause_section(section, level: 1, **)
137
+ render_section(section, level: level, with_subsections: true)
157
138
  end
158
139
 
159
- def render_standard_section(section, level: 1, **_opts)
160
- attrs = element_attrs(id: safe_attr(section, :id))
161
- tag("div", attrs) do
162
- render_standard_title(section, level)
163
- render_standard_section_blocks(section, level)
164
- end
140
+ def render_annex_section(section, level: 1, **)
141
+ render_section(section, level: level, with_subsections: true)
165
142
  end
166
143
 
167
- def render_terms_section(terms, level: 1, **_opts)
168
- attrs = element_attrs(id: safe_attr(terms, :id))
169
- tag("div", attrs) do
170
- render_standard_title(terms, level)
171
- render_standard_section_blocks(terms, level)
172
- terms.terms&.each { |term| render_term(term) }
173
- end
144
+ def render_standard_section(section, level: 1, **)
145
+ render_section(section, level: level)
174
146
  end
175
147
 
176
- def render_abstract_section(section, level: 1, **_opts)
177
- attrs = element_attrs(id: safe_attr(section, :id))
178
- tag("div", attrs) do
179
- render_standard_title(section, level, default_class: "intro-title")
180
- render_standard_section_blocks(section, level)
181
- end
148
+ def render_terms_section(section, level: 1, **)
149
+ render_section(section, level: level, with_terms: true)
182
150
  end
183
151
 
184
- def render_foreword_section(section, level: 1, **_opts)
185
- attrs = element_attrs(id: safe_attr(section, :id))
186
- tag("div", attrs) do
187
- render_standard_title(section, level, default_class: "foreword-title")
188
- render_standard_section_blocks(section, level)
189
- end
152
+ def render_abstract_section(section, level: 1, **)
153
+ render_section(section, level: level, title_class: "intro-title")
190
154
  end
191
155
 
192
- def render_introduction_section(section, level: 1, **_opts)
193
- attrs = element_attrs(id: safe_attr(section, :id))
194
- tag("div", attrs) do
195
- render_standard_title(section, level, default_class: "intro-title")
196
- render_standard_section_blocks(section, level)
197
- end
156
+ def render_foreword_section(section, level: 1, **)
157
+ render_section(section, level: level, title_class: "foreword-title")
158
+ end
159
+
160
+ def render_introduction_section(section, level: 1, **)
161
+ render_section(section, level: level, title_class: "intro-title")
198
162
  end
199
163
 
200
164
  def render_floating_title(title_node, **_opts)
@@ -285,11 +249,7 @@ module Metanorma
285
249
  tag("div", attrs) do
286
250
  label = extract_termnote_label(note)
287
251
  @output << "<p><span class=\"term-note-label\">#{escape_html(label)}: </span>"
288
- if note.p && !note.p.empty?
289
- note.p.each do |para|
290
- render_mixed_inline(para)
291
- end
292
- end
252
+ note.p&.each { |para| render_mixed_inline(para) }
293
253
  @output << "</p>"
294
254
  note.ul&.each { |ul| render_unordered_list(ul) }
295
255
  note.ol&.each { |ol| render_ordered_list(ol) }
@@ -302,11 +262,7 @@ module Metanorma
302
262
  tag("div", attrs) do
303
263
  label = extract_block_label(example, "EXAMPLE")
304
264
  @output << "<p><span class=\"example-label\">#{escape_html(label)}</span>&nbsp;"
305
- if example.p && !example.p.empty?
306
- example.p.each do |para|
307
- render_mixed_inline(para)
308
- end
309
- end
265
+ example.p&.each { |para| render_mixed_inline(para) }
310
266
  @output << "</p>"
311
267
  example.ul&.each { |ul| render_unordered_list(ul) }
312
268
  example.ol&.each { |ol| render_ordered_list(ol) }
@@ -433,7 +389,7 @@ module Metanorma
433
389
  end
434
390
  if primary
435
391
  id_val = extract_text_value(primary)
436
- @output << "<span class=\"std-doc-number\">#{escape_html(id_val)}</span>" unless id_val.to_s.empty?
392
+ @output << "<span class=\"ref-doc-number\">#{escape_html(id_val)}</span>" unless id_val.to_s.empty?
437
393
  end
438
394
  end
439
395
 
@@ -442,7 +398,7 @@ module Metanorma
442
398
  date_on = date.is_a?(Metanorma::Document::Relaton::BibliographicDate) ? date.on : nil
443
399
  date_val = extract_text_value(date_on || safe_attr(date, :text))
444
400
  if date_val && !date_val.to_s.empty?
445
- @output << ":<span class=\"std-year\">#{escape_html(date_val.to_s)}</span>"
401
+ @output << ":<span class=\"ref-year\">#{escape_html(date_val.to_s)}</span>"
446
402
  end
447
403
  end
448
404
  end
@@ -8,7 +8,6 @@ module Metanorma
8
8
  autoload :Generator, "metanorma/html/generator"
9
9
  autoload :Theme, "metanorma/html/theme"
10
10
  autoload :AssetPipeline, "metanorma/html/asset_pipeline"
11
- autoload :ComponentRegistry, "metanorma/html/component_registry"
12
11
  autoload :Component, "metanorma/html/component"
13
12
  autoload :Drops, "metanorma/html/drops"
14
13
  autoload :StandardRenderer, "metanorma/html/standard_renderer"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-document
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-05 00:00:00.000000000 Z
11
+ date: 2026-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -481,7 +481,6 @@ files:
481
481
  - lib/metanorma/html/component/footnote_collector.rb
482
482
  - lib/metanorma/html/component/index_section.rb
483
483
  - lib/metanorma/html/component/index_term_collector.rb
484
- - lib/metanorma/html/component_registry.rb
485
484
  - lib/metanorma/html/drops.rb
486
485
  - lib/metanorma/html/drops/admonition_drop.rb
487
486
  - lib/metanorma/html/drops/block_element_drop.rb
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Metanorma
4
- module Html
5
- class ComponentRegistry
6
- def initialize
7
- @map = {}
8
- end
9
-
10
- def register(model_class, component_class)
11
- @map[model_class] = component_class
12
- end
13
-
14
- def lookup(model_class)
15
- @map[model_class] ||
16
- model_class.ancestors
17
- .drop(1)
18
- .filter_map { |a| @map[a] }
19
- .first ||
20
- nil
21
- end
22
-
23
- def component_for(renderer, model_class)
24
- component_class = lookup(model_class)
25
- component_class&.new(renderer)
26
- end
27
-
28
- def register_all(component_module)
29
- component_module.constants.each do |const|
30
- klass = component_module.const_get(const)
31
- next unless klass.is_a?(Class) && klass < Component::Base
32
- klass.register_in(self)
33
- end
34
- end
35
- end
36
- end
37
- end