metanorma-document 0.2.12 → 0.3.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: d75a3afa9e8f16619cd88b2bf610d81ecd5b6e4d7bc9b507a26c347832d01536
4
- data.tar.gz: 2739411b950f3871a28eec551346ee5ded7f643c9f76438574f68d1314fc398b
3
+ metadata.gz: 05125b6d3a480509e630600c04a152707d7af1cf32423a3906b99032fbb18fcd
4
+ data.tar.gz: 1c45b49ac852306fca98f05fc59cb0f790a5b814a6a3b8b56cfd6960b9cc726c
5
5
  SHA512:
6
- metadata.gz: 29980c2343686ad9ec1ebb5c582f6483a280c1ff0d7edd5354597ccb5241db71269a01c955ec2c19e215ec2eeaa0675c93563eae33b446926dad6781ed15d2d2
7
- data.tar.gz: d6e50bf824ba8f7ea1efabb168743dfd79e3e655547434f9328ba8cea817bce57012f85809940cfd2c9a995784204c846d2964375132978de4cdadebb68efde9
6
+ metadata.gz: 9ebfc36533344fd420cc2286357b77d4fe94213c8f31a0defdd7c4b06af6938e0258472bd279bd275f6a85f34a2c991a4fd12cd90d0b212e5ae9780506353600
7
+ data.tar.gz: 66b5e24551fd844cb8ad9368a10468509e0b3a7cc15836b5400d57024513da889c76f2571fc0d97cfdfacfacca7e72e6fe8d5d6f92c5bb54bc0a7d57d08d43ad
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-07-17 09:09:03 UTC using RuboCop version 1.86.1.
3
+ # on 2026-07-18 16:00:24 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -11,67 +11,13 @@ Gemspec/RequiredRubyVersion:
11
11
  Exclude:
12
12
  - 'metanorma-document.gemspec'
13
13
 
14
- # Offense count: 2
15
- # This cop supports safe autocorrection (--autocorrect).
16
- # Configuration parameters: EnforcedStyle, IndentationWidth.
17
- # SupportedStyles: with_first_argument, with_fixed_indentation
18
- Layout/ArgumentAlignment:
19
- Exclude:
20
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
21
-
22
- # Offense count: 2
23
- # This cop supports safe autocorrection (--autocorrect).
24
- # Configuration parameters: EnforcedStyle, IndentationWidth.
25
- # SupportedStyles: with_first_element, with_fixed_indentation
26
- Layout/ArrayAlignment:
27
- Exclude:
28
- - 'spec/metanorma/document/components/inline/span_element_bug01_spec.rb'
29
-
30
- # Offense count: 1
31
- # This cop supports safe autocorrection (--autocorrect).
32
- # Configuration parameters: EnforcedStyleAlignWith.
33
- # SupportedStylesAlignWith: either, start_of_block, start_of_line
34
- Layout/BlockAlignment:
35
- Exclude:
36
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
37
-
38
- # Offense count: 1
39
- # This cop supports safe autocorrection (--autocorrect).
40
- Layout/BlockEndNewline:
41
- Exclude:
42
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
43
-
44
- # Offense count: 2
45
- # This cop supports safe autocorrection (--autocorrect).
46
- # Configuration parameters: EnforcedStyle, IndentationWidth.
47
- # SupportedStyles: special_inside_parentheses, consistent, align_brackets
48
- Layout/FirstArrayElementIndentation:
49
- Exclude:
50
- - 'spec/metanorma/document/components/inline/span_element_bug01_spec.rb'
51
-
52
- # Offense count: 2
53
- # This cop supports safe autocorrection (--autocorrect).
54
- # Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
55
- # SupportedStylesAlignWith: start_of_line, relative_to_receiver
56
- Layout/IndentationWidth:
57
- Exclude:
58
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
59
-
60
- # Offense count: 700
14
+ # Offense count: 703
61
15
  # This cop supports safe autocorrection (--autocorrect).
62
16
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
63
17
  # URISchemes: http, https
64
18
  Layout/LineLength:
65
19
  Enabled: false
66
20
 
67
- # Offense count: 1
68
- # This cop supports safe autocorrection (--autocorrect).
69
- # Configuration parameters: EnforcedStyle, IndentationWidth.
70
- # SupportedStyles: aligned, indented, indented_relative_to_receiver
71
- Layout/MultilineMethodCallIndentation:
72
- Exclude:
73
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
74
-
75
21
  # Offense count: 2
76
22
  # Configuration parameters: AllowedMethods.
77
23
  # AllowedMethods: enums
@@ -129,18 +75,18 @@ Lint/UnusedMethodArgument:
129
75
  Metrics/AbcSize:
130
76
  Enabled: false
131
77
 
132
- # Offense count: 11
78
+ # Offense count: 12
133
79
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
134
80
  # AllowedMethods: refine
135
81
  Metrics/BlockLength:
136
- Max: 47
82
+ Max: 76
137
83
 
138
84
  # Offense count: 3
139
85
  # Configuration parameters: CountBlocks, CountModifierForms.
140
86
  Metrics/BlockNesting:
141
87
  Max: 4
142
88
 
143
- # Offense count: 86
89
+ # Offense count: 84
144
90
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
145
91
  Metrics/CyclomaticComplexity:
146
92
  Enabled: false
@@ -155,7 +101,7 @@ Metrics/MethodLength:
155
101
  Metrics/ParameterLists:
156
102
  Max: 7
157
103
 
158
- # Offense count: 69
104
+ # Offense count: 68
159
105
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
160
106
  Metrics/PerceivedComplexity:
161
107
  Enabled: false
@@ -195,24 +141,11 @@ Naming/PredicatePrefix:
195
141
  - 'lib/metanorma/html/base_renderer.rb'
196
142
  - 'lib/metanorma/html/drops/biblio_entry_drop.rb'
197
143
 
198
- # Offense count: 1
199
- # Configuration parameters: MinSize.
200
- Performance/CollectionLiteralInLoop:
201
- Exclude:
202
- - 'lib/metanorma/html/standard_renderer.rb'
203
-
204
144
  # Offense count: 1
205
145
  Performance/MapMethodChain:
206
146
  Exclude:
207
147
  - 'spec/metanorma/flavor_roots/flavor_roots_spec.rb'
208
148
 
209
- # Offense count: 1
210
- # This cop supports unsafe autocorrection (--autocorrect-all).
211
- # Configuration parameters: AllowRegexpMatch.
212
- Performance/RedundantEqualityComparisonBlock:
213
- Exclude:
214
- - 'spec/metanorma/document/components/inline/fmt_stem_element_bug03_spec.rb'
215
-
216
149
  # Offense count: 7
217
150
  RSpec/BeforeAfterAll:
218
151
  Exclude:
@@ -234,15 +167,6 @@ RSpec/ContextWording:
234
167
  RSpec/DescribeClass:
235
168
  Enabled: false
236
169
 
237
- # Offense count: 8
238
- # This cop supports unsafe autocorrection (--autocorrect-all).
239
- # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants.
240
- # SupportedStyles: described_class, explicit
241
- RSpec/DescribedClass:
242
- Exclude:
243
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
244
- - 'spec/metanorma/document/components/inline/xref_element_bug04_spec.rb'
245
-
246
170
  # Offense count: 234
247
171
  # Configuration parameters: CountAsOne.
248
172
  RSpec/ExampleLength:
@@ -254,10 +178,11 @@ RSpec/LeakyConstantDeclaration:
254
178
  - 'spec/metanorma/html/renderer/class_ownership_spec.rb'
255
179
  - 'spec/metanorma/html/renderer/liquid_templates_spec.rb'
256
180
 
257
- # Offense count: 4
181
+ # Offense count: 5
258
182
  RSpec/MultipleDescribes:
259
183
  Exclude:
260
184
  - 'spec/metanorma/document_spec.rb'
185
+ - 'spec/metanorma/html/flavor_registry_spec.rb'
261
186
  - 'spec/metanorma/mirror/handlers/remaining_handlers_spec.rb'
262
187
  - 'spec/metanorma/mirror/serialization_spec.rb'
263
188
  - 'spec/metanorma/standard_document/sections_spec.rb'
@@ -274,7 +199,7 @@ RSpec/NamedSubject:
274
199
  Exclude:
275
200
  - 'spec/metanorma/flavor_roots/flavor_roots_spec.rb'
276
201
 
277
- # Offense count: 1061
202
+ # Offense count: 1076
278
203
  # Configuration parameters: AllowedPatterns.
279
204
  # AllowedPatterns: ^expect_, ^assert_
280
205
  RSpec/NoExpectationExample:
@@ -286,17 +211,6 @@ RSpec/NoExpectationExample:
286
211
  RSpec/SpecFilePathFormat:
287
212
  Enabled: false
288
213
 
289
- # Offense count: 1
290
- # This cop supports safe autocorrection (--autocorrect).
291
- # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
292
- # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
293
- # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
294
- # FunctionalMethods: let, let!, subject, watch
295
- # AllowedMethods: lambda, proc, it
296
- Style/BlockDelimiters:
297
- Exclude:
298
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
299
-
300
214
  # Offense count: 5
301
215
  # This cop supports safe autocorrection (--autocorrect).
302
216
  Style/ComparableClamp:
@@ -304,24 +218,3 @@ Style/ComparableClamp:
304
218
  - 'lib/metanorma/html/iso_renderer.rb'
305
219
  - 'lib/metanorma/html/renderers/section_renderer.rb'
306
220
  - 'lib/metanorma/html/standard_renderer.rb'
307
-
308
- # Offense count: 1
309
- # This cop supports unsafe autocorrection (--autocorrect-all).
310
- Style/PredicateWithKind:
311
- Exclude:
312
- - 'spec/metanorma/document/components/inline/fmt_stem_element_bug03_spec.rb'
313
-
314
- # Offense count: 2
315
- # This cop supports unsafe autocorrection (--autocorrect-all).
316
- Style/SelectByKind:
317
- Exclude:
318
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
319
-
320
- # Offense count: 3
321
- # This cop supports safe autocorrection (--autocorrect).
322
- # Configuration parameters: EnforcedStyleForMultiline.
323
- # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
324
- Style/TrailingCommaInArguments:
325
- Exclude:
326
- - 'spec/metanorma/document/components/inline/semantic_content_bug05_spec.rb'
327
- - 'spec/metanorma/document/components/inline/xref_element_bug04_spec.rb'
data/README.adoc CHANGED
@@ -275,7 +275,7 @@ Metanorma::Mirror.build_default_registry
275
275
 
276
276
  === Metadata Service
277
277
 
278
- `Metanorma::Mirror::Metadata` is a standalone service that extracts document metadata (title, etc.) from a parsed bibdata object. It is consumed by both the forward pipeline (`Output::Pipeline::AttachMetadata`) and the forward transformer (`MetanormaToMirror#extract_root_title`), keeping title-extraction logic in one place rather than duplicated across the Handlers layer.
278
+ `Metanorma::Mirror::Metadata` is a standalone service that extracts document metadata (title, etc.) from a parsed bibdata object. It is consumed by both the forward pipeline (`Output::Pipeline::AttachMetadata`) and the forward transformer (`Transformer#extract_root_title`), keeping title-extraction logic in one place rather than duplicated across the Handlers layer.
279
279
 
280
280
  [source,ruby]
281
281
  Metanorma::Mirror::Metadata.title_from_bibdata(doc.bibdata)
@@ -6,14 +6,13 @@ module Metanorma
6
6
  module Inline
7
7
  # <semx> wraps a rendered-display slice of an inline element.
8
8
  #
9
- # Known limitation (BUGS.sts 06, filed upstream against
10
- # lutaml-model): when this element is parsed nested inside
11
- # another mixed_content parent, the direct text content of
12
- # <semx> may be captured by the parent's text attribute rather
13
- # than by SemxElement#text. Standalone parsing works correctly.
14
- # See TODO.bugs/06-nested-mixed-content-text-ownership.md and
15
- # https://github.com/lutaml/lutaml-model bug
16
- # "nested-mixed-content-text-ownership".
9
+ # History: BUGS.sts 06 reported that direct text content of
10
+ # <semx> was captured by the parent's text attribute rather
11
+ # than SemxElement#text when parsed nested. Verified
12
+ # non-reproducible at lutaml-model commit 38f90b7a (which
13
+ # added regression coverage for nested mixed_content text
14
+ # ownership in PR lutaml/lutaml-model#732). Likely resolved
15
+ # upstream between metanorma-document 0.2.9 and current HEAD.
17
16
  class SemxElement < Lutaml::Model::Serializable
18
17
  attribute :element_attr, :string
19
18
  attribute :source, :string
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Metanorma
4
4
  module Document
5
- VERSION = "0.2.12"
5
+ VERSION = "0.3.0"
6
6
  Version = VERSION
7
7
  end
8
8
  end
@@ -11,6 +11,8 @@ module Metanorma
11
11
  autoload :BlockRenderer, "metanorma/html/renderers/block_renderer"
12
12
  autoload :SectionRenderer, "metanorma/html/renderers/section_renderer"
13
13
  autoload :PubidRenderer, "metanorma/html/renderers/pubid_renderer"
14
+ autoload :ElementOrderTraversal,
15
+ "metanorma/html/renderers/element_order_traversal"
14
16
  end
15
17
 
16
18
  class BaseRenderer
@@ -84,16 +86,13 @@ module Metanorma
84
86
  end
85
87
 
86
88
  class RendererContext
89
+ include RendererDelegation
90
+
87
91
  def initialize(renderer)
88
92
  @renderer = renderer
89
93
  end
90
94
 
91
- def safe_attr(...) = @renderer.safe_attr(...)
92
- def escape_html(...) = @renderer.escape_html(...)
93
- def extract_block_label(...)= @renderer.extract_block_label(...)
94
- def extract_plain_text(...)= @renderer.extract_plain_text(...)
95
95
  def render_paragraph(...) = @renderer.render_paragraph(...)
96
- def render_mixed_inline(...)= @renderer.render_mixed_inline(...)
97
96
  def render_inline_element(...)= @renderer.render_inline_element(...)
98
97
  def render_unordered_list(...)= @renderer.render_unordered_list(...)
99
98
  def render_ordered_list(...)= @renderer.render_ordered_list(...)
@@ -107,7 +106,6 @@ module Metanorma
107
106
  def render_image(...) = @renderer.render_image(...)
108
107
  def render_stem_content(...)= @renderer.render_stem_content(...)
109
108
  def register_figure_entry(...)= @renderer.register_figure_entry(...)
110
- def render_liquid(...) = @renderer.render_liquid(...)
111
109
  def render_note_children(...) = @renderer.render_note_children(...)
112
110
  def render_simple_children(...) = @renderer.render_simple_children(...)
113
111
  def render_full_block_children(...) = @renderer.render_full_block_children(...)
@@ -134,22 +132,6 @@ module Metanorma
134
132
 
135
133
  # --- Flavor configuration hooks ---
136
134
 
137
- FLAVOR_MAP = {
138
- "IsoDocument" => :iso,
139
- "IecDocument" => :iec,
140
- "IeeeDocument" => :ieee,
141
- "IetfDocument" => :ietf,
142
- "ItuDocument" => :itu,
143
- "IhoDocument" => :iho,
144
- "BipmDocument" => :bipm,
145
- "OgcDocument" => :ogc,
146
- "OimlDocument" => :oiml,
147
- "CcDocument" => :cc,
148
- "IccDocument" => :icc,
149
- "RiboseDocument" => :ribose,
150
- "PdfaDocument" => :pdfa,
151
- }.freeze
152
-
153
135
  def theme
154
136
  @theme ||= resolve_theme
155
137
  end
@@ -162,9 +144,15 @@ module Metanorma
162
144
  def flavor_name
163
145
  return nil unless defined?(@document) && @document
164
146
 
165
- @document.class.name&.split("::")&.detect do |ns|
166
- FLAVOR_MAP.key?(ns)
167
- end&.then { |ns| FLAVOR_MAP[ns] }
147
+ Metanorma::Html::Generator.flavors.name_for(@document.class)
148
+ end
149
+
150
+ # Pubid module for the current document's flavor, or nil if the
151
+ # flavor has no Pubid support. Queries the shared FlavorRegistry.
152
+ def pubid_module
153
+ return nil unless defined?(@document) && @document
154
+
155
+ Metanorma::Html::Generator.flavors.pubid_module_for(@document.class)
168
156
  end
169
157
 
170
158
  def flavor_publishers(_doc_id)
@@ -460,12 +448,8 @@ module Metanorma
460
448
  xml_mapping = node.class.mappings_for(:xml, node.lutaml_register)
461
449
 
462
450
  if node.element_order.is_a?(Array) && xml_mapping
463
- element_to_attr = {}
464
- xml_mapping.mapping_elements_hash.each_value do |rule_or_array|
465
- Array(rule_or_array).each do |rule|
466
- element_to_attr[rule.name.to_s] = rule.to
467
- end
468
- end
451
+ element_to_attr =
452
+ Renderers::ElementOrderTraversal.element_to_attr_map(xml_mapping)
469
453
 
470
454
  indices = Hash.new(0)
471
455
  node.element_order.each do |el|
@@ -624,50 +608,40 @@ module Metanorma
624
608
  :render_index
625
609
  register_inline_render Metanorma::Document::Components::Blocks::NoteBlock,
626
610
  :render_note_inline
627
- register_inline_render Metanorma::Document::Components::Inline::FmtNameElement,
628
- :render_mixed_inline
629
- register_inline_render Metanorma::Document::Components::Inline::FmtTitleElement,
630
- :render_mixed_inline
631
- register_inline_render Metanorma::Document::Components::Inline::FmtXrefLabelElement,
632
- :render_mixed_inline
633
- register_inline_render Metanorma::Document::Components::Inline::FmtFnLabelElement,
634
- :render_mixed_inline
635
- register_inline_render Metanorma::Document::Components::Inline::FmtConceptElement,
636
- :render_mixed_inline
637
- register_inline_render Metanorma::Document::Components::Inline::FmtAnnotationStartElement,
638
- :render_mixed_inline
639
- register_inline_render Metanorma::Document::Components::Inline::FmtAnnotationEndElement,
640
- :render_mixed_inline
641
- register_inline_render Metanorma::Document::Components::Inline::FmtAnnotationBodyElement,
642
- :render_mixed_inline
643
- register_inline_render Metanorma::Document::Components::Inline::VariantTitleElement,
644
- :render_mixed_inline
645
- register_inline_render Metanorma::Document::Components::Inline::LocalizedStringElement,
646
- :render_mixed_inline
647
- register_inline_render Metanorma::Document::Components::Inline::TitleWithAnnotationElement,
648
- :render_mixed_inline
649
- register_inline_render Metanorma::Document::Components::Inline::BiblioTagElement,
650
- :render_mixed_inline
651
- register_inline_render Metanorma::Document::Components::Inline::NameWithIdElement,
652
- :render_mixed_inline
653
- register_inline_render Metanorma::Document::Components::Inline::DisplayTextElement,
654
- :render_mixed_inline
655
- register_inline_render Metanorma::Document::Components::Inline::FmtFootnoteContainerElement,
656
- :render_mixed_inline
657
- register_inline_render Metanorma::Document::Components::Inline::FmtFnBodyElement,
658
- :render_mixed_inline
659
- register_inline_render Metanorma::Document::Components::Inline::FmtPreferredElement,
660
- :render_mixed_inline
661
- register_inline_render Metanorma::Document::Components::Inline::FmtDefinitionElement,
662
- :render_mixed_inline
663
- register_inline_render Metanorma::Document::Components::Inline::FmtTermsourceElement,
664
- :render_mixed_inline
665
- register_inline_render Metanorma::Document::Components::Inline::FmtAdmittedElement,
666
- :render_mixed_inline
667
- register_inline_render Metanorma::Document::Components::Inline::FmtIdentifierElement,
668
- :render_mixed_inline
669
- register_inline_render Metanorma::Document::Components::Inline::FmtSourcecodeElement,
670
- :render_mixed_inline
611
+
612
+ # Transparent inline wrappers: element classes whose render
613
+ # semantics are "iterate mixed_content and render each child".
614
+ # Grouped here so adding a new transparent wrapper is one entry,
615
+ # not another register_inline_render line. Classes with a more
616
+ # specific handler register themselves above.
617
+ TRANSPARENT_INLINE_WRAPPERS = [
618
+ Metanorma::Document::Components::Inline::FmtNameElement,
619
+ Metanorma::Document::Components::Inline::FmtTitleElement,
620
+ Metanorma::Document::Components::Inline::FmtXrefLabelElement,
621
+ Metanorma::Document::Components::Inline::FmtFnLabelElement,
622
+ Metanorma::Document::Components::Inline::FmtConceptElement,
623
+ Metanorma::Document::Components::Inline::FmtAnnotationStartElement,
624
+ Metanorma::Document::Components::Inline::FmtAnnotationEndElement,
625
+ Metanorma::Document::Components::Inline::FmtAnnotationBodyElement,
626
+ Metanorma::Document::Components::Inline::VariantTitleElement,
627
+ Metanorma::Document::Components::Inline::LocalizedStringElement,
628
+ Metanorma::Document::Components::Inline::TitleWithAnnotationElement,
629
+ Metanorma::Document::Components::Inline::BiblioTagElement,
630
+ Metanorma::Document::Components::Inline::NameWithIdElement,
631
+ Metanorma::Document::Components::Inline::DisplayTextElement,
632
+ Metanorma::Document::Components::Inline::FmtFootnoteContainerElement,
633
+ Metanorma::Document::Components::Inline::FmtFnBodyElement,
634
+ Metanorma::Document::Components::Inline::FmtPreferredElement,
635
+ Metanorma::Document::Components::Inline::FmtDefinitionElement,
636
+ Metanorma::Document::Components::Inline::FmtTermsourceElement,
637
+ Metanorma::Document::Components::Inline::FmtAdmittedElement,
638
+ Metanorma::Document::Components::Inline::FmtIdentifierElement,
639
+ Metanorma::Document::Components::Inline::FmtSourcecodeElement,
640
+ ].freeze
641
+
642
+ TRANSPARENT_INLINE_WRAPPERS.each do |klass|
643
+ register_inline_render klass, :render_mixed_inline
644
+ end
671
645
 
672
646
  def lookup_dispatch(type_class, registry_method)
673
647
  self.class.ancestors.each do |ancestor|
@@ -4,6 +4,8 @@ module Metanorma
4
4
  module Html
5
5
  module Component
6
6
  class Base
7
+ include Metanorma::Html::RendererDelegation
8
+
7
9
  attr_reader :renderer
8
10
 
9
11
  def initialize(renderer)
@@ -38,18 +40,9 @@ module Metanorma
38
40
  raise NotImplementedError, "#{self.class}#render not implemented"
39
41
  end
40
42
 
41
- def escape_html(...) = renderer.escape_html(...)
42
- def render_mixed_inline(...) = renderer.render_mixed_inline(...)
43
43
  def render_mixed_content_in_order(...) = renderer.render_mixed_content_in_order(...)
44
44
  def element_attrs(...) = renderer.element_attrs(...)
45
- def safe_attr(obj, method_name) = renderer.safe_attr(obj, method_name)
46
- def extract_plain_text(node) = renderer.extract_plain_text(node)
47
45
  def extract_text_value(val) = renderer.extract_text_value(val)
48
- def render_liquid(...) = renderer.render_liquid(...)
49
-
50
- def extract_block_label(block, default)
51
- renderer.extract_block_label(block, default)
52
- end
53
46
 
54
47
  def self.register_in(registry)
55
48
  handled_classes.each do |klass|
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Html
5
+ # A flavor ties together the four concepts that identify a Metanorma
6
+ # document variant:
7
+ # - a symbolic name (used for theme loading, CSS class names)
8
+ # - a model class (the typed root class for the flavor)
9
+ # - a renderer class (the HTML renderer that handles the flavor)
10
+ # - an optional Pubid module (for parsing identifiers like ISO/IEC/etc.)
11
+ #
12
+ # Flavor centralizes flavor identity so BaseRenderer, PubidRenderer,
13
+ # and Generator all read from the same source of truth. Adding a new
14
+ # flavor is one Flavor entry in the registry, not three coordinated
15
+ # edits across three files.
16
+ class Flavor
17
+ attr_reader :name, :model_class, :renderer_class, :pubid_module
18
+
19
+ def initialize(name:, model_class:, renderer_class:, pubid_module: nil)
20
+ @name = name
21
+ @model_class = model_class
22
+ @renderer_class = renderer_class
23
+ @pubid_module = pubid_module
24
+ end
25
+
26
+ # Match by ancestry: a document whose class is the flavor's model
27
+ # class, or any descendant of it, belongs to this flavor. Walks
28
+ # parent classes via is_a? to support subclassing.
29
+ def matches?(document_class)
30
+ return false unless document_class.is_a?(Class)
31
+
32
+ # `<=` returns nil for unrelated classes; coerce to boolean.
33
+ !!(document_class <= model_class)
34
+ end
35
+
36
+ def pubid_module_const
37
+ return nil unless pubid_module
38
+
39
+ Object.const_get(pubid_module.to_s)
40
+ rescue NameError
41
+ nil
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ module Html
5
+ # Registry of known flavors. Queried by:
6
+ # - Generator — to pick the renderer for a document
7
+ # - BaseRenderer — to resolve flavor_name for theme loading
8
+ # - PubidRenderer — to resolve the Pubid module
9
+ #
10
+ # Single source of truth for flavor identity. Adding a flavor is
11
+ # one Flavor entry via #register.
12
+ class FlavorRegistry
13
+ include Enumerable
14
+
15
+ def initialize
16
+ @flavors = []
17
+ end
18
+
19
+ def register(flavor)
20
+ @flavors << flavor
21
+ self
22
+ end
23
+
24
+ def each(&)
25
+ @flavors.each(&)
26
+ end
27
+
28
+ # Returns the most-specific Flavor whose model_class matches the
29
+ # given document class (by ancestry). Most-specific = registered
30
+ # last. We scan in reverse so general flavors (registered first)
31
+ # only match if no more specific flavor does.
32
+ def find_for(document_class)
33
+ @flavors.reverse_each.find { |f| f.matches?(document_class) }
34
+ end
35
+
36
+ def name_for(document_class)
37
+ find_for(document_class)&.name
38
+ end
39
+
40
+ def renderer_for(document_class)
41
+ find_for(document_class)&.renderer_class
42
+ end
43
+
44
+ def pubid_module_for(document_class)
45
+ find_for(document_class)&.pubid_module_const
46
+ end
47
+ end
48
+ end
49
+ end