coradoc 2.0.24 → 2.0.26

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: bcce76c634ae51a857397a301fc2638950cb25e1a97048589ee5510f8a7aee40
4
- data.tar.gz: 6cd3b391ae83e360ed282b40f778dad1c4290202b386b457b881e264ddca464e
3
+ metadata.gz: 6b56fd30c086580f71f5b576a209ba6ad20c5aa590fbd09d62477a7f85a4988f
4
+ data.tar.gz: 8be8c3211cf7dc7d1545c8d86966687c645e4929046d65c54a4a2d4f6c9f5cd2
5
5
  SHA512:
6
- metadata.gz: f424dfb3267d2497dc46b9e764fab868083add0ef5cd82163aa89a35b884e75b81c7426a47d9c0b81abd15659dcc15b49f6f309b786848cba7c0aed42557cf3a
7
- data.tar.gz: 07fe25f772cf02b7673f25f4bb41e9abb65e25631898af071407eb68961bba9c2a74c19c7d20d85544c722a4ad07404c4846d389278fb989d2a186caab36bdbb
6
+ metadata.gz: 9d6ec746cd4a049f0668aa367091817faf0c87ed9c35840f3b548124836701c0f1ac6c0ddc99fcdcb5420e6c15e3f972eec6c869c055d464dc518049af4d242c
7
+ data.tar.gz: 33abcf1caabfb83b74f803affc8a12d4f74bd9290e0607d91a7033121db317229831c43da321edcf30dc3c97a080ca6b304c1186101dc17038867413ad8e802f
@@ -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
@@ -44,9 +44,12 @@ module Coradoc
44
44
 
45
45
  # @!attribute link
46
46
  # @return [String, nil] URL to link to when image is clicked
47
- # @note Not yet wired by any transformer; reserved for future use
48
47
  attribute :link, :string
49
48
 
49
+ # @!attribute role
50
+ # @return [String, nil] CSS role/class for the image (AsciiDoc role attr)
51
+ attribute :role, :string
52
+
50
53
  # @!attribute inline
51
54
  # @return [Boolean] whether this is an inline image
52
55
  attribute :inline, :boolean, default: -> { false }
@@ -59,7 +62,7 @@ module Coradoc
59
62
  private
60
63
 
61
64
  def comparable_attributes
62
- super + %i[src alt caption width height link inline]
65
+ super + %i[src alt caption width height link role inline]
63
66
  end
64
67
  end
65
68
  end
@@ -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"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coradoc
4
- VERSION = '2.0.24'
4
+ VERSION = '2.0.26'
5
5
  end
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.24
4
+ version: 2.0.26
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