linkparser 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +526 -0
 - data/LICENSE +27 -0
 - data/README +88 -0
 - data/Rakefile +315 -0
 - data/Rakefile.local +60 -0
 - data/ext/dictionary.c +269 -0
 - data/ext/extconf.rb +53 -0
 - data/ext/linkage.c +894 -0
 - data/ext/linkparser.c +120 -0
 - data/ext/linkparser.h +112 -0
 - data/ext/parseoptions.c +1188 -0
 - data/ext/sentence.c +536 -0
 - data/lib/linkparser.rb +38 -0
 - data/lib/linkparser/linkage.rb +248 -0
 - data/lib/linkparser/sentence.rb +106 -0
 - data/rake/dependencies.rb +76 -0
 - data/rake/helpers.rb +395 -0
 - data/rake/manual.rb +755 -0
 - data/rake/packaging.rb +112 -0
 - data/rake/publishing.rb +308 -0
 - data/rake/rdoc.rb +47 -0
 - data/rake/style.rb +62 -0
 - data/rake/svn.rb +602 -0
 - data/rake/testing.rb +202 -0
 - data/rake/verifytask.rb +64 -0
 - data/spec/bugfixes_spec.rb +42 -0
 - data/spec/linkparser/dictionary_spec.rb +90 -0
 - data/spec/linkparser/linkage_spec.rb +434 -0
 - data/spec/linkparser/parseoptions_spec.rb +78 -0
 - data/spec/linkparser/sentence_spec.rb +117 -0
 - data/spec/linkparser_spec.rb +30 -0
 - metadata +219 -0
 
    
        data/rake/helpers.rb
    ADDED
    
    | 
         @@ -0,0 +1,395 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #####################################################################
         
     | 
| 
      
 2 
     | 
    
         
            +
            ###	G L O B A L   H E L P E R   F U N C T I O N S
         
     | 
| 
      
 3 
     | 
    
         
            +
            #####################################################################
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'readline'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            # Set some ANSI escape code constants (Shamelessly stolen from Perl's
         
     | 
| 
      
 9 
     | 
    
         
            +
            # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
         
     | 
| 
      
 10 
     | 
    
         
            +
            ANSI_ATTRIBUTES = {
         
     | 
| 
      
 11 
     | 
    
         
            +
            	'clear'      => 0,
         
     | 
| 
      
 12 
     | 
    
         
            +
            	'reset'      => 0,
         
     | 
| 
      
 13 
     | 
    
         
            +
            	'bold'       => 1,
         
     | 
| 
      
 14 
     | 
    
         
            +
            	'dark'       => 2,
         
     | 
| 
      
 15 
     | 
    
         
            +
            	'underline'  => 4,
         
     | 
| 
      
 16 
     | 
    
         
            +
            	'underscore' => 4,
         
     | 
| 
      
 17 
     | 
    
         
            +
            	'blink'      => 5,
         
     | 
| 
      
 18 
     | 
    
         
            +
            	'reverse'    => 7,
         
     | 
| 
      
 19 
     | 
    
         
            +
            	'concealed'  => 8,
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            	'black'      => 30,   'on_black'   => 40, 
         
     | 
| 
      
 22 
     | 
    
         
            +
            	'red'        => 31,   'on_red'     => 41, 
         
     | 
| 
      
 23 
     | 
    
         
            +
            	'green'      => 32,   'on_green'   => 42, 
         
     | 
| 
      
 24 
     | 
    
         
            +
            	'yellow'     => 33,   'on_yellow'  => 43, 
         
     | 
| 
      
 25 
     | 
    
         
            +
            	'blue'       => 34,   'on_blue'    => 44, 
         
     | 
| 
      
 26 
     | 
    
         
            +
            	'magenta'    => 35,   'on_magenta' => 45, 
         
     | 
| 
      
 27 
     | 
    
         
            +
            	'cyan'       => 36,   'on_cyan'    => 46, 
         
     | 
| 
      
 28 
     | 
    
         
            +
            	'white'      => 37,   'on_white'   => 47
         
     | 
| 
      
 29 
     | 
    
         
            +
            }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            MULTILINE_PROMPT = <<-'EOF'
         
     | 
| 
      
 33 
     | 
    
         
            +
            Enter one or more values for '%s'.
         
     | 
| 
      
 34 
     | 
    
         
            +
            A blank line finishes input.
         
     | 
| 
      
 35 
     | 
    
         
            +
            EOF
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            CLEAR_TO_EOL       = "\e[K"
         
     | 
| 
      
 39 
     | 
    
         
            +
            CLEAR_CURRENT_LINE = "\e[2K"
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            ### Output a logging message
         
     | 
| 
      
 43 
     | 
    
         
            +
            def log( *msg )
         
     | 
| 
      
 44 
     | 
    
         
            +
            	output = colorize( msg.flatten.join(' '), 'cyan' )
         
     | 
| 
      
 45 
     | 
    
         
            +
            	$deferr.puts( output )
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            ### Output a logging message if tracing is on
         
     | 
| 
      
 50 
     | 
    
         
            +
            def trace( *msg )
         
     | 
| 
      
 51 
     | 
    
         
            +
            	return unless $trace
         
     | 
| 
      
 52 
     | 
    
         
            +
            	output = colorize( msg.flatten.join(' '), 'yellow' )
         
     | 
| 
      
 53 
     | 
    
         
            +
            	$deferr.puts( output )
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            ### Run the specified command +cmd+ with system(), failing if the execution
         
     | 
| 
      
 58 
     | 
    
         
            +
            ### fails.
         
     | 
| 
      
 59 
     | 
    
         
            +
            def run( *cmd )
         
     | 
| 
      
 60 
     | 
    
         
            +
            	cmd.flatten!
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            	if cmd.length > 1
         
     | 
| 
      
 63 
     | 
    
         
            +
            		trace( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} ) 
         
     | 
| 
      
 64 
     | 
    
         
            +
            	else
         
     | 
| 
      
 65 
     | 
    
         
            +
            		trace( cmd )
         
     | 
| 
      
 66 
     | 
    
         
            +
            	end
         
     | 
| 
      
 67 
     | 
    
         
            +
            	
         
     | 
| 
      
 68 
     | 
    
         
            +
            	if $dryrun
         
     | 
| 
      
 69 
     | 
    
         
            +
            		$deferr.puts "(dry run mode)"
         
     | 
| 
      
 70 
     | 
    
         
            +
            	else
         
     | 
| 
      
 71 
     | 
    
         
            +
            		system( *cmd )
         
     | 
| 
      
 72 
     | 
    
         
            +
            		unless $?.success?
         
     | 
| 
      
 73 
     | 
    
         
            +
            			fail "Command failed: [%s]" % [cmd.join(' ')]
         
     | 
| 
      
 74 
     | 
    
         
            +
            		end
         
     | 
| 
      
 75 
     | 
    
         
            +
            	end
         
     | 
| 
      
 76 
     | 
    
         
            +
            end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
            ### Run a subordinate Rake process with the same options and the specified +targets+.
         
     | 
| 
      
 80 
     | 
    
         
            +
            def rake( *targets )
         
     | 
| 
      
 81 
     | 
    
         
            +
            	opts = ARGV.select {|arg| arg[0,1] == '-' }
         
     | 
| 
      
 82 
     | 
    
         
            +
            	args = opts + targets.map {|t| t.to_s }
         
     | 
| 
      
 83 
     | 
    
         
            +
            	run 'rake', '-N', *args
         
     | 
| 
      
 84 
     | 
    
         
            +
            end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
            ### Open a pipe to a process running the given +cmd+ and call the given block with it.
         
     | 
| 
      
 88 
     | 
    
         
            +
            def pipeto( *cmd )
         
     | 
| 
      
 89 
     | 
    
         
            +
            	$DEBUG = true
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
            	cmd.flatten!
         
     | 
| 
      
 92 
     | 
    
         
            +
            	log( "Opening a pipe to: ", cmd.collect {|part| part =~ /\s/ ? part.inspect : part} ) 
         
     | 
| 
      
 93 
     | 
    
         
            +
            	if $dryrun
         
     | 
| 
      
 94 
     | 
    
         
            +
            		$deferr.puts "(dry run mode)"
         
     | 
| 
      
 95 
     | 
    
         
            +
            	else
         
     | 
| 
      
 96 
     | 
    
         
            +
            		open( '|-', 'w+' ) do |io|
         
     | 
| 
      
 97 
     | 
    
         
            +
            		
         
     | 
| 
      
 98 
     | 
    
         
            +
            			# Parent
         
     | 
| 
      
 99 
     | 
    
         
            +
            			if io
         
     | 
| 
      
 100 
     | 
    
         
            +
            				yield( io )
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
            			# Child
         
     | 
| 
      
 103 
     | 
    
         
            +
            			else
         
     | 
| 
      
 104 
     | 
    
         
            +
            				exec( *cmd )
         
     | 
| 
      
 105 
     | 
    
         
            +
            				fail "Command failed: [%s]" % [cmd.join(' ')]
         
     | 
| 
      
 106 
     | 
    
         
            +
            			end
         
     | 
| 
      
 107 
     | 
    
         
            +
            		end
         
     | 
| 
      
 108 
     | 
    
         
            +
            	end
         
     | 
| 
      
 109 
     | 
    
         
            +
            end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            ### Download the file at +sourceuri+ via HTTP and write it to +targetfile+.
         
     | 
| 
      
 113 
     | 
    
         
            +
            def download( sourceuri, targetfile=nil )
         
     | 
| 
      
 114 
     | 
    
         
            +
            	oldsync = $defout.sync
         
     | 
| 
      
 115 
     | 
    
         
            +
            	$defout.sync = true
         
     | 
| 
      
 116 
     | 
    
         
            +
            	require 'net/http'
         
     | 
| 
      
 117 
     | 
    
         
            +
            	require 'uri'
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
            	targetpath = Pathname.new( targetfile )
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
            	log "Downloading %s to %s" % [sourceuri, targetfile]
         
     | 
| 
      
 122 
     | 
    
         
            +
            	targetpath.open( File::WRONLY|File::TRUNC|File::CREAT, 0644 ) do |ofh|
         
     | 
| 
      
 123 
     | 
    
         
            +
            	
         
     | 
| 
      
 124 
     | 
    
         
            +
            		url = sourceuri.is_a?( URI ) ? sourceuri : URI.parse( sourceuri )
         
     | 
| 
      
 125 
     | 
    
         
            +
            		downloaded = false
         
     | 
| 
      
 126 
     | 
    
         
            +
            		limit = 5
         
     | 
| 
      
 127 
     | 
    
         
            +
            		
         
     | 
| 
      
 128 
     | 
    
         
            +
            		until downloaded or limit.zero?
         
     | 
| 
      
 129 
     | 
    
         
            +
            			Net::HTTP.start( url.host, url.port ) do |http|
         
     | 
| 
      
 130 
     | 
    
         
            +
            				req = Net::HTTP::Get.new( url.path )
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
            				http.request( req ) do |res|
         
     | 
| 
      
 133 
     | 
    
         
            +
            					if res.is_a?( Net::HTTPSuccess )
         
     | 
| 
      
 134 
     | 
    
         
            +
            						log "Downloading..."
         
     | 
| 
      
 135 
     | 
    
         
            +
            						res.read_body do |buf|
         
     | 
| 
      
 136 
     | 
    
         
            +
            							ofh.print( buf )
         
     | 
| 
      
 137 
     | 
    
         
            +
            						end
         
     | 
| 
      
 138 
     | 
    
         
            +
            						downloaded = true
         
     | 
| 
      
 139 
     | 
    
         
            +
            						puts "done."
         
     | 
| 
      
 140 
     | 
    
         
            +
            		
         
     | 
| 
      
 141 
     | 
    
         
            +
            					elsif res.is_a?( Net::HTTPRedirection )
         
     | 
| 
      
 142 
     | 
    
         
            +
            						url = URI.parse( res['location'] )
         
     | 
| 
      
 143 
     | 
    
         
            +
            						log "...following redirection to: %s" % [ url ]
         
     | 
| 
      
 144 
     | 
    
         
            +
            						limit -= 1
         
     | 
| 
      
 145 
     | 
    
         
            +
            						sleep 0.2
         
     | 
| 
      
 146 
     | 
    
         
            +
            						next
         
     | 
| 
      
 147 
     | 
    
         
            +
            				
         
     | 
| 
      
 148 
     | 
    
         
            +
            					else
         
     | 
| 
      
 149 
     | 
    
         
            +
            						res.error!
         
     | 
| 
      
 150 
     | 
    
         
            +
            					end
         
     | 
| 
      
 151 
     | 
    
         
            +
            				end
         
     | 
| 
      
 152 
     | 
    
         
            +
            			end
         
     | 
| 
      
 153 
     | 
    
         
            +
            		end
         
     | 
| 
      
 154 
     | 
    
         
            +
            		
         
     | 
| 
      
 155 
     | 
    
         
            +
            	end
         
     | 
| 
      
 156 
     | 
    
         
            +
            	
         
     | 
| 
      
 157 
     | 
    
         
            +
            	return targetpath
         
     | 
| 
      
 158 
     | 
    
         
            +
            ensure
         
     | 
| 
      
 159 
     | 
    
         
            +
            	$defout.sync = oldsync
         
     | 
| 
      
 160 
     | 
    
         
            +
            end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
            ### Return the fully-qualified path to the specified +program+ in the PATH.
         
     | 
| 
      
 164 
     | 
    
         
            +
            def which( program )
         
     | 
| 
      
 165 
     | 
    
         
            +
            	ENV['PATH'].split(/:/).
         
     | 
| 
      
 166 
     | 
    
         
            +
            		collect {|dir| Pathname.new(dir) + program }.
         
     | 
| 
      
 167 
     | 
    
         
            +
            		find {|path| path.exist? && path.executable? }
         
     | 
| 
      
 168 
     | 
    
         
            +
            end
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
            ### Create a string that contains the ANSI codes specified and return it
         
     | 
| 
      
 172 
     | 
    
         
            +
            def ansi_code( *attributes )
         
     | 
| 
      
 173 
     | 
    
         
            +
            	attributes.flatten!
         
     | 
| 
      
 174 
     | 
    
         
            +
            	attributes.collect! {|at| at.to_s }
         
     | 
| 
      
 175 
     | 
    
         
            +
            	# $deferr.puts "Returning ansicode for TERM = %p: %p" %
         
     | 
| 
      
 176 
     | 
    
         
            +
            	# 	[ ENV['TERM'], attributes ]
         
     | 
| 
      
 177 
     | 
    
         
            +
            	return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
         
     | 
| 
      
 178 
     | 
    
         
            +
            	attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
            	# $deferr.puts "  attr is: %p" % [attributes]
         
     | 
| 
      
 181 
     | 
    
         
            +
            	if attributes.empty? 
         
     | 
| 
      
 182 
     | 
    
         
            +
            		return ''
         
     | 
| 
      
 183 
     | 
    
         
            +
            	else
         
     | 
| 
      
 184 
     | 
    
         
            +
            		return "\e[%sm" % attributes
         
     | 
| 
      
 185 
     | 
    
         
            +
            	end
         
     | 
| 
      
 186 
     | 
    
         
            +
            end
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
            ### Colorize the given +string+ with the specified +attributes+ and return it, handling 
         
     | 
| 
      
 190 
     | 
    
         
            +
            ### line-endings, color reset, etc.
         
     | 
| 
      
 191 
     | 
    
         
            +
            def colorize( *args )
         
     | 
| 
      
 192 
     | 
    
         
            +
            	string = ''
         
     | 
| 
      
 193 
     | 
    
         
            +
            	
         
     | 
| 
      
 194 
     | 
    
         
            +
            	if block_given?
         
     | 
| 
      
 195 
     | 
    
         
            +
            		string = yield
         
     | 
| 
      
 196 
     | 
    
         
            +
            	else
         
     | 
| 
      
 197 
     | 
    
         
            +
            		string = args.shift
         
     | 
| 
      
 198 
     | 
    
         
            +
            	end
         
     | 
| 
      
 199 
     | 
    
         
            +
            	
         
     | 
| 
      
 200 
     | 
    
         
            +
            	ending = string[/(\s)$/] || ''
         
     | 
| 
      
 201 
     | 
    
         
            +
            	string = string.rstrip
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
            	return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
         
     | 
| 
      
 204 
     | 
    
         
            +
            end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
            ### Output the specified <tt>msg</tt> as an ANSI-colored error message
         
     | 
| 
      
 208 
     | 
    
         
            +
            ### (white on red).
         
     | 
| 
      
 209 
     | 
    
         
            +
            def error_message( msg, details='' )
         
     | 
| 
      
 210 
     | 
    
         
            +
            	$deferr.puts colorize( 'bold', 'white', 'on_red' ) { msg } + details
         
     | 
| 
      
 211 
     | 
    
         
            +
            end
         
     | 
| 
      
 212 
     | 
    
         
            +
            alias :error :error_message
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
            ### Highlight and embed a prompt control character in the given +string+ and return it.
         
     | 
| 
      
 216 
     | 
    
         
            +
            def make_prompt_string( string )
         
     | 
| 
      
 217 
     | 
    
         
            +
            	return CLEAR_CURRENT_LINE + colorize( 'bold', 'green' ) { string + ' ' }
         
     | 
| 
      
 218 
     | 
    
         
            +
            end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
            ### Output the specified <tt>prompt_string</tt> as a prompt (in green) and
         
     | 
| 
      
 222 
     | 
    
         
            +
            ### return the user's input with leading and trailing spaces removed.  If a
         
     | 
| 
      
 223 
     | 
    
         
            +
            ### test is provided, the prompt will repeat until the test returns true.
         
     | 
| 
      
 224 
     | 
    
         
            +
            ### An optional failure message can also be passed in.
         
     | 
| 
      
 225 
     | 
    
         
            +
            def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
         
     | 
| 
      
 226 
     | 
    
         
            +
            	prompt_string.chomp!
         
     | 
| 
      
 227 
     | 
    
         
            +
            	prompt_string << ":" unless /\W$/.match( prompt_string )
         
     | 
| 
      
 228 
     | 
    
         
            +
            	response = nil
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
            	begin
         
     | 
| 
      
 231 
     | 
    
         
            +
            		prompt = make_prompt_string( prompt_string )
         
     | 
| 
      
 232 
     | 
    
         
            +
            		response = Readline.readline( prompt ) || ''
         
     | 
| 
      
 233 
     | 
    
         
            +
            		response.strip!
         
     | 
| 
      
 234 
     | 
    
         
            +
            		if block_given? && ! yield( response ) 
         
     | 
| 
      
 235 
     | 
    
         
            +
            			error_message( failure_msg + "\n\n" )
         
     | 
| 
      
 236 
     | 
    
         
            +
            			response = nil
         
     | 
| 
      
 237 
     | 
    
         
            +
            		end
         
     | 
| 
      
 238 
     | 
    
         
            +
            	end while response.nil?
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
            	return response
         
     | 
| 
      
 241 
     | 
    
         
            +
            end
         
     | 
| 
      
 242 
     | 
    
         
            +
             
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
            ### Prompt the user with the given <tt>prompt_string</tt> via #prompt,
         
     | 
| 
      
 245 
     | 
    
         
            +
            ### substituting the given <tt>default</tt> if the user doesn't input
         
     | 
| 
      
 246 
     | 
    
         
            +
            ### anything.  If a test is provided, the prompt will repeat until the test
         
     | 
| 
      
 247 
     | 
    
         
            +
            ### returns true.  An optional failure message can also be passed in.
         
     | 
| 
      
 248 
     | 
    
         
            +
            def prompt_with_default( prompt_string, default, failure_msg="Try again." )
         
     | 
| 
      
 249 
     | 
    
         
            +
            	response = nil
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
            	begin
         
     | 
| 
      
 252 
     | 
    
         
            +
            		default ||= '~'
         
     | 
| 
      
 253 
     | 
    
         
            +
            		response = prompt( "%s [%s]" % [ prompt_string, default ] )
         
     | 
| 
      
 254 
     | 
    
         
            +
            		response = default.to_s if !response.nil? && response.empty? 
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
            		trace "Validating response %p" % [ response ]
         
     | 
| 
      
 257 
     | 
    
         
            +
             
     | 
| 
      
 258 
     | 
    
         
            +
            		# the block is a validator.  We need to make sure that the user didn't
         
     | 
| 
      
 259 
     | 
    
         
            +
            		# enter '~', because if they did, it's nil and we should move on.  If
         
     | 
| 
      
 260 
     | 
    
         
            +
            		# they didn't, then call the block.
         
     | 
| 
      
 261 
     | 
    
         
            +
            		if block_given? && response != '~' && ! yield( response )
         
     | 
| 
      
 262 
     | 
    
         
            +
            			error_message( failure_msg + "\n\n" )
         
     | 
| 
      
 263 
     | 
    
         
            +
            			response = nil
         
     | 
| 
      
 264 
     | 
    
         
            +
            		end
         
     | 
| 
      
 265 
     | 
    
         
            +
            	end while response.nil?
         
     | 
| 
      
 266 
     | 
    
         
            +
             
     | 
| 
      
 267 
     | 
    
         
            +
            	return nil if response == '~'
         
     | 
| 
      
 268 
     | 
    
         
            +
            	return response
         
     | 
| 
      
 269 
     | 
    
         
            +
            end
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
             
     | 
| 
      
 272 
     | 
    
         
            +
            ### Prompt for an array of values
         
     | 
| 
      
 273 
     | 
    
         
            +
            def prompt_for_multiple_values( label, default=nil )
         
     | 
| 
      
 274 
     | 
    
         
            +
                $stderr.puts( MULTILINE_PROMPT % [label] )
         
     | 
| 
      
 275 
     | 
    
         
            +
                if default
         
     | 
| 
      
 276 
     | 
    
         
            +
            		$stderr.puts "Enter a single blank line to keep the default:\n  %p" % [ default ]
         
     | 
| 
      
 277 
     | 
    
         
            +
            	end
         
     | 
| 
      
 278 
     | 
    
         
            +
             
     | 
| 
      
 279 
     | 
    
         
            +
                results = []
         
     | 
| 
      
 280 
     | 
    
         
            +
                result = nil
         
     | 
| 
      
 281 
     | 
    
         
            +
                
         
     | 
| 
      
 282 
     | 
    
         
            +
                begin
         
     | 
| 
      
 283 
     | 
    
         
            +
                    result = Readline.readline( make_prompt_string("> ") )
         
     | 
| 
      
 284 
     | 
    
         
            +
            		if result.nil? || result.empty?
         
     | 
| 
      
 285 
     | 
    
         
            +
            			results << default if default && results.empty?
         
     | 
| 
      
 286 
     | 
    
         
            +
            		else
         
     | 
| 
      
 287 
     | 
    
         
            +
                    	results << result 
         
     | 
| 
      
 288 
     | 
    
         
            +
            		end
         
     | 
| 
      
 289 
     | 
    
         
            +
                end until result.nil? || result.empty?
         
     | 
| 
      
 290 
     | 
    
         
            +
                
         
     | 
| 
      
 291 
     | 
    
         
            +
                return results.flatten
         
     | 
| 
      
 292 
     | 
    
         
            +
            end
         
     | 
| 
      
 293 
     | 
    
         
            +
             
     | 
| 
      
 294 
     | 
    
         
            +
             
     | 
| 
      
 295 
     | 
    
         
            +
            ### Turn echo and masking of input on/off. 
         
     | 
| 
      
 296 
     | 
    
         
            +
            def noecho( masked=false )
         
     | 
| 
      
 297 
     | 
    
         
            +
            	require 'termios'
         
     | 
| 
      
 298 
     | 
    
         
            +
             
     | 
| 
      
 299 
     | 
    
         
            +
            	rval = nil
         
     | 
| 
      
 300 
     | 
    
         
            +
            	term = Termios.getattr( $stdin )
         
     | 
| 
      
 301 
     | 
    
         
            +
             
     | 
| 
      
 302 
     | 
    
         
            +
            	begin
         
     | 
| 
      
 303 
     | 
    
         
            +
            		newt = term.dup
         
     | 
| 
      
 304 
     | 
    
         
            +
            		newt.c_lflag &= ~Termios::ECHO
         
     | 
| 
      
 305 
     | 
    
         
            +
            		newt.c_lflag &= ~Termios::ICANON if masked
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
            		Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
            		rval = yield
         
     | 
| 
      
 310 
     | 
    
         
            +
            	ensure
         
     | 
| 
      
 311 
     | 
    
         
            +
            		Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
         
     | 
| 
      
 312 
     | 
    
         
            +
            	end
         
     | 
| 
      
 313 
     | 
    
         
            +
            	
         
     | 
| 
      
 314 
     | 
    
         
            +
            	return rval
         
     | 
| 
      
 315 
     | 
    
         
            +
            end
         
     | 
| 
      
 316 
     | 
    
         
            +
             
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
            ### Prompt the user for her password, turning off echo if the 'termios' module is
         
     | 
| 
      
 319 
     | 
    
         
            +
            ### available.
         
     | 
| 
      
 320 
     | 
    
         
            +
            def prompt_for_password( prompt="Password: " )
         
     | 
| 
      
 321 
     | 
    
         
            +
            	return noecho( true ) do
         
     | 
| 
      
 322 
     | 
    
         
            +
            		$stderr.print( prompt )
         
     | 
| 
      
 323 
     | 
    
         
            +
            		($stdin.gets || '').chomp
         
     | 
| 
      
 324 
     | 
    
         
            +
            	end
         
     | 
| 
      
 325 
     | 
    
         
            +
            end
         
     | 
| 
      
 326 
     | 
    
         
            +
             
     | 
| 
      
 327 
     | 
    
         
            +
             
     | 
| 
      
 328 
     | 
    
         
            +
            ### Display a description of a potentially-dangerous task, and prompt
         
     | 
| 
      
 329 
     | 
    
         
            +
            ### for confirmation. If the user answers with anything that begins
         
     | 
| 
      
 330 
     | 
    
         
            +
            ### with 'y', yield to the block. If +abort_on_decline+ is +true+,
         
     | 
| 
      
 331 
     | 
    
         
            +
            ### any non-'y' answer will fail with an error message.
         
     | 
| 
      
 332 
     | 
    
         
            +
            def ask_for_confirmation( description, abort_on_decline=true )
         
     | 
| 
      
 333 
     | 
    
         
            +
            	puts description
         
     | 
| 
      
 334 
     | 
    
         
            +
             
     | 
| 
      
 335 
     | 
    
         
            +
            	answer = prompt_with_default( "Continue?", 'n' ) do |input|
         
     | 
| 
      
 336 
     | 
    
         
            +
            		input =~ /^[yn]/i
         
     | 
| 
      
 337 
     | 
    
         
            +
            	end
         
     | 
| 
      
 338 
     | 
    
         
            +
             
     | 
| 
      
 339 
     | 
    
         
            +
            	if answer =~ /^y/i
         
     | 
| 
      
 340 
     | 
    
         
            +
            		return yield
         
     | 
| 
      
 341 
     | 
    
         
            +
            	elsif abort_on_decline
         
     | 
| 
      
 342 
     | 
    
         
            +
            		error "Aborted."
         
     | 
| 
      
 343 
     | 
    
         
            +
            		fail
         
     | 
| 
      
 344 
     | 
    
         
            +
            	end
         
     | 
| 
      
 345 
     | 
    
         
            +
             
     | 
| 
      
 346 
     | 
    
         
            +
            	return false
         
     | 
| 
      
 347 
     | 
    
         
            +
            end
         
     | 
| 
      
 348 
     | 
    
         
            +
            alias :prompt_for_confirmation :ask_for_confirmation
         
     | 
| 
      
 349 
     | 
    
         
            +
             
     | 
| 
      
 350 
     | 
    
         
            +
             
     | 
| 
      
 351 
     | 
    
         
            +
            ### Search line-by-line in the specified +file+ for the given +regexp+, returning the
         
     | 
| 
      
 352 
     | 
    
         
            +
            ### first match, or nil if no match was found. If the +regexp+ has any capture groups,
         
     | 
| 
      
 353 
     | 
    
         
            +
            ### those will be returned in an Array, else the whole matching line is returned.
         
     | 
| 
      
 354 
     | 
    
         
            +
            def find_pattern_in_file( regexp, file )
         
     | 
| 
      
 355 
     | 
    
         
            +
            	rval = nil
         
     | 
| 
      
 356 
     | 
    
         
            +
            	
         
     | 
| 
      
 357 
     | 
    
         
            +
            	File.open( file, 'r' ).each do |line|
         
     | 
| 
      
 358 
     | 
    
         
            +
            		if (( match = regexp.match(line) ))
         
     | 
| 
      
 359 
     | 
    
         
            +
            			rval = match.captures.empty? ? match[0] : match.captures
         
     | 
| 
      
 360 
     | 
    
         
            +
            			break
         
     | 
| 
      
 361 
     | 
    
         
            +
            		end
         
     | 
| 
      
 362 
     | 
    
         
            +
            	end
         
     | 
| 
      
 363 
     | 
    
         
            +
             
     | 
| 
      
 364 
     | 
    
         
            +
            	return rval
         
     | 
| 
      
 365 
     | 
    
         
            +
            end
         
     | 
| 
      
 366 
     | 
    
         
            +
             
     | 
| 
      
 367 
     | 
    
         
            +
             
     | 
| 
      
 368 
     | 
    
         
            +
            ### Invoke the user's editor on the given +filename+ and return the exit code
         
     | 
| 
      
 369 
     | 
    
         
            +
            ### from doing so.
         
     | 
| 
      
 370 
     | 
    
         
            +
            def edit( filename )
         
     | 
| 
      
 371 
     | 
    
         
            +
            	editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
         
     | 
| 
      
 372 
     | 
    
         
            +
            	system editor, filename
         
     | 
| 
      
 373 
     | 
    
         
            +
            	unless $?.success?
         
     | 
| 
      
 374 
     | 
    
         
            +
            		fail "Editor exited uncleanly."
         
     | 
| 
      
 375 
     | 
    
         
            +
            	end
         
     | 
| 
      
 376 
     | 
    
         
            +
            end
         
     | 
| 
      
 377 
     | 
    
         
            +
             
     | 
| 
      
 378 
     | 
    
         
            +
             
     | 
| 
      
 379 
     | 
    
         
            +
            ### Extract all the non Rake-target arguments from ARGV and return them.
         
     | 
| 
      
 380 
     | 
    
         
            +
            def get_target_args
         
     | 
| 
      
 381 
     | 
    
         
            +
            	args = ARGV.reject {|arg| arg =~ /^-/ || Rake::Task.task_defined?(arg) }
         
     | 
| 
      
 382 
     | 
    
         
            +
            	return args
         
     | 
| 
      
 383 
     | 
    
         
            +
            end
         
     | 
| 
      
 384 
     | 
    
         
            +
             
     | 
| 
      
 385 
     | 
    
         
            +
             
     | 
| 
      
 386 
     | 
    
         
            +
            ### Log a subdirectory change, execute a block, and exit the subdirectory
         
     | 
| 
      
 387 
     | 
    
         
            +
            def in_subdirectory( subdir )
         
     | 
| 
      
 388 
     | 
    
         
            +
            	block = Proc.new
         
     | 
| 
      
 389 
     | 
    
         
            +
            	
         
     | 
| 
      
 390 
     | 
    
         
            +
            	log "Entering #{subdir}"
         
     | 
| 
      
 391 
     | 
    
         
            +
            	Dir.chdir( subdir, &block )
         
     | 
| 
      
 392 
     | 
    
         
            +
            	log "Leaving #{subdir}"
         
     | 
| 
      
 393 
     | 
    
         
            +
            end
         
     | 
| 
      
 394 
     | 
    
         
            +
            	
         
     | 
| 
      
 395 
     | 
    
         
            +
             
     | 
    
        data/rake/manual.rb
    ADDED
    
    | 
         @@ -0,0 +1,755 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # 
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Manual-generation Rake tasks and classes
         
     | 
| 
      
 3 
     | 
    
         
            +
            # $Id: manual.rb 41 2008-08-28 16:46:09Z deveiant $
         
     | 
| 
      
 4 
     | 
    
         
            +
            # 
         
     | 
| 
      
 5 
     | 
    
         
            +
            # Authors:
         
     | 
| 
      
 6 
     | 
    
         
            +
            # * Michael Granger <ged@FaerieMUD.org>
         
     | 
| 
      
 7 
     | 
    
         
            +
            # * Mahlon E. Smith <mahlon@martini.nu>
         
     | 
| 
      
 8 
     | 
    
         
            +
            # 
         
     | 
| 
      
 9 
     | 
    
         
            +
            # This was born out of a frustration with other static HTML generation modules
         
     | 
| 
      
 10 
     | 
    
         
            +
            # and systems. I've tried webby, webgen, rote, staticweb, staticmatic, and
         
     | 
| 
      
 11 
     | 
    
         
            +
            # nanoc, but I didn't find any of them really suitable (except rote, which was
         
     | 
| 
      
 12 
     | 
    
         
            +
            # excellent but apparently isn't maintained and has a fundamental
         
     | 
| 
      
 13 
     | 
    
         
            +
            # incompatibilty with Rake because of some questionable monkeypatching.)
         
     | 
| 
      
 14 
     | 
    
         
            +
            # 
         
     | 
| 
      
 15 
     | 
    
         
            +
            # So, since nothing seemed to scratch my itch, I'm going to scratch it myself.
         
     | 
| 
      
 16 
     | 
    
         
            +
            # 
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 19 
     | 
    
         
            +
            require 'singleton'
         
     | 
| 
      
 20 
     | 
    
         
            +
            require 'rake/tasklib'
         
     | 
| 
      
 21 
     | 
    
         
            +
            require 'erb'
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            ### Namespace for Manual-generation classes
         
     | 
| 
      
 25 
     | 
    
         
            +
            module Manual
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            	### Manual page-generation class
         
     | 
| 
      
 28 
     | 
    
         
            +
            	class Page
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            		### An abstract filter class for manual content transformation.
         
     | 
| 
      
 31 
     | 
    
         
            +
            		class Filter
         
     | 
| 
      
 32 
     | 
    
         
            +
            			include Singleton
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            			# A list of inheriting classes, keyed by normalized name
         
     | 
| 
      
 35 
     | 
    
         
            +
            			@derivatives = {}
         
     | 
| 
      
 36 
     | 
    
         
            +
            			class << self; attr_reader :derivatives; end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            			### Inheritance callback -- keep track of all inheriting classes for
         
     | 
| 
      
 39 
     | 
    
         
            +
            			### later.
         
     | 
| 
      
 40 
     | 
    
         
            +
            			def self::inherited( subclass )
         
     | 
| 
      
 41 
     | 
    
         
            +
            				key = subclass.name.
         
     | 
| 
      
 42 
     | 
    
         
            +
            					sub( /^.*::/, '' ).
         
     | 
| 
      
 43 
     | 
    
         
            +
            					gsub( /[^[:alpha:]]+/, '_' ).
         
     | 
| 
      
 44 
     | 
    
         
            +
            					downcase.
         
     | 
| 
      
 45 
     | 
    
         
            +
            					sub( /filter$/, '' )
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            				self.derivatives[ key ] = subclass
         
     | 
| 
      
 48 
     | 
    
         
            +
            				self.derivatives[ key.to_sym ] = subclass
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            				super
         
     | 
| 
      
 51 
     | 
    
         
            +
            			end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            			### Export any static resources required by this filter to the given +output_dir+.
         
     | 
| 
      
 55 
     | 
    
         
            +
            			def export_resources( output_dir )
         
     | 
| 
      
 56 
     | 
    
         
            +
            				# No-op by default
         
     | 
| 
      
 57 
     | 
    
         
            +
            			end
         
     | 
| 
      
 58 
     | 
    
         
            +
            			
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            			### Process the +page+'s source with the filter and return the altered content.
         
     | 
| 
      
 61 
     | 
    
         
            +
            			def process( source, page, metadata )
         
     | 
| 
      
 62 
     | 
    
         
            +
            				raise NotImplementedError,
         
     | 
| 
      
 63 
     | 
    
         
            +
            					"%s does not implement the #process method" % [ self.class.name ]
         
     | 
| 
      
 64 
     | 
    
         
            +
            			end
         
     | 
| 
      
 65 
     | 
    
         
            +
            		end # class Filter
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            		### The default page configuration if none is specified.
         
     | 
| 
      
 69 
     | 
    
         
            +
            		DEFAULT_CONFIG = {
         
     | 
| 
      
 70 
     | 
    
         
            +
            			'filters' => [ 'erb', 'links', 'textile' ],
         
     | 
| 
      
 71 
     | 
    
         
            +
            			'layout'  => 'default.page',
         
     | 
| 
      
 72 
     | 
    
         
            +
            			'cleanup' => true,
         
     | 
| 
      
 73 
     | 
    
         
            +
            		  }.freeze
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            		# Pattern to match a source page with a YAML header
         
     | 
| 
      
 76 
     | 
    
         
            +
            		PAGE_WITH_YAML_HEADER = /
         
     | 
| 
      
 77 
     | 
    
         
            +
            			\A---\s*$	# It should should start with three hyphens
         
     | 
| 
      
 78 
     | 
    
         
            +
            			(.*?)		# ...have some YAML stuff
         
     | 
| 
      
 79 
     | 
    
         
            +
            			^---\s*$	# then have another three-hyphen line,
         
     | 
| 
      
 80 
     | 
    
         
            +
            			(.*)\Z		# then the rest of the document
         
     | 
| 
      
 81 
     | 
    
         
            +
            		  /xm
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
            		# Options to pass to libtidy
         
     | 
| 
      
 84 
     | 
    
         
            +
            		TIDY_OPTIONS = {
         
     | 
| 
      
 85 
     | 
    
         
            +
            			:show_warnings     => true,
         
     | 
| 
      
 86 
     | 
    
         
            +
            			:indent            => true,
         
     | 
| 
      
 87 
     | 
    
         
            +
            			:indent_attributes => false,
         
     | 
| 
      
 88 
     | 
    
         
            +
            			:indent_spaces     => 4,
         
     | 
| 
      
 89 
     | 
    
         
            +
            			:vertical_space    => true,
         
     | 
| 
      
 90 
     | 
    
         
            +
            			:tab_size          => 4,
         
     | 
| 
      
 91 
     | 
    
         
            +
            			:wrap_attributes   => true,
         
     | 
| 
      
 92 
     | 
    
         
            +
            			:wrap              => 100,
         
     | 
| 
      
 93 
     | 
    
         
            +
            			:char_encoding     => 'utf8'
         
     | 
| 
      
 94 
     | 
    
         
            +
            		  }
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
            		### Create a new page-generator for the given +sourcefile+, which will use
         
     | 
| 
      
 98 
     | 
    
         
            +
            		### ones of the templates in +layouts_dir+ as a wrapper. The +basepath+ 
         
     | 
| 
      
 99 
     | 
    
         
            +
            		### is the path to the base output directory, and the +catalog+ is the
         
     | 
| 
      
 100 
     | 
    
         
            +
            		### Manual::PageCatalog to which the page belongs.
         
     | 
| 
      
 101 
     | 
    
         
            +
            		def initialize( catalog, sourcefile, layouts_dir, basepath='.' )
         
     | 
| 
      
 102 
     | 
    
         
            +
            			@catalog     = catalog
         
     | 
| 
      
 103 
     | 
    
         
            +
            			@sourcefile  = Pathname.new( sourcefile )
         
     | 
| 
      
 104 
     | 
    
         
            +
            			@layouts_dir = Pathname.new( layouts_dir )
         
     | 
| 
      
 105 
     | 
    
         
            +
            			@basepath    = basepath
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
            			rawsource = @sourcefile.read
         
     | 
| 
      
 108 
     | 
    
         
            +
            			@config, @source = self.read_page_config( rawsource )
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
            			# $stderr.puts "Config is: %p" % [@config],
         
     | 
| 
      
 111 
     | 
    
         
            +
            			# 	"Source is: %p" % [ @source[0,100] ]
         
     | 
| 
      
 112 
     | 
    
         
            +
            			@filters = self.load_filters( @config['filters'] )
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            			super()
         
     | 
| 
      
 115 
     | 
    
         
            +
            		end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
            		######
         
     | 
| 
      
 119 
     | 
    
         
            +
            		public
         
     | 
| 
      
 120 
     | 
    
         
            +
            		######
         
     | 
| 
      
 121 
     | 
    
         
            +
            		
         
     | 
| 
      
 122 
     | 
    
         
            +
            		# The Manual::PageCatalog to which the page belongs
         
     | 
| 
      
 123 
     | 
    
         
            +
            		attr_reader :catalog
         
     | 
| 
      
 124 
     | 
    
         
            +
            		
         
     | 
| 
      
 125 
     | 
    
         
            +
            		# The relative path to the base directory, for prepending to page paths
         
     | 
| 
      
 126 
     | 
    
         
            +
            		attr_reader :basepath
         
     | 
| 
      
 127 
     | 
    
         
            +
            		
         
     | 
| 
      
 128 
     | 
    
         
            +
            		# The Pathname object that specifys the page source file
         
     | 
| 
      
 129 
     | 
    
         
            +
            		attr_reader :sourcefile
         
     | 
| 
      
 130 
     | 
    
         
            +
            		
         
     | 
| 
      
 131 
     | 
    
         
            +
            		# The configured layouts directory as a Pathname object.
         
     | 
| 
      
 132 
     | 
    
         
            +
            		attr_reader :layouts_dir
         
     | 
| 
      
 133 
     | 
    
         
            +
            		
         
     | 
| 
      
 134 
     | 
    
         
            +
            		# The page configuration, as read from its YAML header
         
     | 
| 
      
 135 
     | 
    
         
            +
            		attr_reader :config
         
     | 
| 
      
 136 
     | 
    
         
            +
            		
         
     | 
| 
      
 137 
     | 
    
         
            +
            		# The raw source of the page
         
     | 
| 
      
 138 
     | 
    
         
            +
            		attr_reader :source
         
     | 
| 
      
 139 
     | 
    
         
            +
            		
         
     | 
| 
      
 140 
     | 
    
         
            +
            		# The filters the page will use to render itself
         
     | 
| 
      
 141 
     | 
    
         
            +
            		attr_reader :filters
         
     | 
| 
      
 142 
     | 
    
         
            +
            		
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
            		### Generate HTML output from the page and return it.
         
     | 
| 
      
 145 
     | 
    
         
            +
            		def generate( metadata )
         
     | 
| 
      
 146 
     | 
    
         
            +
            			content = self.generate_content( @source, metadata )
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
            			layout = self.config['layout'].sub( /\.page$/, '' )
         
     | 
| 
      
 149 
     | 
    
         
            +
            			templatepath = @layouts_dir + "#{layout}.page"
         
     | 
| 
      
 150 
     | 
    
         
            +
            			template = ERB.new( templatepath.read )
         
     | 
| 
      
 151 
     | 
    
         
            +
            			page = self
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
            			html = template.result( binding() )
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
            			# Use Tidy to clean up the html if 'cleanup' is turned on, but remove the Tidy
         
     | 
| 
      
 156 
     | 
    
         
            +
            			# meta-generator propaganda/advertising.
         
     | 
| 
      
 157 
     | 
    
         
            +
            			html = self.cleanup( html ).sub( %r:<meta name="generator"[^>]*tidy[^>]*/>:im, '' ) if
         
     | 
| 
      
 158 
     | 
    
         
            +
            				self.config['cleanup']
         
     | 
| 
      
 159 
     | 
    
         
            +
            				
         
     | 
| 
      
 160 
     | 
    
         
            +
            			return html
         
     | 
| 
      
 161 
     | 
    
         
            +
            		end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
            		### Return the page title as specified in the YAML options
         
     | 
| 
      
 165 
     | 
    
         
            +
            		def title
         
     | 
| 
      
 166 
     | 
    
         
            +
            			return self.config['title'] || self.sourcefile.basename
         
     | 
| 
      
 167 
     | 
    
         
            +
            		end
         
     | 
| 
      
 168 
     | 
    
         
            +
            		
         
     | 
| 
      
 169 
     | 
    
         
            +
            		
         
     | 
| 
      
 170 
     | 
    
         
            +
            		### Run the various filters on the given input and return the transformed
         
     | 
| 
      
 171 
     | 
    
         
            +
            		### content.
         
     | 
| 
      
 172 
     | 
    
         
            +
            		def generate_content( input, metadata )
         
     | 
| 
      
 173 
     | 
    
         
            +
            			return @filters.inject( input ) do |source, filter|
         
     | 
| 
      
 174 
     | 
    
         
            +
            				filter.process( source, self, metadata )
         
     | 
| 
      
 175 
     | 
    
         
            +
            			end
         
     | 
| 
      
 176 
     | 
    
         
            +
            		end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
            		### Trim the YAML header from the provided page +source+, convert it to
         
     | 
| 
      
 180 
     | 
    
         
            +
            		### a Ruby object, and return it.
         
     | 
| 
      
 181 
     | 
    
         
            +
            		def read_page_config( source )
         
     | 
| 
      
 182 
     | 
    
         
            +
            			unless source =~ PAGE_WITH_YAML_HEADER
         
     | 
| 
      
 183 
     | 
    
         
            +
            				return DEFAULT_CONFIG.dup, source
         
     | 
| 
      
 184 
     | 
    
         
            +
            			end
         
     | 
| 
      
 185 
     | 
    
         
            +
            			
         
     | 
| 
      
 186 
     | 
    
         
            +
            			pageconfig = YAML.load( $1 )
         
     | 
| 
      
 187 
     | 
    
         
            +
            			source = $2
         
     | 
| 
      
 188 
     | 
    
         
            +
            			
         
     | 
| 
      
 189 
     | 
    
         
            +
            			return DEFAULT_CONFIG.merge( pageconfig ), source
         
     | 
| 
      
 190 
     | 
    
         
            +
            		end
         
     | 
| 
      
 191 
     | 
    
         
            +
            		
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
            		### Clean up and return the given HTML +source+.
         
     | 
| 
      
 194 
     | 
    
         
            +
            		def cleanup( source )
         
     | 
| 
      
 195 
     | 
    
         
            +
            			require 'tidy'
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
            			Tidy.path = '/usr/lib/libtidy.dylib'
         
     | 
| 
      
 198 
     | 
    
         
            +
            			Tidy.open( TIDY_OPTIONS ) do |tidy|
         
     | 
| 
      
 199 
     | 
    
         
            +
            				tidy.options.output_xhtml = true
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
            				xml = tidy.clean( source )
         
     | 
| 
      
 202 
     | 
    
         
            +
            				errors = tidy.errors
         
     | 
| 
      
 203 
     | 
    
         
            +
            				error_message( errors.join ) unless errors.empty?
         
     | 
| 
      
 204 
     | 
    
         
            +
            				trace tidy.diagnostics
         
     | 
| 
      
 205 
     | 
    
         
            +
            				return xml
         
     | 
| 
      
 206 
     | 
    
         
            +
            			end
         
     | 
| 
      
 207 
     | 
    
         
            +
            		rescue LoadError => err
         
     | 
| 
      
 208 
     | 
    
         
            +
            			trace "No cleanup: " + err.message
         
     | 
| 
      
 209 
     | 
    
         
            +
            			return source
         
     | 
| 
      
 210 
     | 
    
         
            +
            		end
         
     | 
| 
      
 211 
     | 
    
         
            +
            		
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
            		### Get (singleton) instances of the filters named in +filterlist+ and return them.
         
     | 
| 
      
 214 
     | 
    
         
            +
            		def load_filters( filterlist )
         
     | 
| 
      
 215 
     | 
    
         
            +
            			filterlist.flatten.collect do |key|
         
     | 
| 
      
 216 
     | 
    
         
            +
            				raise ArgumentError, "filter '#{key}' is not loaded" unless
         
     | 
| 
      
 217 
     | 
    
         
            +
            					Manual::Page::Filter.derivatives.key?( key )
         
     | 
| 
      
 218 
     | 
    
         
            +
            				Manual::Page::Filter.derivatives[ key ].instance
         
     | 
| 
      
 219 
     | 
    
         
            +
            			end
         
     | 
| 
      
 220 
     | 
    
         
            +
            		end
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
            		### Build the index relative to the receiving page and return it as a String
         
     | 
| 
      
 224 
     | 
    
         
            +
            		def make_index_html
         
     | 
| 
      
 225 
     | 
    
         
            +
            			items = [ '<div class="index">' ]
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
            			@catalog.traverse_page_hierarchy( self ) do |type, title, path|
         
     | 
| 
      
 228 
     | 
    
         
            +
            				case type
         
     | 
| 
      
 229 
     | 
    
         
            +
            				when :section
         
     | 
| 
      
 230 
     | 
    
         
            +
            					items << %Q{<div class="section">}
         
     | 
| 
      
 231 
     | 
    
         
            +
            					items << %Q{<h2><a href="#{self.basepath + path}/">#{title}</a></h2>}
         
     | 
| 
      
 232 
     | 
    
         
            +
            					items << '<ul class="index-section">'
         
     | 
| 
      
 233 
     | 
    
         
            +
            					
         
     | 
| 
      
 234 
     | 
    
         
            +
            				when :current_section
         
     | 
| 
      
 235 
     | 
    
         
            +
            					items << %Q{<div class="section current-section">}
         
     | 
| 
      
 236 
     | 
    
         
            +
            					items << %Q{<h2><a href="#{self.basepath + path}/">#{title}</a></h2>}
         
     | 
| 
      
 237 
     | 
    
         
            +
            					items << '<ul class="index-section current-index-section">'
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
            				when :section_end, :current_section_end
         
     | 
| 
      
 240 
     | 
    
         
            +
            					items << '</ul></div>'
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
      
 242 
     | 
    
         
            +
            				when :entry
         
     | 
| 
      
 243 
     | 
    
         
            +
            					items << %Q{<li><a href="#{self.basepath + path}.html">#{title}</a></li>}
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
            				when :current_entry
         
     | 
| 
      
 246 
     | 
    
         
            +
            					items << %Q{<li class="current-entry">#{title}</li>}
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
            				else
         
     | 
| 
      
 249 
     | 
    
         
            +
            					raise "Unknown index entry type %p" % [ type ]
         
     | 
| 
      
 250 
     | 
    
         
            +
            				end
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
            			end
         
     | 
| 
      
 253 
     | 
    
         
            +
             
     | 
| 
      
 254 
     | 
    
         
            +
            			items << '</div>'
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
            			return items.join("\n")
         
     | 
| 
      
 257 
     | 
    
         
            +
            		end
         
     | 
| 
      
 258 
     | 
    
         
            +
             
     | 
| 
      
 259 
     | 
    
         
            +
            	end
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
            	### A catalog of Manual::Page objects that can be referenced by various criteria.
         
     | 
| 
      
 263 
     | 
    
         
            +
            	class PageCatalog
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
            		### Create a new PageCatalog that will load Manual::Page objects for .page files 
         
     | 
| 
      
 266 
     | 
    
         
            +
            		### in the specified +sourcedir+.
         
     | 
| 
      
 267 
     | 
    
         
            +
            		def initialize( sourcedir, layoutsdir )			
         
     | 
| 
      
 268 
     | 
    
         
            +
            			@sourcedir = sourcedir
         
     | 
| 
      
 269 
     | 
    
         
            +
            			@layoutsdir = layoutsdir
         
     | 
| 
      
 270 
     | 
    
         
            +
            			
         
     | 
| 
      
 271 
     | 
    
         
            +
            			@pages       = []
         
     | 
| 
      
 272 
     | 
    
         
            +
            			@path_index  = {}
         
     | 
| 
      
 273 
     | 
    
         
            +
            			@uri_index   = {}
         
     | 
| 
      
 274 
     | 
    
         
            +
            			@title_index = {}
         
     | 
| 
      
 275 
     | 
    
         
            +
            			@hierarchy   = {}
         
     | 
| 
      
 276 
     | 
    
         
            +
            			
         
     | 
| 
      
 277 
     | 
    
         
            +
            			self.find_and_load_pages
         
     | 
| 
      
 278 
     | 
    
         
            +
            		end
         
     | 
| 
      
 279 
     | 
    
         
            +
            		
         
     | 
| 
      
 280 
     | 
    
         
            +
            		
         
     | 
| 
      
 281 
     | 
    
         
            +
            		######
         
     | 
| 
      
 282 
     | 
    
         
            +
            		public
         
     | 
| 
      
 283 
     | 
    
         
            +
            		######
         
     | 
| 
      
 284 
     | 
    
         
            +
             
     | 
| 
      
 285 
     | 
    
         
            +
            		# An index of the pages in the catalog by Pathname
         
     | 
| 
      
 286 
     | 
    
         
            +
            		attr_reader :path_index
         
     | 
| 
      
 287 
     | 
    
         
            +
            		
         
     | 
| 
      
 288 
     | 
    
         
            +
            		# An index of the pages in the catalog by title
         
     | 
| 
      
 289 
     | 
    
         
            +
            		attr_reader :title_index
         
     | 
| 
      
 290 
     | 
    
         
            +
            		
         
     | 
| 
      
 291 
     | 
    
         
            +
            		# An index of the pages in the catalog by the URI of their source relative to the source 
         
     | 
| 
      
 292 
     | 
    
         
            +
            		# directory
         
     | 
| 
      
 293 
     | 
    
         
            +
            		attr_reader :uri_index
         
     | 
| 
      
 294 
     | 
    
         
            +
            		
         
     | 
| 
      
 295 
     | 
    
         
            +
            		# The hierarchy of pages in the catalog, suitable for generating an on-page
         
     | 
| 
      
 296 
     | 
    
         
            +
            		attr_reader :hierarchy
         
     | 
| 
      
 297 
     | 
    
         
            +
            		
         
     | 
| 
      
 298 
     | 
    
         
            +
            		# An Array of all Manual::Page objects found
         
     | 
| 
      
 299 
     | 
    
         
            +
            		attr_reader :pages
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
            		# The Pathname location of the .page files.
         
     | 
| 
      
 302 
     | 
    
         
            +
            		attr_reader :sourcedir
         
     | 
| 
      
 303 
     | 
    
         
            +
            		
         
     | 
| 
      
 304 
     | 
    
         
            +
            		# The Pathname location of look and feel templates.
         
     | 
| 
      
 305 
     | 
    
         
            +
            		attr_reader :layoutsdir
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
            		
         
     | 
| 
      
 308 
     | 
    
         
            +
            		### Traverse the catalog's #hierarchy, yielding to the given +builder+
         
     | 
| 
      
 309 
     | 
    
         
            +
            		### block for each entry, as well as each time a sub-hash is entered or
         
     | 
| 
      
 310 
     | 
    
         
            +
            		### exited, setting the +type+ appropriately. Valid values for +type+ are:
         
     | 
| 
      
 311 
     | 
    
         
            +
            		###	 
         
     | 
| 
      
 312 
     | 
    
         
            +
            		###		:entry, :section, :section_end
         
     | 
| 
      
 313 
     | 
    
         
            +
            		###
         
     | 
| 
      
 314 
     | 
    
         
            +
            		### If the optional +from+ value is given, it should be the Manual::Page object
         
     | 
| 
      
 315 
     | 
    
         
            +
            		### which is considered "current"; if the +from+ object is the same as the 
         
     | 
| 
      
 316 
     | 
    
         
            +
            		### hierarchy entry being yielded, it will be yielded with the +type+ set to 
         
     | 
| 
      
 317 
     | 
    
         
            +
            		### one of:
         
     | 
| 
      
 318 
     | 
    
         
            +
            		### 
         
     | 
| 
      
 319 
     | 
    
         
            +
            		###     :current_entry, :current_section, :current_section_end
         
     | 
| 
      
 320 
     | 
    
         
            +
            		###
         
     | 
| 
      
 321 
     | 
    
         
            +
            		### each of which correspond to the like-named type from above.
         
     | 
| 
      
 322 
     | 
    
         
            +
            		def traverse_page_hierarchy( from=nil, &builder ) # :yields: type, title, path
         
     | 
| 
      
 323 
     | 
    
         
            +
            			raise LocalJumpError, "no block given" unless builder
         
     | 
| 
      
 324 
     | 
    
         
            +
            			self.traverse_hierarchy( Pathname.new(''), self.hierarchy, from, &builder )
         
     | 
| 
      
 325 
     | 
    
         
            +
            		end
         
     | 
| 
      
 326 
     | 
    
         
            +
             
     | 
| 
      
 327 
     | 
    
         
            +
             
     | 
| 
      
 328 
     | 
    
         
            +
            		#########
         
     | 
| 
      
 329 
     | 
    
         
            +
            		protected
         
     | 
| 
      
 330 
     | 
    
         
            +
            		#########
         
     | 
| 
      
 331 
     | 
    
         
            +
             
     | 
| 
      
 332 
     | 
    
         
            +
            		### Sort and traverse the specified +hash+ recursively, yielding for each entry.
         
     | 
| 
      
 333 
     | 
    
         
            +
            		def traverse_hierarchy( path, hash, from=nil, &builder )
         
     | 
| 
      
 334 
     | 
    
         
            +
            			# Now generate the index in the sorted order
         
     | 
| 
      
 335 
     | 
    
         
            +
            			sort_hierarchy( hash ).each do |subpath, page_or_section|
         
     | 
| 
      
 336 
     | 
    
         
            +
            				if page_or_section.is_a?( Hash )
         
     | 
| 
      
 337 
     | 
    
         
            +
            					self.handle_section_callback( path + subpath, page_or_section, from, &builder )
         
     | 
| 
      
 338 
     | 
    
         
            +
            				else
         
     | 
| 
      
 339 
     | 
    
         
            +
            					next if subpath == INDEX_PATH
         
     | 
| 
      
 340 
     | 
    
         
            +
            					self.handle_page_callback( path + subpath, page_or_section, from, &builder )
         
     | 
| 
      
 341 
     | 
    
         
            +
            				end
         
     | 
| 
      
 342 
     | 
    
         
            +
            			end
         
     | 
| 
      
 343 
     | 
    
         
            +
            		end
         
     | 
| 
      
 344 
     | 
    
         
            +
             
     | 
| 
      
 345 
     | 
    
         
            +
             
     | 
| 
      
 346 
     | 
    
         
            +
            		### Return the specified hierarchy of pages as a sorted Array of tuples. 
         
     | 
| 
      
 347 
     | 
    
         
            +
            		### Sort the hierarchy using the 'index' config value of either the
         
     | 
| 
      
 348 
     | 
    
         
            +
            		### page, or the directory's index page if it's a directory.
         
     | 
| 
      
 349 
     | 
    
         
            +
            		def sort_hierarchy( hierarchy )
         
     | 
| 
      
 350 
     | 
    
         
            +
            			hierarchy.sort_by do |subpath, page_or_section|
         
     | 
| 
      
 351 
     | 
    
         
            +
             
     | 
| 
      
 352 
     | 
    
         
            +
            				# Directory
         
     | 
| 
      
 353 
     | 
    
         
            +
            				if page_or_section.is_a?( Hash )
         
     | 
| 
      
 354 
     | 
    
         
            +
             
     | 
| 
      
 355 
     | 
    
         
            +
            					# Use the index of the index page if it exists
         
     | 
| 
      
 356 
     | 
    
         
            +
            					if page_or_section[INDEX_PATH]
         
     | 
| 
      
 357 
     | 
    
         
            +
            						idx = page_or_section[INDEX_PATH].config['index']
         
     | 
| 
      
 358 
     | 
    
         
            +
            						trace "Index page's index for directory '%s' is: %p" % [ subpath, idx ]
         
     | 
| 
      
 359 
     | 
    
         
            +
            						idx || subpath
         
     | 
| 
      
 360 
     | 
    
         
            +
            					else
         
     | 
| 
      
 361 
     | 
    
         
            +
            						trace "Using the path for the sort of directory %p" % [ subpath ]
         
     | 
| 
      
 362 
     | 
    
         
            +
            						subpath
         
     | 
| 
      
 363 
     | 
    
         
            +
            					end
         
     | 
| 
      
 364 
     | 
    
         
            +
            					
         
     | 
| 
      
 365 
     | 
    
         
            +
            				# Page
         
     | 
| 
      
 366 
     | 
    
         
            +
            				else
         
     | 
| 
      
 367 
     | 
    
         
            +
            					if subpath == INDEX_PATH
         
     | 
| 
      
 368 
     | 
    
         
            +
            						trace "Sort index for index page %p is 0" % [ subpath ]
         
     | 
| 
      
 369 
     | 
    
         
            +
            						0
         
     | 
| 
      
 370 
     | 
    
         
            +
            					else
         
     | 
| 
      
 371 
     | 
    
         
            +
            						idx = page_or_section.config['index']
         
     | 
| 
      
 372 
     | 
    
         
            +
            						trace "Sort index for page %p is: %p" % [ subpath, idx ]
         
     | 
| 
      
 373 
     | 
    
         
            +
            						idx || subpath
         
     | 
| 
      
 374 
     | 
    
         
            +
            					end
         
     | 
| 
      
 375 
     | 
    
         
            +
            				end
         
     | 
| 
      
 376 
     | 
    
         
            +
             
     | 
| 
      
 377 
     | 
    
         
            +
            			end # sort_by
         
     | 
| 
      
 378 
     | 
    
         
            +
            		end
         
     | 
| 
      
 379 
     | 
    
         
            +
            		
         
     | 
| 
      
 380 
     | 
    
         
            +
             
     | 
| 
      
 381 
     | 
    
         
            +
            		INDEX_PATH = Pathname.new('index')
         
     | 
| 
      
 382 
     | 
    
         
            +
             
     | 
| 
      
 383 
     | 
    
         
            +
            		### Build up the data structures necessary for calling the +builder+ callback
         
     | 
| 
      
 384 
     | 
    
         
            +
            		### for an index section and call it, then recurse into the section contents.
         
     | 
| 
      
 385 
     | 
    
         
            +
            		def handle_section_callback( path, section, from=nil, &builder )
         
     | 
| 
      
 386 
     | 
    
         
            +
            			from_current = false
         
     | 
| 
      
 387 
     | 
    
         
            +
            			
         
     | 
| 
      
 388 
     | 
    
         
            +
            			# Call the callback with :section -- determine the section title from
         
     | 
| 
      
 389 
     | 
    
         
            +
            			# the 'index.page' file underneath it, or the directory name if no 
         
     | 
| 
      
 390 
     | 
    
         
            +
            			# index.page exists.
         
     | 
| 
      
 391 
     | 
    
         
            +
            			if section.key?( INDEX_PATH )
         
     | 
| 
      
 392 
     | 
    
         
            +
            				if section[INDEX_PATH] == from
         
     | 
| 
      
 393 
     | 
    
         
            +
            					from_current = true
         
     | 
| 
      
 394 
     | 
    
         
            +
            					builder.call( :current_section, section[INDEX_PATH].title, path )
         
     | 
| 
      
 395 
     | 
    
         
            +
            				else
         
     | 
| 
      
 396 
     | 
    
         
            +
            					builder.call( :section, section[INDEX_PATH].title, path )
         
     | 
| 
      
 397 
     | 
    
         
            +
            				end
         
     | 
| 
      
 398 
     | 
    
         
            +
            			else
         
     | 
| 
      
 399 
     | 
    
         
            +
            				title = File.dirname( path ).gsub( /_/, ' ' )
         
     | 
| 
      
 400 
     | 
    
         
            +
            				builder.call( :section, title, path )
         
     | 
| 
      
 401 
     | 
    
         
            +
            			end
         
     | 
| 
      
 402 
     | 
    
         
            +
            			
         
     | 
| 
      
 403 
     | 
    
         
            +
            			# Recurse
         
     | 
| 
      
 404 
     | 
    
         
            +
            			self.traverse_hierarchy( path, section, from, &builder )
         
     | 
| 
      
 405 
     | 
    
         
            +
            			
         
     | 
| 
      
 406 
     | 
    
         
            +
            			# Call the callback with :section_end
         
     | 
| 
      
 407 
     | 
    
         
            +
            			if from_current
         
     | 
| 
      
 408 
     | 
    
         
            +
            				builder.call( :current_section_end, '', path )
         
     | 
| 
      
 409 
     | 
    
         
            +
            			else
         
     | 
| 
      
 410 
     | 
    
         
            +
            				builder.call( :section_end, '', path )
         
     | 
| 
      
 411 
     | 
    
         
            +
            			end
         
     | 
| 
      
 412 
     | 
    
         
            +
            		end
         
     | 
| 
      
 413 
     | 
    
         
            +
            		
         
     | 
| 
      
 414 
     | 
    
         
            +
            		
         
     | 
| 
      
 415 
     | 
    
         
            +
            		### Yield the specified +page+ to the builder
         
     | 
| 
      
 416 
     | 
    
         
            +
            		def handle_page_callback( path, page, from=nil )
         
     | 
| 
      
 417 
     | 
    
         
            +
            			if from == page
         
     | 
| 
      
 418 
     | 
    
         
            +
            				yield( :current_entry, page.title, path )
         
     | 
| 
      
 419 
     | 
    
         
            +
            			else
         
     | 
| 
      
 420 
     | 
    
         
            +
            				yield( :entry, page.title, path )
         
     | 
| 
      
 421 
     | 
    
         
            +
            			end
         
     | 
| 
      
 422 
     | 
    
         
            +
            		end
         
     | 
| 
      
 423 
     | 
    
         
            +
            		
         
     | 
| 
      
 424 
     | 
    
         
            +
             
     | 
| 
      
 425 
     | 
    
         
            +
            		### Find and store
         
     | 
| 
      
 426 
     | 
    
         
            +
            		
         
     | 
| 
      
 427 
     | 
    
         
            +
            		### Find all .page files under the configured +sourcedir+ and create a new
         
     | 
| 
      
 428 
     | 
    
         
            +
            		### Manual::Page object for each one.
         
     | 
| 
      
 429 
     | 
    
         
            +
            		def find_and_load_pages
         
     | 
| 
      
 430 
     | 
    
         
            +
            			Pathname.glob( @sourcedir + '**/*.page' ).each do |pagefile|
         
     | 
| 
      
 431 
     | 
    
         
            +
            				path_to_base = @sourcedir.relative_path_from( pagefile.dirname )
         
     | 
| 
      
 432 
     | 
    
         
            +
             
     | 
| 
      
 433 
     | 
    
         
            +
            				page = Manual::Page.new( self, pagefile, @layoutsdir, path_to_base )
         
     | 
| 
      
 434 
     | 
    
         
            +
            				hierpath = pagefile.relative_path_from( @sourcedir )
         
     | 
| 
      
 435 
     | 
    
         
            +
             
     | 
| 
      
 436 
     | 
    
         
            +
            				@pages << page
         
     | 
| 
      
 437 
     | 
    
         
            +
            				@path_index[ pagefile ]     = page
         
     | 
| 
      
 438 
     | 
    
         
            +
            				@title_index[ page.title ]  = page
         
     | 
| 
      
 439 
     | 
    
         
            +
            				@uri_index[ hierpath.to_s ] = page
         
     | 
| 
      
 440 
     | 
    
         
            +
            				
         
     | 
| 
      
 441 
     | 
    
         
            +
            				# Place the page in the page hierarchy by using inject to find and/or create the 
         
     | 
| 
      
 442 
     | 
    
         
            +
            				# necessary subhashes. The last run of inject will return the leaf hash in which
         
     | 
| 
      
 443 
     | 
    
         
            +
            				# the page will live
         
     | 
| 
      
 444 
     | 
    
         
            +
            				section = hierpath.dirname.split[1..-1].inject( @hierarchy ) do |hier, component|
         
     | 
| 
      
 445 
     | 
    
         
            +
            					hier[ component ] ||= {}
         
     | 
| 
      
 446 
     | 
    
         
            +
            					hier[ component ]
         
     | 
| 
      
 447 
     | 
    
         
            +
            				end
         
     | 
| 
      
 448 
     | 
    
         
            +
             
     | 
| 
      
 449 
     | 
    
         
            +
            				section[ pagefile.basename('.page') ] = page
         
     | 
| 
      
 450 
     | 
    
         
            +
            			end
         
     | 
| 
      
 451 
     | 
    
         
            +
            		end
         
     | 
| 
      
 452 
     | 
    
         
            +
            		
         
     | 
| 
      
 453 
     | 
    
         
            +
            	end
         
     | 
| 
      
 454 
     | 
    
         
            +
             
     | 
| 
      
 455 
     | 
    
         
            +
             
     | 
| 
      
 456 
     | 
    
         
            +
            	### A Textile filter for the manual generation tasklib, implemented using RedCloth.
         
     | 
| 
      
 457 
     | 
    
         
            +
            	class TextileFilter < Manual::Page::Filter
         
     | 
| 
      
 458 
     | 
    
         
            +
             
     | 
| 
      
 459 
     | 
    
         
            +
            		### Load RedCloth when the filter is first created
         
     | 
| 
      
 460 
     | 
    
         
            +
            		def initialize( *args )
         
     | 
| 
      
 461 
     | 
    
         
            +
            			require 'redcloth'
         
     | 
| 
      
 462 
     | 
    
         
            +
            			super
         
     | 
| 
      
 463 
     | 
    
         
            +
            		end
         
     | 
| 
      
 464 
     | 
    
         
            +
            		
         
     | 
| 
      
 465 
     | 
    
         
            +
             
     | 
| 
      
 466 
     | 
    
         
            +
            		### Process the given +source+ as Textile and return the resulting HTML
         
     | 
| 
      
 467 
     | 
    
         
            +
            		### fragment.
         
     | 
| 
      
 468 
     | 
    
         
            +
            		def process( source, *ignored )
         
     | 
| 
      
 469 
     | 
    
         
            +
            			formatter = RedCloth::TextileDoc.new( source )
         
     | 
| 
      
 470 
     | 
    
         
            +
            			formatter.hard_breaks = false
         
     | 
| 
      
 471 
     | 
    
         
            +
            			formatter.no_span_caps = true
         
     | 
| 
      
 472 
     | 
    
         
            +
            			return formatter.to_html
         
     | 
| 
      
 473 
     | 
    
         
            +
            		end
         
     | 
| 
      
 474 
     | 
    
         
            +
             
     | 
| 
      
 475 
     | 
    
         
            +
            	end
         
     | 
| 
      
 476 
     | 
    
         
            +
             
     | 
| 
      
 477 
     | 
    
         
            +
             
     | 
| 
      
 478 
     | 
    
         
            +
            	### An ERB filter for the manual generation tasklib, implemented using Erubis.
         
     | 
| 
      
 479 
     | 
    
         
            +
            	class ErbFilter < Manual::Page::Filter
         
     | 
| 
      
 480 
     | 
    
         
            +
             
     | 
| 
      
 481 
     | 
    
         
            +
            		### Process the given +source+ as ERB and return the resulting HTML
         
     | 
| 
      
 482 
     | 
    
         
            +
            		### fragment.
         
     | 
| 
      
 483 
     | 
    
         
            +
            		def process( source, page, metadata )
         
     | 
| 
      
 484 
     | 
    
         
            +
            			template_name = page.sourcefile.basename
         
     | 
| 
      
 485 
     | 
    
         
            +
            			template = ERB.new( source )
         
     | 
| 
      
 486 
     | 
    
         
            +
            			return template.result( binding() )
         
     | 
| 
      
 487 
     | 
    
         
            +
            		end
         
     | 
| 
      
 488 
     | 
    
         
            +
             
     | 
| 
      
 489 
     | 
    
         
            +
            	end
         
     | 
| 
      
 490 
     | 
    
         
            +
             
     | 
| 
      
 491 
     | 
    
         
            +
             
     | 
| 
      
 492 
     | 
    
         
            +
            	### Manual generation task library
         
     | 
| 
      
 493 
     | 
    
         
            +
            	class GenTask < Rake::TaskLib
         
     | 
| 
      
 494 
     | 
    
         
            +
            		
         
     | 
| 
      
 495 
     | 
    
         
            +
            		# Default values for task config variables
         
     | 
| 
      
 496 
     | 
    
         
            +
            		DEFAULT_NAME         = :manual
         
     | 
| 
      
 497 
     | 
    
         
            +
            		DEFAULT_BASE_DIR     = Pathname.new( 'docs/manual' )
         
     | 
| 
      
 498 
     | 
    
         
            +
            		DEFAULT_SOURCE_DIR   = 'source'
         
     | 
| 
      
 499 
     | 
    
         
            +
            		DEFAULT_LAYOUTS_DIR  = 'layouts'
         
     | 
| 
      
 500 
     | 
    
         
            +
            		DEFAULT_OUTPUT_DIR   = 'output'
         
     | 
| 
      
 501 
     | 
    
         
            +
            		DEFAULT_RESOURCE_DIR = 'resources'
         
     | 
| 
      
 502 
     | 
    
         
            +
            		DEFAULT_LIB_DIR      = 'lib'
         
     | 
| 
      
 503 
     | 
    
         
            +
            		DEFAULT_METADATA     = OpenStruct.new
         
     | 
| 
      
 504 
     | 
    
         
            +
            		
         
     | 
| 
      
 505 
     | 
    
         
            +
             
     | 
| 
      
 506 
     | 
    
         
            +
            		### Define a new manual-generation task with the given +name+.
         
     | 
| 
      
 507 
     | 
    
         
            +
            		def initialize( name=:manual )
         
     | 
| 
      
 508 
     | 
    
         
            +
            			@name           = name
         
     | 
| 
      
 509 
     | 
    
         
            +
             
     | 
| 
      
 510 
     | 
    
         
            +
            			@source_dir		= DEFAULT_SOURCE_DIR
         
     | 
| 
      
 511 
     | 
    
         
            +
            			@layouts_dir	= DEFAULT_LAYOUTS_DIR
         
     | 
| 
      
 512 
     | 
    
         
            +
            			@output_dir		= DEFAULT_OUTPUT_DIR
         
     | 
| 
      
 513 
     | 
    
         
            +
            			@resource_dir	= DEFAULT_RESOURCE_DIR
         
     | 
| 
      
 514 
     | 
    
         
            +
            			@lib_dir	    = DEFAULT_LIB_DIR
         
     | 
| 
      
 515 
     | 
    
         
            +
            			@metadata		= DEFAULT_METADATA
         
     | 
| 
      
 516 
     | 
    
         
            +
            			
         
     | 
| 
      
 517 
     | 
    
         
            +
            			yield( self ) if block_given?
         
     | 
| 
      
 518 
     | 
    
         
            +
            			
         
     | 
| 
      
 519 
     | 
    
         
            +
            			self.define
         
     | 
| 
      
 520 
     | 
    
         
            +
            		end
         
     | 
| 
      
 521 
     | 
    
         
            +
            		
         
     | 
| 
      
 522 
     | 
    
         
            +
            		
         
     | 
| 
      
 523 
     | 
    
         
            +
            		######
         
     | 
| 
      
 524 
     | 
    
         
            +
            		public
         
     | 
| 
      
 525 
     | 
    
         
            +
            		######
         
     | 
| 
      
 526 
     | 
    
         
            +
             
     | 
| 
      
 527 
     | 
    
         
            +
            		attr_accessor :base_dir,
         
     | 
| 
      
 528 
     | 
    
         
            +
            			:source_dir,
         
     | 
| 
      
 529 
     | 
    
         
            +
            			:layouts_dir,
         
     | 
| 
      
 530 
     | 
    
         
            +
            			:output_dir,
         
     | 
| 
      
 531 
     | 
    
         
            +
            			:resource_dir,
         
     | 
| 
      
 532 
     | 
    
         
            +
            			:lib_dir,
         
     | 
| 
      
 533 
     | 
    
         
            +
            			:metadata
         
     | 
| 
      
 534 
     | 
    
         
            +
             
     | 
| 
      
 535 
     | 
    
         
            +
            		attr_reader :name
         
     | 
| 
      
 536 
     | 
    
         
            +
             
     | 
| 
      
 537 
     | 
    
         
            +
             
     | 
| 
      
 538 
     | 
    
         
            +
            		### Set up the tasks for building the manual
         
     | 
| 
      
 539 
     | 
    
         
            +
            		def define
         
     | 
| 
      
 540 
     | 
    
         
            +
             
     | 
| 
      
 541 
     | 
    
         
            +
            			# Set up a description if the caller hasn't already defined one
         
     | 
| 
      
 542 
     | 
    
         
            +
            			unless Rake.application.last_comment
         
     | 
| 
      
 543 
     | 
    
         
            +
            				desc "Generate the manual"
         
     | 
| 
      
 544 
     | 
    
         
            +
            			end
         
     | 
| 
      
 545 
     | 
    
         
            +
             
     | 
| 
      
 546 
     | 
    
         
            +
            			# Make Pathnames of the directories relative to the base_dir
         
     | 
| 
      
 547 
     | 
    
         
            +
            			basedir     = Pathname.new( @base_dir )
         
     | 
| 
      
 548 
     | 
    
         
            +
            			sourcedir   = basedir + @source_dir
         
     | 
| 
      
 549 
     | 
    
         
            +
            			layoutsdir  = basedir + @layouts_dir
         
     | 
| 
      
 550 
     | 
    
         
            +
            			outputdir   = @output_dir
         
     | 
| 
      
 551 
     | 
    
         
            +
            			resourcedir = basedir + @resource_dir
         
     | 
| 
      
 552 
     | 
    
         
            +
            			libdir      = basedir + @lib_dir
         
     | 
| 
      
 553 
     | 
    
         
            +
             
     | 
| 
      
 554 
     | 
    
         
            +
            			load_filter_libraries( libdir )
         
     | 
| 
      
 555 
     | 
    
         
            +
            			catalog = Manual::PageCatalog.new( sourcedir, layoutsdir )
         
     | 
| 
      
 556 
     | 
    
         
            +
            			
         
     | 
| 
      
 557 
     | 
    
         
            +
            			# Declare the tasks outside the namespace that point in
         
     | 
| 
      
 558 
     | 
    
         
            +
            			task @name => "#@name:build"
         
     | 
| 
      
 559 
     | 
    
         
            +
            			task "clobber_#@name" => "#@name:clobber"
         
     | 
| 
      
 560 
     | 
    
         
            +
             
     | 
| 
      
 561 
     | 
    
         
            +
            			namespace( self.name ) do
         
     | 
| 
      
 562 
     | 
    
         
            +
            				setup_resource_copy_tasks( resourcedir, outputdir )
         
     | 
| 
      
 563 
     | 
    
         
            +
            				manual_pages = setup_page_conversion_tasks( sourcedir, outputdir, catalog )
         
     | 
| 
      
 564 
     | 
    
         
            +
            				
         
     | 
| 
      
 565 
     | 
    
         
            +
            				desc "Build the manual"
         
     | 
| 
      
 566 
     | 
    
         
            +
            				task :build => [ :rdoc, :copy_resources, :generate_pages ]
         
     | 
| 
      
 567 
     | 
    
         
            +
            				
         
     | 
| 
      
 568 
     | 
    
         
            +
            				task :clobber do
         
     | 
| 
      
 569 
     | 
    
         
            +
            					RakeFileUtils.verbose( $verbose ) do
         
     | 
| 
      
 570 
     | 
    
         
            +
            						rm_f manual_pages.to_a
         
     | 
| 
      
 571 
     | 
    
         
            +
            					end
         
     | 
| 
      
 572 
     | 
    
         
            +
            					remove_dir( outputdir ) if ( outputdir + '.buildtime' ).exist?
         
     | 
| 
      
 573 
     | 
    
         
            +
            				end
         
     | 
| 
      
 574 
     | 
    
         
            +
            				
         
     | 
| 
      
 575 
     | 
    
         
            +
            				desc "Remove any previously-generated parts of the manual and rebuild it"
         
     | 
| 
      
 576 
     | 
    
         
            +
            				task :rebuild => [ :clobber, self.name ]
         
     | 
| 
      
 577 
     | 
    
         
            +
            	        end
         
     | 
| 
      
 578 
     | 
    
         
            +
             
     | 
| 
      
 579 
     | 
    
         
            +
            		end # def define
         
     | 
| 
      
 580 
     | 
    
         
            +
            		
         
     | 
| 
      
 581 
     | 
    
         
            +
            		
         
     | 
| 
      
 582 
     | 
    
         
            +
            		### Load the filter libraries provided in the given +libdir+
         
     | 
| 
      
 583 
     | 
    
         
            +
            		def load_filter_libraries( libdir )
         
     | 
| 
      
 584 
     | 
    
         
            +
            			Pathname.glob( libdir + '*.rb' ) do |filterlib|
         
     | 
| 
      
 585 
     | 
    
         
            +
            				trace "  loading filter library #{filterlib}"
         
     | 
| 
      
 586 
     | 
    
         
            +
            				require( filterlib )
         
     | 
| 
      
 587 
     | 
    
         
            +
            			end
         
     | 
| 
      
 588 
     | 
    
         
            +
            		end
         
     | 
| 
      
 589 
     | 
    
         
            +
             
     | 
| 
      
 590 
     | 
    
         
            +
             
     | 
| 
      
 591 
     | 
    
         
            +
            		### Set up the main HTML-generation task that will convert files in the given +sourcedir+ to
         
     | 
| 
      
 592 
     | 
    
         
            +
            		### HTML in the +outputdir+
         
     | 
| 
      
 593 
     | 
    
         
            +
            		def setup_page_conversion_tasks( sourcedir, outputdir, catalog )
         
     | 
| 
      
 594 
     | 
    
         
            +
             
     | 
| 
      
 595 
     | 
    
         
            +
            			# we need to figure out what HTML pages need to be generated so we can set up the
         
     | 
| 
      
 596 
     | 
    
         
            +
            			# dependency that causes the rule to be fired for each one when the task is invoked.
         
     | 
| 
      
 597 
     | 
    
         
            +
            			manual_sources = FileList[ catalog.path_index.keys.map {|pn| pn.to_s} ]
         
     | 
| 
      
 598 
     | 
    
         
            +
            			trace "   found %d source files" % [ manual_sources.length ]
         
     | 
| 
      
 599 
     | 
    
         
            +
            			
         
     | 
| 
      
 600 
     | 
    
         
            +
            			# Map .page files to their equivalent .html output
         
     | 
| 
      
 601 
     | 
    
         
            +
            			html_pathmap = "%%{%s,%s}X.html" % [ sourcedir, outputdir ]
         
     | 
| 
      
 602 
     | 
    
         
            +
            			manual_pages = manual_sources.pathmap( html_pathmap )
         
     | 
| 
      
 603 
     | 
    
         
            +
            			trace "Mapping sources like so: \n  %p -> %p" %
         
     | 
| 
      
 604 
     | 
    
         
            +
            				[ manual_sources.first, manual_pages.first ]
         
     | 
| 
      
 605 
     | 
    
         
            +
             
     | 
| 
      
 606 
     | 
    
         
            +
            			# Output directory task
         
     | 
| 
      
 607 
     | 
    
         
            +
            			directory( outputdir.to_s )
         
     | 
| 
      
 608 
     | 
    
         
            +
            			file outputdir.to_s do
         
     | 
| 
      
 609 
     | 
    
         
            +
            				touch outputdir + '.buildtime'
         
     | 
| 
      
 610 
     | 
    
         
            +
            			end
         
     | 
| 
      
 611 
     | 
    
         
            +
             
     | 
| 
      
 612 
     | 
    
         
            +
            			# Rule to generate .html files from .page files
         
     | 
| 
      
 613 
     | 
    
         
            +
            			rule( 
         
     | 
| 
      
 614 
     | 
    
         
            +
            				%r{#{outputdir}/.*\.html$} => [
         
     | 
| 
      
 615 
     | 
    
         
            +
            					proc {|name| name.sub(/\.[^.]+$/, '.page').sub( outputdir, sourcedir) },
         
     | 
| 
      
 616 
     | 
    
         
            +
            					outputdir.to_s
         
     | 
| 
      
 617 
     | 
    
         
            +
            			 	]) do |task|
         
     | 
| 
      
 618 
     | 
    
         
            +
            			
         
     | 
| 
      
 619 
     | 
    
         
            +
            				source = Pathname.new( task.source )
         
     | 
| 
      
 620 
     | 
    
         
            +
            				target = Pathname.new( task.name )
         
     | 
| 
      
 621 
     | 
    
         
            +
            				log "  #{ source } -> #{ target }"
         
     | 
| 
      
 622 
     | 
    
         
            +
            					
         
     | 
| 
      
 623 
     | 
    
         
            +
            				page = catalog.path_index[ source ]
         
     | 
| 
      
 624 
     | 
    
         
            +
            				#trace "  page object is: %p" % [ page ]
         
     | 
| 
      
 625 
     | 
    
         
            +
            					
         
     | 
| 
      
 626 
     | 
    
         
            +
            				target.dirname.mkpath
         
     | 
| 
      
 627 
     | 
    
         
            +
            				target.open( File::WRONLY|File::CREAT|File::TRUNC ) do |io|
         
     | 
| 
      
 628 
     | 
    
         
            +
            					io.write( page.generate(metadata) )
         
     | 
| 
      
 629 
     | 
    
         
            +
            				end
         
     | 
| 
      
 630 
     | 
    
         
            +
            			end
         
     | 
| 
      
 631 
     | 
    
         
            +
            			
         
     | 
| 
      
 632 
     | 
    
         
            +
            			# Group all the manual page output files targets into a containing task
         
     | 
| 
      
 633 
     | 
    
         
            +
            			desc "Generate any pages of the manual that have changed"
         
     | 
| 
      
 634 
     | 
    
         
            +
            			task :generate_pages => manual_pages
         
     | 
| 
      
 635 
     | 
    
         
            +
            			return manual_pages
         
     | 
| 
      
 636 
     | 
    
         
            +
            		end
         
     | 
| 
      
 637 
     | 
    
         
            +
            		
         
     | 
| 
      
 638 
     | 
    
         
            +
            		
         
     | 
| 
      
 639 
     | 
    
         
            +
            		### Copy method for resources -- passed as a block to the various file tasks that copy
         
     | 
| 
      
 640 
     | 
    
         
            +
            		### resources to the output directory.
         
     | 
| 
      
 641 
     | 
    
         
            +
            		def copy_resource( task )
         
     | 
| 
      
 642 
     | 
    
         
            +
            			source = task.prerequisites[ 1 ]
         
     | 
| 
      
 643 
     | 
    
         
            +
            			target = task.name
         
     | 
| 
      
 644 
     | 
    
         
            +
            			
         
     | 
| 
      
 645 
     | 
    
         
            +
            			when_writing do
         
     | 
| 
      
 646 
     | 
    
         
            +
            				log "  #{source} -> #{target}"
         
     | 
| 
      
 647 
     | 
    
         
            +
            				mkpath File.dirname( target )
         
     | 
| 
      
 648 
     | 
    
         
            +
            				cp source, target, :verbose => $trace
         
     | 
| 
      
 649 
     | 
    
         
            +
            			end
         
     | 
| 
      
 650 
     | 
    
         
            +
            		end
         
     | 
| 
      
 651 
     | 
    
         
            +
            			
         
     | 
| 
      
 652 
     | 
    
         
            +
            		
         
     | 
| 
      
 653 
     | 
    
         
            +
            		### Set up a rule for copying files from the resources directory to the output dir.
         
     | 
| 
      
 654 
     | 
    
         
            +
            		def setup_resource_copy_tasks( resourcedir, outputdir )
         
     | 
| 
      
 655 
     | 
    
         
            +
            			resources = FileList[ resourcedir + '**/*.{js,css,png,gif,jpg,html}' ]
         
     | 
| 
      
 656 
     | 
    
         
            +
            			resources.exclude( /\.svn/ )
         
     | 
| 
      
 657 
     | 
    
         
            +
            			target_pathmap = "%%{%s,%s}p" % [ resourcedir, outputdir ]
         
     | 
| 
      
 658 
     | 
    
         
            +
            			targets = resources.pathmap( target_pathmap )
         
     | 
| 
      
 659 
     | 
    
         
            +
            			copier = self.method( :copy_resource ).to_proc
         
     | 
| 
      
 660 
     | 
    
         
            +
            			
         
     | 
| 
      
 661 
     | 
    
         
            +
            			# Create a file task to copy each file to the output directory
         
     | 
| 
      
 662 
     | 
    
         
            +
            			resources.each_with_index do |resource, i|
         
     | 
| 
      
 663 
     | 
    
         
            +
            				file( targets[i] => [ outputdir.to_s, resource ], &copier )
         
     | 
| 
      
 664 
     | 
    
         
            +
            			end
         
     | 
| 
      
 665 
     | 
    
         
            +
             
     | 
| 
      
 666 
     | 
    
         
            +
            			# Now group all the resource file tasks into a containing task
         
     | 
| 
      
 667 
     | 
    
         
            +
            			desc "Copy manual resources to the output directory"
         
     | 
| 
      
 668 
     | 
    
         
            +
            			task :copy_resources => targets
         
     | 
| 
      
 669 
     | 
    
         
            +
            		end
         
     | 
| 
      
 670 
     | 
    
         
            +
            		
         
     | 
| 
      
 671 
     | 
    
         
            +
            	end # class Manual::GenTask
         
     | 
| 
      
 672 
     | 
    
         
            +
            	
         
     | 
| 
      
 673 
     | 
    
         
            +
            end
         
     | 
| 
      
 674 
     | 
    
         
            +
             
     | 
| 
      
 675 
     | 
    
         
            +
             
     | 
| 
      
 676 
     | 
    
         
            +
             
     | 
| 
      
 677 
     | 
    
         
            +
            ### Task: manual generation
         
     | 
| 
      
 678 
     | 
    
         
            +
            if MANUALDIR.exist?
         
     | 
| 
      
 679 
     | 
    
         
            +
            	MANUALOUTPUTDIR = MANUALDIR + 'output'
         
     | 
| 
      
 680 
     | 
    
         
            +
            	trace "Manual will be generated in: #{MANUALOUTPUTDIR}"
         
     | 
| 
      
 681 
     | 
    
         
            +
            	
         
     | 
| 
      
 682 
     | 
    
         
            +
            	begin
         
     | 
| 
      
 683 
     | 
    
         
            +
            		directory MANUALOUTPUTDIR.to_s
         
     | 
| 
      
 684 
     | 
    
         
            +
             
     | 
| 
      
 685 
     | 
    
         
            +
            		Manual::GenTask.new do |manual|
         
     | 
| 
      
 686 
     | 
    
         
            +
            			manual.metadata.version = PKG_VERSION
         
     | 
| 
      
 687 
     | 
    
         
            +
            			manual.metadata.api_dir = RDOCDIR
         
     | 
| 
      
 688 
     | 
    
         
            +
            			manual.output_dir = MANUALOUTPUTDIR
         
     | 
| 
      
 689 
     | 
    
         
            +
            			manual.base_dir = MANUALDIR
         
     | 
| 
      
 690 
     | 
    
         
            +
            			manual.source_dir = 'src'
         
     | 
| 
      
 691 
     | 
    
         
            +
            		end
         
     | 
| 
      
 692 
     | 
    
         
            +
             
     | 
| 
      
 693 
     | 
    
         
            +
            		task :clobber_manual do
         
     | 
| 
      
 694 
     | 
    
         
            +
            			rmtree( MANUALOUTPUTDIR, :verbose => true )
         
     | 
| 
      
 695 
     | 
    
         
            +
            		end
         
     | 
| 
      
 696 
     | 
    
         
            +
             
     | 
| 
      
 697 
     | 
    
         
            +
            	rescue LoadError => err
         
     | 
| 
      
 698 
     | 
    
         
            +
            		task :no_manual do
         
     | 
| 
      
 699 
     | 
    
         
            +
            			$stderr.puts "Manual-generation tasks not defined: %s" % [ err.message ]
         
     | 
| 
      
 700 
     | 
    
         
            +
            		end
         
     | 
| 
      
 701 
     | 
    
         
            +
             
     | 
| 
      
 702 
     | 
    
         
            +
            		task :manual => :no_manual
         
     | 
| 
      
 703 
     | 
    
         
            +
            		task :clobber_manual => :no_manual
         
     | 
| 
      
 704 
     | 
    
         
            +
            	end
         
     | 
| 
      
 705 
     | 
    
         
            +
             
     | 
| 
      
 706 
     | 
    
         
            +
            else
         
     | 
| 
      
 707 
     | 
    
         
            +
            	TEMPLATEDIR = RAKE_TASKDIR + 'manualdir'
         
     | 
| 
      
 708 
     | 
    
         
            +
             
     | 
| 
      
 709 
     | 
    
         
            +
            	if TEMPLATEDIR.exist?
         
     | 
| 
      
 710 
     | 
    
         
            +
             
     | 
| 
      
 711 
     | 
    
         
            +
            		desc "Create a manual for this project from a template"
         
     | 
| 
      
 712 
     | 
    
         
            +
            		task :manual do
         
     | 
| 
      
 713 
     | 
    
         
            +
            			log "No manual directory (#{MANUALDIR}) currently exists."
         
     | 
| 
      
 714 
     | 
    
         
            +
            			ask_for_confirmation( "Create a new manual directory tree from a template?" ) do
         
     | 
| 
      
 715 
     | 
    
         
            +
            				MANUALDIR.mkpath
         
     | 
| 
      
 716 
     | 
    
         
            +
            				
         
     | 
| 
      
 717 
     | 
    
         
            +
            				%w[layouts lib output resources src].each do |dir|
         
     | 
| 
      
 718 
     | 
    
         
            +
            					FileUtils.mkpath( MANUALDIR + dir, :mode => 0755, :verbose => true, :noop => $dryrun )
         
     | 
| 
      
 719 
     | 
    
         
            +
            				end
         
     | 
| 
      
 720 
     | 
    
         
            +
            				
         
     | 
| 
      
 721 
     | 
    
         
            +
            				Pathname.glob( TEMPLATEDIR + '**/*.{rb,css,png,js,erb,page}' ).each do |tmplfile|
         
     | 
| 
      
 722 
     | 
    
         
            +
            					trace "extname is: #{tmplfile.extname}"
         
     | 
| 
      
 723 
     | 
    
         
            +
            					
         
     | 
| 
      
 724 
     | 
    
         
            +
            					# Render ERB files
         
     | 
| 
      
 725 
     | 
    
         
            +
            					if tmplfile.extname == '.erb'
         
     | 
| 
      
 726 
     | 
    
         
            +
            						rname = tmplfile.basename( '.erb' )
         
     | 
| 
      
 727 
     | 
    
         
            +
            						target = MANUALDIR + tmplfile.dirname.relative_path_from( TEMPLATEDIR ) + rname
         
     | 
| 
      
 728 
     | 
    
         
            +
            						template = ERB.new( tmplfile.read, nil, '<>' )
         
     | 
| 
      
 729 
     | 
    
         
            +
             
     | 
| 
      
 730 
     | 
    
         
            +
            						target.dirname.mkpath( :mode => 0755, :verbose => true, :noop => $dryrun ) unless
         
     | 
| 
      
 731 
     | 
    
         
            +
            						 	target.dirname.directory?
         
     | 
| 
      
 732 
     | 
    
         
            +
            						html = template.result( binding() )
         
     | 
| 
      
 733 
     | 
    
         
            +
            						log "generating #{target}: html => #{html[0,20]}"
         
     | 
| 
      
 734 
     | 
    
         
            +
             
     | 
| 
      
 735 
     | 
    
         
            +
            						target.open( File::WRONLY|File::CREAT|File::EXCL, 0644 ) do |fh|
         
     | 
| 
      
 736 
     | 
    
         
            +
            							fh.print( html )
         
     | 
| 
      
 737 
     | 
    
         
            +
            						end
         
     | 
| 
      
 738 
     | 
    
         
            +
            					
         
     | 
| 
      
 739 
     | 
    
         
            +
            					# Just copy anything else
         
     | 
| 
      
 740 
     | 
    
         
            +
            					else
         
     | 
| 
      
 741 
     | 
    
         
            +
            						target = MANUALDIR + tmplfile.relative_path_from( TEMPLATEDIR )
         
     | 
| 
      
 742 
     | 
    
         
            +
            						FileUtils.mkpath target.dirname,
         
     | 
| 
      
 743 
     | 
    
         
            +
            							:mode => 0755, :verbose => true, :noop => $dryrun unless target.dirname.directory?
         
     | 
| 
      
 744 
     | 
    
         
            +
            						FileUtils.install tmplfile, target,
         
     | 
| 
      
 745 
     | 
    
         
            +
            							:mode => 0644, :verbose => true, :noop => $dryrun
         
     | 
| 
      
 746 
     | 
    
         
            +
            					end
         
     | 
| 
      
 747 
     | 
    
         
            +
            				end
         
     | 
| 
      
 748 
     | 
    
         
            +
            			end
         
     | 
| 
      
 749 
     | 
    
         
            +
             
     | 
| 
      
 750 
     | 
    
         
            +
            		end # task :manual
         
     | 
| 
      
 751 
     | 
    
         
            +
             
     | 
| 
      
 752 
     | 
    
         
            +
            	end
         
     | 
| 
      
 753 
     | 
    
         
            +
            end
         
     | 
| 
      
 754 
     | 
    
         
            +
             
     | 
| 
      
 755 
     | 
    
         
            +
             
     |