require 'fileutils' require 'rdoc/generator' require 'rdoc/markup/to_html' ## # We're responsible for generating all the HTML files from the object tree # defined in code_objects.rb. We generate: # # [files] an html file for each input file given. These # input files appear as objects of class # TopLevel # # [classes] an html file for each class or module encountered. # These classes are not grouped by file: if a file # contains four classes, we'll generate an html # file for the file itself, and four html files # for the individual classes. # # [indices] we generate three indices for files, classes, # and methods. These are displayed in a browser # like window with three index panes across the # top and the selected description below # # Method descriptions appear in whatever entity (file, class, or module) that # contains them. # # We generate files in a structure below a specified subdirectory, normally # +doc+. # # opdir # | # |___ files # | |__ per file summaries # | # |___ classes # |__ per class/module descriptions # # HTML is generated using the Template class. class RDoc::Generator::HTML include RDoc::Generator::MarkUp ## # Generator may need to return specific subclasses depending on the # options they are passed. Because of this we create them using a factory def self.for(options) RDoc::Generator::AllReferences.reset RDoc::Generator::Method.reset if options.all_one_file RDoc::Generator::HTMLInOne.new options else new options end end class << self protected :new end ## # Set up a new HTML generator. Basically all we do here is load up the # correct output temlate def initialize(options) #:not-new: @options = options load_html_template end ## # Build the initial indices and output objects # based on an array of TopLevel objects containing # the extracted information. def generate(toplevels) @toplevels = toplevels @files = [] @classes = [] write_style_sheet gen_sub_directories build_indices generate_html end private ## # Load up the HTML template specified in the options. # If the template name contains a slash, use it literally def load_html_template # # If the template is not a path, first look for it # in rdoc's HTML template directory. Perhaps this behavior should # be reversed (first try to include the template and, only if that # fails, try to include it in the default template directory). # One danger with reversing the behavior, however, is that # if something like require 'html' could load up an # unrelated file in the standard library or in a gem. # template = @options.template unless template =~ %r{/|\\} then template = File.join('rdoc', 'generator', @options.generator.key, template) end begin require template @template = self.class.const_get @options.template.upcase @options.template_class = @template rescue LoadError => e # # The template did not exist in the default template directory, so # see if require can find the template elsewhere (in a gem, for # instance). # if(e.message[template] && template != @options.template) template = @options.template retry end $stderr.puts "Could not find HTML template '#{template}': #{e.message}" exit 99 end end ## # Write out the style sheet used by the main frames def write_style_sheet return unless @template.constants.include? :STYLE or @template.constants.include? 'STYLE' template = RDoc::TemplatePage.new @template::STYLE unless @options.css then open RDoc::Generator::CSS_NAME, 'w' do |f| values = {} if @template.constants.include? :FONTS or @template.constants.include? 'FONTS' then values["fonts"] = @template::FONTS end template.write_html_on(f, values) end end end ## # See the comments at the top for a description of the directory structure def gen_sub_directories FileUtils.mkdir_p RDoc::Generator::FILE_DIR FileUtils.mkdir_p RDoc::Generator::CLASS_DIR rescue $stderr.puts $!.message exit 1 end def build_indices @files, @classes = RDoc::Generator::Context.build_indices(@toplevels, @options) end ## # Generate all the HTML def generate_html @main_url = main_url # the individual descriptions for files and classes gen_into(@files) gen_into(@classes) # and the index files gen_file_index gen_class_index gen_method_index gen_main_index # this method is defined in the template file values = { 'title_suffix' => CGI.escapeHTML("[#{@options.title}]"), 'charset' => @options.charset, 'style_url' => style_url('', @options.css), } @template.write_extra_pages(values) if @template.respond_to?(:write_extra_pages) end def gen_into(list) # # The file, class, and method lists technically should be regenerated # for every output file, in order that the relative links be correct # (we are worried here about frameless templates, which need this # information for every generated page). Doing this is a bit slow, # however. For a medium-sized gem, this increased rdoc's runtime by # about 5% (using the 'time' command-line utility). While this is not # necessarily a problem, I do not want to pessimize rdoc for large # projects, however, and so we only regenerate the lists when the # directory of the output file changes, which seems like a reasonable # optimization. # file_list = {} class_list = {} method_list = {} prev_op_dir = nil list.each do |item| next unless item.document_self op_file = item.path op_dir = File.dirname(op_file) if(op_dir != prev_op_dir) file_list = index_to_links op_file, @files class_list = index_to_links op_file, @classes method_list = index_to_links op_file, RDoc::Generator::Method.all_methods end prev_op_dir = op_dir FileUtils.mkdir_p op_dir open op_file, 'w' do |io| item.write_on io, file_list, class_list, method_list end end end def gen_file_index gen_an_index @files, 'Files', @template::FILE_INDEX, "fr_file_index.html" end def gen_class_index gen_an_index(@classes, 'Classes', @template::CLASS_INDEX, "fr_class_index.html") end def gen_method_index gen_an_index(RDoc::Generator::Method.all_methods, 'Methods', @template::METHOD_INDEX, "fr_method_index.html") end def gen_an_index(collection, title, template, filename) template = RDoc::TemplatePage.new @template::FR_INDEX_BODY, template res = [] collection.sort.each do |f| if f.document_self res << { "href" => f.path, "name" => f.index_name } end end values = { "entries" => res, 'title' => CGI.escapeHTML("#{title} [#{@options.title}]"), 'list_title' => CGI.escapeHTML(title), 'index_url' => @main_url, 'charset' => @options.charset, 'style_url' => style_url('', @options.css), } open filename, 'w' do |f| template.write_html_on(f, values) end end ## # The main index page is mostly a template frameset, but includes the # initial page. If the --main option was given, we use this as # our main page, otherwise we use the first file specified on the command # line. def gen_main_index if @template.const_defined? :FRAMELESS then # # If we're using a template without frames, then just redirect # to it from index.html. # # One alternative to this, expanding the main page's template into # index.html, is tricky because the relative URLs will be different # (since index.html is located in at the site's root, # rather than within a files or a classes subdirectory). # open 'index.html', 'w' do |f| f.puts(%{}) f.puts(%{}) f.puts(%{
}) f.puts(%{