asciidoctor-rhrev 1.0.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 +7 -0
- data/LICENSE +25 -0
- data/README.adoc +136 -0
- data/USAGE.adoc +653 -0
- data/lib/asciidoctor/rhrev/catalog.rb +92 -0
- data/lib/asciidoctor/rhrev/converter.rb +437 -0
- data/lib/asciidoctor/rhrev/exporter.rb +242 -0
- data/lib/asciidoctor/rhrev/helpers.rb +117 -0
- data/lib/asciidoctor/rhrev/processors.rb +48 -0
- data/lib/asciidoctor/rhrev/renderer.rb +697 -0
- data/lib/asciidoctor/rhrev/rhrev_html.rb +334 -0
- data/lib/asciidoctor/rhrev/rhrev_pdf.rb +7 -0
- data/lib/asciidoctor/rhrev.rb +34 -0
- data/lib/asciidoctor-rhrev.rb +3 -0
- metadata +69 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'asciidoctor'
|
|
4
|
+
require 'asciidoctor/converter/html5'
|
|
5
|
+
|
|
6
|
+
module Asciidoctor
|
|
7
|
+
module Rhrev
|
|
8
|
+
# Treeprocessor to auto-inject revision history at document start
|
|
9
|
+
class HtmlTreeprocessor < Asciidoctor::Extensions::Treeprocessor
|
|
10
|
+
def process document
|
|
11
|
+
return document unless document.attr? 'rhrev'
|
|
12
|
+
|
|
13
|
+
rhrev_value = document.attr('rhrev')
|
|
14
|
+
return document if rhrev_value == 'macro' || rhrev_value == 'manual'
|
|
15
|
+
|
|
16
|
+
# Insert rhrev block at the beginning of the document
|
|
17
|
+
rhrev_block = create_block document, :rhrev, nil, {}
|
|
18
|
+
document.blocks.unshift rhrev_block
|
|
19
|
+
|
|
20
|
+
document
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module Html5Converter
|
|
25
|
+
include Asciidoctor::Rhrev::Helpers
|
|
26
|
+
|
|
27
|
+
def convert node, transform = node.node_name, opts = {}
|
|
28
|
+
if node.context == :rhrev
|
|
29
|
+
convert_rhrev_block node
|
|
30
|
+
else
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def convert_rhrev_block node
|
|
36
|
+
doc = node.document
|
|
37
|
+
return '' unless doc.attr? 'rhrev'
|
|
38
|
+
return '' if doc.attr('rhrev') == 'manual'
|
|
39
|
+
|
|
40
|
+
# Check for initial release handling
|
|
41
|
+
revnumber = doc.attr('revnumber', '1.0')
|
|
42
|
+
initial_handling = doc.attr('rhrev-initial-handling', 'initial-release')
|
|
43
|
+
|
|
44
|
+
if is_initial_release?(revnumber)
|
|
45
|
+
return '' if initial_handling == 'skip'
|
|
46
|
+
return generate_initial_release_html(doc) if initial_handling == 'initial-release'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Generate revision history HTML table
|
|
50
|
+
catalog = collect_all_entries(doc)
|
|
51
|
+
return '' if catalog.nil? || catalog.entries.empty?
|
|
52
|
+
|
|
53
|
+
generate_html_output(doc, catalog)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def is_initial_release?(revnumber)
|
|
57
|
+
revnumber.to_s == '1.0' || revnumber.to_s == '1'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def generate_html_output(doc, catalog)
|
|
61
|
+
html = []
|
|
62
|
+
discrete = doc.attr? 'rhrev-discrete'
|
|
63
|
+
|
|
64
|
+
# Wrapper
|
|
65
|
+
html << '<div class="rhrev-section">' if discrete
|
|
66
|
+
html << '<section id="revision-history">' unless discrete
|
|
67
|
+
|
|
68
|
+
# Title
|
|
69
|
+
if (title = doc.attr('rhrev-customization-title'))
|
|
70
|
+
unless title.empty?
|
|
71
|
+
version_label = doc.attr('version-label', 'Revision')
|
|
72
|
+
resolved_title = title.gsub('{version-label}', version_label)
|
|
73
|
+
html << %(<h2>#{resolved_title}</h2>)
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
# Default title
|
|
77
|
+
version_label = doc.attr('version-label', 'Revision')
|
|
78
|
+
html << %(<h2>#{version_label} History</h2>)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Table
|
|
82
|
+
html << generate_html_table(doc, catalog)
|
|
83
|
+
|
|
84
|
+
# Close wrapper
|
|
85
|
+
html << (discrete ? '</div>' : '</section>')
|
|
86
|
+
|
|
87
|
+
html.join("\n")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def generate_initial_release_html(doc)
|
|
91
|
+
html = []
|
|
92
|
+
discrete = doc.attr? 'rhrev-discrete'
|
|
93
|
+
|
|
94
|
+
# Wrapper
|
|
95
|
+
html << '<div class="rhrev-section">' if discrete
|
|
96
|
+
html << '<section id="revision-history">' unless discrete
|
|
97
|
+
|
|
98
|
+
# Title
|
|
99
|
+
if (title = doc.attr('rhrev-customization-title'))
|
|
100
|
+
unless title.empty?
|
|
101
|
+
version_label = doc.attr('version-label', 'Revision')
|
|
102
|
+
resolved_title = title.gsub('{version-label}', version_label)
|
|
103
|
+
html << %(<h2>#{resolved_title}</h2>)
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
version_label = doc.attr('version-label', 'Revision')
|
|
107
|
+
html << %(<h2>#{version_label} History</h2>)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Simple table
|
|
111
|
+
initial_text = doc.attr('rhrev-customization-initial-release-text') ||
|
|
112
|
+
doc.attr('rhrev-localization-initial-release') ||
|
|
113
|
+
'Initial release'
|
|
114
|
+
|
|
115
|
+
column_widths = doc.attr('rhrev-table-column-width', '30,70').split(',').map(&:strip)
|
|
116
|
+
section_label = doc.attr('rhrev-localization-location', 'Section')
|
|
117
|
+
major_changes_text = doc.attr('rhrev-localization-major-changes', 'Major changes since')
|
|
118
|
+
version_label = doc.attr('version-label', 'Revision')
|
|
119
|
+
revnumber = doc.attr('revnumber', '1.0')
|
|
120
|
+
|
|
121
|
+
# Build table CSS classes with frame and grid
|
|
122
|
+
frame = doc.attr('rhrev-table-frame', 'all')
|
|
123
|
+
grid = doc.attr('rhrev-table-grid', 'all')
|
|
124
|
+
html << %(<table class="tableblock frame-#{frame} grid-#{grid} stretch">)
|
|
125
|
+
|
|
126
|
+
# Caption as title div
|
|
127
|
+
unless doc.attr? 'rhrev-disable-caption'
|
|
128
|
+
if (caption = doc.attr('rhrev-table-caption'))
|
|
129
|
+
html << %(<div class="title">#{caption}</div>)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
html << '<colgroup>'
|
|
134
|
+
column_widths.each { |w| html << %(<col style="width: #{w}%;">)}
|
|
135
|
+
html << '</colgroup>'
|
|
136
|
+
html << '<tbody>'
|
|
137
|
+
html << '<tr class="rhrev-header">'
|
|
138
|
+
html << %(<th class="tableblock halign-left valign-top">#{section_label}</th>)
|
|
139
|
+
html << %(<th class="tableblock halign-left valign-top"><strong>#{major_changes_text} #{version_label} #{revnumber}</strong></th>)
|
|
140
|
+
html << '</tr>'
|
|
141
|
+
html << '<tr>'
|
|
142
|
+
html << '<td> </td>'
|
|
143
|
+
html << %(<td>#{initial_text}</td>)
|
|
144
|
+
html << '</tr>'
|
|
145
|
+
html << '</tbody>'
|
|
146
|
+
html << '</table>'
|
|
147
|
+
|
|
148
|
+
html << (discrete ? '</div>' : '</section>')
|
|
149
|
+
|
|
150
|
+
html.join("\n")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def collect_all_entries(doc)
|
|
154
|
+
catalog = Catalog.new
|
|
155
|
+
prefix = doc.attr 'revhistoryprefix', 'rhrev'
|
|
156
|
+
|
|
157
|
+
# Collect document-level entries (-all)
|
|
158
|
+
(0..9).each do |major|
|
|
159
|
+
(0..9).each do |minor|
|
|
160
|
+
revision = "#{major}-#{minor}"
|
|
161
|
+
prevrev_attr = "#{revision}-prevrev"
|
|
162
|
+
|
|
163
|
+
if doc.attr(prevrev_attr)
|
|
164
|
+
all_attr_name = "#{prefix}#{revision}-all"
|
|
165
|
+
if (all_value = doc.attr(all_attr_name))
|
|
166
|
+
catalog.add_all_entry revision, all_value
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Skip -cover entries for HTML
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Collect block-level entries by scanning document
|
|
175
|
+
contexts = [:section, :floating_title, :example, :listing, :table, :image]
|
|
176
|
+
doc.find_by { |b| contexts.include?(b.context) }.each do |block|
|
|
177
|
+
next unless block.id
|
|
178
|
+
|
|
179
|
+
if block.respond_to?(:attributes)
|
|
180
|
+
block.attributes.each do |key, value|
|
|
181
|
+
key_str = key.to_s
|
|
182
|
+
next unless key_str.start_with?(prefix)
|
|
183
|
+
next if key_str.include?('-all') || key_str.end_with?('-cover')
|
|
184
|
+
|
|
185
|
+
revision = key_str.sub(prefix, '')
|
|
186
|
+
|
|
187
|
+
catalog.add_entry revision, block.id, value.to_s,
|
|
188
|
+
reftext: block.reftext,
|
|
189
|
+
title: block.title,
|
|
190
|
+
sectnum: block.respond_to?(:sectnum) ? block.sectnum : nil,
|
|
191
|
+
context: block.context,
|
|
192
|
+
is_chapter: false,
|
|
193
|
+
sectname: block.respond_to?(:sectname) ? block.sectname : nil,
|
|
194
|
+
caption_number: nil,
|
|
195
|
+
source_line: block.lineno
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
catalog
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def generate_html_table(doc, catalog)
|
|
204
|
+
column_widths = doc.attr 'rhrev-table-column-width', '30,70'
|
|
205
|
+
widths = column_widths.split(',').map(&:strip)
|
|
206
|
+
|
|
207
|
+
html = []
|
|
208
|
+
|
|
209
|
+
# Build table CSS classes with frame and grid
|
|
210
|
+
frame = doc.attr('rhrev-table-frame', 'all')
|
|
211
|
+
grid = doc.attr('rhrev-table-grid', 'all')
|
|
212
|
+
html << %(<table class="tableblock frame-#{frame} grid-#{grid} stretch">)
|
|
213
|
+
|
|
214
|
+
# Caption as title div
|
|
215
|
+
unless doc.attr? 'rhrev-disable-caption'
|
|
216
|
+
if (caption = doc.attr('rhrev-table-caption'))
|
|
217
|
+
html << %(<div class="title">#{caption}</div>)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
html << '<colgroup>'
|
|
222
|
+
widths.each { |w| html << %(<col style="width: #{w}%;">) }
|
|
223
|
+
html << '</colgroup>'
|
|
224
|
+
html << '<tbody>'
|
|
225
|
+
|
|
226
|
+
# Custom first row
|
|
227
|
+
if (first_row = doc.attr('rhrev-customization-first-row'))
|
|
228
|
+
html << '<tr class="rhrev-custom-first-row">'
|
|
229
|
+
html << %(<td colspan="2">#{first_row}</td>)
|
|
230
|
+
html << '</tr>'
|
|
231
|
+
elsif (first_row_left = doc.attr('rhrev-customization-first-row-left')) ||
|
|
232
|
+
(first_row_right = doc.attr('rhrev-customization-first-row-right'))
|
|
233
|
+
html << '<tr class="rhrev-custom-first-row">'
|
|
234
|
+
html << %(<td>#{first_row_left || ' '}</td>)
|
|
235
|
+
html << %(<td>#{first_row_right || ' '}</td>)
|
|
236
|
+
html << '</tr>'
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
section_label = doc.attr 'rhrev-localization-location', 'Section'
|
|
240
|
+
major_changes_text = doc.attr 'rhrev-localization-major-changes', 'Major changes since'
|
|
241
|
+
all_text = doc.attr 'rhrev-localization-all', 'All'
|
|
242
|
+
|
|
243
|
+
catalog.sorted_revisions.each do |revision|
|
|
244
|
+
prevrev_attr = "#{revision}-prevrev"
|
|
245
|
+
prevrevdate_attr = "#{revision}-prevrevdate"
|
|
246
|
+
revision_label = doc.attr('version-label', 'Revision')
|
|
247
|
+
prev_title = doc.attr(prevrev_attr) || revision.tr('-', '.')
|
|
248
|
+
prev_date = doc.attr(prevrevdate_attr) || ""
|
|
249
|
+
prev_rev_text = format_prev_rev(prev_title, prev_date, doc)
|
|
250
|
+
|
|
251
|
+
# Build header text
|
|
252
|
+
if prev_title.strip.downcase.start_with?(revision_label.downcase)
|
|
253
|
+
header_text = "#{major_changes_text} #{prev_rev_text}"
|
|
254
|
+
else
|
|
255
|
+
header_text = "#{major_changes_text} #{revision_label} #{prev_rev_text}"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Header row for this revision
|
|
259
|
+
html << '<tr class="rhrev-header">'
|
|
260
|
+
html << %(<th class="tableblock halign-left valign-top">#{section_label}</th>)
|
|
261
|
+
html << %(<th class="tableblock halign-left valign-top"><strong>#{header_text}</strong></th>)
|
|
262
|
+
html << '</tr>'
|
|
263
|
+
|
|
264
|
+
# All entry
|
|
265
|
+
if (all_change = catalog.instance_variable_get(:@all_entries)[revision])
|
|
266
|
+
html << '<tr>'
|
|
267
|
+
html << %(<td>#{all_text}</td>)
|
|
268
|
+
html << %(<td>#{format_change_as_html_list(all_change)}</td>)
|
|
269
|
+
html << '</tr>'
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Block entries
|
|
273
|
+
entries = catalog.entries[revision] || []
|
|
274
|
+
entries.sort_by! { |e| e[:sequence] || 0 }
|
|
275
|
+
|
|
276
|
+
entries.each do |entry|
|
|
277
|
+
anchor = entry[:anchor]
|
|
278
|
+
title = entry[:title] || anchor.tr('_-', ' ').split.map(&:capitalize).join(' ')
|
|
279
|
+
|
|
280
|
+
html << '<tr>'
|
|
281
|
+
html << %(<td><a href="##{anchor}">#{title}</a></td>)
|
|
282
|
+
html << %(<td>#{format_change_as_html_list(entry[:change])}</td>)
|
|
283
|
+
html << '</tr>'
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
html << '</tbody>'
|
|
288
|
+
html << '</table>'
|
|
289
|
+
|
|
290
|
+
html.join("\n")
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def format_change_as_html_list(text)
|
|
294
|
+
return '' if text.to_s.empty?
|
|
295
|
+
|
|
296
|
+
# Process attribute content (handle line breaks)
|
|
297
|
+
processed = preprocess_attribute_content(text)
|
|
298
|
+
|
|
299
|
+
# Split by asterisks to create list items
|
|
300
|
+
tokens = processed.split(/\s(?=\*+\s)/)
|
|
301
|
+
|
|
302
|
+
items = []
|
|
303
|
+
if tokens.empty? || tokens.length == 1 && !processed.include?('*')
|
|
304
|
+
# Single item without asterisk - wrap in list anyway
|
|
305
|
+
items << { level: 1, content: processed }
|
|
306
|
+
else
|
|
307
|
+
tokens.each do |token|
|
|
308
|
+
next if token.strip.empty?
|
|
309
|
+
|
|
310
|
+
# Count leading asterisks for nesting level
|
|
311
|
+
if token.strip =~ /^(\*+)\s+(.+)/
|
|
312
|
+
level = $1.length
|
|
313
|
+
content = $2
|
|
314
|
+
items << { level: level, content: content }
|
|
315
|
+
else
|
|
316
|
+
items << { level: 1, content: token.strip }
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Always generate HTML list
|
|
322
|
+
html = ['<ul>']
|
|
323
|
+
items.each do |item|
|
|
324
|
+
html << '<li>' + item[:content] + '</li>'
|
|
325
|
+
end
|
|
326
|
+
html << '</ul>'
|
|
327
|
+
html.join("\n")
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Extend Html5Converter with our module
|
|
334
|
+
Asciidoctor::Converter::Html5Converter.prepend Asciidoctor::Rhrev::Html5Converter
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Load Asciidoctor first
|
|
4
|
+
require 'asciidoctor' unless defined? Asciidoctor
|
|
5
|
+
|
|
6
|
+
# Load shared components (processors, helpers, catalog)
|
|
7
|
+
require_relative 'rhrev/processors'
|
|
8
|
+
require_relative 'rhrev/helpers'
|
|
9
|
+
require_relative 'rhrev/catalog'
|
|
10
|
+
|
|
11
|
+
# Try to load the PDF converter support if asciidoctor-pdf is available
|
|
12
|
+
begin
|
|
13
|
+
require 'asciidoctor-pdf'
|
|
14
|
+
require_relative 'rhrev/rhrev_pdf'
|
|
15
|
+
rescue LoadError
|
|
16
|
+
# PDF not available, that's ok
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Try to load the HTML backend support
|
|
20
|
+
html_available = false
|
|
21
|
+
begin
|
|
22
|
+
require_relative 'rhrev/rhrev_html'
|
|
23
|
+
html_available = true
|
|
24
|
+
rescue LoadError
|
|
25
|
+
# HTML-specific code couldn't be loaded
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Register extensions (processors are backend-agnostic)
|
|
29
|
+
Asciidoctor::Extensions.register do
|
|
30
|
+
preprocessor Asciidoctor::Rhrev::XrefSanitizerPreprocessor
|
|
31
|
+
block_macro Asciidoctor::Rhrev::RhrevBlockMacroProcessor
|
|
32
|
+
inline_macro Asciidoctor::Rhrev::PagerhrefInlineMacroProcessor
|
|
33
|
+
treeprocessor Asciidoctor::Rhrev::HtmlTreeprocessor if html_available
|
|
34
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: asciidoctor-rhrev
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- 白一百 baiyibai
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: asciidoctor
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
description: A comprehensive revision history tracking system for asciidoctor-pdf
|
|
27
|
+
that automatically creates a grouped, formatted table of document changes with clickable
|
|
28
|
+
page numbers, xrefstyle support, and extensive customization options.
|
|
29
|
+
email: rubygems@baiyibai.cyou
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- LICENSE
|
|
35
|
+
- README.adoc
|
|
36
|
+
- USAGE.adoc
|
|
37
|
+
- lib/asciidoctor-rhrev.rb
|
|
38
|
+
- lib/asciidoctor/rhrev.rb
|
|
39
|
+
- lib/asciidoctor/rhrev/catalog.rb
|
|
40
|
+
- lib/asciidoctor/rhrev/converter.rb
|
|
41
|
+
- lib/asciidoctor/rhrev/exporter.rb
|
|
42
|
+
- lib/asciidoctor/rhrev/helpers.rb
|
|
43
|
+
- lib/asciidoctor/rhrev/processors.rb
|
|
44
|
+
- lib/asciidoctor/rhrev/renderer.rb
|
|
45
|
+
- lib/asciidoctor/rhrev/rhrev_html.rb
|
|
46
|
+
- lib/asciidoctor/rhrev/rhrev_pdf.rb
|
|
47
|
+
homepage: https://gitlab.com/baiyibai/rhrev-asciidoctor-automatic-revision-history
|
|
48
|
+
licenses:
|
|
49
|
+
- CC0-1.0
|
|
50
|
+
metadata:
|
|
51
|
+
source_code_uri: https://gitlab.com/baiyibai/rhrev-asciidoctor-automatic-revision-history.git
|
|
52
|
+
rdoc_options: []
|
|
53
|
+
require_paths:
|
|
54
|
+
- lib
|
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: 3.0.0
|
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '0'
|
|
65
|
+
requirements: []
|
|
66
|
+
rubygems_version: 3.6.7
|
|
67
|
+
specification_version: 4
|
|
68
|
+
summary: Comprehensive revision history tracking extension for asciidoctor and asciidoctor-pdf
|
|
69
|
+
test_files: []
|