darkfish-rdoc 1.1.1

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.
@@ -0,0 +1,62 @@
1
+ #
2
+ # Dependency-checking and Installation Rake Tasks
3
+ # $Id: dependencies.rb 10 2008-07-18 15:52:48Z deveiant $
4
+ #
5
+
6
+ require 'rubygems/dependency_installer'
7
+ require 'rubygems/source_index'
8
+ require 'rubygems/requirement'
9
+ require 'rubygems/doc_manager'
10
+
11
+ ### Install the specified +gems+ if they aren't already installed.
12
+ def install_gems( *gems )
13
+ gems.flatten!
14
+
15
+ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
16
+ :generate_rdoc => true,
17
+ :generate_ri => true,
18
+ :install_dir => Gem.dir,
19
+ :format_executable => false,
20
+ :test => false,
21
+ :version => Gem::Requirement.default,
22
+ })
23
+
24
+ # Check for root
25
+ if Process.euid != 0
26
+ $stderr.puts "This probably won't work, as you aren't root, but I'll try anyway"
27
+ end
28
+
29
+ gemindex = Gem::SourceIndex.from_installed_gems
30
+
31
+ gems.each do |gemname|
32
+ if (( specs = gemindex.search(gemname) )) && ! specs.empty?
33
+ log "Version %s of %s is already installed; skipping..." %
34
+ [ specs.first.version, specs.first.name ]
35
+ next
36
+ end
37
+
38
+ rgv = Gem::Version.new( Gem::RubyGemsVersion )
39
+ installer = nil
40
+
41
+ log "Trying to install #{gemname.inspect}..."
42
+ if rgv >= Gem::Version.new( '1.1.1' )
43
+ installer = Gem::DependencyInstaller.new
44
+ installer.install( gemname )
45
+ else
46
+ installer = Gem::DependencyInstaller.new( gemname )
47
+ installer.install
48
+ end
49
+
50
+ installer.installed_gems.each do |spec|
51
+ log "Installed: %s" % [ spec.full_name ]
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+
58
+ ### Task: install gems for development tasks
59
+ task :install_dependencies do
60
+ install_gems( DEPENDENCIES.keys )
61
+ end
62
+
@@ -0,0 +1,350 @@
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
+ log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
63
+ if $dryrun
64
+ $deferr.puts "(dry run mode)"
65
+ else
66
+ system( *cmd )
67
+ unless $?.success?
68
+ fail "Command failed: [%s]" % [cmd.join(' ')]
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ ### Open a pipe to a process running the given +cmd+ and call the given block with it.
75
+ def pipeto( *cmd )
76
+ $DEBUG = true
77
+
78
+ cmd.flatten!
79
+ log( "Opening a pipe to: ", cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
80
+ if $dryrun
81
+ $deferr.puts "(dry run mode)"
82
+ else
83
+ open( '|-', 'w+' ) do |io|
84
+
85
+ # Parent
86
+ if io
87
+ yield( io )
88
+
89
+ # Child
90
+ else
91
+ exec( *cmd )
92
+ fail "Command failed: [%s]" % [cmd.join(' ')]
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+
99
+ ### Download the file at +sourceuri+ via HTTP and write it to +targetfile+.
100
+ def download( sourceuri, targetfile=nil )
101
+ oldsync = $defout.sync
102
+ $defout.sync = true
103
+ require 'net/http'
104
+ require 'uri'
105
+
106
+ targetpath = Pathname.new( targetfile )
107
+
108
+ log "Downloading %s to %s" % [sourceuri, targetfile]
109
+ targetpath.open( File::WRONLY|File::TRUNC|File::CREAT, 0644 ) do |ofh|
110
+
111
+ url = URI.parse( sourceuri )
112
+ downloaded = false
113
+ limit = 5
114
+
115
+ until downloaded or limit.zero?
116
+ Net::HTTP.start( url.host, url.port ) do |http|
117
+ req = Net::HTTP::Get.new( url.path )
118
+
119
+ http.request( req ) do |res|
120
+ if res.is_a?( Net::HTTPSuccess )
121
+ log "Downloading..."
122
+ res.read_body do |buf|
123
+ ofh.print( buf )
124
+ end
125
+ downloaded = true
126
+ puts "done."
127
+
128
+ elsif res.is_a?( Net::HTTPRedirection )
129
+ url = URI.parse( res['location'] )
130
+ log "...following redirection to: %s" % [ url ]
131
+ limit -= 1
132
+ sleep 0.2
133
+ next
134
+
135
+ else
136
+ res.error!
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ return targetpath
145
+ ensure
146
+ $defout.sync = oldsync
147
+ end
148
+
149
+
150
+ ### Return the fully-qualified path to the specified +program+ in the PATH.
151
+ def which( program )
152
+ ENV['PATH'].split(/:/).
153
+ collect {|dir| Pathname.new(dir) + program }.
154
+ find {|path| path.exist? && path.executable? }
155
+ end
156
+
157
+
158
+ ### Create a string that contains the ANSI codes specified and return it
159
+ def ansi_code( *attributes )
160
+ attributes.flatten!
161
+ attributes.collect! {|at| at.to_s }
162
+ # $deferr.puts "Returning ansicode for TERM = %p: %p" %
163
+ # [ ENV['TERM'], attributes ]
164
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
165
+ attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
166
+
167
+ # $deferr.puts " attr is: %p" % [attributes]
168
+ if attributes.empty?
169
+ return ''
170
+ else
171
+ return "\e[%sm" % attributes
172
+ end
173
+ end
174
+
175
+
176
+ ### Colorize the given +string+ with the specified +attributes+ and return it, handling
177
+ ### line-endings, color reset, etc.
178
+ def colorize( *args )
179
+ string = ''
180
+
181
+ if block_given?
182
+ string = yield
183
+ else
184
+ string = args.shift
185
+ end
186
+
187
+ ending = string[/(\s)$/] || ''
188
+ string = string.rstrip
189
+
190
+ return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
191
+ end
192
+
193
+
194
+ ### Output the specified <tt>msg</tt> as an ANSI-colored error message
195
+ ### (white on red).
196
+ def error_message( msg, details='' )
197
+ $deferr.puts colorize( 'bold', 'white', 'on_red' ) { msg } + details
198
+ end
199
+ alias :error :error_message
200
+
201
+
202
+ ### Highlight and embed a prompt control character in the given +string+ and return it.
203
+ def make_prompt_string( string )
204
+ return CLEAR_CURRENT_LINE + colorize( 'bold', 'green' ) { string + ' ' }
205
+ end
206
+
207
+
208
+ ### Output the specified <tt>prompt_string</tt> as a prompt (in green) and
209
+ ### return the user's input with leading and trailing spaces removed. If a
210
+ ### test is provided, the prompt will repeat until the test returns true.
211
+ ### An optional failure message can also be passed in.
212
+ def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
213
+ prompt_string.chomp!
214
+ prompt_string << ":" unless /\W$/.match( prompt_string )
215
+ response = nil
216
+
217
+ begin
218
+ prompt = make_prompt_string( prompt_string )
219
+ response = Readline.readline( prompt ) || ''
220
+ response.strip!
221
+ if block_given? && ! yield( response )
222
+ error_message( failure_msg + "\n\n" )
223
+ response = nil
224
+ end
225
+ end while response.nil?
226
+
227
+ return response
228
+ end
229
+
230
+
231
+ ### Prompt the user with the given <tt>prompt_string</tt> via #prompt,
232
+ ### substituting the given <tt>default</tt> if the user doesn't input
233
+ ### anything. If a test is provided, the prompt will repeat until the test
234
+ ### returns true. An optional failure message can also be passed in.
235
+ def prompt_with_default( prompt_string, default, failure_msg="Try again." )
236
+ response = nil
237
+
238
+ begin
239
+ default ||= '~'
240
+ response = prompt( "%s [%s]" % [ prompt_string, default ] )
241
+ response = default.to_s if !response.nil? && response.empty?
242
+
243
+ trace "Validating reponse %p" % [ response ]
244
+
245
+ # the block is a validator. We need to make sure that the user didn't
246
+ # enter '~', because if they did, it's nil and we should move on. If
247
+ # they didn't, then call the block.
248
+ if block_given? && response != '~' && ! yield( response )
249
+ error_message( failure_msg + "\n\n" )
250
+ response = nil
251
+ end
252
+ end while response.nil?
253
+
254
+ return nil if response == '~'
255
+ return response
256
+ end
257
+
258
+
259
+ ### Prompt for an array of values
260
+ def prompt_for_multiple_values( label, default=nil )
261
+ $stderr.puts( MULTILINE_PROMPT % [label] )
262
+ if default
263
+ $stderr.puts "Enter a single blank line to keep the default:\n %p" % [ default ]
264
+ end
265
+
266
+ results = []
267
+ result = nil
268
+
269
+ begin
270
+ result = Readline.readline( make_prompt_string("> ") )
271
+ if result.nil? || result.empty?
272
+ results << default if default && results.empty?
273
+ else
274
+ results << result
275
+ end
276
+ end until result.nil? || result.empty?
277
+
278
+ return results.flatten
279
+ end
280
+
281
+
282
+ ### Display a description of a potentially-dangerous task, and prompt
283
+ ### for confirmation. If the user answers with anything that begins
284
+ ### with 'y', yield to the block, else raise with an error.
285
+ def ask_for_confirmation( description )
286
+ puts description
287
+
288
+ answer = prompt_with_default( "Continue?", 'n' ) do |input|
289
+ input =~ /^[yn]/i
290
+ end
291
+
292
+ case answer
293
+ when /^y/i
294
+ yield
295
+ else
296
+ error "Aborted."
297
+ fail
298
+ end
299
+ end
300
+
301
+
302
+ ### Search line-by-line in the specified +file+ for the given +regexp+, returning the
303
+ ### first match, or nil if no match was found. If the +regexp+ has any capture groups,
304
+ ### those will be returned in an Array, else the whole matching line is returned.
305
+ def find_pattern_in_file( regexp, file )
306
+ rval = nil
307
+
308
+ File.open( file, 'r' ).each do |line|
309
+ if (( match = regexp.match(line) ))
310
+ rval = match.captures.empty? ? match[0] : match.captures
311
+ break
312
+ end
313
+ end
314
+
315
+ return rval
316
+ end
317
+
318
+
319
+ ### Get a list of the file or files to run rspec on.
320
+ def rspec_files
321
+ if ENV['class']
322
+ classname = ENV['class']
323
+ spec_file = SPECSDIR + "#{classname}_spec.rb"
324
+ raise "Can't find the spec file for the class '#{classname}'" unless spec_file.exist?
325
+ return [ spec_file ]
326
+ else
327
+ return SPEC_FILES
328
+ end
329
+ end
330
+
331
+
332
+ ### Invoke the user's editor on the given +filename+ and return the exit code
333
+ ### from doing so.
334
+ def edit( filename )
335
+ editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
336
+ system editor, filename
337
+ unless $?.success?
338
+ fail "Editor exited uncleanly."
339
+ end
340
+ end
341
+
342
+
343
+ ### Extract all the non Rake-target arguments from ARGV and return them.
344
+ def get_target_args
345
+ args = ARGV.reject {|arg| Rake::Task.task_defined?(arg) }
346
+ return args
347
+ end
348
+
349
+
350
+
@@ -0,0 +1,384 @@
1
+ #
2
+ # Manual-generation Rake tasks and classes
3
+ # $Id: manual.rb 10 2008-07-18 15:52:48Z deveiant $
4
+ #
5
+ # Author: Michael Granger <ged@FaerieMUD.org>
6
+ #
7
+ # This was born out of a frustration with other static HTML generation modules
8
+ # and systems. I've tried webby, webgen, rote, staticweb, staticmatic, and
9
+ # nanoc, but I didn't find any of them really suitable (except rote, which was
10
+ # excellent but apparently isn't maintained and has a fundamental
11
+ # incompatibilty with Rake because of some questionable monkeypatching.)
12
+ #
13
+ # So, since nothing seemed to scratch my itch, I'm going to scratch it myself.
14
+ #
15
+
16
+ require 'pathname'
17
+ require 'singleton'
18
+ require 'rake/tasklib'
19
+ require 'erb'
20
+
21
+
22
+ ### Namespace for Manual-generation classes
23
+ module Manual
24
+
25
+ ### Manual page-generation class
26
+ class Page
27
+
28
+ ### An abstract filter class for manual content transformation.
29
+ class Filter
30
+ include Singleton
31
+
32
+ # A list of inheriting classes, keyed by normalized name
33
+ @derivatives = {}
34
+ class << self; attr_reader :derivatives; end
35
+
36
+ ### Inheritance callback -- keep track of all inheriting classes for
37
+ ### later.
38
+ def self::inherited( subclass )
39
+ key = subclass.name.
40
+ sub( /^.*::/, '' ).
41
+ gsub( /[^[:alpha:]]+/, '_' ).
42
+ downcase.
43
+ sub( /filter$/, '' )
44
+
45
+ self.derivatives[ key ] = subclass
46
+ self.derivatives[ key.to_sym ] = subclass
47
+
48
+ super
49
+ end
50
+
51
+
52
+ ### Process the +page+'s source with the filter and return the altered content.
53
+ def process( source, page, metadata )
54
+ raise NotImplementedError,
55
+ "%s does not implement the #process method" % [ self.class.name ]
56
+ end
57
+ end
58
+
59
+
60
+ ### The default page configuration if none is specified.
61
+ DEFAULT_CONFIG = {
62
+ 'filters' => [ 'textile', 'erb' ],
63
+ 'layout' => 'default.page',
64
+ }.freeze
65
+
66
+ # Pattern to match a source page with a YAML header
67
+ PAGE_WITH_YAML_HEADER = /
68
+ \A---\s*$ # It should should start with three hyphens
69
+ (.*?) # ...have some YAML stuff
70
+ ^---\s*$ # then have another three-hyphen line,
71
+ (.*)\Z # then the rest of the document
72
+ /xm
73
+
74
+ # Options to pass to libtidy
75
+ TIDY_OPTIONS = {
76
+ :show_warnings => true,
77
+ :indent => false,
78
+ :indent_attributes => false,
79
+ :indent_spaces => 4,
80
+ :vertical_space => true,
81
+ :tab_size => 4,
82
+ :wrap_attributes => true,
83
+ :wrap => 100,
84
+ }
85
+
86
+
87
+ ### Create a new page-generator for the given +sourcefile+, which will use
88
+ ### ones of the templates in +layouts_dir+ as a wrapper.
89
+ def initialize( sourcefile, layouts_dir )
90
+ @sourcefile = Pathname.new( sourcefile )
91
+ @layouts_dir = Pathname.new( layouts_dir )
92
+
93
+ rawsource = @sourcefile.read
94
+ @config, @source = self.read_page_config( rawsource )
95
+ # $stderr.puts "Config is: %p" % [@config],
96
+ # "Source is: %p" % [ @source[0,100] ]
97
+ @filters = self.load_filters( @config['filters'] )
98
+
99
+ super()
100
+ end
101
+
102
+
103
+ ######
104
+ public
105
+ ######
106
+
107
+ # The Pathname object that specifys the page source file
108
+ attr_reader :sourcefile
109
+
110
+ # The configured layouts directory as a Pathname object.
111
+ attr_reader :layouts_dir
112
+
113
+ # The page configuration, as read from its YAML header
114
+ attr_reader :config
115
+
116
+ # The raw source of the page
117
+ attr_reader :source
118
+
119
+ # The filters the page will use to render itself
120
+ attr_reader :filters
121
+
122
+
123
+ ### Generate HTML output from the page and return it.
124
+ def generate( metadata )
125
+ content = self.generate_content( @source, metadata )
126
+
127
+ # :TODO: Read config from page for layout, filters, etc.
128
+ templatepath = @layouts_dir + 'default.page'
129
+ template = ERB.new( templatepath.read )
130
+ page = self
131
+
132
+ html = template.result( binding() )
133
+
134
+ # return self.cleanup( html )
135
+ return html
136
+ end
137
+
138
+
139
+ ### Run the various filters on the given input and return the transformed
140
+ ### content.
141
+ def generate_content( input, metadata )
142
+ return @filters.inject( input ) do |source, filter|
143
+ filter.process( source, self, metadata )
144
+ end
145
+ end
146
+
147
+
148
+ ### Trim the YAML header from the provided page +source+, convert it to
149
+ ### a Ruby object, and return it.
150
+ def read_page_config( source )
151
+ unless source =~ PAGE_WITH_YAML_HEADER
152
+ return DEFAULT_CONFIG.dup, source
153
+ end
154
+
155
+ pageconfig = YAML.load( $1 )
156
+ source = $2
157
+
158
+ return DEFAULT_CONFIG.merge( pageconfig ), source
159
+ end
160
+
161
+
162
+ ### Clean up and return the given HTML +source+.
163
+ def cleanup( source )
164
+ require 'tidy'
165
+
166
+ Tidy.path = '/usr/lib/libtidy.dylib'
167
+ Tidy.open( TIDY_OPTIONS ) do |tidy|
168
+ tidy.options.output_xhtml = true
169
+
170
+ xml = tidy.clean( source )
171
+ puts tidy.errors
172
+ puts tidy.diagnostics
173
+ return xml
174
+ end
175
+ rescue LoadError => err
176
+ trace "No cleanup: " + err.message
177
+ return source
178
+ end
179
+
180
+
181
+ ### Get (singleton) instances of the filters named in +filterlist+ and return them.
182
+ def load_filters( filterlist )
183
+ filterlist.flatten.collect do |key|
184
+ raise ArgumentError, "filter '#{key}' is not loaded" unless
185
+ Manual::Page::Filter.derivatives.key?( key )
186
+ Manual::Page::Filter.derivatives[ key ].instance
187
+ end
188
+ end
189
+
190
+ end
191
+
192
+
193
+ ### A Textile filter for the manual generation tasklib, implemented using RedCloth.
194
+ class TextileFilter < Manual::Page::Filter
195
+
196
+ ### Load RedCloth when the filter is first created
197
+ def initialize( *args )
198
+ require 'redcloth'
199
+ super
200
+ end
201
+
202
+
203
+ ### Process the given +source+ as Textile and return the resulting HTML
204
+ ### fragment.
205
+ def process( source, *ignored )
206
+ return RedCloth.new( source ).to_html
207
+ end
208
+
209
+ end
210
+
211
+
212
+ ### An ERB filter for the manual generation tasklib, implemented using Erubis.
213
+ class ErbFilter < Manual::Page::Filter
214
+
215
+ ### Process the given +source+ as ERB and return the resulting HTML
216
+ ### fragment.
217
+ def process( source, page, metadata )
218
+ template_name = page.sourcefile.basename
219
+ template = ERB.new( source )
220
+ return template.result( binding() )
221
+ end
222
+
223
+ end
224
+
225
+
226
+ ### Manual generation task library
227
+ class GenTask < Rake::TaskLib
228
+
229
+ # Default values for task config variables
230
+ DEFAULT_NAME = :manual
231
+ DEFAULT_BASE_DIR = Pathname.new( 'docs/manual' )
232
+ DEFAULT_SOURCE_DIR = 'source'
233
+ DEFAULT_LAYOUTS_DIR = 'layouts'
234
+ DEFAULT_OUTPUT_DIR = 'output'
235
+ DEFAULT_RESOURCE_DIR = 'resources'
236
+ DEFAULT_LIB_DIR = 'lib'
237
+ DEFAULT_METADATA = OpenStruct.new
238
+
239
+
240
+ ### Define a new manual-generation task with the given +name+.
241
+ def initialize( name=:manual )
242
+ @name = name
243
+
244
+ @source_dir = DEFAULT_SOURCE_DIR
245
+ @layouts_dir = DEFAULT_LAYOUTS_DIR
246
+ @output_dir = DEFAULT_OUTPUT_DIR
247
+ @resource_dir = DEFAULT_RESOURCE_DIR
248
+ @lib_dir = DEFAULT_LIB_DIR
249
+ @metadata = DEFAULT_METADATA
250
+
251
+ yield( self ) if block_given?
252
+
253
+ self.define
254
+ end
255
+
256
+
257
+ ######
258
+ public
259
+ ######
260
+
261
+ attr_accessor :base_dir,
262
+ :source_dir,
263
+ :layouts_dir,
264
+ :output_dir,
265
+ :resource_dir,
266
+ :lib_dir,
267
+ :metadata
268
+
269
+ attr_reader :name
270
+
271
+ ### Load the filter libraries provided in the given +libdir+
272
+ def load_filter_libraries( libdir )
273
+ Pathname.glob( libdir + '*.rb' ) do |filterlib|
274
+ trace " loading filter library #{filterlib}"
275
+ require( filterlib )
276
+ end
277
+ end
278
+
279
+
280
+ ### Set up the main HTML-generation task that will convert files in the given +sourcedir+ to
281
+ ### HTML in the +outputdir+
282
+ def setup_page_conversion_tasks( sourcedir, outputdir, layoutsdir )
283
+ source_glob = sourcedir + '**/*.page'
284
+ trace "Looking for sources that match: %s" % [ source_glob ]
285
+
286
+ # Find the list of source .page files
287
+ manual_sources = FileList[ source_glob ]
288
+ trace " found %d source files" % [ manual_sources.length ]
289
+
290
+ # Map .page files to their equivalent .html output
291
+ html_pathmap = "%%{%s,%s}X.html" % [ sourcedir, outputdir ]
292
+ manual_pages = manual_sources.pathmap( html_pathmap )
293
+ trace "Mapping sources like so: \n %p -> %p" %
294
+ [ manual_sources.first, manual_pages.first ]
295
+
296
+ # Output directory task
297
+ directory( outputdir.to_s )
298
+ file outputdir.to_s do
299
+ touch outputdir + '.buildtime'
300
+ end
301
+
302
+ # Rule to generate .html files from .page files
303
+ rule(
304
+ %r{#{outputdir}/.*\.html$} => [
305
+ proc {|name| name.sub(/\.[^.]+$/, '.page').sub( outputdir, sourcedir) },
306
+ outputdir.to_s
307
+ ]) do |task|
308
+
309
+ source = Pathname.new( task.source ).relative_path_from( Pathname.getwd )
310
+ target = Pathname.new( task.name ).relative_path_from( Pathname.getwd )
311
+ log " #{ source } -> #{ target }"
312
+
313
+ page = Manual::Page.new( source, layoutsdir )
314
+
315
+ target.dirname.mkpath
316
+ target.open( File::WRONLY|File::CREAT|File::TRUNC ) do |io|
317
+ io.write( page.generate(metadata) )
318
+ end
319
+ end
320
+
321
+ return manual_pages
322
+ end
323
+
324
+
325
+ ### Set up a rule for copying files from the resources directory to the output dir.
326
+ def setup_resource_copy_tasks( resourcedir, outputdir )
327
+
328
+ task :copy_resources => [ outputdir.to_s ] do
329
+ when_writing( "Copying resources" ) do
330
+ verbose do
331
+ files = FileList[ resourcedir + '*' ]
332
+ files.exclude( /\.svn/ )
333
+ unless files.empty?
334
+ trace " Copying resource files: #{files}"
335
+ cp_r files, outputdir, :verbose => true
336
+ end
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+
343
+ ### Set up the tasks for building the manual
344
+ def define
345
+
346
+ # Set up a description if the caller hasn't already defined one
347
+ unless Rake.application.last_comment
348
+ desc "Generate the manual"
349
+ end
350
+
351
+ # Make Pathnames of the directories relative to the base_dir
352
+ basedir = Pathname.new( @base_dir )
353
+ sourcedir = basedir + @source_dir
354
+ layoutsdir = basedir + @layouts_dir
355
+ outputdir = basedir + @output_dir
356
+ resourcedir = basedir + @resource_dir
357
+ libdir = basedir + @lib_dir
358
+
359
+ load_filter_libraries( libdir )
360
+
361
+ # Declare the tasks outside the namespace that point in
362
+ task @name => "#@name:build"
363
+ task "clobber_#@name" => "#@name:clobber"
364
+
365
+ namespace( self.name ) do
366
+ setup_resource_copy_tasks( resourcedir, outputdir )
367
+ manual_pages = setup_page_conversion_tasks( sourcedir, outputdir, layoutsdir )
368
+
369
+ task :build => [ :copy_resources, *manual_pages ]
370
+
371
+ task :clobber do
372
+ RakeFileUtils.verbose( $verbose ) do
373
+ rm_f manual_pages.to_a
374
+ end
375
+ remove_dir( outputdir ) if ( outputdir + '.buildtime' ).exist?
376
+ end
377
+ task :rebuild => [ :clobber, :build ]
378
+ end
379
+
380
+ end # def define
381
+
382
+ end # class Manual::GenTask
383
+
384
+ end