lib/giblish/buildindex.rb in giblish-0.2.12 vs lib/giblish/buildindex.rb in giblish-0.3.0
- old
+ new
@@ -1,504 +1,451 @@
-#!/usr/bin/env ruby
-#
-
require "pathname"
require "git"
-require_relative "cmdline"
+
require_relative "pathtree"
require_relative "gititf"
+require_relative "docinfo"
-# Container class for bundling together the data we cache for
-# each asciidoc file we come across
-class DocInfo
- # Cache git info
- class DocHistory
- attr_accessor :date
- attr_accessor :author
- attr_accessor :message
- end
+module Giblish
- attr_accessor :converted
- attr_accessor :title
- attr_accessor :doc_id
- attr_accessor :purpose_str
- attr_accessor :status
- attr_accessor :history
- attr_accessor :error_msg
- attr_accessor :stderr
- # these two members can have encoding issues when
- # running in a mixed Windows/Linux setting.
- # that is why the explicit utf-8 read methods are
- # provided.
- attr_accessor :relPath
- attr_accessor :srcFile
+ # Base class with common functionality for all index builders
+ class BasicIndexBuilder
+ # set up the basic index building info
+ def initialize(processed_docs, path_manager, handle_docid = false)
+ @paths = path_manager
+ @nof_missing_titles = 0
+ @processed_docs = processed_docs
+ @src_str = ""
+ @manage_docid = handle_docid
+ end
- def relPath_utf8
- return nil if @relPath.nil?
- @relPath.to_s.encode("utf-8")
- end
+ def source(dep_graph_exists = false)
+ <<~DOC_STR
+ #{generate_header}
+ #{generate_tree(dep_graph_exists)}
+ #{generate_details}
+ #{generate_footer}
+ DOC_STR
+ end
- def srcFile_utf8
- return nil if @srcFile.nil?
- @srcFile.to_s.encode("utf-8")
- end
+ protected
+ # return the adoc string for displaying the source file
+ def display_source_file(doc_info)
+ <<~SRC_FILE_TXT
+ Source file::
+ #{doc_info.srcFile_utf8}
- def initialize
- @history = []
- end
+ SRC_FILE_TXT
+ end
- def to_s
- "DocInfo: title: #{@title} srcFile: #{srcFile_utf8}"
- end
-end
+ def generate_header
+ t = Time.now
+ <<~DOC_HEADER
+ = Document index
+ from #{@paths.src_root_abs}
-# Base class with common functionality for all index builders
-class BasicIndexBuilder
- # set up the basic index building info
- def initialize(path_manager, handle_docid = false)
- @paths = path_manager
- @nof_missing_titles = 0
- @added_docs = []
- @src_str = ""
- @manage_docid = handle_docid
- end
+ Generated by Giblish at::
- # creates a DocInfo instance, fills it with basic info and
- # returns the filled in instance so that derived implementations can
- # add more data
- def add_doc(adoc, adoc_stderr)
- Giblog.logger.debug { "Adding adoc: #{adoc} Asciidoctor stderr: #{adoc_stderr}" }
- Giblog.logger.debug {"Doc attributes: #{adoc.attributes}"}
+ #{t.strftime('%Y-%m-%d %H:%M')}
- info = DocInfo.new
- info.converted = true
- info.stderr = adoc_stderr
+ DOC_HEADER
+ end
- # Get the purpose info if it exists
- info.purpose_str = get_purpose_info adoc
+ def get_docid_statistics
+ largest = ""
+ clash = []
+ @processed_docs.each do |d|
+ # get the lexically largest doc id
+ largest = d.doc_id if !d.doc_id.nil? && d.doc_id > largest
- # Get the relative path beneath the root dir to the doc
- d_attr = adoc.attributes
- info.relPath = Pathname.new(
- "#{d_attr['outdir']}/#{d_attr['docname']}#{d_attr['docfilesuffix']}"
- ).relative_path_from(
- @paths.dst_root_abs
- )
+ # collect all ids in an array to find duplicates later on
+ clash << d.doc_id unless d.doc_id.nil?
+ end
+ # find the duplicate doc ids (if any)
+ duplicates = clash.select { |id| clash.count(id) > 1 }.uniq.sort
- # Get the doc id if it exists
- info.doc_id = adoc.attributes["docid"]
+ return largest,duplicates
+ end
- # Get the source file path
- info.srcFile = adoc.attributes["docfile"]
+ def generate_doc_id_info(dep_graph_exists)
+ largest,duplicates = get_docid_statistics
+ docid_info_str = if ! @manage_docid
+ ""
+ else
+ "The 'largest' document id found when resolving :docid: tags is *#{largest}*."
+ end
- # If a docid exists, set titel to docid - title if we care about
- # doc ids.
- info.title = if !info.doc_id.nil? && @manage_docid
- "#{info.doc_id} - #{adoc.doctitle}"
- else
- adoc.doctitle
- end
+ docid_warn_str = if duplicates.length.zero?
+ ""
+ else
+ "WARNING: The following document ids are used for more than one document. " +
+ "_#{duplicates.map {|id| id.to_s}.join(",") }_"
+ end
- # Cache the created DocInfo
- @added_docs << info
- info
- end
+ # include link to dependency graph if it exists
+ dep_graph_str = if dep_graph_exists
+ "_(a visual graph of document dependencies can be found " \
+ "<<./graph.adoc#,here>>)_"
+ else
+ ""
+ end
- def add_doc_fail(filepath, exception)
- info = DocInfo.new
+ if @manage_docid
+ <<~DOC_ID_INFO
+ Document id numbers::
+ The generation of this repository uses document id numbers. #{docid_info_str} #{dep_graph_str}
+
+ #{docid_warn_str}
- # the only info we have is the source file name
- info.converted = false
- info.srcFile = filepath
- info.error_msg = exception.message
+ DOC_ID_INFO
+ else
+ ""
+ end
+ end
- # Cache the DocInfo
- @added_docs << info
- info
- end
+ def generate_tree(dep_graph_exists)
+ # output tree intro
+ tree_string = <<~DOC_HEADER
+ == Document Overview
- def index_source
- <<~DOC_STR
- #{generate_header}
- #{generate_tree}
- #{generate_details}
- #{generate_footer}
- DOC_STR
- end
+ _Click on the title to open the document or on `details` to see more
+ info about the document. A `(warn)` label indicates that there were
+ warnings while converting the document from its asciidoc format._
- protected
+ #{generate_doc_id_info dep_graph_exists}
- def generate_header
- t = Time.now
- <<~DOC_HEADER
- = Document index
- from #{@paths.src_root_abs}
+ [subs=\"normal\"]
+ ----
+ DOC_HEADER
- Generated by Giblish at::
+ # build up tree of paths
+ root = PathTree.new
+ @processed_docs.each do |d|
+ root.add_path(d.rel_path.to_s, d)
+ end
- #{t.strftime('%Y-%m-%d %H:%M')}
+ # generate each tree entry string
+ root.traverse_top_down do |level, node|
+ tree_string << tree_entry_string(level, node)
+ end
- DOC_HEADER
- end
+ # generate the tree footer
+ tree_string << "\n----\n"
+ end
- def generate_footer
- ""
- end
-
- private
-
- def get_purpose_info(adoc)
- # Get the 'Purpose' section if it exists
- purpose_str = ""
- adoc.blocks.each do |section|
- next unless section.is_a?(Asciidoctor::Section) &&
- (section.level == 1) &&
- (section.name =~ /^Purpose$/)
- purpose_str = "Purpose::\n\n"
-
- # filter out 'odd' text, such as lists etc...
- section.blocks.each do |bb|
- next unless bb.is_a?(Asciidoctor::Block)
- purpose_str << "#{bb.source}\n+\n"
- end
+ def generate_footer
+ ""
end
- purpose_str
- end
- def generate_conversion_info(d)
- return "" if d.stderr.empty?
- # extract conversion warnings from asciddoctor std err
- conv_warnings = d.stderr.gsub("asciidoctor:", "\n * asciidoctor:")
- Giblog.logger.warn { "Conversion warnings: #{conv_warnings}" }
+ private
- # assemble info to index page
- <<~CONV_INFO
- Conversion info::
+ def generate_conversion_info(d)
+ return "" if d.stderr.empty?
+ # extract conversion warnings from asciddoctor std err
+ conv_warnings = d.stderr.gsub("asciidoctor:", "\n * asciidoctor:")
+ Giblog.logger.warn {"Conversion warnings: #{conv_warnings}"}
- #{conv_warnings}
- CONV_INFO
- end
+ # assemble info to index page
+ <<~CONV_INFO
+ Conversion info::
- # Private: Return adoc elements for displaying a clickable title
- # and a 'details' ref that points to a section that uses the title as an id.
- #
- # Returns [ clickableTitleStr, clickableDetailsStr ]
- def format_title_and_ref(doc_info)
- unless doc_info.title
- @nof_missing_titles += 1
- doc_info.title = "NO TITLE FOUND (#{@nof_missing_titles}) !"
+ #{conv_warnings}
+ CONV_INFO
end
- return "<<#{doc_info.relPath_utf8}#,#{doc_info.title}>>".encode("utf-8"),
- "<<#{Giblish.to_valid_id(doc_info.title)},details>>\n".encode("utf-8")
- end
- # Generate an adoc string that will display as
- # DocTitle (warn) details
- # Where the DocTitle and details are links to the doc itself and a section
- # identified with the doc's title respectively.
- def tree_entry_converted(prefix_str, doc_info)
- # Get the elements of the entry
- doc_title, doc_details = format_title_and_ref doc_info
- warning_label = doc_info.stderr.empty? ? "" : "(warn)"
+ # Private: Return adoc elements for displaying a clickable title
+ # and a 'details' ref that points to a section that uses the title as an id.
+ #
+ # Returns [ title, clickableTitleStr, clickableDetailsStr ]
+ def format_title_and_ref(doc_info)
+ unless doc_info.title
+ @nof_missing_titles += 1
+ doc_info.title = "NO TITLE FOUND (#{@nof_missing_titles}) !"
+ end
- # Calculate padding to get (warn) and details aligned between entries
- padding = 80
- [doc_info.title, prefix_str, warning_label].each { |p| padding -= p.length }
- padding = 0 unless padding.positive?
- "#{prefix_str} #{doc_title}#{' ' * padding}#{warning_label} #{doc_details}"
- end
+ # Manipulate the doc title if we have a doc id
+ title = if !doc_info.doc_id.nil? && @manage_docid
+ "#{doc_info.doc_id} - #{doc_info.title}"
+ else
+ doc_info.title
+ end
- def tree_entry_string(level, node)
- # indent 2 * level
- prefix_str = " " * (level + 1)
+ [title, "<<#{doc_info.rel_path}#,#{title}>>".encode("utf-8"),
+ "<<#{Giblish.to_valid_id(doc_info.title)},details>>\n".encode("utf-8")]
+ end
- # return only name for directories
- return "#{prefix_str} #{node.name}\n" unless node.leaf?
+ # Generate an adoc string that will display as
+ # DocTitle (warn) details
+ # Where the DocTitle and details are links to the doc itself and a section
+ # identified with the doc's title respectively.
+ def tree_entry_converted(prefix_str, doc_info)
+ # Get the elements of the entry
+ doc_title, doc_link, doc_details = format_title_and_ref doc_info
+ warning_label = doc_info.stderr.empty? ? "" : "(warn)"
- # return links to content and details for files
- d = node.data
- if d.converted
- tree_entry_converted prefix_str, d
- else
- # no converted file exists, show what we know
- "#{prefix_str} FAIL: #{d.srcFile_utf8} <<#{d.srcFile_utf8},details>>\n"
+ # Calculate padding to get (warn) and details aligned between entries
+ padding = 70
+ [doc_title, prefix_str, warning_label].each {|p| padding -= p.length}
+ padding = 0 unless padding.positive?
+ "#{prefix_str} #{doc_link}#{' ' * padding}#{warning_label} #{doc_details}"
end
- end
- def generate_tree
- # build up tree of paths
- root = PathTree.new
- @added_docs.each do |d|
- root.add_path(d.relPath.to_s, d)
- end
+ def tree_entry_string(level, node)
+ # indent 2 * level
+ prefix_str = " " * (level + 1)
- # output tree intro
- tree_string = <<~DOC_HEADER
- == Document Overview
+ # return only name for directories
+ return "#{prefix_str} #{node.name}\n" unless node.leaf?
- _Click on the title to open the document or on `details` to see more
- info about the document. A `(warn)` label indicates that there were
- warnings while converting the document._
+ # return links to content and details for files
+ # node.data is a DocInfo instance
+ d = node.data
+ if d.converted
+ tree_entry_converted prefix_str, d
+ else
+ # no converted file exists, show what we know
+ "#{prefix_str} FAIL: #{d.srcFile_utf8} <<#{d.srcFile_utf8},details>>\n"
+ end
+ end
- [subs=\"normal\"]
- ----
- DOC_HEADER
-
- # generate each tree entry string
- root.traverse_top_down do |level, node|
- tree_string << tree_entry_string(level, node)
+ # Derived classes can override this with useful info
+ def generate_history_info(_d)
+ ""
end
- # generate the tree footer
- tree_string << "\n----\n"
- end
+ def generate_detail_fail(d)
+ <<~FAIL_INFO
+ === #{d.srcFile_utf8}
- # Derived classes can override this with useful info
- def generate_history_info(_d)
- ""
- end
+ #{display_source_file(d)}
- def generate_detail_fail(d)
- <<~FAIL_INFO
- === #{d.srcFile_utf8}
+ Error detail::
+ #{d.stderr}
- Source file::
+ ''''
- #{d.srcFile_utf8}
+ FAIL_INFO
+ end
- Error detail::
- #{d.stderr}
+ def generate_detail(d)
+ # Generate detail info
+ purpose_str = if d.purpose_str.nil?
+ ""
+ else
+ "Purpose::\n#{d.purpose_str}"
+ end
- ''''
+ doc_id_str = if !d.doc_id.nil? && @manage_docid
+ "Doc id::\n_#{d.doc_id}_"
+ else
+ ""
+ end
- FAIL_INFO
- end
+ <<~DETAIL_SRC
+ [[#{Giblish.to_valid_id(d.title.encode("utf-8"))}]]
+ === #{d.title.encode("utf-8")}
- def generate_detail(d)
- # Generate detail info
- <<~DETAIL_SRC
- [[#{Giblish.to_valid_id(d.title.encode("utf-8"))}]]
- === #{d.title.encode("utf-8")}
+ #{doc_id_str}
- #{d.purpose_str}
+ #{purpose_str}
- #{generate_conversion_info d}
+ #{generate_conversion_info d}
- Source file::
- #{d.srcFile_utf8}
+ #{display_source_file(d)}
- #{generate_history_info d}
+ #{generate_history_info d}
- ''''
+ ''''
- DETAIL_SRC
- end
-
- def generate_details
- root = PathTree.new
- @added_docs.each do |d|
- root.add_path(d.relPath.to_s, d)
+ DETAIL_SRC
end
- details_str = "== Document details\n\n"
+ def generate_details
+ root = PathTree.new
+ @processed_docs.each do |d|
+ root.add_path(d.rel_path.to_s, d)
+ end
- root.traverse_top_down do |_level, node|
- details_str << if node.leaf?
- d = node.data
- if d.converted
- generate_detail(d)
+ details_str = "== Document details\n\n"
+
+ root.traverse_top_down do |_level, node|
+ details_str << if node.leaf?
+ d = node.data
+ if d.converted
+ generate_detail(d)
+ else
+ generate_detail_fail(d)
+ end
else
- generate_detail_fail(d)
+ ""
end
- else
- ""
- end
+ end
+ details_str
end
- details_str
end
-end
-# A simple index generator that shows a table with the generated documents
-class SimpleIndexBuilder < BasicIndexBuilder
- def initialize(path_manager, manage_docid = false)
- super path_manager, manage_docid
+ # A simple index generator that shows a table with the generated documents
+ class SimpleIndexBuilder < BasicIndexBuilder
+ def initialize(processed_docs, path_manager, manage_docid = false)
+ super processed_docs, path_manager, manage_docid
+ end
end
- def add_doc(adoc, adoc_stderr)
- super(adoc, adoc_stderr)
- end
-end
+ # Builds an index of the generated documents and includes some git metadata
+ # from the repository
+ class GitRepoIndexBuilder < BasicIndexBuilder
+ def initialize(processed_docs, path_manager, manage_docid, git_repo_root)
+ super processed_docs, path_manager, manage_docid
-# Builds an index of the generated documents and includes some git metadata
-# repository
-class GitRepoIndexBuilder < BasicIndexBuilder
- def initialize(path_manager, manage_docid, git_repo_root)
- super path_manager, manage_docid
+ # no repo root given...
+ return unless git_repo_root
- # initialize state variables
- @git_repo_root = git_repo_root
-
- # no repo root given...
- return unless @git_repo_root
-
- begin
- # Make sure that we can "talk" to git if user feeds us
- # a git repo root
- @git_repo = Git.open(@git_repo_root)
- rescue Exception => e
- Giblog.logger.error { "No git repo! exception: #{e.message}" }
+ begin
+ # Make sure that we can "talk" to git if user feeds us
+ # a git repo root
+ @git_repo = Git.open(git_repo_root)
+ @git_repo_root = git_repo_root
+ rescue Exception => e
+ Giblog.logger.error {"No git repo! exception: #{e.message}"}
+ end
end
- end
- def add_doc(adoc, adoc_stderr)
- info = super(adoc, adoc_stderr)
+ protected
+ # override basic version and use the relative path to the
+ # git repo root instead
+ def display_source_file(doc_info)
+ # Use the path relative to the git repo root as display
+ src_file = Pathname.
+ new(doc_info.src_file).
+ relative_path_from(@git_repo_root).to_s
+ <<~SRC_FILE_TXT
+ Source file::
+ #{src_file}
- # Redefine the srcFile to mean the relative path to the git repo root
- info.srcFile = Pathname.new(info.srcFile).relative_path_from(@git_repo_root).to_s
-
- # Get the commit history of the doc
- # (use a homegrown git log to get 'follow' flag)
- gi = Giblish::GitItf.new(@git_repo_root)
- gi.file_log(info.srcFile_utf8).each do |i|
- h = DocInfo::DocHistory.new
- h.date = i["date"]
- h.message = i["message"]
- h.author = i["author"]
- info.history << h
+ SRC_FILE_TXT
end
- end
- protected
- def generate_header
- t = Time.now
- <<~DOC_HEADER
- = Document index
- #{@git_repo.current_branch}
+ def generate_header
+ t = Time.now
+ <<~DOC_HEADER
+ = Document index
+ #{@git_repo.current_branch}
- Generated by Giblish at::
+ Generated by Giblish at::
+ #{t.strftime('%Y-%m-%d %H:%M')}
- #{t.strftime('%Y-%m-%d %H:%M')}
+ DOC_HEADER
+ end
- DOC_HEADER
- end
+ def generate_history_info(d)
+ str = <<~HISTORY_HEADER
+ File history::
- def generate_history_info(d)
- str = <<~HISTORY_HEADER
- File history::
+ [cols=\"2,3,8\",options=\"header\"]
+ |===
+ |Date |Author |Message
+ HISTORY_HEADER
- [cols=\"2,3,8\",options=\"header\"]
- |===
- |Date |Author |Message
- HISTORY_HEADER
+ # Generate table rows of history information
+ d.history.each do |h|
+ str << <<~HISTORY_ROW
+ |#{h.date.strftime('%Y-%m-%d')}
+ |#{h.author}
+ |#{h.message}
- # Generate table rows of history information
- d.history.each do |h|
- str << <<~HISTORY_ROW
- |#{h.date.strftime('%Y-%m-%d')}
- |#{h.author}
- |#{h.message}
-
- HISTORY_ROW
+ HISTORY_ROW
+ end
+ str << "|===\n\n"
end
- str << "|===\n\n"
end
-end
-# Builds an index page with a summary of what branches have
-# been generated
-class GitSummaryIndexBuilder
- def initialize(repo)
- @branches = []
- @tags = []
- @git_repo = repo
- @repo_url = repo.remote.url
- end
+ # Builds an index page with a summary of what branches have
+ # been generated
+ class GitSummaryIndexBuilder
+ def initialize(repo, branches, tags)
+ @branches = branches
+ @tags = tags
+ @git_repo = repo
+ @repo_url = repo.remote.url
+ end
- def add_branch(b)
- @branches << b
- end
+ def source
+ <<~ADOC_SRC
+ #{generate_header}
+ #{generate_branch_info}
+ #{generate_tag_info}
+ #{generate_footer}
+ ADOC_SRC
+ end
- def add_tag(t)
- @tags << t
- end
+ private
- def index_source
- <<~ADOC_SRC
- #{generate_header}
- #{generate_branch_info}
- #{generate_tag_info}
- #{generate_footer}
- ADOC_SRC
- end
+ def generate_header
+ t = Time.now
+ <<~DOC_HEADER
+ = Document repository
+ From #{@repo_url}
- private
+ Generated by Giblish at::
+ #{t.strftime('%Y-%m-%d %H:%M')}
- def generate_header
- t = Time.now
- <<~DOC_HEADER
- = Document repository
- From #{@repo_url}
+ DOC_HEADER
+ end
- Generated by Giblish at::
+ def generate_footer
+ ""
+ end
- #{t.strftime('%Y-%m-%d %H:%M')}
+ def generate_branch_info
+ return "" if @branches.empty?
- DOC_HEADER
- end
+ # get the branch-unique dst-dir
+ str = <<~BRANCH_INFO
+ == Branches
- def generate_footer
- ""
- end
+ BRANCH_INFO
- def generate_branch_info
- return "" if @branches.empty?
-
- # get the branch-unique dst-dir
- str = <<~BRANCH_INFO
- == Branches
-
- BRANCH_INFO
-
- @branches.each do |b|
- dirname = b.name.tr "/", "_"
- str << " * link:#{dirname}/index.html[#{b.name}]\n"
+ @branches.each do |b|
+ dirname = b.name.tr "/", "_"
+ str << " * link:#{dirname}/index.html[#{b.name}]\n"
+ end
+ str
end
- str
- end
- def generate_tag_info
- return "" if @tags.empty?
+ def generate_tag_info
+ return "" if @tags.empty?
- # get the branch-unique dst-dir
- str = <<~TAG_INFO
- == Tags
+ # get the branch-unique dst-dir
+ str = <<~TAG_INFO
+ == Tags
+
+ |===
+ |Tag |Tag comment |Creator |Tagged commit
- |===
- |Tag |Tag comment |Creator |Tagged commit
+ TAG_INFO
- TAG_INFO
+ str << @tags.collect do |t|
+ dirname = t.name.tr "/", "_"
+ c = @git_repo.gcommit(t.sha)
- str << @tags.collect do |t|
- dirname = t.name.tr "/", "_"
- c = @git_repo.gcommit(t.sha)
+ <<~A_ROW
+ |link:#{dirname}/index.html[#{t.name}]
+ |#{t.annotated? ? t.message : "-"}
+ |#{t.annotated? ? t.tagger.name : "-"}
+ |#{t.sha[0, 8]}... committed at #{c.author.date}
+ A_ROW
+ end.join("\n")
- <<~A_ROW
- |link:#{dirname}/index.html[#{t.name}]
- |#{t.annotated? ? t.message : "-"}
- |#{t.annotated? ? t.tagger.name : "-"}
- |#{t.sha[0,8]}... committed at #{c.author.date}
- A_ROW
- end.join("\n")
+ str << "|===\n"
- str << "|===\n"
-
- # @tags.each do |t|
- # dirname = t.name.tr "/", "_"
- # str << " * link:#{dirname}/index.html[#{t.name}]"
- # if t.annotated?
- # str << "created at #{t.tagger.date} by #{t.tagger.name} with message: #{t.message}"
- # end
- # end
- str
+ # @tags.each do |t|
+ # dirname = t.name.tr "/", "_"
+ # str << " * link:#{dirname}/index.html[#{t.name}]"
+ # if t.annotated?
+ # str << "created at #{t.tagger.date} by #{t.tagger.name} with message: #{t.message}"
+ # end
+ # end
+ str
+ end
end
-end
+end
\ No newline at end of file