#-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. # All rights reserved. # See LICENSE.txt for permissions. #++ require 'yaml' require 'ostruct' require 'rubygems' SEPSTRING = ' .. ' # Taken from 'extensions' RubyForge project. module Enumerable def partition_by result = {} self.each do |e| value = yield e (result[value] ||= []) << e end result end end # Returns a link to the given anchor on the page. def _link(attribute, text=nil) link = "##{attribute}" %{#{text || attribute}} end def _themed_toc_more_vspace SECTIONS.each do |s| puts "\n* *#{s['name']}*" puts s['attributes'].collect { |a| ": #{_link(a, '#')} #{a}" } end end def _themed_toc_less_vspace SECTIONS.each do |s| puts "\n* *#{s['name']}:* " puts s['attributes'].collect { |a| _link(a) }.join(SEPSTRING) end end def timestamp puts "p(((. Last generated: #{Time.now.strftime '%Y-%m-%d %H:%M:%S %Z (%A)'}" end # Prints a thematic table of contents, drawing from the structure in the YAML file. def themed_toc _themed_toc_less_vspace end # Prints an alphabetical table of contents in a fairly compact yet readable way, with all # Wiki formatting included. def alpha_toc attributes = SECTIONS.collect { |s| s['attributes'] }.flatten # -> ['author', 'autorequire', ...] attr_map = attributes.partition_by { |a| a[0,1] } # -> { 'a' => ['author', ...], 'b' => ... } attributes = attr_map.collect { |letter, attrs| [letter.upcase, attrs.sort] }.sort_by { |l, _| l } # -> [ ['A', ['author', ...], ...] attributes = attributes.collect { |letter, attrs| "* *#{letter}* " << attrs.collect { |a| _link(a) }.join(SEPSTRING) } # -> [ 'A author | autorequire', 'B bindir', ...] puts attributes.join("\n") end # Print the "important" table of contents, which consists of the attributes given as # arguments. def important_toc(*attributes) puts attributes.collect { |a| _link(a) }.join(SEPSTRING) end # Returns text like "Optional; default = 'bin'", with Wiki formatting. def _metadata(attribute) type = attribute.klass || "Unknown" required = case attribute.mandatory when nil, false 'Optional' else 'Required' end default_str = case attribute.default when nil then '' when 'nil' then 'nil' when '...' then '(see below)' else attribute.default.inspect end default_str = "; default = #{default_str}" unless default_str.empty? result = sprintf "Type: %s; %s%s", type, required, default_str # result.gsub(/ /, ' ') end # Turns 'L(section)' into a link to that section. def _resolve_links(text) text.gsub(/L\((\w+)\)/) { _link($1) } end # For each attribute ('extra_rdoc_files', 'bindir', etc.), prints a section containing: # * a heading # * a line describing the attribute's type, mandatoriness, and default # * the description, usage, and notes for that attribute # * a link to the table of contents def attribute_survey heading = proc { |str| %{\n\nh3. #{str}\n\n} } subheading = proc { |str| "\n\nh4. #{str}\n\n" } pre = proc { |str| "
\n#{str}\n\n" }
para = proc { |str| str.gsub(/\n/, "\n\n") }
toclink = "\nGoto #{_link('toc', 'Table of Contents')}"
ATTRIBUTES.sort_by { |a| a['name'] }.each do |a|
a = OpenStruct.new(a)
puts "\n----\n"
puts heading[a.name]
puts _metadata(a)
puts subheading['Description']
puts _resolve_links(a.description)
puts subheading['Usage']
puts pre[a.usage.strip.gsub(/^/, ' ')]
if a.notes
puts subheading['Notes']
puts _resolve_links(para[a.notes.strip])
end
puts toclink
end
end
# Checks to see that all the attributes are documented. Warn on STDERR of any
# discrepencies.
def _check_attribute_completeness
documented_attributes = ATTRIBUTES.collect { |a| a['name'].to_s }
# ^ The attributes documented in specdoc.yaml.
coded_attributes = Gem::Specification.attribute_names.collect { |a| a.to_s }
# ^ The attributes defined in specification.rb.
missing = coded_attributes - documented_attributes
unless missing.empty?
STDERR.puts "Defined attributes that are not documented:"
STDERR.puts ' ' + missing.join(', ')
end
missing = documented_attributes - coded_attributes
unless missing.empty?
STDERR.puts "Documented attributes that are not defined:"
STDERR.puts ' ' + missing.join(', ')
end
end
# # # M A I N # # #
data = YAML.load(File.read('specdoc.yaml'))
SECTIONS = data['SECTIONS']
ATTRIBUTES = data['ATTRIBUTES'].reject { |a| a['name'] == '...' }
_check_attribute_completeness
IO.foreach('specdoc.data') do |line|
case line
when /^!(\S+)/
# A line beginning with a ! is a command. We call the method of that name.
cmd, *args = line[1..-1].split
self.send(cmd, *args)
else
puts line.chomp
end
end