wordnet 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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
+