# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- gem 'rdoc' require 'uri' require 'yajl' require 'inversion' require 'loggability' require 'fileutils' require 'pathname' require 'rdoc/rdoc' require 'rdoc/generator/json_index' # The Fivefish generator class. class RDoc::Generator::Fivefish extend Loggability include FileUtils # Loggability API -- set up a Logger for Fivefish log_as :fivefish # The data directory in the project if that exists, otherwise the gem datadir DATADIR = if ENV['FIVEFISH_DATADIR'] Pathname( ENV['FIVEFISH_DATADIR'] ).expand_path( Pathname.pwd ) elsif File.directory?( 'data/rdoc-generator-fivefish' ) Pathname( 'data/rdoc-generator-fivefish' ).expand_path( Pathname.pwd ) elsif path = Gem.latest_spec_for( 'rdoc-generator-fivefish' )&.datadir Pathname( path ) else raise ScriptError, "can't find the data directory!" end # Register with RDoc as an alternative generator RDoc::RDoc.add_generator( self ) ### Add generator-specific options to the option-parser def self::setup_options( rdoc_options ) op = rdoc_options.option_parser op.accept( URI ) do |string| uri = URI.parse( string ) rescue nil raise OptionParser::InvalidArgument unless uri uri end op.on( '--additional-stylesheet=URL', URI, "Add an additional (preferred) stylesheet", "link to each generated page. This allows", "the output style to be overridden." ) do |url| rdoc_options.additional_stylesheet = url end end ### Set up some instance variables def initialize( store, options ) @store = store @options = options $DEBUG_RDOC = $VERBOSE || $DEBUG self.log.debug "Setting up generator for %p with options: %p" % [ @store, @options ] extend( FileUtils::Verbose ) if $DEBUG_RDOC extend( FileUtils::DryRun ) if options.dry_run @base_dir = Pathname.pwd.expand_path @template_dir = DATADIR @output_dir = Pathname( @options.op_dir ).expand_path( @base_dir ) @template_cache = {} @files = nil @classes = nil @search_index = {} Inversion::Template.configure( :template_paths => [self.template_dir + 'templates'] ) end ###### public ###### # The base directory (current working directory) as a Pathname attr_reader :base_dir # The directory containing templates as a Pathname attr_reader :template_dir # The output directory as a Pathname attr_reader :output_dir # The command-line options given to the rdoc command attr_reader :options # The RDoc::Store that contains the parsed CodeObjects attr_reader :store ### Output progress information if debugging is enabled def debug_msg( *msg ) return unless $DEBUG_RDOC $stderr.puts( *msg ) end ### Backward-compatible (no-op) method. def class_dir # :nodoc: nil end alias_method :file_dir, :class_dir ### Create the directories the generated docs will live in if they don't ### already exist. def gen_sub_directories self.output_dir.mkpath end ### Build the initial indices and output objects based on the files in the generator's store. def generate self.populate_data_objects self.generate_index_page self.generate_class_files self.generate_file_files self.generate_search_index self.copy_static_assets end ### Populate the data objects necessary to generate documentation from the generator's ### #store. def populate_data_objects @files = self.store.all_files.sort @classes = self.store.all_classes_and_modules.sort @methods = @classes.map {|m| m.method_list }.flatten.sort @modsort = self.get_sorted_module_list( @classes ) end ### Generate an index page which lists all the classes which are documented. def generate_index_page self.log.debug "Generating index page" layout = self.load_layout_template template = self.load_template( 'index.tmpl' ) out_file = self.output_dir + 'index.html' out_file.dirname.mkpath mainpage = nil if mpname = self.options.main_page mainpage = @files.find {|f| f.full_name == mpname } else mainpage = @files.find {|f| f.full_name =~ /\breadme\b/i } end self.log.debug " using main_page (%s)" % [ mainpage ] if mainpage template.mainpage = mainpage template.synopsis = self.extract_synopsis( mainpage ) end layout.rel_prefix = self.output_dir.relative_path_from( out_file.dirname ) layout.contents = template layout.pageclass = 'index-page' out_file.open( 'w', 0644 ) {|io| io.print(layout.render) } end ### Generate a documentation file for each class and module def generate_class_files layout = self.load_layout_template template = self.load_template( 'class.tmpl' ) self.log.debug "Generating class documentation in #{self.output_dir}" @classes.each do |klass| self.log.debug " working on %s (%s)" % [klass.full_name, klass.path] out_file = self.output_dir + klass.path out_file.dirname.mkpath template.klass = klass layout.contents = template layout.rel_prefix = self.output_dir.relative_path_from( out_file.dirname ) layout.pageclass = 'class-page' out_file.open( 'w', 0644 ) {|io| io.print(layout.render) } end end ### Generate a documentation file for each file def generate_file_files layout = self.load_layout_template template = self.load_template( 'file.tmpl' ) self.log.debug "Generating file documentation in #{self.output_dir}" @files.select {|f| f.text? }.each do |file| out_file = self.output_dir + file.path out_file.dirname.mkpath self.log.debug " working on %s (%s)" % [file.full_name, out_file] template.file = file # If the page itself has an H1, use it for the header, otherwise make one # out of the name of the file if md = file.description.match( %r{
}im ) first_para = paras.map( &:strip ).find do |para| # Discard paragraphs consisting only of a link !( para.start_with?('
') ) end return heading + first_para end end # class RDoc::Generator::Fivefish # Reopen to add custom option attrs. class RDoc::Options ## # Allow setting a custom stylesheet attr_accessor :additional_stylesheet end