coradoc 2.0.24 → 2.0.25
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/core_model/attribute_reference_resolver.rb +143 -0
- data/lib/coradoc/core_model.rb +1 -0
- data/lib/coradoc/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 89b8c2a91f2cc76dbd1aa2694eb7fa06b689c46e92fe8d240e2bad3206a39da3
|
|
4
|
+
data.tar.gz: 9645f7e85a7ac1f8ada41f535219b72238b63c503380c79642d04eb2fe1b6589
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa10ea51a7a4ac5146d28cd7a681d5722998b7937ec05169c2376a80af592a65b8a430773e99cf874aca769c2a7dcc61b065ce3ae8e4da283685f77ae3bd22c3
|
|
7
|
+
data.tar.gz: a063ca46ab3f5a8f166044311b118fe82a96185d77f12de607e426ba5ad6ad330cbfb58ee5323dae5a3a7f6b6fa90f12f4fc422730878b219058f318f72eda6a
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module CoreModel
|
|
5
|
+
# Walks a CoreModel tree and resolves InlineElement nodes whose
|
|
6
|
+
# format_type is 'attribute_reference' against a Metadata store.
|
|
7
|
+
#
|
|
8
|
+
# AsciiDoc `{foo}` references round-trip as InlineElement with
|
|
9
|
+
# format_type: 'attribute_reference' and target: 'foo'. After the
|
|
10
|
+
# document is parsed and its attributes are known, this visitor
|
|
11
|
+
# rewrites those nodes in place to TextContent carrying the value.
|
|
12
|
+
# Unresolved references are left untouched so they survive
|
|
13
|
+
# serialisation back to the source format.
|
|
14
|
+
#
|
|
15
|
+
# The visitor never mutates its input tree; it returns a new tree
|
|
16
|
+
# sharing unchanged subtrees. InlineElement#with_content already
|
|
17
|
+
# produces non-mutating copies for inline content; the same pattern
|
|
18
|
+
# is used here at the block level via `replace_children`.
|
|
19
|
+
class AttributeReferenceResolver
|
|
20
|
+
attr_reader :attributes
|
|
21
|
+
|
|
22
|
+
def self.call(root, attributes)
|
|
23
|
+
new(attributes).visit(root)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(attributes)
|
|
27
|
+
@attributes = attributes || Metadata.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def visit(node)
|
|
31
|
+
return node.map { |child| visit(child) } if node.is_a?(Array)
|
|
32
|
+
|
|
33
|
+
return node unless node.is_a?(Base)
|
|
34
|
+
|
|
35
|
+
visit_typed(node)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def visit_typed(node)
|
|
41
|
+
case node
|
|
42
|
+
when DocumentElement, SectionElement
|
|
43
|
+
rebuild_children(node)
|
|
44
|
+
when Table
|
|
45
|
+
rebuild_collection(node, :rows)
|
|
46
|
+
when TableRow
|
|
47
|
+
rebuild_collection(node, :cells)
|
|
48
|
+
when Block, ParagraphBlock, ListItem, TableCell, Term,
|
|
49
|
+
QuoteBlock, ExampleBlock, SidebarBlock, OpenBlock,
|
|
50
|
+
AnnotationBlock, ReviewerBlock, VerseBlock, LiteralBlock,
|
|
51
|
+
PassBlock, StemBlock, ListingBlock, SourceBlock,
|
|
52
|
+
CommentBlock, FrontmatterBlock
|
|
53
|
+
rebuild_block(node)
|
|
54
|
+
when DefinitionList, DefinitionItem
|
|
55
|
+
rebuild_children(node)
|
|
56
|
+
when InlineElement, RawInlineElement
|
|
57
|
+
resolve_inline(node)
|
|
58
|
+
else
|
|
59
|
+
node
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def rebuild_children(node)
|
|
64
|
+
return node unless node.respond_to?(:children)
|
|
65
|
+
|
|
66
|
+
original = node.children
|
|
67
|
+
return node if original.nil?
|
|
68
|
+
|
|
69
|
+
updated = original.is_a?(Array) ? original.map { |c| visit(c) } : visit(original)
|
|
70
|
+
return node if updated == original
|
|
71
|
+
|
|
72
|
+
node.dup.tap { |copy| copy.children = updated }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# For nodes whose child collection lives on a non-`children`
|
|
76
|
+
# attribute (Table#rows, TableRow#cells). Walks the collection,
|
|
77
|
+
# rebuilds the parent only when at least one child changed.
|
|
78
|
+
def rebuild_collection(node, attr_name)
|
|
79
|
+
original = node.public_send(attr_name)
|
|
80
|
+
return node if original.nil?
|
|
81
|
+
|
|
82
|
+
updated = original.is_a?(Array) ?
|
|
83
|
+
original.map { |c| visit(c) } :
|
|
84
|
+
visit(original)
|
|
85
|
+
return node if updated == original
|
|
86
|
+
|
|
87
|
+
node.dup.tap { |copy| copy.public_send("#{attr_name}=", updated) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def rebuild_block(node)
|
|
91
|
+
return node unless node.respond_to?(:children)
|
|
92
|
+
|
|
93
|
+
original_children = node.children
|
|
94
|
+
return node if original_children.nil?
|
|
95
|
+
|
|
96
|
+
updated_children = original_children.is_a?(Array) ?
|
|
97
|
+
original_children.map { |c| visit(c) } :
|
|
98
|
+
visit(original_children)
|
|
99
|
+
|
|
100
|
+
if inline_resolvable?(node)
|
|
101
|
+
original_content = node.content
|
|
102
|
+
updated_content = resolve_content(original_content)
|
|
103
|
+
else
|
|
104
|
+
updated_content = node.content
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
return node if updated_children == original_children && updated_content == node.content
|
|
108
|
+
|
|
109
|
+
node.dup.tap do |copy|
|
|
110
|
+
copy.children = updated_children
|
|
111
|
+
copy.content = updated_content if inline_resolvable?(node)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def inline_resolvable?(node)
|
|
116
|
+
node.respond_to?(:content) && node.content.is_a?(String)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Resolve attribute references inside a flat content string.
|
|
120
|
+
# Only replaces `{name}` when +name+ exists in +attributes+;
|
|
121
|
+
# unknown references are preserved verbatim for round-trip.
|
|
122
|
+
def resolve_content(content)
|
|
123
|
+
return content unless content.is_a?(String)
|
|
124
|
+
|
|
125
|
+
content.gsub(/\{([a-zA-Z0-9_-]+)\}/) do |match|
|
|
126
|
+
name = Regexp.last_match(1)
|
|
127
|
+
attributes.key?(name) ? attributes[name].to_s : match
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def resolve_inline(node)
|
|
132
|
+
return node unless node.is_a?(InlineElement)
|
|
133
|
+
return node unless node.resolve_format_type == 'attribute_reference'
|
|
134
|
+
|
|
135
|
+
target = node.target
|
|
136
|
+
return node if target.nil? || target.empty?
|
|
137
|
+
return node unless attributes.key?(target)
|
|
138
|
+
|
|
139
|
+
TextContent.new(text: attributes[target].to_s)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
data/lib/coradoc/core_model.rb
CHANGED
|
@@ -81,6 +81,7 @@ module Coradoc
|
|
|
81
81
|
autoload :CommentLine, "#{__dir__}/core_model/comment_line"
|
|
82
82
|
autoload :HorizontalRuleBlock, "#{__dir__}/core_model/horizontal_rule_block"
|
|
83
83
|
autoload :IdGenerator, "#{__dir__}/core_model/id_generator"
|
|
84
|
+
autoload :AttributeReferenceResolver, "#{__dir__}/core_model/attribute_reference_resolver"
|
|
84
85
|
autoload :Include, "#{__dir__}/core_model/include"
|
|
85
86
|
autoload :IncludeOptions, "#{__dir__}/core_model/include_options"
|
|
86
87
|
autoload :IncludeLevelOffset, "#{__dir__}/core_model/include_level_offset"
|
data/lib/coradoc/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: coradoc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -56,6 +56,7 @@ files:
|
|
|
56
56
|
- lib/coradoc/coradoc.rb
|
|
57
57
|
- lib/coradoc/core_model.rb
|
|
58
58
|
- lib/coradoc/core_model/annotation_block.rb
|
|
59
|
+
- lib/coradoc/core_model/attribute_reference_resolver.rb
|
|
59
60
|
- lib/coradoc/core_model/base.rb
|
|
60
61
|
- lib/coradoc/core_model/bibliography.rb
|
|
61
62
|
- lib/coradoc/core_model/bibliography_entry.rb
|