coradoc-mirror 0.1.1 → 0.1.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.
- checksums.yaml +4 -4
- data/lib/coradoc/mirror/core_model_to_mirror.rb +8 -3
- data/lib/coradoc/mirror/frontmatter_query.rb +50 -0
- data/lib/coradoc/mirror/frontmatter_tree_to_hash.rb +40 -0
- data/lib/coradoc/mirror/handlers/definition_list.rb +15 -13
- data/lib/coradoc/mirror/partitioner.rb +19 -6
- data/lib/coradoc/mirror/reverse_builder.rb +3 -28
- data/lib/coradoc/mirror/version.rb +1 -1
- data/lib/coradoc/mirror.rb +9 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e474854dfc3ccc3cb6edce0dce14958aa962cfd87d40ce77a530348d6f7488b
|
|
4
|
+
data.tar.gz: 50b26a4e35effc7a2065e06a739eb3ca4bfc62ccf1b0242987c8d29f42a9722b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6dee99c97897a0a65cf8667b5b78ee6ac549511b5e16208454b467955baf8f48030fcfe724120f4f42ec183521c61660811cd89be834c46be8deee0994052c68
|
|
7
|
+
data.tar.gz: 94c0a6bcb1e973cea8fee23324a7f4d4eaa70673c1400313da931c7a2eab0132facc7d7356cbb9f639c3895a67cf1f968f6df08c12d43fc23db1db202c2c8045
|
|
@@ -48,12 +48,17 @@ module Coradoc
|
|
|
48
48
|
)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
# Partitions flat doc children into [preface?, sections?,
|
|
52
|
-
# *trailing] per the @metanorma/mirror JS structural
|
|
53
|
-
# Partitioner for the bucketing rules.
|
|
51
|
+
# Partitions flat doc children into [*metadata, preface?, sections?,
|
|
52
|
+
# *bibliography, *trailing] per the @metanorma/mirror JS structural
|
|
53
|
+
# contract. See Partitioner for the bucketing rules.
|
|
54
|
+
#
|
|
55
|
+
# Metadata blocks (frontmatter) are prepended verbatim so consumers
|
|
56
|
+
# like FrontmatterQuery can find them by walking content[0..n], and
|
|
57
|
+
# renderers can skip them via their type ('frontmatter').
|
|
54
58
|
def wrap_structural(children)
|
|
55
59
|
partitioned = Partitioner.partition(children)
|
|
56
60
|
wrapped = []
|
|
61
|
+
wrapped.concat(partitioned[:metadata])
|
|
57
62
|
wrapped << Node::Preamble.new(content: partitioned[:preface]) if partitioned[:preface].any?
|
|
58
63
|
wrapped << Node::Sections.new(content: partitioned[:sections]) if partitioned[:sections].any?
|
|
59
64
|
wrapped.concat(partitioned[:bibliography])
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Mirror
|
|
5
|
+
# Public read-API: extract a flat Ruby Hash from a Mirror document's
|
|
6
|
+
# frontmatter node.
|
|
7
|
+
#
|
|
8
|
+
# Why this exists: site generators (e.g. metanorma.org's convert-adoc.rb)
|
|
9
|
+
# need frontmatter as a plain Hash for templating VitePress/Jekyll
|
|
10
|
+
# frontmatter output. Without this, callers would either (a) re-parse
|
|
11
|
+
# the source YAML — violating FrontmatterBlock::Codec's "single source
|
|
12
|
+
# of truth" rule — or (b) hand-walk the typed FrontmatterEntry /
|
|
13
|
+
# FrontmatterValue tree themselves, duplicating the reverse builder's
|
|
14
|
+
# logic. This module is the single entry point for both problems.
|
|
15
|
+
#
|
|
16
|
+
# Reuses FrontmatterTreeToHash — same translator the reverse builder
|
|
17
|
+
# uses — so the read-path is shared (DRY/MECE).
|
|
18
|
+
module FrontmatterQuery
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# @param mirror_doc [Mirror::Node::Document, nil]
|
|
22
|
+
# @return [Hash{String,Object}] flat key→value mapping; empty Hash
|
|
23
|
+
# if the document has no frontmatter node or no entries
|
|
24
|
+
def to_hash(mirror_doc)
|
|
25
|
+
frontmatter = find_frontmatter(mirror_doc)
|
|
26
|
+
return {} unless frontmatter
|
|
27
|
+
|
|
28
|
+
entries = frontmatter.attrs&.entries || []
|
|
29
|
+
FrontmatterTreeToHash.to_hash(entries)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param mirror_doc [Mirror::Node::Document, nil] (see #to_hash)
|
|
33
|
+
# @return [Boolean] true if the document carries a frontmatter node
|
|
34
|
+
# with at least one entry
|
|
35
|
+
def has_frontmatter?(mirror_doc)
|
|
36
|
+
frontmatter = find_frontmatter(mirror_doc)
|
|
37
|
+
!frontmatter.nil? && !(frontmatter.attrs&.entries || []).empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def find_frontmatter(mirror_doc)
|
|
41
|
+
return nil if mirror_doc.nil?
|
|
42
|
+
|
|
43
|
+
content = mirror_doc.content
|
|
44
|
+
return nil unless content
|
|
45
|
+
|
|
46
|
+
content.find { |node| node.is_a?(Node) && node.type == 'frontmatter' }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Mirror
|
|
5
|
+
# Walks a typed FrontmatterEntry / FrontmatterValue tree (the Mirror
|
|
6
|
+
# representation of CoreModel::FrontmatterBlock#data) and rebuilds the
|
|
7
|
+
# flat Ruby Hash. Inverse of Handlers::Frontmatter.build_value.
|
|
8
|
+
#
|
|
9
|
+
# Single source of truth for tree → Hash translation. Consumed by:
|
|
10
|
+
# - ReverseBuilder::Frontmatter (mirror → CoreModel round-trip)
|
|
11
|
+
# - FrontmatterQuery (mirror doc → flat Hash for downstream readers)
|
|
12
|
+
#
|
|
13
|
+
# Extracted from ReverseBuilder so the read-path is shared (DRY/MECE)
|
|
14
|
+
# and FrontmatterQuery does not depend on the reverse-builder constant.
|
|
15
|
+
module FrontmatterTreeToHash
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def to_hash(entries)
|
|
19
|
+
entries.each_with_object({}) do |entry, result|
|
|
20
|
+
result[entry.key] = unwrap_value(entry.value)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def unwrap_value(value)
|
|
25
|
+
case value.value_type
|
|
26
|
+
when 'map' then to_hash(value.entries || [])
|
|
27
|
+
when 'array' then (value.items || []).map { |v| unwrap_value(v) }
|
|
28
|
+
when 'integer' then value.integer_value
|
|
29
|
+
when 'float' then value.float_value
|
|
30
|
+
when 'boolean' then value.boolean_value
|
|
31
|
+
when 'date' then value.date_value
|
|
32
|
+
when 'datetime' then value.datetime_value
|
|
33
|
+
when 'symbol' then value.symbol_value&.to_sym
|
|
34
|
+
when 'nil' then nil
|
|
35
|
+
else value.string_value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -43,24 +43,26 @@ module Coradoc
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def build_description(item, context)
|
|
46
|
-
|
|
47
|
-
return nil
|
|
46
|
+
desc_nodes = description_nodes(item, context)
|
|
47
|
+
return nil if desc_nodes.empty?
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
end
|
|
49
|
+
Node::DefinitionDescription.new(content: desc_nodes)
|
|
50
|
+
end
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
# Emit one text node per source — never both. Rich
|
|
53
|
+
# definition_children (inline nodes) win over plain definitions
|
|
54
|
+
# (strings) because they preserve formatting; falling back to
|
|
55
|
+
# the plain string keeps this handler robust for DefinitionItem
|
|
56
|
+
# instances populated by paths that don't build inline children.
|
|
57
|
+
def description_nodes(item, context)
|
|
58
|
+
unless item.is_a?(CoreModel::DefinitionItem)
|
|
59
|
+
return Array(item.definitions).map { |defn| context.text_node(defn.to_s) }
|
|
59
60
|
end
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
children = item.definition_children
|
|
63
|
+
return Array(item.definitions).map { |defn| context.text_node(defn.to_s) } if children.empty?
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
children.flat_map { |child| Handlers::Inline.process_child(child, context) }
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
68
|
end
|
|
@@ -15,9 +15,15 @@ module Coradoc
|
|
|
15
15
|
# (clause, annex, abstract, foreword, introduction, terms,
|
|
16
16
|
# definitions, references, content_section, acknowledgements).
|
|
17
17
|
SECTION_TYPES = Set.new(%w[
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
clause annex content_section abstract foreword introduction
|
|
19
|
+
acknowledgements terms definitions references section
|
|
20
|
+
]).freeze
|
|
21
|
+
|
|
22
|
+
# Mirror node types that are doc-level metadata, not body content.
|
|
23
|
+
# They pass through the partitioner untouched (in document order)
|
|
24
|
+
# so the renderer can choose to skip them — they never land in the
|
|
25
|
+
# preface bucket where they would render as visible body.
|
|
26
|
+
METADATA_TYPES = Set.new(%w[frontmatter]).freeze
|
|
21
27
|
|
|
22
28
|
module_function
|
|
23
29
|
|
|
@@ -29,11 +35,16 @@ module Coradoc
|
|
|
29
35
|
# Once a bibliography appears → :trailing.
|
|
30
36
|
# Footnotes blocks always go into :trailing regardless of state.
|
|
31
37
|
#
|
|
38
|
+
# Metadata blocks (frontmatter) are returned in their own bucket,
|
|
39
|
+
# outside the preface/sections/trailing flow, so they survive the
|
|
40
|
+
# partition round-trip but are never rendered as body content.
|
|
41
|
+
#
|
|
32
42
|
# @param children [Array<Node>] flat list of built Mirror nodes
|
|
33
43
|
# @return [Hash{Symbol=>Array<Node>}] buckets under :preface,
|
|
34
|
-
# :sections, :bibliography, :trailing
|
|
44
|
+
# :sections, :bibliography, :trailing, :metadata
|
|
35
45
|
def partition(children)
|
|
36
|
-
buckets = { preface: [], sections: [], bibliography: [],
|
|
46
|
+
buckets = { preface: [], sections: [], bibliography: [],
|
|
47
|
+
trailing: [], metadata: [] }
|
|
37
48
|
state = :preface
|
|
38
49
|
|
|
39
50
|
children.each do |child|
|
|
@@ -44,7 +55,9 @@ module Coradoc
|
|
|
44
55
|
buckets[:bibliography] << child
|
|
45
56
|
state = :trailing
|
|
46
57
|
else
|
|
47
|
-
if
|
|
58
|
+
if METADATA_TYPES.include?(child.type)
|
|
59
|
+
buckets[:metadata] << child
|
|
60
|
+
elsif SECTION_TYPES.include?(child.type)
|
|
48
61
|
buckets[:sections] << child
|
|
49
62
|
state = :sections
|
|
50
63
|
elsif child.type == 'footnotes'
|
|
@@ -26,6 +26,9 @@ module Coradoc
|
|
|
26
26
|
# full before any caller references it. Mirror-level mark dispatch
|
|
27
27
|
# lives in MarkReverseBuilder (mark_reverse_builder.rb).
|
|
28
28
|
module ReverseBuilder
|
|
29
|
+
# Not frozen: subclasses call `register` from their class body at
|
|
30
|
+
# load time, and `registers` may fire late via autoload. Freezing
|
|
31
|
+
# here breaks the first mirror-to-core round-trip after load.
|
|
29
32
|
REGISTRY = {}
|
|
30
33
|
|
|
31
34
|
module_function
|
|
@@ -567,34 +570,6 @@ module Coradoc
|
|
|
567
570
|
|
|
568
571
|
LIST_TYPES = %w[bullet_list ordered_list].freeze
|
|
569
572
|
private_constant :LIST_TYPES
|
|
570
|
-
|
|
571
|
-
# Walks a typed FrontmatterEntry / FrontmatterValue tree and
|
|
572
|
-
# rebuilds the CoreModel `data` hash. Inverse of
|
|
573
|
-
# Handlers::Frontmatter.build_value.
|
|
574
|
-
module FrontmatterTreeToHash
|
|
575
|
-
module_function
|
|
576
|
-
|
|
577
|
-
def to_hash(entries)
|
|
578
|
-
entries.each_with_object({}) do |entry, result|
|
|
579
|
-
result[entry.key] = unwrap_value(entry.value)
|
|
580
|
-
end
|
|
581
|
-
end
|
|
582
|
-
|
|
583
|
-
def unwrap_value(value)
|
|
584
|
-
case value.value_type
|
|
585
|
-
when 'map' then to_hash(value.entries || [])
|
|
586
|
-
when 'array' then (value.items || []).map { |v| unwrap_value(v) }
|
|
587
|
-
when 'integer' then value.integer_value
|
|
588
|
-
when 'float' then value.float_value
|
|
589
|
-
when 'boolean' then value.boolean_value
|
|
590
|
-
when 'date' then value.date_value
|
|
591
|
-
when 'datetime' then value.datetime_value
|
|
592
|
-
when 'symbol' then value.symbol_value&.to_sym
|
|
593
|
-
when 'nil' then nil
|
|
594
|
-
else value.string_value
|
|
595
|
-
end
|
|
596
|
-
end
|
|
597
|
-
end
|
|
598
573
|
end
|
|
599
574
|
end
|
|
600
575
|
end
|
data/lib/coradoc/mirror.rb
CHANGED
|
@@ -14,6 +14,15 @@ module Coradoc
|
|
|
14
14
|
autoload :CoreModelToMirror, "#{__dir__}/mirror/core_model_to_mirror"
|
|
15
15
|
autoload :MirrorToCoreModel, "#{__dir__}/mirror/mirror_to_core_model"
|
|
16
16
|
autoload :Partitioner, "#{__dir__}/mirror/partitioner"
|
|
17
|
+
# Shared tree→Hash translator for the frontmatter typed-tree. Read by
|
|
18
|
+
# ReverseBuilder::Frontmatter and FrontmatterQuery — single source of
|
|
19
|
+
# truth for the inverse of Handlers::Frontmatter.build_value.
|
|
20
|
+
autoload :FrontmatterTreeToHash, "#{__dir__}/mirror/frontmatter_tree_to_hash"
|
|
21
|
+
# Public read-API for downstream consumers (e.g. site generators) that
|
|
22
|
+
# need a flat Ruby Hash of a Mirror doc's frontmatter without re-parsing
|
|
23
|
+
# the source YAML. Frontmatter lives in the CoreModel and the Mirror
|
|
24
|
+
# doc, not in a parallel YAML parse.
|
|
25
|
+
autoload :FrontmatterQuery, "#{__dir__}/mirror/frontmatter_query"
|
|
17
26
|
# ReverseBuilder's REGISTRY is populated by the built-in builder
|
|
18
27
|
# subclasses (defined inside reverse_builder.rb) at load time. The
|
|
19
28
|
# file is the autoload target, so the registry is full by the time
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: coradoc-mirror
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -77,6 +77,8 @@ files:
|
|
|
77
77
|
- lib/coradoc-mirror.rb
|
|
78
78
|
- lib/coradoc/mirror.rb
|
|
79
79
|
- lib/coradoc/mirror/core_model_to_mirror.rb
|
|
80
|
+
- lib/coradoc/mirror/frontmatter_query.rb
|
|
81
|
+
- lib/coradoc/mirror/frontmatter_tree_to_hash.rb
|
|
80
82
|
- lib/coradoc/mirror/handler_registry.rb
|
|
81
83
|
- lib/coradoc/mirror/handlers.rb
|
|
82
84
|
- lib/coradoc/mirror/handlers/admonition.rb
|