Hoe::

ManualGen

module
Included Modules
FileUtils
FileUtils::Verbose
FileUtils::DryRun

Rake tasks for generating a project manual or tutorial.

This was born out of a frustration with other static HTML generation modules and systems. I've tried webby, webgen, rote, staticweb, staticmatic, and nanoc, but I didn't find any of them really suitable (except rote, which was excellent but apparently isn't maintained and has a fundamental incompatibilty with Rake because of some questionable monkeypatching.)

So, since nothing seemed to scratch my itch, I'm going to scratch it myself.

@author Michael Granger <ged@FaerieMUD.org> @author Mahlon E. Smith <mahlon@martini.nu>

Constants

DEFAULT_BASE_DIR

Configuration defaults

DEFAULT_LAYOUTS_DIR
DEFAULT_LIB_DIR
DEFAULT_MANUAL_SUBDIRS

The subdirectories to create under the manual dir

DEFAULT_MANUAL_TEMPLATE_DIR
DEFAULT_METADATA
DEFAULT_OUTPUT_DIR
DEFAULT_RESOURCE_DIR
DEFAULT_SOURCE_DIR
RESOURCE_EXTNAMES

A glob pattern for matching resource files when copying them around

RESOURCE_GLOB_PATTERN
REVISION

Version-control revision constant

VERSION

Library version constant

Attributes

manual_base_dir[RW]
manual_layouts_dir[RW]
manual_lib_dir[RW]
manual_metadata[RW]
manual_output_dir[RW]
manual_paths[RW]
manual_resource_dir[RW]
manual_source_dir[RW]
manual_template_dir[RW]

Public Instance Methods

anchor
copy_resource( task )

Copy method for resources – passed as a block to the various file tasks that copy resources to the output directory.

# File lib/hoe/manualgen.rb, line 776
def copy_resource( task )
        source = task.prerequisites[ 1 ]
        target = task.name

        when_writing do
                trace "  #{source} -> #{target}"
                mkpath File.dirname( target ), :verbose => $trace unless
                        File.directory?( File.dirname(target) )
                install source, target, :mode => 0644, :verbose => $trace
        end
end
anchor
define_existing_manual_tasks( paths )

Define tasks for generating output for an existing manual.

# File lib/hoe/manualgen.rb, line 667
def define_existing_manual_tasks( paths )

        # Read all of the filters, pages, and layouts
        load_filter_libraries( paths[:libdir] )
        trace "Creating the manual page catalog with source at %p, layouts in %p" %
                paths.values_at( :sourcedir, :layoutsdir )
        catalog = PageCatalog.new( paths[:sourcedir], paths[:layoutsdir] )

        # Declare the tasks outside the namespace that point in
        desc "Generate the manual"
        task :manual => "manual:build"

        CLEAN.include( paths[:outputdir].to_s )

        # Namespace all our tasks
        namespace :manual do

                # Set up a file task for each resource, then a conversion task for
                # each page in the sourcedir so pages re-generate if they're modified
                setup_resource_copy_tasks( paths[:resourcedir], paths[:outputdir] )
                manual_pages = setup_page_conversion_tasks( paths[:sourcedir], paths[:outputdir], catalog )

                # The main task
                desc "Build the manual"
                task :build => [ :copy_resources, :copy_apidocs, :generate_pages ]

                task :clean do
                        RakeFileUtils.verbose( $verbose ) do
                                rm_f manual_pages.to_a
                        end
                        remove_dir( paths[:outputdir] ) if ( paths[:outputdir] + '.buildtime' ).exist?
                end

                desc "Force a rebuild of the manual"
                task :rebuild => [ :clean, :build ]

                desc "Update the resources templates for the manual to the latest versions"
                task :update do
                        ask_for_confirmation( "Update the resources/templates in the manual directory?" ) do
                                log "Updating..."
                                install_manual_directory( paths[:basedir], paths[:templatedir], false )
                        end

                end # task :manual

end
end
anchor
define_manual_setup_tasks( paths )

Define tasks for creating a skeleton manual

# File lib/hoe/manualgen.rb, line 609
def define_manual_setup_tasks( paths )
        templatedir = paths[:templatedir]
        trace "Templatedir is: %s" % [ templatedir ]
        manualdir = paths[:basedir]

        desc "Create a manual for this project from a template"
        task :manual do
                log "No manual directory (#{manualdir}) currently exists."
                ask_for_confirmation( "Create a new manual directory tree from a template?" ) do
                        log "Generating manual skeleton"
                        install_manual_directory( manualdir, templatedir )
                end

        end # task :manual

end
anchor
define_manualgen_tasks()

Set up the tasks for building the manual

# File lib/hoe/manualgen.rb, line 583
def define_manualgen_tasks

        # Make Pathnames of the directories relative to the base_dir
        basedir = Pathname( self.manual_base_dir )
        @manual_paths = {
                :templatedir => Pathname( self.manual_template_dir ),
                :basedir     => basedir,
                :sourcedir   => basedir + self.manual_source_dir,
                :layoutsdir  => basedir + self.manual_layouts_dir,
                :resourcedir => basedir + self.manual_resource_dir,
                :libdir      => basedir + self.manual_lib_dir,
                :outputdir   => basedir + self.manual_output_dir,
        }

        if basedir.directory?
                trace "Basedir %s exists, so defining tasks for building the manual" % [ basedir ]
                define_existing_manual_tasks( @manual_paths )
        else
                trace "Basedir %s doesn't exist, so defining tasks for creating a new manual" % [ basedir ]
                define_manual_setup_tasks( @manual_paths )
        end

end
anchor
initialize_manualgen()

Hoe callback – set up defaults

# File lib/hoe/manualgen.rb, line 566
def initialize_manualgen
        @manual_template_dir = DEFAULT_MANUAL_TEMPLATE_DIR
        @manual_base_dir     = DEFAULT_BASE_DIR
        @manual_source_dir   = DEFAULT_SOURCE_DIR
        @manual_layouts_dir  = DEFAULT_LAYOUTS_DIR
        @manual_output_dir   = DEFAULT_OUTPUT_DIR
        @manual_resource_dir = DEFAULT_RESOURCE_DIR
        @manual_lib_dir      = DEFAULT_LIB_DIR
        @manual_metadata     = DEFAULT_METADATA
        @manual_paths = {}

        self.extra_dev_deps << ['hoe-manualgen', "~> #{VERSION}"] unless
                self.name == 'hoe-manualgen'
end
anchor
install_manual_directory( manualdir, templatedir, include_srcdir=true )

Generate (or refresh) a manual directory from the specified templatedir.

# File lib/hoe/manualgen.rb, line 628
def install_manual_directory( manualdir, templatedir, include_srcdir=true )

        self.manual_paths.each do |key, dir|
                mkpath( dir, :mode => 0755 )
        end

        Pathname.glob( templatedir + RESOURCE_GLOB_PATTERN ).each do |tmplfile|
                if tmplfile.to_s =~ %r{/src/}
                        trace "Skipping %s" % [ tmplfile ]
                        next unless include_srcdir
                end

                # Render ERB files
                if tmplfile.extname == '.erb'
                        rname = tmplfile.basename( '.erb' )
                        target = manualdir + tmplfile.dirname.relative_path_from( templatedir ) + rname
                        template = ERB.new( tmplfile.read, nil, '<>' )

                        target.dirname.mkpath unless target.dirname.directory?
                        html = template.result( binding() )
                        log "generating #{target}"

                        target.open( File::WRONLY|File::CREAT|File::TRUNC, 0644 ) do |fh|
                                fh.print( html )
                        end

                # Just copy anything else
                else
                        target = manualdir + tmplfile.relative_path_from( templatedir )
                        mkpath target.dirname,
                                :mode => 0755, :noop => $dryrun unless target.dirname.directory?
                        install tmplfile, target,
                                :mode => 0644, :noop => $dryrun
                end
        end
end
anchor
load_filter_libraries( libdir )

Load the filter libraries provided in the given libdir

# File lib/hoe/manualgen.rb, line 717
def load_filter_libraries( libdir )
        Pathname.glob( libdir.expand_path + '*.rb' ) do |filterlib|
                trace "  loading filter library #{filterlib}"
                require( filterlib )
        end
end
anchor
setup_page_conversion_tasks( sourcedir, outputdir, catalog )

Set up the main HTML-generation task that will convert files in the given sourcedir to HTML in the outputdir

# File lib/hoe/manualgen.rb, line 727
def setup_page_conversion_tasks( sourcedir, outputdir, catalog )

        # we need to figure out what HTML pages need to be generated so we can set up the
        # dependency that causes the rule to be fired for each one when the task is invoked.
        manual_sources = Rake::FileList[ catalog.path_index.keys.map(&:to_s) ]
        trace "   found %d source files" % [ manual_sources.length ]

        # Map .page files to their equivalent .html output
        html_pathmap = "%%{%s,%s}X.html" % [ sourcedir, outputdir ]
        manual_pages = manual_sources.pathmap( html_pathmap )
        trace "Mapping sources like so: \n  %p -> %p" %
                [ manual_sources.first, manual_pages.first ]

        # Output directory task
        directory( outputdir.to_s )
        file outputdir.to_s do
                touch outputdir + '.buildtime'
        end

        # Rule to generate .html files from .page files
        rule(
                %r{#{outputdir}/.*\.html$} => [
                        proc {|name| name.sub(/\.[^.]+$/, '.page').sub(outputdir.to_s, sourcedir.to_s) },
                        outputdir.to_s
              ]) do |task|

                source = Pathname.new( task.source )
                target = Pathname.new( task.name )
                log "  #{ source } -> #{ target }"

                page = catalog.path_index[ source ]
                html = page.generate( self.manual_metadata )
                #trace "  page object is: %p" % [ page ]

                target.dirname.mkpath
                target.open( File::WRONLY|File::CREAT|File::TRUNC ) do |io|
                        io.write( html )
                end
        end

        # Group all the manual page output files targets into a containing task
        desc "Generate any pages of the manual that have changed"
        task :generate_pages => manual_pages
        return manual_pages
end
anchor
setup_resource_copy_tasks( resourcedir, outputdir )

Set up a rule for copying files from the resources directory to the output dir.

# File lib/hoe/manualgen.rb, line 790
def setup_resource_copy_tasks( resourcedir, outputdir )
        glob = resourcedir + RESOURCE_GLOB_PATTERN
        resources = FileList[ glob.to_s ]
        resources.exclude( /\.svn/ )
        target_pathmap = "%%{%s,%s}p" % [ resourcedir, outputdir ]
        targets = resources.pathmap( target_pathmap )
        copier = self.method( :copy_resource ).to_proc

        # Create a file task to copy each file to the output directory
        resources.each_with_index do |resource, i|
                file( targets[i] => [ outputdir.to_s, resource ], &copier )
        end

        desc "Copy API documentation to the manual output directory"
        task :copy_apidocs => [ outputdir.to_s, :docs ] do
                # Since Hoe hard-codes the 'docs' output dir, it's hard-coded
                # here too.
                apidir = outputdir + 'api'
                self.manual_metadata.api_dir = apidir
                cp_r( 'doc', apidir )
        end

        # Now group all the resource file tasks into a containing task
        desc "Copy manual resources to the output directory"
        task :copy_resources => targets do
                log "Copying manual resources"
        end
end