class RDoc::Generator::Starkfish

Starkfish RDoc HTML Generator

$Id$

Author/s

Contributors

License

Copyright © 2007-2010, Michael Granger. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Constants

ANAME_TRANSFORMS

An array of transforms to run on a method name to derive a suitable anchor name. The pairs are used in pairs as arguments to gsub.

CLASS_DIR

Directory where generated classes live relative to the root

FILE_DIR

Directory where generated files live relative to the root

GENERATOR_DIR

Path to this file’s parent directory. Used to find templates and other resources.

SVNID_PATTERN

%q$Id$“

SVNId

Subversion ID

SVNRev

Subversion rev

VERSION

Release Version

Attributes

outputdir[R]

The output directory

Public Class Methods

for( options ) click to toggle source

Standard generator factory method

# File lib/rdoc/generator/starkfish.rb, line 96
def self::for( options )
        new( options )
end
new( options ) click to toggle source

Initialize a few instance variables before we start

Calls superclass method
# File lib/rdoc/generator/starkfish.rb, line 106
def initialize( options )
        @options = options
        @template = nil
        
        template = options.template || 'starkfish'
        @template_dir = (template =~ /\A\//) ? 
                Pathname.new( template ) :
                GENERATOR_DIR + 'template/' + template
        
        configfile = @template_dir + 'config.yml'
        @config = (configfile.file?) ? YAML.load_file( configfile.to_s ) : {}
        
        @files      = []
        @classes    = []
        @hyperlinks = {}
        
        @basedir = Pathname.pwd.expand_path
        
        options.diagram = false
        
        super()
end

Public Instance Methods

data() click to toggle source

Return the data section from the config file (if any)

# File lib/rdoc/generator/starkfish.rb, line 147
def data
        return @config['data']
end
debug_msg( *msg ) click to toggle source

Output progress information if debugging is enabled

# File lib/rdoc/generator/starkfish.rb, line 153
def debug_msg( *msg )
        return unless $DEBUG
        $stderr.puts( *msg )
end
gen_sub_directories() click to toggle source

Create the directories the generated docs will live in if they don’t already exist.

# File lib/rdoc/generator/starkfish.rb, line 161
def gen_sub_directories
        @outputdir.mkpath
end
generate( toplevels ) click to toggle source

Build the initial indices and output objects based on an array of TopLevel objects containing the extracted information.

# File lib/rdoc/generator/starkfish.rb, line 182
def generate( toplevels )
        @outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir )
        if RDoc::Generator::Context.respond_to?( :build_indicies)
       @files, @classes = RDoc::Generator::Context.build_indicies( toplevels, @options )
        else
       @files, @classes = RDoc::Generator::Context.build_indices( toplevels, @options )
        end

        # Now actually write the output
        generate_xhtml( @options, @files, @classes )

rescue StandardError => err
        debug_msg "%s: %s\n  %s" % [ err.class.name, err.message, err.backtrace.join("\n  ") ]
        raise
end
generate_xhtml( options, files, classes ) click to toggle source

Generate output

# File lib/rdoc/generator/starkfish.rb, line 205
def generate_xhtml( options, files, classes )
        files = gen_into( @files )
        classes = gen_into( @classes )

        # Make a hash of class info keyed by class name
        classes_by_classname = classes.inject({}) {|hash, classinfo|
                hash[ classinfo[:full_name] ] = classinfo
                hash[ classinfo[:full_name] ][:outfile] =
                        classinfo[:full_name].gsub( /::/, '/' ) + '.html'
                hash
        }

        # Make a hash of file info keyed by path
        files_by_path = files.inject({}) {|hash, fileinfo|
                hash[ fileinfo[:full_path] ] = fileinfo
                hash[ fileinfo[:full_path] ][:outfile] = fileinfo[:full_path] + '.html'
                hash
        }

        self.write_style_sheet
        self.generate_index( options, files_by_path, classes_by_classname )
        self.generate_class_files( options, files_by_path, classes_by_classname )
        self.generate_file_files( options, files_by_path, classes_by_classname )
end
import( erbfile ) click to toggle source

Read the spcified To be called from ERB template to import (embed) another template

# File lib/rdoc/generator/starkfish.rb, line 140
def import( erbfile )
        erb = File.open( erbfile ) {|fp| ERb.new(fp.read) }
        return erb.run( binding() )
end
write_style_sheet() click to toggle source

Copy over the stylesheet into the appropriate place in the output directory.

# File lib/rdoc/generator/starkfish.rb, line 168
def write_style_sheet
        debug_msg "Copying over static files"
        staticfiles = @config['static'] || %w[rdoc.css js images]
        staticfiles = staticfiles.split( /\s+/ ) if staticfiles.is_a?( String )
        staticfiles.each do |path|
                FileUtils.cp_r( @template_dir + path, '.', :verbose => $DEBUG, :noop => $dryrun )
        end
end

Protected Instance Methods

generate_class_files( options, files, classes ) click to toggle source

Generate a documentation file for each class present in the given hash of classes.

# File lib/rdoc/generator/starkfish.rb, line 274
def generate_class_files( options, files, classes )
        debug_msg "Generating class documentation in #@outputdir"
        templatefile = @template_dir + 'classpage.rhtml'
        outputdir = @outputdir

        modsort = self.get_sorted_module_list( classes )

        classes.sort_by {|k,v| k }.each do |classname, classinfo|
                debug_msg "  working on %s (%s)" % [ classname, classinfo[:outfile] ]
                outfile    = outputdir + classinfo[:outfile]
                rel_prefix = outputdir.relative_path_from( outfile.dirname )
                svninfo    = self.get_svninfo( classinfo )

                debug_msg "  rendering #{outfile}"
                self.render_template( templatefile, binding(), outfile )
        end
end
generate_file_files( options, files, classes ) click to toggle source

Generate a documentation file for each file present in the given hash of files.

# File lib/rdoc/generator/starkfish.rb, line 295
def generate_file_files( options, files, classes )
        debug_msg "Generating file documentation in #@outputdir"
        templatefile = @template_dir + 'filepage.rhtml'

        modsort = self.get_sorted_module_list( classes )

        files.sort_by {|k,v| k }.each do |path, fileinfo|
                outfile     = @outputdir + fileinfo[:outfile]
                debug_msg "  working on %s (%s)" % [ path, outfile ]
                rel_prefix  = @outputdir.relative_path_from( outfile.dirname )
                context     = binding()

                debug_msg "  rendering #{outfile}"
                self.render_template( templatefile, binding(), outfile )
        end
end
generate_index( options, files, classes ) click to toggle source

Generate an index page which lists all the classes which are documented.

# File lib/rdoc/generator/starkfish.rb, line 261
def generate_index( options, files, classes )
        debug_msg "Rendering the index page..."

        templatefile = @template_dir + 'index.rhtml'
        modsort = self.get_sorted_module_list( classes )
        outfile = @basedir + @options.op_dir + 'index.html'
        
        self.render_template( templatefile, binding(), outfile )
end
get_sorted_module_list( classes ) click to toggle source

Return a list of the documented modules sorted by salience first, then by name.

# File lib/rdoc/generator/starkfish.rb, line 237
def get_sorted_module_list( classes )
        nscounts = classes.keys.inject({}) do |counthash, name|
                toplevel = name.gsub( /::.*/, '' )
                counthash[toplevel] ||= 0
                counthash[toplevel] += 1
                
                counthash
        end

        # Sort based on how often the toplevel namespace occurs, and then on the name
        # of the module -- this works for projects that put their stuff into a
        # namespace, of course, but doesn't hurt if they don't.
        return classes.keys.sort_by do |name| 
                toplevel = name.gsub( /::.*/, '' )
                [
                        nscounts[ toplevel ] * -1,
                        name
                ]
        end
end
get_svninfo( classinfo ) click to toggle source

Try to extract Subversion information out of the first constant whose value looks like a subversion Id tag. If no matching constant is found, and empty hash is returned.

# File lib/rdoc/generator/starkfish.rb, line 343
def get_svninfo( classinfo )
        return {} unless classinfo[:sections]
        constants = classinfo[:sections].first[: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.to_i - commitdate.to_i ),
                :committer   => committer,
        }
end
render_template( templatefile, context, outfile ) click to toggle source

Load and render the erb template in the given templatefile within the specified context (a Binding object) and write it out to outfile. Both templatefile and outfile should be Pathname-like objects.

# File lib/rdoc/generator/starkfish.rb, line 365
def render_template( templatefile, context, outfile )
        template_src = templatefile.read
        template = ERB.new( template_src, nil, '<>' )
        template.filename = templatefile.to_s

        output = begin
                template.result( context )
        rescue NoMethodError => err
                raise "Error while evaluating %s: %s (at %p)" % [
                        templatefile.to_s,
                        err.message,
                        eval( "_erbout[-50,50]", context )
                ]
        end

        output = self.wrap_content( output, context )

        unless $dryrun
                outfile.dirname.mkpath
                outfile.open( 'w', 0644 ) do |ofh|
                        ofh.print( output )
                end
        else
                debug_msg "  would have written %d bytes to %s" %
                [ output.length, outfile ]
        end
end
time_delta_string( seconds ) click to toggle source

Return a string describing the amount of time in the given number of seconds in terms a human can understand easily.

# File lib/rdoc/generator/starkfish.rb, line 315
def time_delta_string( seconds )
        return 'less than a minute' if seconds < 1.minute
        return (seconds / 1.minute).to_s + ' minute' + (seconds/60 == 1 ? '' : 's') if seconds < 50.minutes
        return 'about one hour' if seconds < 90.minutes
        return (seconds / 1.hour).to_s + ' hours' if seconds < 18.hours
        return 'one day' if seconds < 1.day
        return 'about one day' if seconds < 2.days
        return (seconds / 1.day).to_s + ' days' if seconds < 1.week
        return 'about one week' if seconds < 2.week
        return (seconds / 1.week).to_s + ' weeks' if seconds < 3.months
        return (seconds / 1.month).to_s + ' months' if seconds < 1.year
        return (seconds / 1.year).to_s + ' years'
end
wrap_content( output, context ) click to toggle source

Load the configured wrapper file and wrap it around the given content.

# File lib/rdoc/generator/starkfish.rb, line 395
def wrap_content( output, context )
        wrapper = @options.wrapper || @config['wrapper'] || 'wrapper.rhtml'
        wrapperfile = (wrapper =~ /\A\//) ? 
                Pathname.new( wrapper ) :
                @template_dir + wrapper
        
        if wrapperfile.file?
                # Add 'content' to the context binding for the wrapper template
                eval( "content = %p" % [output], context )

                template_src = wrapperfile.read
                template = ERB.new( template_src, nil, '<>' )
                template.filename = templatefile.to_s

                begin
                        return template.result( context )
                rescue NoMethodError => err
                        raise "Error while evaluating %s: %s (at %p)" % [
                                templatefile.to_s,
                                err.message,
                                eval( "_erbout[-50,50]", context )
                        ]
                end
        end

end

Private Instance Methods

aname_from_method( methodname ) click to toggle source

Given the name of a Ruby method, return a name suitable for use as target names in A tags.

# File lib/rdoc/generator/starkfish.rb, line 429
def aname_from_method( methodname )
        return ANAME_TRANSFORMS.enum_slice( 2 ).inject( methodname.to_s ) do |name, xform|
                name.gsub( *xform )
        end
end