bluecloth 2.0.7-x86-mingw32 → 2.0.11pre158-x86-mingw32

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