sysexits 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/rake/hg.rb DELETED
@@ -1,287 +0,0 @@
1
- #
2
- # Mercurial Rake Tasks
3
-
4
- require 'enumerator'
5
-
6
- #
7
- # Authors:
8
- # * Michael Granger <ged@FaerieMUD.org>
9
- #
10
-
11
- unless defined?( HG_DOTDIR )
12
-
13
- # Mercurial constants
14
- HG_DOTDIR = BASEDIR + '.hg'
15
- HG_STORE = HG_DOTDIR + 'store'
16
-
17
- IGNORE_FILE = BASEDIR + '.hgignore'
18
-
19
-
20
- ###
21
- ### Helpers
22
- ###
23
-
24
- module MercurialHelpers
25
- require './helpers.rb' unless defined?( RakefileHelpers )
26
- include RakefileHelpers
27
-
28
- ###############
29
- module_function
30
- ###############
31
-
32
- ### Generate a commit log from a diff and return it as a String.
33
- def make_commit_log
34
- diff = read_command_output( 'hg', 'diff' )
35
- fail "No differences." if diff.empty?
36
-
37
- return diff
38
- end
39
-
40
- ### Generate a commit log and invoke the user's editor on it.
41
- def edit_commit_log
42
- diff = make_commit_log()
43
-
44
- File.open( COMMIT_MSG_FILE, File::WRONLY|File::TRUNC|File::CREAT ) do |fh|
45
- fh.print( diff )
46
- end
47
-
48
- edit( COMMIT_MSG_FILE )
49
- end
50
-
51
- ### Generate a changelog.
52
- def make_changelog
53
- log = read_command_output( 'hg', 'log', '--style', 'compact' )
54
- return log
55
- end
56
-
57
- ### Get the 'tip' info and return it as a Hash
58
- def get_tip_info
59
- data = read_command_output( 'hg', 'tip' )
60
- return YAML.load( data )
61
- end
62
-
63
- ### Return the ID for the current rev
64
- def get_current_rev
65
- id = read_command_output( 'hg', '-q', 'identify' )
66
- return id.chomp
67
- end
68
-
69
- ### Read the list of existing tags and return them as an Array
70
- def get_tags
71
- taglist = read_command_output( 'hg', 'tags' )
72
- return taglist.split( /\n/ )
73
- end
74
-
75
-
76
- ### Read any remote repo paths known by the current repo and return them as a hash.
77
- def get_repo_paths
78
- paths = {}
79
- pathspec = read_command_output( 'hg', 'paths' )
80
- pathspec.split.each_slice( 3 ) do |name, _, url|
81
- paths[ name ] = url
82
- end
83
- return paths
84
- end
85
-
86
- ### Return the list of files which are of status 'unknown'
87
- def get_unknown_files
88
- list = read_command_output( 'hg', 'status', '-un', '--color', 'never' )
89
- list = list.split( /\n/ )
90
-
91
- trace "New files: %p" % [ list ]
92
- return list
93
- end
94
-
95
- ### Returns a human-scannable file list by joining and truncating the list if it's too long.
96
- def humanize_file_list( list, indent=FILE_INDENT )
97
- listtext = list[0..5].join( "\n#{indent}" )
98
- if list.length > 5
99
- listtext << " (and %d other/s)" % [ list.length - 5 ]
100
- end
101
-
102
- return listtext
103
- end
104
-
105
-
106
- ### Add the list of +pathnames+ to the .hgignore list.
107
- def hg_ignore_files( *pathnames )
108
- patterns = pathnames.flatten.collect do |path|
109
- '^' + Regexp.escape(path) + '$'
110
- end
111
- trace "Ignoring %d files." % [ pathnames.length ]
112
-
113
- IGNORE_FILE.open( File::CREAT|File::WRONLY|File::APPEND, 0644 ) do |fh|
114
- fh.puts( patterns )
115
- end
116
- end
117
-
118
-
119
- ### Delete the files in the given +filelist+ after confirming with the user.
120
- def delete_extra_files( filelist )
121
- description = humanize_file_list( filelist, ' ' )
122
- log "Files to delete:\n ", description
123
- ask_for_confirmation( "Really delete them?", false ) do
124
- filelist.each do |f|
125
- rm_rf( f, :verbose => true )
126
- end
127
- end
128
- end
129
-
130
- end # module MercurialHelpers
131
-
132
-
133
- ### Rakefile support
134
- def get_vcs_rev( dir='.' )
135
- return MercurialHelpers.get_current_rev
136
- end
137
- def make_changelog
138
- return MercurialHelpers.make_changelog
139
- end
140
-
141
-
142
- ###
143
- ### Tasks
144
- ###
145
-
146
- desc "Mercurial tasks"
147
- namespace :hg do
148
- include MercurialHelpers
149
-
150
- desc "Prepare for a new release"
151
- task :prep_release do
152
- tags = get_tags()
153
- rev = get_current_rev()
154
-
155
- # Look for a tag for the current release version, and if it exists abort
156
- if tags.include?( PKG_VERSION )
157
- error "Version #{PKG_VERSION} already has a tag. Did you mean " +
158
- "to increment the version in #{VERSION_FILE}?"
159
- fail
160
- end
161
-
162
- # Sign the current rev
163
- log "Signing rev #{rev}"
164
- run 'hg', 'sign'
165
-
166
- # Tag the current rev
167
- log "Tagging rev #{rev} as #{PKG_VERSION}"
168
- run 'hg', 'tag', PKG_VERSION
169
-
170
- # Offer to push
171
- Rake::Task['hg:push'].invoke
172
- end
173
-
174
- desc "Check for new files and offer to add/ignore/delete them."
175
- task :newfiles do
176
- log "Checking for new files..."
177
-
178
- entries = get_unknown_files()
179
-
180
- unless entries.empty?
181
- files_to_add = []
182
- files_to_ignore = []
183
- files_to_delete = []
184
-
185
- entries.each do |entry|
186
- action = prompt_with_default( " #{entry}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
187
- case action
188
- when 'a'
189
- files_to_add << entry
190
- when 'i'
191
- files_to_ignore << entry
192
- when 'd'
193
- files_to_delete << entry
194
- end
195
- end
196
-
197
- unless files_to_add.empty?
198
- run 'hg', 'add', *files_to_add
199
- end
200
-
201
- unless files_to_ignore.empty?
202
- hg_ignore_files( *files_to_ignore )
203
- end
204
-
205
- unless files_to_delete.empty?
206
- delete_extra_files( files_to_delete )
207
- end
208
- end
209
- end
210
- task :add => :newfiles
211
-
212
-
213
- desc "Pull and update from the default repo"
214
- task :pull do
215
- paths = get_repo_paths()
216
- if origin_url = paths['default']
217
- ask_for_confirmation( "Pull and update from '#{origin_url}'?", false ) do
218
- Rake::Task['hg:pull_without_confirmation'].invoke
219
- end
220
- else
221
- trace "Skipping pull: No 'default' path."
222
- end
223
- end
224
-
225
-
226
- desc "Pull and update without confirmation"
227
- task :pull_without_confirmation do
228
- run 'hg', 'pull', '-u'
229
- end
230
-
231
-
232
- desc "Check the current code in if tests pass"
233
- task :checkin => ['hg:pull', 'hg:newfiles', 'test', COMMIT_MSG_FILE] do
234
- targets = get_target_args()
235
- $stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
236
- ask_for_confirmation( "Continue with checkin?" ) do
237
- run 'hg', 'ci', '-l', COMMIT_MSG_FILE, targets
238
- rm_f COMMIT_MSG_FILE
239
- end
240
- Rake::Task['hg:push'].invoke
241
- end
242
- task :commit => :checkin
243
- task :ci => :checkin
244
-
245
- CLEAN.include( COMMIT_MSG_FILE )
246
-
247
- desc "Push to the default origin repo (if there is one)"
248
- task :push do
249
- paths = get_repo_paths()
250
- if origin_url = paths['default']
251
- ask_for_confirmation( "Push to '#{origin_url}'?", false ) do
252
- Rake::Task['hg:push_without_confirmation'].invoke
253
- end
254
- else
255
- trace "Skipping push: No 'default' path."
256
- end
257
- end
258
-
259
- desc "Push to the default repo without confirmation"
260
- task :push_without_confirmation do
261
- run 'hg', 'push'
262
- end
263
-
264
- end
265
-
266
- if HG_DOTDIR.exist?
267
- trace "Defining mercurial VCS tasks"
268
-
269
- desc "Check in all the changes in your current working copy"
270
- task :ci => 'hg:ci'
271
- desc "Check in all the changes in your current working copy"
272
- task :checkin => 'hg:ci'
273
-
274
- desc "Tag and sign revision before a release"
275
- task :prep_release => 'hg:prep_release'
276
-
277
- file COMMIT_MSG_FILE do
278
- edit_commit_log()
279
- end
280
-
281
- else
282
- trace "Not defining mercurial tasks: no #{HG_DOTDIR}"
283
- end
284
-
285
- end
286
-
287
-
@@ -1,787 +0,0 @@
1
- #
2
- # Manual-generation Rake tasks and classes
3
-
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' => false,
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 = nil
151
- if Object.const_defined?( :Encoding )
152
- template = ERB.new( templatepath.read(:encoding => 'UTF-8') )
153
- else
154
- template = ERB.new( templatepath.read )
155
- end
156
-
157
- page = self
158
- html = template.result( binding() )
159
-
160
- # Use Tidy to clean up the html if 'cleanup' is turned on, but remove the Tidy
161
- # meta-generator propaganda/advertising.
162
- html = self.cleanup( html ).sub( %r:<meta name="generator"[^>]*tidy[^>]*/>:im, '' ) if
163
- self.config['cleanup']
164
-
165
- return html
166
- end
167
-
168
-
169
- ### Return the page title as specified in the YAML options
170
- def title
171
- return self.config['title'] || self.sourcefile.basename
172
- end
173
-
174
-
175
- ### Run the various filters on the given input and return the transformed
176
- ### content.
177
- def generate_content( input, metadata )
178
- return @filters.inject( input ) do |source, filter|
179
- filter.process( source, self, metadata )
180
- end
181
- end
182
-
183
-
184
- ### Trim the YAML header from the provided page +source+, convert it to
185
- ### a Ruby object, and return it.
186
- def read_page_config( source )
187
- unless source =~ PAGE_WITH_YAML_HEADER
188
- return DEFAULT_CONFIG.dup, source
189
- end
190
-
191
- pageconfig = YAML.load( $1 )
192
- source = $2
193
-
194
- return DEFAULT_CONFIG.merge( pageconfig ), source
195
- end
196
-
197
-
198
- ### Clean up and return the given HTML +source+.
199
- def cleanup( source )
200
- require 'tidy'
201
-
202
- Tidy.path = '/usr/lib/libtidy.dylib'
203
- Tidy.open( TIDY_OPTIONS ) do |tidy|
204
- tidy.options.output_xhtml = true
205
-
206
- xml = tidy.clean( source )
207
- errors = tidy.errors
208
- error_message( errors.join ) unless errors.empty?
209
- trace tidy.diagnostics
210
- return xml
211
- end
212
- rescue LoadError => err
213
- trace "No cleanup: " + err.message
214
- return source
215
- end
216
-
217
-
218
- ### Get (singleton) instances of the filters named in +filterlist+ and return them.
219
- def load_filters( filterlist )
220
- filterlist.flatten.collect do |key|
221
- raise ArgumentError, "filter '#{key}' is not loaded" unless
222
- Manual::Page::Filter.derivatives.key?( key )
223
- Manual::Page::Filter.derivatives[ key ].instance
224
- end
225
- end
226
-
227
-
228
- ### Build the index relative to the receiving page and return it as a String
229
- def make_index_html
230
- items = [ '<div class="index">' ]
231
-
232
- @catalog.traverse_page_hierarchy( self ) do |type, title, path|
233
- case type
234
- when :section
235
- items << %Q{<div class="section">}
236
- items << %Q{<h2><a href="#{self.basepath + path}/">#{title}</a></h2>}
237
- items << '<ul class="index-section">'
238
-
239
- when :current_section
240
- items << %Q{<div class="section current-section">}
241
- items << %Q{<h2><a href="#{self.basepath + path}/">#{title}</a></h2>}
242
- items << '<ul class="index-section current-index-section">'
243
-
244
- when :section_end, :current_section_end
245
- items << '</ul></div>'
246
-
247
- when :entry
248
- items << %Q{<li><a href="#{self.basepath + path}.html">#{title}</a></li>}
249
-
250
- when :current_entry
251
- items << %Q{<li class="current-entry">#{title}</li>}
252
-
253
- else
254
- raise "Unknown index entry type %p" % [ type ]
255
- end
256
-
257
- end
258
-
259
- items << '</div>'
260
-
261
- return items.join("\n")
262
- end
263
-
264
- end
265
-
266
-
267
- ### A catalog of Manual::Page objects that can be referenced by various criteria.
268
- class PageCatalog
269
-
270
- ### Create a new PageCatalog that will load Manual::Page objects for .page files
271
- ### in the specified +sourcedir+.
272
- def initialize( sourcedir, layoutsdir )
273
- @sourcedir = sourcedir
274
- @layoutsdir = layoutsdir
275
-
276
- @pages = []
277
- @path_index = {}
278
- @uri_index = {}
279
- @title_index = {}
280
- @hierarchy = {}
281
-
282
- self.find_and_load_pages
283
- end
284
-
285
-
286
- ######
287
- public
288
- ######
289
-
290
- # An index of the pages in the catalog by Pathname
291
- attr_reader :path_index
292
-
293
- # An index of the pages in the catalog by title
294
- attr_reader :title_index
295
-
296
- # An index of the pages in the catalog by the URI of their source relative to the source
297
- # directory
298
- attr_reader :uri_index
299
-
300
- # The hierarchy of pages in the catalog, suitable for generating an on-page index
301
- attr_reader :hierarchy
302
-
303
- # An Array of all Manual::Page objects found
304
- attr_reader :pages
305
-
306
- # The Pathname location of the .page files.
307
- attr_reader :sourcedir
308
-
309
- # The Pathname location of look and feel templates.
310
- attr_reader :layoutsdir
311
-
312
-
313
- ### Traverse the catalog's #hierarchy, yielding to the given +builder+
314
- ### block for each entry, as well as each time a sub-hash is entered or
315
- ### exited, setting the +type+ appropriately. Valid values for +type+ are:
316
- ###
317
- ### :entry, :section, :section_end
318
- ###
319
- ### If the optional +from+ value is given, it should be the Manual::Page object
320
- ### which is considered "current"; if the +from+ object is the same as the
321
- ### hierarchy entry being yielded, it will be yielded with the +type+ set to
322
- ### one of:
323
- ###
324
- ### :current_entry, :current_section, :current_section_end
325
- ###
326
- ### each of which correspond to the like-named type from above.
327
- def traverse_page_hierarchy( from=nil, &builder ) # :yields: type, title, path
328
- raise LocalJumpError, "no block given" unless builder
329
- self.traverse_hierarchy( Pathname.new(''), self.hierarchy, from, &builder )
330
- end
331
-
332
-
333
- #########
334
- protected
335
- #########
336
-
337
- ### Sort and traverse the specified +hash+ recursively, yielding for each entry.
338
- def traverse_hierarchy( path, hash, from=nil, &builder )
339
- # Now generate the index in the sorted order
340
- sort_hierarchy( hash ).each do |subpath, page_or_section|
341
- if page_or_section.is_a?( Hash )
342
- self.handle_section_callback( path + subpath, page_or_section, from, &builder )
343
- else
344
- next if subpath == INDEX_PATH
345
- self.handle_page_callback( path + subpath, page_or_section, from, &builder )
346
- end
347
- end
348
- end
349
-
350
-
351
- ### Return the specified hierarchy of pages as a sorted Array of tuples.
352
- ### Sort the hierarchy using the 'index' config value of either the
353
- ### page, or the directory's index page if it's a directory.
354
- def sort_hierarchy( hierarchy )
355
- hierarchy.sort_by do |subpath, page_or_section|
356
-
357
- # Directory
358
- if page_or_section.is_a?( Hash )
359
-
360
- # Use the index of the index page if it exists
361
- if page_or_section[INDEX_PATH]
362
- idx = page_or_section[INDEX_PATH].config['index']
363
- trace "Index page's index for directory '%s' is: %p" % [ subpath, idx ]
364
- idx.to_s || subpath.to_s
365
- else
366
- trace "Using the path for the sort of directory %p" % [ subpath ]
367
- subpath.to_s
368
- end
369
-
370
- # Page
371
- else
372
- if subpath == INDEX_PATH
373
- trace "Sort index for index page %p is 0" % [ subpath ]
374
- '0'
375
- else
376
- idx = page_or_section.config['index']
377
- trace "Sort index for page %p is: %p" % [ subpath, idx ]
378
- idx.to_s || subpath.to_s
379
- end
380
- end
381
-
382
- end # sort_by
383
- end
384
-
385
-
386
- INDEX_PATH = Pathname.new('index')
387
-
388
- ### Build up the data structures necessary for calling the +builder+ callback
389
- ### for an index section and call it, then recurse into the section contents.
390
- def handle_section_callback( path, section, from=nil, &builder )
391
- from_current = false
392
- trace "Section handler: path=%p, section keys=%p, from=%s" %
393
- [ path, section.keys, from.sourcefile ]
394
-
395
- # Call the callback with :section -- determine the section title from
396
- # the 'index.page' file underneath it, or the directory name if no
397
- # index.page exists.
398
- if section.key?( INDEX_PATH )
399
- if section[INDEX_PATH].sourcefile.dirname == from.sourcefile.dirname
400
- from_current = true
401
- builder.call( :current_section, section[INDEX_PATH].title, path )
402
- else
403
- builder.call( :section, section[INDEX_PATH].title, path )
404
- end
405
- else
406
- title = File.dirname( path ).gsub( /_/, ' ' )
407
- builder.call( :section, title, path )
408
- end
409
-
410
- # Recurse
411
- self.traverse_hierarchy( path, section, from, &builder )
412
-
413
- # Call the callback with :section_end
414
- if from_current
415
- builder.call( :current_section_end, '', path )
416
- else
417
- builder.call( :section_end, '', path )
418
- end
419
- end
420
-
421
-
422
- ### Yield the specified +page+ to the builder
423
- def handle_page_callback( path, page, from=nil )
424
- if from == page
425
- yield( :current_entry, page.title, path )
426
- else
427
- yield( :entry, page.title, path )
428
- end
429
- end
430
-
431
-
432
- ### Find and store
433
-
434
- ### Find all .page files under the configured +sourcedir+ and create a new
435
- ### Manual::Page object for each one.
436
- def find_and_load_pages
437
- Pathname.glob( @sourcedir + '**/*.page' ).each do |pagefile|
438
- path_to_base = @sourcedir.relative_path_from( pagefile.dirname )
439
-
440
- page = Manual::Page.new( self, pagefile, @layoutsdir, path_to_base )
441
- hierpath = pagefile.relative_path_from( @sourcedir )
442
-
443
- @pages << page
444
- @path_index[ pagefile ] = page
445
- @title_index[ page.title ] = page
446
- @uri_index[ hierpath.to_s ] = page
447
-
448
- # Place the page in the page hierarchy by using inject to find and/or create the
449
- # necessary subhashes. The last run of inject will return the leaf hash in which
450
- # the page will live
451
- section = hierpath.dirname.split[1..-1].inject( @hierarchy ) do |hier, component|
452
- hier[ component ] ||= {}
453
- hier[ component ]
454
- end
455
-
456
- section[ pagefile.basename('.page') ] = page
457
- end
458
- end
459
-
460
- end
461
-
462
-
463
- ### A Textile filter for the manual generation tasklib, implemented using RedCloth.
464
- class TextileFilter < Manual::Page::Filter
465
-
466
- ### Load RedCloth when the filter is first created
467
- def initialize( *args )
468
- require 'redcloth'
469
- super
470
- end
471
-
472
-
473
- ### Process the given +source+ as Textile and return the resulting HTML
474
- ### fragment.
475
- def process( source, *ignored )
476
- formatter = RedCloth::TextileDoc.new( source )
477
- formatter.hard_breaks = false
478
- formatter.no_span_caps = true
479
- return formatter.to_html
480
- end
481
-
482
- end
483
-
484
-
485
- ### An ERB filter for the manual generation tasklib, implemented using Erubis.
486
- class ErbFilter < Manual::Page::Filter
487
-
488
- ### Process the given +source+ as ERB and return the resulting HTML
489
- ### fragment.
490
- def process( source, page, metadata )
491
- template_name = page.sourcefile.basename
492
- template = ERB.new( source )
493
- return template.result( binding() )
494
- end
495
-
496
- end
497
-
498
-
499
- ### Manual generation task library
500
- class GenTask < Rake::TaskLib
501
-
502
- # Default values for task config variables
503
- DEFAULT_NAME = :manual
504
- DEFAULT_BASE_DIR = Pathname.new( 'docs/manual' )
505
- DEFAULT_SOURCE_DIR = 'source'
506
- DEFAULT_LAYOUTS_DIR = 'layouts'
507
- DEFAULT_OUTPUT_DIR = 'output'
508
- DEFAULT_RESOURCE_DIR = 'resources'
509
- DEFAULT_LIB_DIR = 'lib'
510
- DEFAULT_METADATA = OpenStruct.new
511
-
512
-
513
- ### Define a new manual-generation task with the given +name+.
514
- def initialize( name=:manual )
515
- @name = name
516
-
517
- @source_dir = DEFAULT_SOURCE_DIR
518
- @layouts_dir = DEFAULT_LAYOUTS_DIR
519
- @output_dir = DEFAULT_OUTPUT_DIR
520
- @resource_dir = DEFAULT_RESOURCE_DIR
521
- @lib_dir = DEFAULT_LIB_DIR
522
- @metadata = DEFAULT_METADATA
523
-
524
- yield( self ) if block_given?
525
-
526
- self.define
527
- end
528
-
529
-
530
- ######
531
- public
532
- ######
533
-
534
- attr_accessor :base_dir,
535
- :source_dir,
536
- :layouts_dir,
537
- :output_dir,
538
- :resource_dir,
539
- :lib_dir,
540
- :metadata
541
-
542
- attr_reader :name
543
-
544
-
545
- ### Set up the tasks for building the manual
546
- def define
547
-
548
- # Set up a description if the caller hasn't already defined one
549
- unless Rake.application.last_comment
550
- desc "Generate the manual"
551
- end
552
-
553
- # Make Pathnames of the directories relative to the base_dir
554
- basedir = Pathname.new( @base_dir )
555
- sourcedir = basedir + @source_dir
556
- layoutsdir = basedir + @layouts_dir
557
- outputdir = @output_dir
558
- resourcedir = basedir + @resource_dir
559
- libdir = basedir + @lib_dir
560
-
561
- load_filter_libraries( libdir )
562
- catalog = Manual::PageCatalog.new( sourcedir, layoutsdir )
563
-
564
- # Declare the tasks outside the namespace that point in
565
- task @name => "#@name:build"
566
- task "clobber_#@name" => "#@name:clobber"
567
-
568
- namespace( self.name ) do
569
- setup_resource_copy_tasks( resourcedir, outputdir )
570
- manual_pages = setup_page_conversion_tasks( sourcedir, outputdir, catalog )
571
-
572
- desc "Build the manual"
573
- task :build => [ :apidocs, :copy_resources, :copy_apidocs, :generate_pages ]
574
-
575
- task :clobber do
576
- RakeFileUtils.verbose( $verbose ) do
577
- rm_f manual_pages.to_a
578
- end
579
- remove_dir( outputdir ) if ( outputdir + '.buildtime' ).exist?
580
- end
581
-
582
- desc "Remove any previously-generated parts of the manual and rebuild it"
583
- task :rebuild => [ :clobber, self.name ]
584
-
585
- desc "Watch for changes to the source files and rebuild when they change"
586
- task :autobuild do
587
- scope = [ self.name ]
588
- loop do
589
- t = Rake.application.lookup( :build, scope )
590
- t.reenable
591
- t.prerequisites.each do |pt|
592
- if task = Rake.application.lookup( pt, scope )
593
- task.reenable
594
- else
595
- trace "Hmmm... no %p task in scope %p?" % [ pt, scope ]
596
- end
597
- end
598
- t.invoke
599
- sleep 2
600
- trace " waking up..."
601
- end
602
- end
603
- end
604
-
605
- end # def define
606
-
607
-
608
- ### Load the filter libraries provided in the given +libdir+
609
- def load_filter_libraries( libdir )
610
- Pathname.glob( libdir + '*.rb' ) do |filterlib|
611
- trace " loading filter library #{filterlib}"
612
- require( filterlib )
613
- end
614
- end
615
-
616
-
617
- ### Set up the main HTML-generation task that will convert files in the given +sourcedir+ to
618
- ### HTML in the +outputdir+
619
- def setup_page_conversion_tasks( sourcedir, outputdir, catalog )
620
-
621
- # we need to figure out what HTML pages need to be generated so we can set up the
622
- # dependency that causes the rule to be fired for each one when the task is invoked.
623
- manual_sources = FileList[ catalog.path_index.keys.map {|pn| pn.to_s} ]
624
- trace " found %d source files" % [ manual_sources.length ]
625
-
626
- # Map .page files to their equivalent .html output
627
- html_pathmap = "%%{%s,%s}X.html" % [ sourcedir, outputdir ]
628
- manual_pages = manual_sources.pathmap( html_pathmap )
629
- trace "Mapping sources like so: \n %p -> %p" %
630
- [ manual_sources.first, manual_pages.first ]
631
-
632
- # Output directory task
633
- directory( outputdir.to_s )
634
- file outputdir.to_s do
635
- touch outputdir + '.buildtime'
636
- end
637
-
638
- # Rule to generate .html files from .page files
639
- rule(
640
- %r{#{outputdir}/.*\.html$} => [
641
- proc {|name| name.sub(/\.[^.]+$/, '.page').sub( outputdir, sourcedir) },
642
- outputdir.to_s
643
- ]) do |task|
644
-
645
- source = Pathname.new( task.source )
646
- target = Pathname.new( task.name )
647
- log " #{ source } -> #{ target }"
648
-
649
- page = catalog.path_index[ source ]
650
- #trace " page object is: %p" % [ page ]
651
-
652
- target.dirname.mkpath
653
- target.open( File::WRONLY|File::CREAT|File::TRUNC ) do |io|
654
- io.write( page.generate(metadata) )
655
- end
656
- end
657
-
658
- # Group all the manual page output files targets into a containing task
659
- desc "Generate any pages of the manual that have changed"
660
- task :generate_pages => manual_pages
661
- return manual_pages
662
- end
663
-
664
-
665
- ### Copy method for resources -- passed as a block to the various file tasks that copy
666
- ### resources to the output directory.
667
- def copy_resource( task )
668
- source = task.prerequisites[ 1 ]
669
- target = task.name
670
-
671
- when_writing do
672
- trace " #{source} -> #{target}"
673
- mkpath File.dirname( target ), :verbose => $trace unless
674
- File.directory?( File.dirname(target) )
675
- install source, target, :mode => 0644, :verbose => $trace
676
- end
677
- end
678
-
679
-
680
- ### Set up a rule for copying files from the resources directory to the output dir.
681
- def setup_resource_copy_tasks( resourcedir, outputdir )
682
- resources = FileList[ resourcedir + '**/*.{js,css,png,gif,jpg,html,svg,svgz,swf}' ]
683
- resources.exclude( /\.svn/ )
684
- target_pathmap = "%%{%s,%s}p" % [ resourcedir, outputdir ]
685
- targets = resources.pathmap( target_pathmap )
686
- copier = self.method( :copy_resource ).to_proc
687
-
688
- # Create a file task to copy each file to the output directory
689
- resources.each_with_index do |resource, i|
690
- file( targets[i] => [ outputdir.to_s, resource ], &copier )
691
- end
692
-
693
- desc "Copy API documentation to the manual output directory"
694
- task :copy_apidocs => :apidocs do
695
- cp_r( API_DOCSDIR, outputdir )
696
- end
697
-
698
- # Now group all the resource file tasks into a containing task
699
- desc "Copy manual resources to the output directory"
700
- task :copy_resources => targets do
701
- log "Copying manual resources"
702
- end
703
- end
704
-
705
- end # class Manual::GenTask
706
-
707
- end
708
-
709
-
710
-
711
- ### Task: manual generation
712
- if MANUALDIR.exist?
713
- MANUALOUTPUTDIR = MANUALDIR + 'output'
714
- trace "Manual will be generated in: #{MANUALOUTPUTDIR}"
715
-
716
- begin
717
- directory MANUALOUTPUTDIR.to_s
718
-
719
- Manual::GenTask.new do |manual|
720
- manual.metadata.version = PKG_VERSION
721
- manual.metadata.api_dir = API_DOCSDIR
722
- manual.output_dir = MANUALOUTPUTDIR
723
- manual.base_dir = MANUALDIR
724
- manual.source_dir = 'src'
725
- end
726
-
727
- CLOBBER.include( MANUALOUTPUTDIR.to_s )
728
-
729
- rescue LoadError => err
730
- task :no_manual do
731
- $stderr.puts "Manual-generation tasks not defined: %s" % [ err.message ]
732
- end
733
-
734
- task :manual => :no_manual
735
- task :clobber_manual => :no_manual
736
- end
737
-
738
- else
739
- TEMPLATEDIR = RAKE_TASKDIR + 'manualdir'
740
-
741
- if TEMPLATEDIR.exist?
742
-
743
- desc "Create a manual for this project from a template"
744
- task :manual do
745
- log "No manual directory (#{MANUALDIR}) currently exists."
746
- ask_for_confirmation( "Create a new manual directory tree from a template?" ) do
747
- MANUALDIR.mkpath
748
-
749
- %w[layouts lib output resources src].each do |dir|
750
- FileUtils.mkpath( MANUALDIR + dir, :mode => 0755, :verbose => true, :noop => $dryrun )
751
- end
752
-
753
- Pathname.glob( TEMPLATEDIR + '**/*.{rb,css,png,js,erb,page}' ).each do |tmplfile|
754
- trace "extname is: #{tmplfile.extname}"
755
-
756
- # Render ERB files
757
- if tmplfile.extname == '.erb'
758
- rname = tmplfile.basename( '.erb' )
759
- target = MANUALDIR + tmplfile.dirname.relative_path_from( TEMPLATEDIR ) + rname
760
- template = ERB.new( tmplfile.read, nil, '<>' )
761
-
762
- target.dirname.mkpath( :mode => 0755, :verbose => true, :noop => $dryrun ) unless
763
- target.dirname.directory?
764
- html = template.result( binding() )
765
- log "generating #{target}: html => #{html[0,20]}"
766
-
767
- target.open( File::WRONLY|File::CREAT|File::EXCL, 0644 ) do |fh|
768
- fh.print( html )
769
- end
770
-
771
- # Just copy anything else
772
- else
773
- target = MANUALDIR + tmplfile.relative_path_from( TEMPLATEDIR )
774
- FileUtils.mkpath target.dirname,
775
- :mode => 0755, :verbose => true, :noop => $dryrun unless target.dirname.directory?
776
- FileUtils.install tmplfile, target,
777
- :mode => 0644, :verbose => true, :noop => $dryrun
778
- end
779
- end
780
- end
781
-
782
- end # task :manual
783
-
784
- end
785
- end
786
-
787
-