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>
Configuration defaults
The subdirectories to create under the manual dir
A glob pattern for matching resource files when copying them around
Version-control revision constant
Library version constant
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
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
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
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
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
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
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
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
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