Class: Coradoc::Mirror::CoreModelToMirror

Inherits:
Object
  • Object
show all
Defined in:
lib/coradoc/mirror/core_model_to_mirror.rb

Overview

Transforms CoreModel documents into ProseMirror-compatible Mirror nodes.

Uses a HandlerRegistry for OCP-compliant dispatch: each CoreModel type maps to a handler module/class that produces the corresponding Mirror node.

Defined Under Namespace

Classes: FootnoteData

Constant Summary collapse

COLLECTION_ACCESSORS =

Maps element types to their children accessors.

{
  CoreModel::ListBlock => :items,
  CoreModel::DefinitionList => :items,
  CoreModel::Table => :rows,
  CoreModel::Bibliography => :entries
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(registry: Coradoc::Mirror.default_registry) ⇒ CoreModelToMirror

Returns a new instance of CoreModelToMirror.



23
24
25
26
27
28
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 23

def initialize(registry: Coradoc::Mirror.default_registry)
  @registry = registry
  @footnote_counter = 0
  @footnotes = []
  @partition_structural = false
end

Instance Attribute Details

#partition_structuralObject

Read by Handlers::Structural.section to decide whether to emit generic ‘section` (legacy) or a JS SECTION_TYPE (`clause`, `annex`, etc.) when partition_structural mode is on.



33
34
35
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 33

def partition_structural
  @partition_structural
end

#registryObject (readonly)

Returns the value of attribute registry.



10
11
12
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 10

def registry
  @registry
end

Instance Method Details

#call(document, partition_structural: false) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 35

def call(document, partition_structural: false)
  @footnote_counter = 0
  @footnotes = []
  @partition_structural = partition_structural

  content = extract_content(document)
  fn_block = flush_footnotes
  content << fn_block if fn_block

  attrs = build_document_attrs(document)
  Node::Document.new(
    attrs: Node::Document::Attrs.new(title: attrs[:title], id: attrs[:id]),
    content: partition_structural ? wrap_structural(content) : content
  )
end

#extract_content(element) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 69

def extract_content(element)
  children = element_children(element)

  if children && !children.empty?
    content = []
    children.each { |child| handle_element(child, content) }
    content.compact
  elsif element_has_text_content?(element)
    process_inline_content(element)
  else
    []
  end
end

#flush_footnotesObject



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 126

def flush_footnotes
  return nil if @footnotes.empty?

  entries = @footnotes.map do |fn|
    Node::FootnoteEntry.new(
      attrs: Node::FootnoteEntry::Attrs.new(
        id: fn.id, ref_id: fn.ref_id, number: fn.number
      ),
      content: fn.content
    )
  end

  @footnotes = []
  Node::Footnotes.new(content: entries)
end

#process_inline_content(element) ⇒ Object



83
84
85
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 83

def process_inline_content(element)
  Handlers::Inline.process(element, context: self)
end

#register_footnote(footnote) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 91

def register_footnote(footnote)
  @footnote_counter += 1
  num = @footnote_counter
  fn_id = footnote.id || "fn-#{num}"
  ref_id = "fn-ref-#{num}"

  fn_content = footnote.content ? [text_node(footnote.content)] : []
  @footnotes << FootnoteData.new(
    id: fn_id, ref_id: ref_id, number: num, content: fn_content
  )

  Node::FootnoteMarker.new(
    attrs: Node::FootnoteMarker::Attrs.new(
      id: fn_id, ref_id: ref_id, number: num
    )
  )
end

#resolve_footnote_reference(ref) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 109

def resolve_footnote_reference(ref)
  target_id = ref.id
  entry = @footnotes.find { |fn| fn.id == target_id } if target_id

  if entry
    Node::FootnoteMarker.new(
      attrs: Node::FootnoteMarker::Attrs.new(
        id: entry.id,
        ref_id: "fn-ref-#{entry.number}-dup-#{@footnote_counter}",
        number: entry.number
      )
    )
  else
    text_node("[#{target_id || 'footnote'}]")
  end
end

#text_node(text, marks: []) ⇒ Object



87
88
89
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 87

def text_node(text, marks: [])
  Node::Text.new(text: text, marks: marks)
end

#wrap_structural(children) ⇒ Object

Partitions flat doc children into [*metadata, preface?, sections?, *bibliography, *trailing] per the @metanorma/mirror JS structural contract. See Partitioner for the bucketing rules.

Metadata blocks (frontmatter) are prepended verbatim so consumers like FrontmatterQuery can find them by walking content, and renderers can skip them via their type (‘frontmatter’).



58
59
60
61
62
63
64
65
66
67
# File 'lib/coradoc/mirror/core_model_to_mirror.rb', line 58

def wrap_structural(children)
  partitioned = Partitioner.partition(children)
  wrapped = []
  wrapped.concat(partitioned[:metadata])
  wrapped << Node::Preamble.new(content: partitioned[:preface]) if partitioned[:preface].any?
  wrapped << Node::Sections.new(content: partitioned[:sections]) if partitioned[:sections].any?
  wrapped.concat(partitioned[:bibliography])
  wrapped.concat(partitioned[:trailing])
  wrapped
end