giblish 0.2.12 → 0.3.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 +5 -5
- data/.ruby-version +1 -0
- data/Rakefile +2 -3
- data/giblish.gemspec +1 -0
- data/lib/giblish/application.rb +14 -5
- data/lib/giblish/buildgraph.rb +179 -0
- data/lib/giblish/buildindex.rb +361 -414
- data/lib/giblish/cmdline.rb +11 -7
- data/lib/giblish/core.rb +251 -400
- data/lib/giblish/docconverter.rb +231 -0
- data/lib/giblish/docid.rb +107 -58
- data/lib/giblish/docinfo.rb +85 -0
- data/lib/giblish/gititf.rb +1 -0
- data/lib/giblish/utils.rb +29 -5
- data/lib/giblish/version.rb +1 -1
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9db70ccddaf7473a9da59c81a9a592635a027faa4ffbec31a7d30a976c9fa7c6
|
4
|
+
data.tar.gz: a2c1034a4ef601d1f37ec1402d07de6258d1273a8524e4b6347418f0fb42cd2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05d9414c48c69d13eec80cbedc5d5a6049d711e53af054a7d26af9192e7845ec114667459ab090af3c0fab610f1c3f6b50629b15a51024fc5f15b01dc546d87d
|
7
|
+
data.tar.gz: da0330a4f1fe0fd9b009fa2689dda4b2f6f9e95e76acd9ba6755055d252e8b1b62d0ed974cbb7ca5ddbf0dd408781d9915dff68f843c9b8cec472115d7287d1d
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.5.3
|
data/Rakefile
CHANGED
@@ -7,11 +7,10 @@ Rake::TestTask.new(:test) do |t|
|
|
7
7
|
t.test_files = FileList["test/**/*_test.rb"]
|
8
8
|
end
|
9
9
|
|
10
|
-
Rake::TestTask.new(:
|
10
|
+
Rake::TestTask.new(:current) do |t|
|
11
11
|
t.libs << "test"
|
12
12
|
t.libs << "lib"
|
13
|
-
t.test_files = FileList["test/**/
|
14
|
-
# t.test_files = FileList["test/**/docid_test.rb"]
|
13
|
+
t.test_files = FileList["test/**/depgraph_test.rb"]
|
15
14
|
end
|
16
15
|
|
17
16
|
Rake::TestTask.new(:sandbox) do |t|
|
data/giblish.gemspec
CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
|
37
37
|
# Usage: spec.add_runtime_dependency "[gem name]", [[version]]
|
38
38
|
spec.add_runtime_dependency "asciidoctor", "~>1.5", ">= 1.5.7.1"
|
39
|
+
spec.add_runtime_dependency "asciidoctor-diagram", ["~> 1.5"]
|
39
40
|
spec.add_runtime_dependency "asciidoctor-pdf", [">= 1.5.0.alpha.16"]
|
40
41
|
spec.add_runtime_dependency "asciidoctor-rouge", ["~> 0.3"]
|
41
42
|
spec.add_runtime_dependency "git", "~> 1.3"
|
data/lib/giblish/application.rb
CHANGED
@@ -6,15 +6,22 @@ require_relative "utils"
|
|
6
6
|
module Giblish
|
7
7
|
class Application
|
8
8
|
|
9
|
+
# return exit status (0 for success)
|
9
10
|
def run_with_args(args)
|
10
11
|
run args
|
11
12
|
end
|
12
13
|
|
14
|
+
# does not return, exits with status code
|
13
15
|
def run_from_cmd_line
|
14
|
-
run ARGV
|
16
|
+
status = run ARGV
|
17
|
+
exit(status)
|
15
18
|
end
|
16
19
|
|
20
|
+
# return exit status (0 for success)
|
17
21
|
def run(args)
|
22
|
+
# force immediate output
|
23
|
+
$stdout.sync = true
|
24
|
+
|
18
25
|
# setup logging
|
19
26
|
Giblog.setup
|
20
27
|
|
@@ -27,15 +34,17 @@ module Giblish
|
|
27
34
|
begin
|
28
35
|
if cmdline.args[:gitRepoRoot]
|
29
36
|
Giblog.logger.info { "User asked to parse a git repo" }
|
30
|
-
|
37
|
+
gc = GitRepoConverter.new cmdline.args
|
38
|
+
gc.convert
|
31
39
|
else
|
32
|
-
tc =
|
33
|
-
tc.
|
40
|
+
tc = FileTreeConverter.new cmdline.args
|
41
|
+
tc.convert
|
34
42
|
end
|
35
43
|
Giblog.logger.info { "Giblish is done!" }
|
44
|
+
0
|
36
45
|
rescue Exception => e
|
37
46
|
log_error e
|
38
|
-
|
47
|
+
1
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
@@ -0,0 +1,179 @@
|
|
1
|
+
|
2
|
+
module Giblish
|
3
|
+
# Builds an asciidoc page with an svg image with a
|
4
|
+
# digraph showing how documents reference each other.
|
5
|
+
#
|
6
|
+
# Graphviz is used as the graph generator and must be available
|
7
|
+
# as a valid engine via asciidoctor-diagram for this class to work.
|
8
|
+
class GraphBuilderGraphviz
|
9
|
+
|
10
|
+
# the dependency graph relies on graphwiz (dot), check if we can access that
|
11
|
+
def self.supported
|
12
|
+
return !Giblish.which('dot').nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
# Supported options:
|
16
|
+
# :extension - file extension for URL links (default is .html)
|
17
|
+
def initialize(processed_docs, paths, options = {})
|
18
|
+
|
19
|
+
# this class relies on graphwiz (dot), make sure we can access that
|
20
|
+
raise "Could not find the 'dot' tool needed to generate a dependency graph!" unless GraphBuilderGraphviz.supported
|
21
|
+
|
22
|
+
# require asciidoctor module needed for generating diagrams
|
23
|
+
require "asciidoctor-diagram/graphviz"
|
24
|
+
|
25
|
+
@noid_docs = {}
|
26
|
+
@next_id = 0
|
27
|
+
@processed_docs = processed_docs
|
28
|
+
@paths = paths
|
29
|
+
@options = options.dup
|
30
|
+
@extension = options.key?(:extension) ? options[:extension] : "html"
|
31
|
+
@docid_cache = DocidCollector.docid_cache
|
32
|
+
@docid_deps = DocidCollector.docid_deps
|
33
|
+
@dep_graph = build_dep_graph
|
34
|
+
end
|
35
|
+
|
36
|
+
# get the asciidoc source for the document.
|
37
|
+
def source
|
38
|
+
<<~DOC_STR
|
39
|
+
#{generate_header}
|
40
|
+
#{generate_labels}
|
41
|
+
#{generate_deps}
|
42
|
+
#{generate_footer}
|
43
|
+
DOC_STR
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# build a hash with {DocInfo => [doc_id array]}
|
49
|
+
def build_dep_graph
|
50
|
+
result = {}
|
51
|
+
@docid_deps.each do |src_file, id_array|
|
52
|
+
d = @processed_docs.find do |doc|
|
53
|
+
doc.src_file.to_s.eql? src_file
|
54
|
+
end
|
55
|
+
raise "Inconsistent docs when building graph!! found no match for #{src_file}" if d.nil?
|
56
|
+
result[d] = id_array
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def generate_header
|
62
|
+
t = Time.now
|
63
|
+
<<~DOC_STR
|
64
|
+
= Document-id reference graph
|
65
|
+
from #{@paths.src_root_abs}
|
66
|
+
|
67
|
+
Generated by Giblish at::
|
68
|
+
#{t.strftime('%Y-%m-%d %H:%M')}
|
69
|
+
|
70
|
+
Below is a graph that visualizes what documents (by doc-id) a specific
|
71
|
+
document references.
|
72
|
+
|
73
|
+
[graphviz,"docdeps","svg",options="inline"]
|
74
|
+
....
|
75
|
+
digraph notebook {
|
76
|
+
bgcolor="#33333310"
|
77
|
+
node [shape=note,
|
78
|
+
fillcolor="#ebf26680",
|
79
|
+
style="filled,solid"
|
80
|
+
]
|
81
|
+
|
82
|
+
rankdir="LR"
|
83
|
+
|
84
|
+
DOC_STR
|
85
|
+
end
|
86
|
+
|
87
|
+
def generate_footer
|
88
|
+
<<~DOC_STR
|
89
|
+
}
|
90
|
+
....
|
91
|
+
DOC_STR
|
92
|
+
end
|
93
|
+
|
94
|
+
def make_dot_entry(doc_dict, info)
|
95
|
+
# split title into multiple rows if it is too long
|
96
|
+
line_length = 15
|
97
|
+
lines = [""]
|
98
|
+
info.title.split(" ").inject("") do |l,w|
|
99
|
+
line = l + " " + w
|
100
|
+
lines[-1] = line
|
101
|
+
if line.length > line_length
|
102
|
+
# create a new, empty, line
|
103
|
+
lines << ""
|
104
|
+
""
|
105
|
+
else
|
106
|
+
line
|
107
|
+
end
|
108
|
+
end unless info.title.nil?
|
109
|
+
title = lines.select { |l| l.length > 0 }.map {|l| l}.join("\n")
|
110
|
+
|
111
|
+
# create the label used to display the node in the graph
|
112
|
+
dot_entry = if info.doc_id.nil?
|
113
|
+
doc_id = next_fake_id
|
114
|
+
@noid_docs[info] = doc_id
|
115
|
+
"\"#{doc_id}\"[label=\"-\\n#{title}\""
|
116
|
+
else
|
117
|
+
doc_id = info.doc_id
|
118
|
+
"\"#{info.doc_id}\"[label=\"#{info.doc_id}\\n#{title}\""
|
119
|
+
end
|
120
|
+
# add clickable links in the case of html output (this is not supported
|
121
|
+
# out-of-the-box for pdf).
|
122
|
+
rp = info.rel_path.sub_ext(".#{@extension}")
|
123
|
+
case @extension
|
124
|
+
when "html"
|
125
|
+
dot_entry += ", URL=\"#{rp}\" ]"
|
126
|
+
else
|
127
|
+
dot_entry += " ]"
|
128
|
+
end
|
129
|
+
doc_dict[doc_id] = dot_entry
|
130
|
+
end
|
131
|
+
|
132
|
+
def generate_labels
|
133
|
+
# create an entry in the 'dot' description for each
|
134
|
+
# document, sort them according to descending doc id to
|
135
|
+
# get them displayed in the opposite order in the graph
|
136
|
+
node_dict = {}
|
137
|
+
@dep_graph.each_key do |info|
|
138
|
+
make_dot_entry node_dict, info
|
139
|
+
end
|
140
|
+
# sort the nodes by reverse doc id
|
141
|
+
node_dict = node_dict.sort.reverse.to_h
|
142
|
+
|
143
|
+
# produce the string with all node entries
|
144
|
+
node_str = node_dict.map do |k,v|
|
145
|
+
v
|
146
|
+
end.join("\n")
|
147
|
+
node_str
|
148
|
+
end
|
149
|
+
|
150
|
+
def generate_deps
|
151
|
+
dep_str = ""
|
152
|
+
@dep_graph.each do |info, targets|
|
153
|
+
# set either the real or the generated id as source
|
154
|
+
src_part = if info.doc_id.nil?
|
155
|
+
"\"#{@noid_docs[info]}\""
|
156
|
+
else
|
157
|
+
"\"#{info.doc_id}\""
|
158
|
+
end
|
159
|
+
|
160
|
+
if targets.length.zero?
|
161
|
+
dep_str += "#{src_part}\n"
|
162
|
+
next
|
163
|
+
end
|
164
|
+
|
165
|
+
dep_str += "#{src_part} -> {" + targets.reduce("") do |acc, target|
|
166
|
+
acc + " \"#{target}\""
|
167
|
+
end
|
168
|
+
# replace last comma with newline
|
169
|
+
dep_str += "}\n"
|
170
|
+
end
|
171
|
+
dep_str
|
172
|
+
end
|
173
|
+
|
174
|
+
def next_fake_id
|
175
|
+
@next_id += 1
|
176
|
+
"_generated_id_#{@next_id.to_s.rjust(4, '0')}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/lib/giblish/buildindex.rb
CHANGED
@@ -1,504 +1,451 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
|
-
|
4
1
|
require "pathname"
|
5
2
|
require "git"
|
6
|
-
|
3
|
+
|
7
4
|
require_relative "pathtree"
|
8
5
|
require_relative "gititf"
|
6
|
+
require_relative "docinfo"
|
7
|
+
|
8
|
+
module Giblish
|
9
|
+
|
10
|
+
# Base class with common functionality for all index builders
|
11
|
+
class BasicIndexBuilder
|
12
|
+
# set up the basic index building info
|
13
|
+
def initialize(processed_docs, path_manager, handle_docid = false)
|
14
|
+
@paths = path_manager
|
15
|
+
@nof_missing_titles = 0
|
16
|
+
@processed_docs = processed_docs
|
17
|
+
@src_str = ""
|
18
|
+
@manage_docid = handle_docid
|
19
|
+
end
|
9
20
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
21
|
+
def source(dep_graph_exists = false)
|
22
|
+
<<~DOC_STR
|
23
|
+
#{generate_header}
|
24
|
+
#{generate_tree(dep_graph_exists)}
|
25
|
+
#{generate_details}
|
26
|
+
#{generate_footer}
|
27
|
+
DOC_STR
|
28
|
+
end
|
19
29
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
attr_accessor :error_msg
|
27
|
-
attr_accessor :stderr
|
28
|
-
# these two members can have encoding issues when
|
29
|
-
# running in a mixed Windows/Linux setting.
|
30
|
-
# that is why the explicit utf-8 read methods are
|
31
|
-
# provided.
|
32
|
-
attr_accessor :relPath
|
33
|
-
attr_accessor :srcFile
|
34
|
-
|
35
|
-
def relPath_utf8
|
36
|
-
return nil if @relPath.nil?
|
37
|
-
@relPath.to_s.encode("utf-8")
|
38
|
-
end
|
30
|
+
protected
|
31
|
+
# return the adoc string for displaying the source file
|
32
|
+
def display_source_file(doc_info)
|
33
|
+
<<~SRC_FILE_TXT
|
34
|
+
Source file::
|
35
|
+
#{doc_info.srcFile_utf8}
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
@srcFile.to_s.encode("utf-8")
|
43
|
-
end
|
37
|
+
SRC_FILE_TXT
|
38
|
+
end
|
44
39
|
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
def generate_header
|
41
|
+
t = Time.now
|
42
|
+
<<~DOC_HEADER
|
43
|
+
= Document index
|
44
|
+
from #{@paths.src_root_abs}
|
48
45
|
|
49
|
-
|
50
|
-
"DocInfo: title: #{@title} srcFile: #{srcFile_utf8}"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Base class with common functionality for all index builders
|
55
|
-
class BasicIndexBuilder
|
56
|
-
# set up the basic index building info
|
57
|
-
def initialize(path_manager, handle_docid = false)
|
58
|
-
@paths = path_manager
|
59
|
-
@nof_missing_titles = 0
|
60
|
-
@added_docs = []
|
61
|
-
@src_str = ""
|
62
|
-
@manage_docid = handle_docid
|
63
|
-
end
|
46
|
+
Generated by Giblish at::
|
64
47
|
|
65
|
-
|
66
|
-
# returns the filled in instance so that derived implementations can
|
67
|
-
# add more data
|
68
|
-
def add_doc(adoc, adoc_stderr)
|
69
|
-
Giblog.logger.debug { "Adding adoc: #{adoc} Asciidoctor stderr: #{adoc_stderr}" }
|
70
|
-
Giblog.logger.debug {"Doc attributes: #{adoc.attributes}"}
|
71
|
-
|
72
|
-
info = DocInfo.new
|
73
|
-
info.converted = true
|
74
|
-
info.stderr = adoc_stderr
|
75
|
-
|
76
|
-
# Get the purpose info if it exists
|
77
|
-
info.purpose_str = get_purpose_info adoc
|
78
|
-
|
79
|
-
# Get the relative path beneath the root dir to the doc
|
80
|
-
d_attr = adoc.attributes
|
81
|
-
info.relPath = Pathname.new(
|
82
|
-
"#{d_attr['outdir']}/#{d_attr['docname']}#{d_attr['docfilesuffix']}"
|
83
|
-
).relative_path_from(
|
84
|
-
@paths.dst_root_abs
|
85
|
-
)
|
86
|
-
|
87
|
-
# Get the doc id if it exists
|
88
|
-
info.doc_id = adoc.attributes["docid"]
|
89
|
-
|
90
|
-
# Get the source file path
|
91
|
-
info.srcFile = adoc.attributes["docfile"]
|
92
|
-
|
93
|
-
# If a docid exists, set titel to docid - title if we care about
|
94
|
-
# doc ids.
|
95
|
-
info.title = if !info.doc_id.nil? && @manage_docid
|
96
|
-
"#{info.doc_id} - #{adoc.doctitle}"
|
97
|
-
else
|
98
|
-
adoc.doctitle
|
99
|
-
end
|
100
|
-
|
101
|
-
# Cache the created DocInfo
|
102
|
-
@added_docs << info
|
103
|
-
info
|
104
|
-
end
|
48
|
+
#{t.strftime('%Y-%m-%d %H:%M')}
|
105
49
|
|
106
|
-
|
107
|
-
|
50
|
+
DOC_HEADER
|
51
|
+
end
|
108
52
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
53
|
+
def get_docid_statistics
|
54
|
+
largest = ""
|
55
|
+
clash = []
|
56
|
+
@processed_docs.each do |d|
|
57
|
+
# get the lexically largest doc id
|
58
|
+
largest = d.doc_id if !d.doc_id.nil? && d.doc_id > largest
|
113
59
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
60
|
+
# collect all ids in an array to find duplicates later on
|
61
|
+
clash << d.doc_id unless d.doc_id.nil?
|
62
|
+
end
|
63
|
+
# find the duplicate doc ids (if any)
|
64
|
+
duplicates = clash.select { |id| clash.count(id) > 1 }.uniq.sort
|
118
65
|
|
119
|
-
|
120
|
-
|
121
|
-
#{generate_header}
|
122
|
-
#{generate_tree}
|
123
|
-
#{generate_details}
|
124
|
-
#{generate_footer}
|
125
|
-
DOC_STR
|
126
|
-
end
|
66
|
+
return largest,duplicates
|
67
|
+
end
|
127
68
|
|
128
|
-
|
69
|
+
def generate_doc_id_info(dep_graph_exists)
|
70
|
+
largest,duplicates = get_docid_statistics
|
71
|
+
docid_info_str = if ! @manage_docid
|
72
|
+
""
|
73
|
+
else
|
74
|
+
"The 'largest' document id found when resolving :docid: tags is *#{largest}*."
|
75
|
+
end
|
129
76
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
77
|
+
docid_warn_str = if duplicates.length.zero?
|
78
|
+
""
|
79
|
+
else
|
80
|
+
"WARNING: The following document ids are used for more than one document. " +
|
81
|
+
"_#{duplicates.map {|id| id.to_s}.join(",") }_"
|
82
|
+
end
|
135
83
|
|
136
|
-
|
84
|
+
# include link to dependency graph if it exists
|
85
|
+
dep_graph_str = if dep_graph_exists
|
86
|
+
"_(a visual graph of document dependencies can be found " \
|
87
|
+
"<<./graph.adoc#,here>>)_"
|
88
|
+
else
|
89
|
+
""
|
90
|
+
end
|
91
|
+
|
92
|
+
if @manage_docid
|
93
|
+
<<~DOC_ID_INFO
|
94
|
+
Document id numbers::
|
95
|
+
The generation of this repository uses document id numbers. #{docid_info_str} #{dep_graph_str}
|
96
|
+
|
97
|
+
#{docid_warn_str}
|
98
|
+
|
99
|
+
DOC_ID_INFO
|
100
|
+
else
|
101
|
+
""
|
102
|
+
end
|
103
|
+
end
|
137
104
|
|
138
|
-
|
105
|
+
def generate_tree(dep_graph_exists)
|
106
|
+
# output tree intro
|
107
|
+
tree_string = <<~DOC_HEADER
|
108
|
+
== Document Overview
|
139
109
|
|
140
|
-
|
141
|
-
|
110
|
+
_Click on the title to open the document or on `details` to see more
|
111
|
+
info about the document. A `(warn)` label indicates that there were
|
112
|
+
warnings while converting the document from its asciidoc format._
|
142
113
|
|
143
|
-
|
144
|
-
""
|
145
|
-
end
|
114
|
+
#{generate_doc_id_info dep_graph_exists}
|
146
115
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
# Get the 'Purpose' section if it exists
|
151
|
-
purpose_str = ""
|
152
|
-
adoc.blocks.each do |section|
|
153
|
-
next unless section.is_a?(Asciidoctor::Section) &&
|
154
|
-
(section.level == 1) &&
|
155
|
-
(section.name =~ /^Purpose$/)
|
156
|
-
purpose_str = "Purpose::\n\n"
|
157
|
-
|
158
|
-
# filter out 'odd' text, such as lists etc...
|
159
|
-
section.blocks.each do |bb|
|
160
|
-
next unless bb.is_a?(Asciidoctor::Block)
|
161
|
-
purpose_str << "#{bb.source}\n+\n"
|
162
|
-
end
|
163
|
-
end
|
164
|
-
purpose_str
|
165
|
-
end
|
116
|
+
[subs=\"normal\"]
|
117
|
+
----
|
118
|
+
DOC_HEADER
|
166
119
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
120
|
+
# build up tree of paths
|
121
|
+
root = PathTree.new
|
122
|
+
@processed_docs.each do |d|
|
123
|
+
root.add_path(d.rel_path.to_s, d)
|
124
|
+
end
|
172
125
|
|
173
|
-
|
174
|
-
|
175
|
-
|
126
|
+
# generate each tree entry string
|
127
|
+
root.traverse_top_down do |level, node|
|
128
|
+
tree_string << tree_entry_string(level, node)
|
129
|
+
end
|
176
130
|
|
177
|
-
#
|
178
|
-
|
179
|
-
|
131
|
+
# generate the tree footer
|
132
|
+
tree_string << "\n----\n"
|
133
|
+
end
|
180
134
|
|
181
|
-
|
182
|
-
|
183
|
-
#
|
184
|
-
# Returns [ clickableTitleStr, clickableDetailsStr ]
|
185
|
-
def format_title_and_ref(doc_info)
|
186
|
-
unless doc_info.title
|
187
|
-
@nof_missing_titles += 1
|
188
|
-
doc_info.title = "NO TITLE FOUND (#{@nof_missing_titles}) !"
|
135
|
+
def generate_footer
|
136
|
+
""
|
189
137
|
end
|
190
|
-
return "<<#{doc_info.relPath_utf8}#,#{doc_info.title}>>".encode("utf-8"),
|
191
|
-
"<<#{Giblish.to_valid_id(doc_info.title)},details>>\n".encode("utf-8")
|
192
|
-
end
|
193
138
|
|
194
|
-
|
195
|
-
# DocTitle (warn) details
|
196
|
-
# Where the DocTitle and details are links to the doc itself and a section
|
197
|
-
# identified with the doc's title respectively.
|
198
|
-
def tree_entry_converted(prefix_str, doc_info)
|
199
|
-
# Get the elements of the entry
|
200
|
-
doc_title, doc_details = format_title_and_ref doc_info
|
201
|
-
warning_label = doc_info.stderr.empty? ? "" : "(warn)"
|
202
|
-
|
203
|
-
# Calculate padding to get (warn) and details aligned between entries
|
204
|
-
padding = 80
|
205
|
-
[doc_info.title, prefix_str, warning_label].each { |p| padding -= p.length }
|
206
|
-
padding = 0 unless padding.positive?
|
207
|
-
"#{prefix_str} #{doc_title}#{' ' * padding}#{warning_label} #{doc_details}"
|
208
|
-
end
|
139
|
+
private
|
209
140
|
|
210
|
-
|
211
|
-
|
212
|
-
|
141
|
+
def generate_conversion_info(d)
|
142
|
+
return "" if d.stderr.empty?
|
143
|
+
# extract conversion warnings from asciddoctor std err
|
144
|
+
conv_warnings = d.stderr.gsub("asciidoctor:", "\n * asciidoctor:")
|
145
|
+
Giblog.logger.warn {"Conversion warnings: #{conv_warnings}"}
|
213
146
|
|
214
|
-
|
215
|
-
|
147
|
+
# assemble info to index page
|
148
|
+
<<~CONV_INFO
|
149
|
+
Conversion info::
|
216
150
|
|
217
|
-
|
218
|
-
|
219
|
-
if d.converted
|
220
|
-
tree_entry_converted prefix_str, d
|
221
|
-
else
|
222
|
-
# no converted file exists, show what we know
|
223
|
-
"#{prefix_str} FAIL: #{d.srcFile_utf8} <<#{d.srcFile_utf8},details>>\n"
|
151
|
+
#{conv_warnings}
|
152
|
+
CONV_INFO
|
224
153
|
end
|
225
|
-
end
|
226
154
|
|
227
|
-
|
228
|
-
#
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
155
|
+
# Private: Return adoc elements for displaying a clickable title
|
156
|
+
# and a 'details' ref that points to a section that uses the title as an id.
|
157
|
+
#
|
158
|
+
# Returns [ title, clickableTitleStr, clickableDetailsStr ]
|
159
|
+
def format_title_and_ref(doc_info)
|
160
|
+
unless doc_info.title
|
161
|
+
@nof_missing_titles += 1
|
162
|
+
doc_info.title = "NO TITLE FOUND (#{@nof_missing_titles}) !"
|
163
|
+
end
|
233
164
|
|
234
|
-
|
235
|
-
|
236
|
-
|
165
|
+
# Manipulate the doc title if we have a doc id
|
166
|
+
title = if !doc_info.doc_id.nil? && @manage_docid
|
167
|
+
"#{doc_info.doc_id} - #{doc_info.title}"
|
168
|
+
else
|
169
|
+
doc_info.title
|
170
|
+
end
|
237
171
|
|
238
|
-
|
239
|
-
|
240
|
-
|
172
|
+
[title, "<<#{doc_info.rel_path}#,#{title}>>".encode("utf-8"),
|
173
|
+
"<<#{Giblish.to_valid_id(doc_info.title)},details>>\n".encode("utf-8")]
|
174
|
+
end
|
241
175
|
|
242
|
-
|
243
|
-
|
244
|
-
|
176
|
+
# Generate an adoc string that will display as
|
177
|
+
# DocTitle (warn) details
|
178
|
+
# Where the DocTitle and details are links to the doc itself and a section
|
179
|
+
# identified with the doc's title respectively.
|
180
|
+
def tree_entry_converted(prefix_str, doc_info)
|
181
|
+
# Get the elements of the entry
|
182
|
+
doc_title, doc_link, doc_details = format_title_and_ref doc_info
|
183
|
+
warning_label = doc_info.stderr.empty? ? "" : "(warn)"
|
184
|
+
|
185
|
+
# Calculate padding to get (warn) and details aligned between entries
|
186
|
+
padding = 70
|
187
|
+
[doc_title, prefix_str, warning_label].each {|p| padding -= p.length}
|
188
|
+
padding = 0 unless padding.positive?
|
189
|
+
"#{prefix_str} #{doc_link}#{' ' * padding}#{warning_label} #{doc_details}"
|
190
|
+
end
|
245
191
|
|
246
|
-
|
247
|
-
|
248
|
-
|
192
|
+
def tree_entry_string(level, node)
|
193
|
+
# indent 2 * level
|
194
|
+
prefix_str = " " * (level + 1)
|
195
|
+
|
196
|
+
# return only name for directories
|
197
|
+
return "#{prefix_str} #{node.name}\n" unless node.leaf?
|
198
|
+
|
199
|
+
# return links to content and details for files
|
200
|
+
# node.data is a DocInfo instance
|
201
|
+
d = node.data
|
202
|
+
if d.converted
|
203
|
+
tree_entry_converted prefix_str, d
|
204
|
+
else
|
205
|
+
# no converted file exists, show what we know
|
206
|
+
"#{prefix_str} FAIL: #{d.srcFile_utf8} <<#{d.srcFile_utf8},details>>\n"
|
207
|
+
end
|
249
208
|
end
|
250
209
|
|
251
|
-
#
|
252
|
-
|
253
|
-
|
210
|
+
# Derived classes can override this with useful info
|
211
|
+
def generate_history_info(_d)
|
212
|
+
""
|
213
|
+
end
|
254
214
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
end
|
215
|
+
def generate_detail_fail(d)
|
216
|
+
<<~FAIL_INFO
|
217
|
+
=== #{d.srcFile_utf8}
|
259
218
|
|
260
|
-
|
261
|
-
<<~FAIL_INFO
|
262
|
-
=== #{d.srcFile_utf8}
|
219
|
+
#{display_source_file(d)}
|
263
220
|
|
264
|
-
|
221
|
+
Error detail::
|
222
|
+
#{d.stderr}
|
265
223
|
|
266
|
-
|
224
|
+
''''
|
267
225
|
|
268
|
-
|
269
|
-
|
226
|
+
FAIL_INFO
|
227
|
+
end
|
270
228
|
|
271
|
-
|
229
|
+
def generate_detail(d)
|
230
|
+
# Generate detail info
|
231
|
+
purpose_str = if d.purpose_str.nil?
|
232
|
+
""
|
233
|
+
else
|
234
|
+
"Purpose::\n#{d.purpose_str}"
|
235
|
+
end
|
272
236
|
|
273
|
-
|
274
|
-
|
237
|
+
doc_id_str = if !d.doc_id.nil? && @manage_docid
|
238
|
+
"Doc id::\n_#{d.doc_id}_"
|
239
|
+
else
|
240
|
+
""
|
241
|
+
end
|
275
242
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
[[#{Giblish.to_valid_id(d.title.encode("utf-8"))}]]
|
280
|
-
=== #{d.title.encode("utf-8")}
|
243
|
+
<<~DETAIL_SRC
|
244
|
+
[[#{Giblish.to_valid_id(d.title.encode("utf-8"))}]]
|
245
|
+
=== #{d.title.encode("utf-8")}
|
281
246
|
|
282
|
-
|
247
|
+
#{doc_id_str}
|
283
248
|
|
284
|
-
|
249
|
+
#{purpose_str}
|
285
250
|
|
286
|
-
|
287
|
-
#{d.srcFile_utf8}
|
251
|
+
#{generate_conversion_info d}
|
288
252
|
|
289
|
-
|
253
|
+
#{display_source_file(d)}
|
290
254
|
|
291
|
-
|
255
|
+
#{generate_history_info d}
|
292
256
|
|
293
|
-
|
294
|
-
end
|
257
|
+
''''
|
295
258
|
|
296
|
-
|
297
|
-
root = PathTree.new
|
298
|
-
@added_docs.each do |d|
|
299
|
-
root.add_path(d.relPath.to_s, d)
|
259
|
+
DETAIL_SRC
|
300
260
|
end
|
301
261
|
|
302
|
-
|
262
|
+
def generate_details
|
263
|
+
root = PathTree.new
|
264
|
+
@processed_docs.each do |d|
|
265
|
+
root.add_path(d.rel_path.to_s, d)
|
266
|
+
end
|
303
267
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
268
|
+
details_str = "== Document details\n\n"
|
269
|
+
|
270
|
+
root.traverse_top_down do |_level, node|
|
271
|
+
details_str << if node.leaf?
|
272
|
+
d = node.data
|
273
|
+
if d.converted
|
274
|
+
generate_detail(d)
|
275
|
+
else
|
276
|
+
generate_detail_fail(d)
|
277
|
+
end
|
309
278
|
else
|
310
|
-
|
279
|
+
""
|
311
280
|
end
|
312
|
-
|
313
|
-
|
314
|
-
end
|
315
|
-
end
|
316
|
-
details_str
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
# A simple index generator that shows a table with the generated documents
|
321
|
-
class SimpleIndexBuilder < BasicIndexBuilder
|
322
|
-
def initialize(path_manager, manage_docid = false)
|
323
|
-
super path_manager, manage_docid
|
324
|
-
end
|
325
|
-
|
326
|
-
def add_doc(adoc, adoc_stderr)
|
327
|
-
super(adoc, adoc_stderr)
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
|
-
# Builds an index of the generated documents and includes some git metadata
|
332
|
-
# repository
|
333
|
-
class GitRepoIndexBuilder < BasicIndexBuilder
|
334
|
-
def initialize(path_manager, manage_docid, git_repo_root)
|
335
|
-
super path_manager, manage_docid
|
336
|
-
|
337
|
-
# initialize state variables
|
338
|
-
@git_repo_root = git_repo_root
|
339
|
-
|
340
|
-
# no repo root given...
|
341
|
-
return unless @git_repo_root
|
342
|
-
|
343
|
-
begin
|
344
|
-
# Make sure that we can "talk" to git if user feeds us
|
345
|
-
# a git repo root
|
346
|
-
@git_repo = Git.open(@git_repo_root)
|
347
|
-
rescue Exception => e
|
348
|
-
Giblog.logger.error { "No git repo! exception: #{e.message}" }
|
281
|
+
end
|
282
|
+
details_str
|
349
283
|
end
|
350
284
|
end
|
351
285
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
info.srcFile = Pathname.new(info.srcFile).relative_path_from(@git_repo_root).to_s
|
357
|
-
|
358
|
-
# Get the commit history of the doc
|
359
|
-
# (use a homegrown git log to get 'follow' flag)
|
360
|
-
gi = Giblish::GitItf.new(@git_repo_root)
|
361
|
-
gi.file_log(info.srcFile_utf8).each do |i|
|
362
|
-
h = DocInfo::DocHistory.new
|
363
|
-
h.date = i["date"]
|
364
|
-
h.message = i["message"]
|
365
|
-
h.author = i["author"]
|
366
|
-
info.history << h
|
286
|
+
# A simple index generator that shows a table with the generated documents
|
287
|
+
class SimpleIndexBuilder < BasicIndexBuilder
|
288
|
+
def initialize(processed_docs, path_manager, manage_docid = false)
|
289
|
+
super processed_docs, path_manager, manage_docid
|
367
290
|
end
|
368
291
|
end
|
369
292
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
= Document index
|
376
|
-
#{@git_repo.current_branch}
|
377
|
-
|
378
|
-
Generated by Giblish at::
|
379
|
-
|
380
|
-
#{t.strftime('%Y-%m-%d %H:%M')}
|
381
|
-
|
382
|
-
DOC_HEADER
|
383
|
-
end
|
384
|
-
|
385
|
-
def generate_history_info(d)
|
386
|
-
str = <<~HISTORY_HEADER
|
387
|
-
File history::
|
388
|
-
|
389
|
-
[cols=\"2,3,8\",options=\"header\"]
|
390
|
-
|===
|
391
|
-
|Date |Author |Message
|
392
|
-
HISTORY_HEADER
|
293
|
+
# Builds an index of the generated documents and includes some git metadata
|
294
|
+
# from the repository
|
295
|
+
class GitRepoIndexBuilder < BasicIndexBuilder
|
296
|
+
def initialize(processed_docs, path_manager, manage_docid, git_repo_root)
|
297
|
+
super processed_docs, path_manager, manage_docid
|
393
298
|
|
394
|
-
|
395
|
-
|
396
|
-
str << <<~HISTORY_ROW
|
397
|
-
|#{h.date.strftime('%Y-%m-%d')}
|
398
|
-
|#{h.author}
|
399
|
-
|#{h.message}
|
299
|
+
# no repo root given...
|
300
|
+
return unless git_repo_root
|
400
301
|
|
401
|
-
|
302
|
+
begin
|
303
|
+
# Make sure that we can "talk" to git if user feeds us
|
304
|
+
# a git repo root
|
305
|
+
@git_repo = Git.open(git_repo_root)
|
306
|
+
@git_repo_root = git_repo_root
|
307
|
+
rescue Exception => e
|
308
|
+
Giblog.logger.error {"No git repo! exception: #{e.message}"}
|
309
|
+
end
|
402
310
|
end
|
403
|
-
str << "|===\n\n"
|
404
|
-
end
|
405
|
-
end
|
406
|
-
|
407
|
-
# Builds an index page with a summary of what branches have
|
408
|
-
# been generated
|
409
|
-
class GitSummaryIndexBuilder
|
410
|
-
def initialize(repo)
|
411
|
-
@branches = []
|
412
|
-
@tags = []
|
413
|
-
@git_repo = repo
|
414
|
-
@repo_url = repo.remote.url
|
415
|
-
end
|
416
311
|
|
417
|
-
|
418
|
-
|
419
|
-
|
312
|
+
protected
|
313
|
+
# override basic version and use the relative path to the
|
314
|
+
# git repo root instead
|
315
|
+
def display_source_file(doc_info)
|
316
|
+
# Use the path relative to the git repo root as display
|
317
|
+
src_file = Pathname.
|
318
|
+
new(doc_info.src_file).
|
319
|
+
relative_path_from(@git_repo_root).to_s
|
320
|
+
<<~SRC_FILE_TXT
|
321
|
+
Source file::
|
322
|
+
#{src_file}
|
323
|
+
|
324
|
+
SRC_FILE_TXT
|
325
|
+
end
|
420
326
|
|
421
|
-
def add_tag(t)
|
422
|
-
@tags << t
|
423
|
-
end
|
424
327
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
#{generate_footer}
|
431
|
-
ADOC_SRC
|
432
|
-
end
|
328
|
+
def generate_header
|
329
|
+
t = Time.now
|
330
|
+
<<~DOC_HEADER
|
331
|
+
= Document index
|
332
|
+
#{@git_repo.current_branch}
|
433
333
|
|
434
|
-
|
334
|
+
Generated by Giblish at::
|
335
|
+
#{t.strftime('%Y-%m-%d %H:%M')}
|
435
336
|
|
436
|
-
|
437
|
-
|
438
|
-
<<~DOC_HEADER
|
439
|
-
= Document repository
|
440
|
-
From #{@repo_url}
|
337
|
+
DOC_HEADER
|
338
|
+
end
|
441
339
|
|
442
|
-
|
340
|
+
def generate_history_info(d)
|
341
|
+
str = <<~HISTORY_HEADER
|
342
|
+
File history::
|
443
343
|
|
444
|
-
|
344
|
+
[cols=\"2,3,8\",options=\"header\"]
|
345
|
+
|===
|
346
|
+
|Date |Author |Message
|
347
|
+
HISTORY_HEADER
|
445
348
|
|
446
|
-
|
447
|
-
|
349
|
+
# Generate table rows of history information
|
350
|
+
d.history.each do |h|
|
351
|
+
str << <<~HISTORY_ROW
|
352
|
+
|#{h.date.strftime('%Y-%m-%d')}
|
353
|
+
|#{h.author}
|
354
|
+
|#{h.message}
|
448
355
|
|
449
|
-
|
450
|
-
|
356
|
+
HISTORY_ROW
|
357
|
+
end
|
358
|
+
str << "|===\n\n"
|
359
|
+
end
|
451
360
|
end
|
452
361
|
|
453
|
-
|
454
|
-
|
362
|
+
# Builds an index page with a summary of what branches have
|
363
|
+
# been generated
|
364
|
+
class GitSummaryIndexBuilder
|
365
|
+
def initialize(repo, branches, tags)
|
366
|
+
@branches = branches
|
367
|
+
@tags = tags
|
368
|
+
@git_repo = repo
|
369
|
+
@repo_url = repo.remote.url
|
370
|
+
end
|
455
371
|
|
456
|
-
|
457
|
-
|
458
|
-
|
372
|
+
def source
|
373
|
+
<<~ADOC_SRC
|
374
|
+
#{generate_header}
|
375
|
+
#{generate_branch_info}
|
376
|
+
#{generate_tag_info}
|
377
|
+
#{generate_footer}
|
378
|
+
ADOC_SRC
|
379
|
+
end
|
459
380
|
|
460
|
-
|
381
|
+
private
|
461
382
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
end
|
383
|
+
def generate_header
|
384
|
+
t = Time.now
|
385
|
+
<<~DOC_HEADER
|
386
|
+
= Document repository
|
387
|
+
From #{@repo_url}
|
468
388
|
|
469
|
-
|
470
|
-
|
389
|
+
Generated by Giblish at::
|
390
|
+
#{t.strftime('%Y-%m-%d %H:%M')}
|
471
391
|
|
472
|
-
|
473
|
-
|
474
|
-
== Tags
|
392
|
+
DOC_HEADER
|
393
|
+
end
|
475
394
|
|
476
|
-
|
477
|
-
|
395
|
+
def generate_footer
|
396
|
+
""
|
397
|
+
end
|
478
398
|
|
479
|
-
|
399
|
+
def generate_branch_info
|
400
|
+
return "" if @branches.empty?
|
480
401
|
|
481
|
-
|
482
|
-
|
483
|
-
|
402
|
+
# get the branch-unique dst-dir
|
403
|
+
str = <<~BRANCH_INFO
|
404
|
+
== Branches
|
484
405
|
|
485
|
-
|
486
|
-
|link:#{dirname}/index.html[#{t.name}]
|
487
|
-
|#{t.annotated? ? t.message : "-"}
|
488
|
-
|#{t.annotated? ? t.tagger.name : "-"}
|
489
|
-
|#{t.sha[0,8]}... committed at #{c.author.date}
|
490
|
-
A_ROW
|
491
|
-
end.join("\n")
|
406
|
+
BRANCH_INFO
|
492
407
|
|
493
|
-
|
408
|
+
@branches.each do |b|
|
409
|
+
dirname = b.name.tr "/", "_"
|
410
|
+
str << " * link:#{dirname}/index.html[#{b.name}]\n"
|
411
|
+
end
|
412
|
+
str
|
413
|
+
end
|
494
414
|
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
415
|
+
def generate_tag_info
|
416
|
+
return "" if @tags.empty?
|
417
|
+
|
418
|
+
# get the branch-unique dst-dir
|
419
|
+
str = <<~TAG_INFO
|
420
|
+
== Tags
|
421
|
+
|
422
|
+
|===
|
423
|
+
|Tag |Tag comment |Creator |Tagged commit
|
424
|
+
|
425
|
+
TAG_INFO
|
426
|
+
|
427
|
+
str << @tags.collect do |t|
|
428
|
+
dirname = t.name.tr "/", "_"
|
429
|
+
c = @git_repo.gcommit(t.sha)
|
430
|
+
|
431
|
+
<<~A_ROW
|
432
|
+
|link:#{dirname}/index.html[#{t.name}]
|
433
|
+
|#{t.annotated? ? t.message : "-"}
|
434
|
+
|#{t.annotated? ? t.tagger.name : "-"}
|
435
|
+
|#{t.sha[0, 8]}... committed at #{c.author.date}
|
436
|
+
A_ROW
|
437
|
+
end.join("\n")
|
438
|
+
|
439
|
+
str << "|===\n"
|
440
|
+
|
441
|
+
# @tags.each do |t|
|
442
|
+
# dirname = t.name.tr "/", "_"
|
443
|
+
# str << " * link:#{dirname}/index.html[#{t.name}]"
|
444
|
+
# if t.annotated?
|
445
|
+
# str << "created at #{t.tagger.date} by #{t.tagger.name} with message: #{t.message}"
|
446
|
+
# end
|
447
|
+
# end
|
448
|
+
str
|
449
|
+
end
|
503
450
|
end
|
504
|
-
end
|
451
|
+
end
|