class RDoc::Generator::Darkfish

Darkfish RDoc HTML 生成器

$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $

作者

贡献者

许可证

版权所有 © 2007, 2008, Michael Granger。 保留所有权利。

如果满足以下条件,则允许以源代码和二进制形式进行再分发和使用,无论是否修改:

  • 源代码的再分发必须保留上述版权声明、此条件列表和以下免责声明。

  • 以二进制形式的再分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、此条件列表和以下免责声明。

  • 未经事先明确书面许可,不得使用作者/们的姓名或项目贡献者的姓名来认可或推广由此软件衍生的产品。

本软件由版权所有者和贡献者“按原样”提供,并且不承担任何明示或暗示的保证,包括但不限于对适销性和特定用途适用性的暗示保证。在任何情况下,版权所有者或贡献者均不对任何直接、间接、偶然、特殊、惩戒性或后果性损害(包括但不限于采购替代商品或服务;使用、数据或利润损失;或业务中断)承担责任,无论其原因以及任何责任理论,无论是合同、严格责任还是侵权行为(包括疏忽或其他),均以任何方式因使用本软件而引起,即使已被告知可能发生此类损害。

归属

Darkfish 使用 Mark James 的 Silk 图标集。

常量

DESCRIPTION

此生成器的描述

GENERATOR_DIR

此文件父目录的路径。用于查找模板和其他资源。

SVNID_PATTERN

%q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $“

VERSION

发布版本

属性

asset_rel_path[RW]

样式表和 javascript 的相对路径。默认情况下,此路径与 rel_prefix 相同。

base_dir[R]

要将文件生成到的路径,与选项中的 --op 组合得到完整路径。

classes[R]

此生成器使用的类和模块,不一定显示。另请参阅 modsort

dry_run[RW]

dry_run 为 true 时,将不会写入任何文件。

file_output[RW]

当为 false 时,生成方法返回 String 而不是写入文件。默认为 true。

files[R]

此生成器显示的文件

json_index[R]

Darkfish 生成器的 JSON 索引生成器

methods[R]

此生成器显示的方法

modsort[R]

此生成器显示的类和模块的排序列表

outputdir[R]

输出目录

store[R]

作为生成内容源的 RDoc::Store

公共类方法

new(store, options) 点击切换源代码

在开始之前初始化一些实例变量

# File rdoc/generator/darkfish.rb, line 159
def initialize store, options
  @store   = store
  @options = options

  @asset_rel_path = ''
  @base_dir       = Pathname.pwd.expand_path
  @dry_run        = @options.dry_run
  @file_output    = true
  @template_dir   = Pathname.new options.template_dir
  @template_cache = {}

  @classes = nil
  @context = nil
  @files   = nil
  @methods = nil
  @modsort = nil

  @json_index = RDoc::Generator::JsonIndex.new self, options
end

公共实例方法

assemble_template(body_file) 点击切换源代码

从其组件和 body_file 创建模板。

为了向后兼容,如果 body_file 包含“<html”,则直接使用主体。

# File rdoc/generator/darkfish.rb, line 675
  def assemble_template body_file
    body = body_file.read
    return body if body =~ /<html/

    head_file = @template_dir + '_head.rhtml'

    <<-TEMPLATE
<!DOCTYPE html>

<html>
<head>
#{head_file.read}

#{body}
    TEMPLATE
  end
class_dir() 点击切换源代码

生成的类 HTML 文件相对于输出目录所在的目录。

# File rdoc/generator/darkfish.rb, line 191
def class_dir
  nil
end
copy_static() 点击切换源代码

将静态文件从 static_path 复制到输出目录

# File rdoc/generator/darkfish.rb, line 265
def copy_static
  return if @options.static_path.empty?

  fu_options = { :verbose => $DEBUG_RDOC, :noop => @dry_run }

  @options.static_path.each do |path|
    unless File.directory? path then
      FileUtils.install path, @outputdir, **fu_options.merge(:mode => 0644)
      next
    end

    Dir.chdir path do
      Dir[File.join('**', '*')].each do |entry|
        dest_file = @outputdir + entry

        if File.directory? entry then
          FileUtils.mkdir_p entry, **fu_options
        else
          FileUtils.install entry, dest_file, **fu_options.merge(:mode => 0644)
        end
      end
    end
  end
end
debug_msg(*msg) 点击切换源代码

如果启用了调试,则输出进度信息

# File rdoc/generator/darkfish.rb, line 182
def debug_msg *msg
  return unless $DEBUG_RDOC
  $stderr.puts(*msg)
end
excerpt(content) 点击切换源代码

返回内容的摘录,用于元描述标签

# File rdoc/generator/darkfish.rb, line 784
def excerpt(content)
  text = case content
  when RDoc::Comment
    content.text
  when RDoc::Markup::Document
    # This case is for page files that are not markdown nor rdoc
    # We convert them to markdown for now as it's easier to extract the text
    formatter = RDoc::Markup::ToMarkdown.new
    formatter.start_accepting
    formatter.accept_document(content)
    formatter.end_accepting
  else
    content
  end

  # Match from a capital letter to the first period, discarding any links, so
  # that we don't end up matching badges in the README
  first_paragraph_match = text.match(/[A-Z][^\.:\/]+\./)
  return text[0...150].gsub(/\n/, " ").squeeze(" ") unless first_paragraph_match

  extracted_text = first_paragraph_match[0]
  second_paragraph = first_paragraph_match.post_match.match(/[A-Z][^\.:\/]+\./)
  extracted_text << " " << second_paragraph[0] if second_paragraph

  extracted_text[0...150].gsub(/\n/, " ").squeeze(" ")
end
file_dir() 点击切换源代码

生成的类 HTML 文件相对于输出目录所在的目录。

# File rdoc/generator/darkfish.rb, line 199
def file_dir
  nil
end
gen_sub_directories() 点击切换源代码

如果生成的文档尚不存在,则创建它们所在的目录。

# File rdoc/generator/darkfish.rb, line 207
def gen_sub_directories
  @outputdir.mkpath
end
generate() 点击切换源代码

基于包含提取信息的 TopLevel 对象数组构建初始索引并输出对象。

# File rdoc/generator/darkfish.rb, line 241
def generate
  setup

  write_style_sheet
  generate_index
  generate_class_files
  generate_file_files
  generate_table_of_contents
  @json_index.generate
  @json_index.generate_gzipped

  copy_static

rescue => e
  debug_msg "%s: %s\n  %s" % [
    e.class.name, e.message, e.backtrace.join("\n  ")
  ]

  raise
end
generate_ancestor_list(ancestors, klass) 点击切换源代码
# File rdoc/generator/darkfish.rb, line 811
def generate_ancestor_list(ancestors, klass)
  return '' if ancestors.empty?

  ancestor = ancestors.shift
  content = +'<ul><li>'

  if ancestor.is_a?(RDoc::NormalClass)
    content << "<a href=\"#{klass.aref_to ancestor.path}\">#{ancestor.full_name}</a>"
  else
    content << ancestor.to_s
  end

  # Recursively call the method for the remaining ancestors
  content << generate_ancestor_list(ancestors, klass)

  content << '</li></ul>'
end
generate_class(klass, template_file = nil) 点击切换源代码

klass 生成一个类文件

# File rdoc/generator/darkfish.rb, line 337
def generate_class klass, template_file = nil
  setup

  current = klass

  template_file ||= @template_dir + 'class.rhtml'

  debug_msg "  working on %s (%s)" % [klass.full_name, klass.path]
  out_file   = @outputdir + klass.path
  rel_prefix = @outputdir.relative_path_from out_file.dirname
  search_index_rel_prefix = rel_prefix
  search_index_rel_prefix += @asset_rel_path if @file_output

  asset_rel_prefix = rel_prefix + @asset_rel_path
  svninfo          = get_svninfo(current)

  @title = "#{klass.type} #{klass.full_name} - #{@options.title}"

  debug_msg "  rendering #{out_file}"
  render_template template_file, out_file do |io|
    here = binding
    # suppress 1.9.3 warning
    here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
    here.local_variable_set(:svninfo, svninfo)
    here
  end
end
generate_class_files() 点击切换源代码

为每个类和模块生成一个文档文件

# File rdoc/generator/darkfish.rb, line 368
def generate_class_files
  setup

  template_file = @template_dir + 'class.rhtml'
  template_file = @template_dir + 'classpage.rhtml' unless
    template_file.exist?
  return unless template_file.exist?
  debug_msg "Generating class documentation in #{@outputdir}"

  current = nil

  @classes.each do |klass|
    current = klass

    generate_class klass, template_file
  end
rescue => e
  error = RDoc::Error.new \
    "error generating #{current.path}: #{e.message} (#{e.class})"
  error.set_backtrace e.backtrace

  raise error
end
generate_file_files() 点击切换源代码

为每个文件生成一个文档文件

# File rdoc/generator/darkfish.rb, line 395
def generate_file_files
  setup

  page_file     = @template_dir + 'page.rhtml'
  fileinfo_file = @template_dir + 'fileinfo.rhtml'

  # for legacy templates
  filepage_file = @template_dir + 'filepage.rhtml' unless
    page_file.exist? or fileinfo_file.exist?

  return unless
    page_file.exist? or fileinfo_file.exist? or filepage_file.exist?

  debug_msg "Generating file documentation in #{@outputdir}"

  out_file = nil
  current = nil

  @files.each do |file|
    current = file

    if file.text? and page_file.exist? then
      generate_page file
      next
    end

    template_file = nil
    out_file = @outputdir + file.path
    debug_msg "  working on %s (%s)" % [file.full_name, out_file]
    rel_prefix = @outputdir.relative_path_from out_file.dirname
    search_index_rel_prefix = rel_prefix
    search_index_rel_prefix += @asset_rel_path if @file_output

    asset_rel_prefix = rel_prefix + @asset_rel_path

    unless filepage_file then
      if file.text? then
        next unless page_file.exist?
        template_file = page_file
        @title = file.page_name
      else
        next unless fileinfo_file.exist?
        template_file = fileinfo_file
        @title = "File: #{file.base_name}"
      end
    end

    @title += " - #{@options.title}"
    template_file ||= filepage_file

    render_template template_file, out_file do |io|
      here = binding
      # suppress 1.9.3 warning
      here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
      here.local_variable_set(:current, current)
      here
    end
  end
rescue => e
  error =
    RDoc::Error.new "error generating #{out_file}: #{e.message} (#{e.class})"
  error.set_backtrace e.backtrace

  raise error
end
generate_index() 点击切换源代码

生成一个索引页面,其中列出所有已记录的类。

# File rdoc/generator/darkfish.rb, line 303
def generate_index
  setup

  template_file = @template_dir + 'index.rhtml'
  return unless template_file.exist?

  debug_msg "Rendering the index page..."

  out_file = @base_dir + @options.op_dir + 'index.html'
  rel_prefix = @outputdir.relative_path_from out_file.dirname
  search_index_rel_prefix = rel_prefix
  search_index_rel_prefix += @asset_rel_path if @file_output

  asset_rel_prefix = rel_prefix + @asset_rel_path

  @title = @options.title

  render_template template_file, out_file do |io|
    here = binding
    # suppress 1.9.3 warning
    here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
    here
  end
rescue => e
  error = RDoc::Error.new \
    "error generating index.html: #{e.message} (#{e.class})"
  error.set_backtrace e.backtrace

  raise error
end
generate_page(file) 点击切换源代码

file 生成一个页面文件

# File rdoc/generator/darkfish.rb, line 464
def generate_page file
  setup

  template_file = @template_dir + 'page.rhtml'

  out_file = @outputdir + file.path
  debug_msg "  working on %s (%s)" % [file.full_name, out_file]
  rel_prefix = @outputdir.relative_path_from out_file.dirname
  search_index_rel_prefix = rel_prefix
  search_index_rel_prefix += @asset_rel_path if @file_output

  current          = file
  asset_rel_prefix = rel_prefix + @asset_rel_path

  @title = "#{file.page_name} - #{@options.title}"

  debug_msg "  rendering #{out_file}"
  render_template template_file, out_file do |io|
    here = binding
    # suppress 1.9.3 warning
    here.local_variable_set(:current, current)
    here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
    here
  end
end
generate_servlet_not_found(message) 点击切换源代码

RDoc servlet 生成 404 页面

# File rdoc/generator/darkfish.rb, line 493
def generate_servlet_not_found message
  setup

  template_file = @template_dir + 'servlet_not_found.rhtml'
  return unless template_file.exist?

  debug_msg "Rendering the servlet 404 Not Found page..."

  rel_prefix = rel_prefix = ''
  search_index_rel_prefix = rel_prefix
  search_index_rel_prefix += @asset_rel_path if @file_output

  asset_rel_prefix = ''

  @title = 'Not Found'

  render_template template_file do |io|
    here = binding
    # suppress 1.9.3 warning
    here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
    here
  end
rescue => e
  error = RDoc::Error.new \
    "error generating servlet_not_found: #{e.message} (#{e.class})"
  error.set_backtrace e.backtrace

  raise error
end
generate_servlet_root(installed) 点击切换源代码

RDoc servlet 生成 servlet 根页面

# File rdoc/generator/darkfish.rb, line 526
def generate_servlet_root installed
  setup

  template_file = @template_dir + 'servlet_root.rhtml'
  return unless template_file.exist?

  debug_msg 'Rendering the servlet root page...'

  rel_prefix = '.'
  asset_rel_prefix = rel_prefix
  search_index_rel_prefix = asset_rel_prefix
  search_index_rel_prefix += @asset_rel_path if @file_output

  @title = 'Local RDoc Documentation'

  render_template template_file do |io| binding end
rescue => e
  error = RDoc::Error.new \
    "error generating servlet_root: #{e.message} (#{e.class})"
  error.set_backtrace e.backtrace

  raise error
end
generate_table_of_contents() 点击切换源代码

生成一个索引页面,其中列出所有已记录的类。

# File rdoc/generator/darkfish.rb, line 553
def generate_table_of_contents
  setup

  template_file = @template_dir + 'table_of_contents.rhtml'
  return unless template_file.exist?

  debug_msg "Rendering the Table of Contents..."

  out_file = @outputdir + 'table_of_contents.html'
  rel_prefix = @outputdir.relative_path_from out_file.dirname
  search_index_rel_prefix = rel_prefix
  search_index_rel_prefix += @asset_rel_path if @file_output

  asset_rel_prefix = rel_prefix + @asset_rel_path

  @title = "Table of Contents - #{@options.title}"

  render_template template_file, out_file do |io|
    here = binding
    # suppress 1.9.3 warning
    here.local_variable_set(:asset_rel_prefix, asset_rel_prefix)
    here
  end
rescue => e
  error = RDoc::Error.new \
    "error generating table_of_contents.html: #{e.message} (#{e.class})"
  error.set_backtrace e.backtrace

  raise error
end
get_sorted_module_list(classes) 点击切换源代码

返回按显着性排序,然后按名称排序的已记录模块列表。

# File rdoc/generator/darkfish.rb, line 294
def get_sorted_module_list classes
  classes.select do |klass|
    klass.display?
  end.sort
end
get_svninfo(klass) 点击切换源代码

尝试从第一个值看起来像 Subversion Id 标记的常量中提取 Subversion 信息。如果找不到匹配的常量,则返回空哈希。

# File rdoc/generator/darkfish.rb, line 652
def get_svninfo klass
  constants = klass.constants or return {}

  constants.find { |c| c.value =~ SVNID_PATTERN } or return {}

  filename, rev, date, time, committer = $~.captures
  commitdate = Time.parse "#{date} #{time}"

  return {
    :filename    => filename,
    :rev         => Integer(rev),
    :commitdate  => commitdate,
    :commitdelta => time_delta_string(Time.now - commitdate),
    :committer   => committer,
  }
end
render(file_name) 点击切换源代码

呈现相对于模板目录的 file_name 中包含的 ERb,并根据当前上下文返回结果。

# File rdoc/generator/darkfish.rb, line 696
def render file_name
  template_file = @template_dir + file_name

  template = template_for template_file, false, RDoc::ERBPartial

  template.filename = template_file.to_s

  template.result @context
end
render_template(template_file, out_file = nil) { |io| ... } 点击切换源代码

加载并呈现给定 template_file 中的 erb 模板,并将其写入 out_file

template_fileout_file 都应该是 Pathname 类对象。

将产生一个 io,必须由调用者绑定捕获。

# File rdoc/generator/darkfish.rb, line 714
def render_template template_file, out_file = nil # :yield: io
  io_output = out_file && !@dry_run && @file_output
  erb_klass = io_output ? RDoc::ERBIO : ERB

  template = template_for template_file, true, erb_klass

  if io_output then
    debug_msg "Outputting to %s" % [out_file.expand_path]

    out_file.dirname.mkpath
    out_file.open 'w', 0644 do |io|
      io.set_encoding @options.encoding

      @context = yield io

      template_result template, @context, template_file
    end
  else
    @context = yield nil

    output = template_result template, @context, template_file

    debug_msg "  would have written %d characters to %s" % [
      output.length, out_file.expand_path
    ] if @dry_run

    output
  end
end
setup() 点击切换源代码

准备从当前目录生成输出

# File rdoc/generator/darkfish.rb, line 604
def setup
  return if instance_variable_defined? :@outputdir

  @outputdir = Pathname.new(@options.op_dir).expand_path @base_dir

  return unless @store

  @classes = @store.all_classes_and_modules.sort
  @files   = @store.all_files.sort
  @methods = @classes.flat_map { |m| m.method_list }.sort
  @modsort = get_sorted_module_list @classes
end
template_for(file, page = true, klass = ERB) 点击切换源代码

检索 file 的缓存模板(如果存在),或填充缓存。

# File rdoc/generator/darkfish.rb, line 761
def template_for file, page = true, klass = ERB
  template = @template_cache[file]

  return template if template

  if page then
    template = assemble_template file
    erbout = 'io'
  else
    template = file.read
    template = template.encode @options.encoding

    file_var = File.basename(file).sub(/\..*/, '')

    erbout = "_erbout_#{file_var}"
  end

  template = klass.new template, trim_mode: '-', eoutvar: erbout
  @template_cache[file] = template
  template
end
template_result(template, context, template_file) 点击切换源代码

使用 contexttemplate 创建结果。如果引发错误,Pathname template_file 将指示发生错误的文件。

# File rdoc/generator/darkfish.rb, line 748
def template_result template, context, template_file
  template.filename = template_file.to_s
  template.result context
rescue NoMethodError => e
  raise RDoc::Error, "Error while evaluating %s: %s" % [
    template_file.expand_path,
    e.message,
  ], e.backtrace
end
time_delta_string(seconds) 点击切换源代码

返回一个字符串,该字符串以人类容易理解的方式描述给定秒数的时间量。

# File rdoc/generator/darkfish.rb, line 621
def time_delta_string seconds
  return 'less than a minute'          if seconds < 60
  return "#{seconds / 60} minute#{seconds / 60 == 1 ? '' : 's'}" if
                                          seconds < 3000     # 50 minutes
  return 'about one hour'              if seconds < 5400     # 90 minutes
  return "#{seconds / 3600} hours"     if seconds < 64800    # 18 hours
  return 'one day'                     if seconds < 86400    #  1 day
  return 'about one day'               if seconds < 172800   #  2 days
  return "#{seconds / 86400} days"     if seconds < 604800   #  1 week
  return 'about one week'              if seconds < 1209600  #  2 week
  return "#{seconds / 604800} weeks"   if seconds < 7257600  #  3 months
  return "#{seconds / 2419200} months" if seconds < 31536000 #  1 year
  return "#{seconds / 31536000} years"
end
write_style_sheet() 点击切换源代码

将样式表复制到输出目录中的适当位置。

# File rdoc/generator/darkfish.rb, line 215
def write_style_sheet
  debug_msg "Copying static files"
  options = { :verbose => $DEBUG_RDOC, :noop => @dry_run }

  BUILTIN_STYLE_ITEMS.each do |item|
    install_rdoc_static_file @template_dir + item, "./#{item}", options
  end

  unless @options.template_stylesheets.empty?
    FileUtils.cp @options.template_stylesheets, '.', **options
  end

  Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
    next if File.directory? path
    next if File.basename(path) =~ /^\./

    dst = Pathname.new(path).relative_path_from @template_dir

    install_rdoc_static_file @template_dir + path, dst, options
  end
end