rake/manual.rb in linkparser-1.0.4 vs rake/manual.rb in linkparser-1.1.0

- old
+ new

@@ -1,8 +1,8 @@ # # Manual-generation Rake tasks and classes -# $Id: manual.rb 95 2009-04-02 15:21:01Z deveiant $ + # # Authors: # * Michael Granger <ged@FaerieMUD.org> # * Mahlon E. Smith <mahlon@martini.nu> # @@ -53,12 +53,12 @@ ### Export any static resources required by this filter to the given +output_dir+. def export_resources( output_dir ) # No-op by default end - + ### Process the +page+'s source with the filter and return the altered content. def process( source, page, metadata ) raise NotImplementedError, "%s does not implement the #process method" % [ self.class.name ] end @@ -67,11 +67,11 @@ ### The default page configuration if none is specified. DEFAULT_CONFIG = { 'filters' => [ 'erb', 'links', 'textile' ], 'layout' => 'default.page', - 'cleanup' => true, + 'cleanup' => false, }.freeze # Pattern to match a source page with a YAML header PAGE_WITH_YAML_HEADER = / \A---\s*$ # It should should start with three hyphens @@ -116,59 +116,64 @@ ###### public ###### - + # The Manual::PageCatalog to which the page belongs attr_reader :catalog - + # The relative path to the base directory, for prepending to page paths attr_reader :basepath - + # The Pathname object that specifys the page source file attr_reader :sourcefile - + # The configured layouts directory as a Pathname object. attr_reader :layouts_dir - + # The page configuration, as read from its YAML header attr_reader :config - + # The raw source of the page attr_reader :source - + # The filters the page will use to render itself attr_reader :filters - + ### Generate HTML output from the page and return it. def generate( metadata ) content = self.generate_content( @source, metadata ) layout = self.config['layout'].sub( /\.page$/, '' ) templatepath = @layouts_dir + "#{layout}.page" - template = ERB.new( templatepath.read ) - page = self + template = nil + if Object.const_defined?( :Encoding ) + template = ERB.new( templatepath.read(:encoding => 'UTF-8') ) + else + template = ERB.new( templatepath.read ) + end + page = self html = template.result( binding() ) # Use Tidy to clean up the html if 'cleanup' is turned on, but remove the Tidy # meta-generator propaganda/advertising. html = self.cleanup( html ).sub( %r:<meta name="generator"[^>]*tidy[^>]*/>:im, '' ) if self.config['cleanup'] - + return html end ### Return the page title as specified in the YAML options def title return self.config['title'] || self.sourcefile.basename end - - + + ### Run the various filters on the given input and return the transformed ### content. def generate_content( input, metadata ) return @filters.inject( input ) do |source, filter| filter.process( source, self, metadata ) @@ -180,18 +185,18 @@ ### a Ruby object, and return it. def read_page_config( source ) unless source =~ PAGE_WITH_YAML_HEADER return DEFAULT_CONFIG.dup, source end - + pageconfig = YAML.load( $1 ) source = $2 - + return DEFAULT_CONFIG.merge( pageconfig ), source end - + ### Clean up and return the given HTML +source+. def cleanup( source ) require 'tidy' Tidy.path = '/usr/lib/libtidy.dylib' @@ -206,12 +211,12 @@ end rescue LoadError => err trace "No cleanup: " + err.message return source end - + ### Get (singleton) instances of the filters named in +filterlist+ and return them. def load_filters( filterlist ) filterlist.flatten.collect do |key| raise ArgumentError, "filter '#{key}' is not loaded" unless Manual::Page::Filter.derivatives.key?( key ) @@ -228,11 +233,11 @@ case type when :section items << %Q{<div class="section">} items << %Q{<h2><a href="#{self.basepath + path}/">#{title}</a></h2>} items << '<ul class="index-section">' - + when :current_section items << %Q{<div class="section current-section">} items << %Q{<h2><a href="#{self.basepath + path}/">#{title}</a></h2>} items << '<ul class="index-section current-index-section">' @@ -265,48 +270,48 @@ ### Create a new PageCatalog that will load Manual::Page objects for .page files ### in the specified +sourcedir+. def initialize( sourcedir, layoutsdir ) @sourcedir = sourcedir @layoutsdir = layoutsdir - + @pages = [] @path_index = {} @uri_index = {} @title_index = {} @hierarchy = {} - + self.find_and_load_pages end - - + + ###### public ###### # An index of the pages in the catalog by Pathname attr_reader :path_index - + # An index of the pages in the catalog by title attr_reader :title_index - + # An index of the pages in the catalog by the URI of their source relative to the source # directory attr_reader :uri_index - + # The hierarchy of pages in the catalog, suitable for generating an on-page index attr_reader :hierarchy - + # An Array of all Manual::Page objects found attr_reader :pages # The Pathname location of the .page files. attr_reader :sourcedir - + # The Pathname location of look and feel templates. attr_reader :layoutsdir - + ### Traverse the catalog's #hierarchy, yielding to the given +builder+ ### block for each entry, as well as each time a sub-hash is entered or ### exited, setting the +type+ appropriately. Valid values for +type+ are: ### ### :entry, :section, :section_end @@ -359,11 +364,11 @@ idx.to_s || subpath.to_s else trace "Using the path for the sort of directory %p" % [ subpath ] subpath.to_s end - + # Page else if subpath == INDEX_PATH trace "Sort index for index page %p is 0" % [ subpath ] '0' @@ -374,21 +379,21 @@ end end end # sort_by end - + INDEX_PATH = Pathname.new('index') ### Build up the data structures necessary for calling the +builder+ callback ### for an index section and call it, then recurse into the section contents. def handle_section_callback( path, section, from=nil, &builder ) from_current = false trace "Section handler: path=%p, section keys=%p, from=%s" % [ path, section.keys, from.sourcefile ] - + # Call the callback with :section -- determine the section title from # the 'index.page' file underneath it, or the directory name if no # index.page exists. if section.key?( INDEX_PATH ) if section[INDEX_PATH].sourcefile.dirname == from.sourcefile.dirname @@ -399,35 +404,35 @@ end else title = File.dirname( path ).gsub( /_/, ' ' ) builder.call( :section, title, path ) end - + # Recurse self.traverse_hierarchy( path, section, from, &builder ) - + # Call the callback with :section_end if from_current builder.call( :current_section_end, '', path ) else builder.call( :section_end, '', path ) end end - - + + ### Yield the specified +page+ to the builder def handle_page_callback( path, page, from=nil ) if from == page yield( :current_entry, page.title, path ) else yield( :entry, page.title, path ) end end - + ### Find and store - + ### Find all .page files under the configured +sourcedir+ and create a new ### Manual::Page object for each one. def find_and_load_pages Pathname.glob( @sourcedir + '**/*.page' ).each do |pagefile| path_to_base = @sourcedir.relative_path_from( pagefile.dirname ) @@ -437,11 +442,11 @@ @pages << page @path_index[ pagefile ] = page @title_index[ page.title ] = page @uri_index[ hierpath.to_s ] = page - + # Place the page in the page hierarchy by using inject to find and/or create the # necessary subhashes. The last run of inject will return the leaf hash in which # the page will live section = hierpath.dirname.split[1..-1].inject( @hierarchy ) do |hier, component| hier[ component ] ||= {} @@ -449,11 +454,11 @@ end section[ pagefile.basename('.page') ] = page end end - + end ### A Textile filter for the manual generation tasklib, implemented using RedCloth. class TextileFilter < Manual::Page::Filter @@ -461,12 +466,12 @@ ### Load RedCloth when the filter is first created def initialize( *args ) require 'redcloth' super end - + ### Process the given +source+ as Textile and return the resulting HTML ### fragment. def process( source, *ignored ) formatter = RedCloth::TextileDoc.new( source ) formatter.hard_breaks = false @@ -491,39 +496,39 @@ end ### Manual generation task library class GenTask < Rake::TaskLib - + # Default values for task config variables DEFAULT_NAME = :manual DEFAULT_BASE_DIR = Pathname.new( 'docs/manual' ) DEFAULT_SOURCE_DIR = 'source' DEFAULT_LAYOUTS_DIR = 'layouts' DEFAULT_OUTPUT_DIR = 'output' DEFAULT_RESOURCE_DIR = 'resources' DEFAULT_LIB_DIR = 'lib' DEFAULT_METADATA = OpenStruct.new - + ### Define a new manual-generation task with the given +name+. def initialize( name=:manual ) @name = name @source_dir = DEFAULT_SOURCE_DIR @layouts_dir = DEFAULT_LAYOUTS_DIR @output_dir = DEFAULT_OUTPUT_DIR @resource_dir = DEFAULT_RESOURCE_DIR @lib_dir = DEFAULT_LIB_DIR @metadata = DEFAULT_METADATA - + yield( self ) if block_given? - + self.define end - - + + ###### public ###### attr_accessor :base_dir, @@ -553,32 +558,32 @@ resourcedir = basedir + @resource_dir libdir = basedir + @lib_dir load_filter_libraries( libdir ) catalog = Manual::PageCatalog.new( sourcedir, layoutsdir ) - + # Declare the tasks outside the namespace that point in task @name => "#@name:build" task "clobber_#@name" => "#@name:clobber" namespace( self.name ) do setup_resource_copy_tasks( resourcedir, outputdir ) manual_pages = setup_page_conversion_tasks( sourcedir, outputdir, catalog ) - + desc "Build the manual" - task :build => [ :rdoc, :copy_resources, :copy_apidocs, :generate_pages ] - + task :build => [ :apidocs, :copy_resources, :copy_apidocs, :generate_pages ] + task :clobber do RakeFileUtils.verbose( $verbose ) do rm_f manual_pages.to_a end remove_dir( outputdir ) if ( outputdir + '.buildtime' ).exist? end - + desc "Remove any previously-generated parts of the manual and rebuild it" task :rebuild => [ :clobber, self.name ] - + desc "Watch for changes to the source files and rebuild when they change" task :autobuild do scope = [ self.name ] loop do t = Rake.application.lookup( :build, scope ) @@ -596,12 +601,12 @@ end end end end # def define - - + + ### Load the filter libraries provided in the given +libdir+ def load_filter_libraries( libdir ) Pathname.glob( libdir + '*.rb' ) do |filterlib| trace " loading filter library #{filterlib}" require( filterlib ) @@ -615,11 +620,11 @@ # 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 = FileList[ catalog.path_index.keys.map {|pn| pn.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 ] @@ -634,88 +639,88 @@ rule( %r{#{outputdir}/.*\.html$} => [ proc {|name| name.sub(/\.[^.]+$/, '.page').sub( outputdir, sourcedir) }, outputdir.to_s ]) do |task| - + source = Pathname.new( task.source ) target = Pathname.new( task.name ) log " #{ source } -> #{ target }" - + page = catalog.path_index[ source ] #trace " page object is: %p" % [ page ] - + target.dirname.mkpath target.open( File::WRONLY|File::CREAT|File::TRUNC ) do |io| io.write( page.generate(metadata) ) 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 - - + + ### Copy method for resources -- passed as a block to the various file tasks that copy ### resources to the output directory. 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 - - + + ### Set up a rule for copying files from the resources directory to the output dir. def setup_resource_copy_tasks( resourcedir, outputdir ) - resources = FileList[ resourcedir + '**/*.{js,css,png,gif,jpg,html}' ] + resources = FileList[ resourcedir + '**/*.{js,css,png,gif,jpg,html,svg,svgz,swf}' ] 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 => :rdoc do - cp_r( RDOCDIR, outputdir ) + task :copy_apidocs => :apidocs do + cp_r( API_DOCSDIR, outputdir ) 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 - + end # class Manual::GenTask - + end ### Task: manual generation if MANUALDIR.exist? MANUALOUTPUTDIR = MANUALDIR + 'output' trace "Manual will be generated in: #{MANUALOUTPUTDIR}" - + begin directory MANUALOUTPUTDIR.to_s Manual::GenTask.new do |manual| manual.metadata.version = PKG_VERSION - manual.metadata.api_dir = RDOCDIR + manual.metadata.api_dir = API_DOCSDIR manual.output_dir = MANUALOUTPUTDIR manual.base_dir = MANUALDIR manual.source_dir = 'src' end @@ -738,18 +743,18 @@ 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 MANUALDIR.mkpath - + %w[layouts lib output resources src].each do |dir| FileUtils.mkpath( MANUALDIR + dir, :mode => 0755, :verbose => true, :noop => $dryrun ) end - + Pathname.glob( TEMPLATEDIR + '**/*.{rb,css,png,js,erb,page}' ).each do |tmplfile| trace "extname is: #{tmplfile.extname}" - + # 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, '<>' ) @@ -760,10 +765,10 @@ log "generating #{target}: html => #{html[0,20]}" target.open( File::WRONLY|File::CREAT|File::EXCL, 0644 ) do |fh| fh.print( html ) end - + # Just copy anything else else target = MANUALDIR + tmplfile.relative_path_from( TEMPLATEDIR ) FileUtils.mkpath target.dirname, :mode => 0755, :verbose => true, :noop => $dryrun unless target.dirname.directory?