hoe-manualgen 0.0.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.
- data/.autotest +23 -0
- data/History.md +4 -0
- data/README.md +72 -0
- data/Rakefile +85 -0
- data/data/hoe-manualgen/layouts/default.erb.erb +87 -0
- data/data/hoe-manualgen/lib/api-filter.rb +76 -0
- data/data/hoe-manualgen/lib/editorial-filter.rb +59 -0
- data/data/hoe-manualgen/lib/examples-filter.rb +238 -0
- data/data/hoe-manualgen/lib/links-filter.rb +111 -0
- data/data/hoe-manualgen/resources/css/manual.css.erb +755 -0
- data/data/hoe-manualgen/resources/fonts/GraublauWeb.otf +0 -0
- data/data/hoe-manualgen/resources/fonts/GraublauWebBold.otf +0 -0
- data/data/hoe-manualgen/resources/fonts/Inconsolata.otf +0 -0
- data/data/hoe-manualgen/resources/images/arrow_225_small.png +0 -0
- data/data/hoe-manualgen/resources/images/arrow_315_small.png +0 -0
- data/data/hoe-manualgen/resources/images/arrow_skip.png +0 -0
- data/data/hoe-manualgen/resources/images/cc-by.png +0 -0
- data/data/hoe-manualgen/resources/images/dialog-error.png +0 -0
- data/data/hoe-manualgen/resources/images/dialog-information.png +0 -0
- data/data/hoe-manualgen/resources/images/dialog-warning.png +0 -0
- data/data/hoe-manualgen/resources/images/emblem-important.png +0 -0
- data/data/hoe-manualgen/resources/images/help.png +0 -0
- data/data/hoe-manualgen/resources/images/information.png +0 -0
- data/data/hoe-manualgen/resources/images/magnifier.png +0 -0
- data/data/hoe-manualgen/resources/images/magnifier_left.png +0 -0
- data/data/hoe-manualgen/resources/images/page_white_code.png +0 -0
- data/data/hoe-manualgen/resources/images/page_white_copy.png +0 -0
- data/data/hoe-manualgen/resources/images/printer.png +0 -0
- data/data/hoe-manualgen/resources/images/question.png +0 -0
- data/data/hoe-manualgen/resources/images/scripts_code.png +0 -0
- data/data/hoe-manualgen/resources/images/wrap.png +0 -0
- data/data/hoe-manualgen/resources/images/wrapping.png +0 -0
- data/data/hoe-manualgen/resources/js/jquery-1.4.4.min.js +167 -0
- data/data/hoe-manualgen/resources/js/manual.js +30 -0
- data/data/hoe-manualgen/resources/js/sh.js +580 -0
- data/data/hoe-manualgen/resources/swf/clipboard.swf +0 -0
- data/data/hoe-manualgen/src/index.page.erb +59 -0
- data/lib/hoe/manualgen.rb +804 -0
- data.tar.gz.sig +0 -0
- metadata +230 -0
- metadata.gz.sig +4 -0
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/History.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# hoe-manualgen
|
2
|
+
|
3
|
+
* http://deveiate.org/hoe-manualgen.html
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
A manual-generation plugin for Hoe.
|
8
|
+
|
9
|
+
This is a plugin for [Hoe][hoe] that adds tasks and resources for
|
10
|
+
generating a manual or cookbook.
|
11
|
+
|
12
|
+
It's self-documenting, so for more, see [the latest manual for hoe-manualgen itself][hoe-manualgen-manual].
|
13
|
+
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
gem install hoe-manualgen
|
18
|
+
|
19
|
+
|
20
|
+
## Contributing
|
21
|
+
|
22
|
+
You can check out the current development source with Mercurial like so:
|
23
|
+
|
24
|
+
hg clone http://bitbucket.org/ged/hoe-manualgen
|
25
|
+
|
26
|
+
Or if you prefer Git, via its Github mirror:
|
27
|
+
|
28
|
+
https://github.com/ged/hoe-manualgen
|
29
|
+
|
30
|
+
After checking out the source, run:
|
31
|
+
|
32
|
+
$ rake newb
|
33
|
+
|
34
|
+
This task will install any missing dependencies, run the tests/specs,
|
35
|
+
and generate the API documentation.
|
36
|
+
|
37
|
+
|
38
|
+
## License
|
39
|
+
|
40
|
+
Copyright (c) 2010, 2011, Michael Granger
|
41
|
+
All rights reserved.
|
42
|
+
|
43
|
+
Redistribution and use in source and binary forms, with or without
|
44
|
+
modification, are permitted provided that the following conditions are met:
|
45
|
+
|
46
|
+
* Redistributions of source code must retain the above copyright notice,
|
47
|
+
this list of conditions and the following disclaimer.
|
48
|
+
|
49
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
50
|
+
this list of conditions and the following disclaimer in the documentation
|
51
|
+
and/or other materials provided with the distribution.
|
52
|
+
|
53
|
+
* Neither the name of the author/s, nor the names of the project's
|
54
|
+
contributors may be used to endorse or promote products derived from this
|
55
|
+
software without specific prior written permission.
|
56
|
+
|
57
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
58
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
59
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
60
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
61
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
62
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
63
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
64
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
65
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
66
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
67
|
+
|
68
|
+
|
69
|
+
[hoe]:http://seattlerb.rubyforge.org/hoe/
|
70
|
+
[hoe-manualgen-manual]:http://deveiate.org/code/hoe-manualgen/
|
71
|
+
|
72
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'hoe'
|
5
|
+
rescue LoadError
|
6
|
+
$stderr.puts "This Rakefile requires Hoe (gem install hoe)"
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake/clean'
|
10
|
+
|
11
|
+
Hoe.add_include_dirs 'lib'
|
12
|
+
|
13
|
+
Hoe.plugin :mercurial
|
14
|
+
Hoe.plugin :signing
|
15
|
+
Hoe.plugin :manualgen
|
16
|
+
|
17
|
+
Hoe.plugins.delete :rubyforge
|
18
|
+
|
19
|
+
hoespec = Hoe.spec 'hoe-manualgen' do
|
20
|
+
self.readme_file = 'README.md'
|
21
|
+
self.history_file = 'History.md'
|
22
|
+
|
23
|
+
self.developer 'Michael Granger', 'ged@FaerieMUD.org'
|
24
|
+
|
25
|
+
self.extra_deps.push *{
|
26
|
+
'RedCloth' => '~> 4.2.3',
|
27
|
+
'rcodetools' => '~> 0.8.5.0',
|
28
|
+
}
|
29
|
+
self.extra_dev_deps.push *{
|
30
|
+
'rspec' => '~> 2.4.0',
|
31
|
+
'tidy-ext' => '~> 0.1.10',
|
32
|
+
}
|
33
|
+
|
34
|
+
self.spec_extras[:licenses] = ["BSD"]
|
35
|
+
self.spec_extras[:signing_key] = '/Volumes/Keys/ged-private_gem_key.pem'
|
36
|
+
|
37
|
+
self.require_ruby_version( '>=1.8.7' )
|
38
|
+
|
39
|
+
self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
|
40
|
+
|
41
|
+
self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
|
42
|
+
end
|
43
|
+
|
44
|
+
ENV['VERSION'] ||= hoespec.spec.version.to_s
|
45
|
+
|
46
|
+
begin
|
47
|
+
include Hoe::MercurialHelpers
|
48
|
+
|
49
|
+
### Task: prerelease
|
50
|
+
desc "Append the package build number to package versions"
|
51
|
+
task :pre do
|
52
|
+
rev = get_numeric_rev()
|
53
|
+
trace "Current rev is: %p" % [ rev ]
|
54
|
+
hoespec.spec.version.version << "pre#{rev}"
|
55
|
+
Rake::Task[:gem].clear
|
56
|
+
|
57
|
+
Gem::PackageTask.new( hoespec.spec ) do |pkg|
|
58
|
+
pkg.need_zip = true
|
59
|
+
pkg.need_tar = true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
### Make the ChangeLog update if the repo has changed since it was last built
|
64
|
+
file '.hg/branch'
|
65
|
+
file 'ChangeLog' => '.hg/branch' do |task|
|
66
|
+
$stderr.puts "Updating the changelog..."
|
67
|
+
content = make_changelog()
|
68
|
+
File.open( task.name, 'w', 0644 ) do |fh|
|
69
|
+
fh.print( content )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Rebuild the ChangeLog immediately before release
|
74
|
+
task :prerelease => 'ChangeLog'
|
75
|
+
|
76
|
+
rescue NameError => err
|
77
|
+
task :no_hg_helpers do
|
78
|
+
fail "Couldn't define the :pre task: %s: %s" % [ err.class.name, err.message ]
|
79
|
+
end
|
80
|
+
|
81
|
+
task :pre => :no_hg_helpers
|
82
|
+
task 'ChangeLog' => :no_hg_helpers
|
83
|
+
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<!--
|
3
|
+
|
4
|
+
Main Manual Layout
|
5
|
+
$Id: default.erb.erb,v 8077c9cdbac9 2011/01/26 02:15:09 ged $
|
6
|
+
|
7
|
+
Author: Michael Granger <ged@FaerieMUD.org>
|
8
|
+
Two column fluid layout adapted from Matthew James Taylor's "Perfect 'Left Menu'
|
9
|
+
2 Column Liquid Layout":
|
10
|
+
http://matthewjamestaylor.com/blog/perfect-2-column-left-menu.htm
|
11
|
+
|
12
|
+
-->
|
13
|
+
<html lang="en">
|
14
|
+
<head>
|
15
|
+
<meta charset="utf-8">
|
16
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
17
|
+
|
18
|
+
<title><%= name %> Manual — <%%= page.config['title'] || "untitled" %></title>
|
19
|
+
|
20
|
+
<link rel="stylesheet" href="css/manual.css">
|
21
|
+
|
22
|
+
<script src="js/jquery-1.4.4.min.js"></script>
|
23
|
+
<script src="js/sh.js"></script>
|
24
|
+
<script src="js/manual.js"></script>
|
25
|
+
|
26
|
+
</head>
|
27
|
+
<body>
|
28
|
+
<div id="page">
|
29
|
+
|
30
|
+
<header>
|
31
|
+
<hgroup>
|
32
|
+
<a href="/">
|
33
|
+
<h1><%= name %></h1>
|
34
|
+
</a>
|
35
|
+
<%% if metadata.version %><h2 class="version"><%%= metadata.version %></h2><%% end %>
|
36
|
+
<%% if page.config['tagline'] %><h2 class="tagline"><%%= page.config['tagline'] %></h2><%% end %>
|
37
|
+
</hgroup>
|
38
|
+
</header>
|
39
|
+
|
40
|
+
<div class="colmask leftmenu">
|
41
|
+
<div class="colleft">
|
42
|
+
|
43
|
+
<section id="content" class="col1">
|
44
|
+
<!-- Generated content -->
|
45
|
+
<%%= content %>
|
46
|
+
<!-- end of generated content -->
|
47
|
+
</section>
|
48
|
+
|
49
|
+
<aside id="sidebar" class="col2">
|
50
|
+
<nav>
|
51
|
+
<%%
|
52
|
+
@catalog.traverse_page_hierarchy( self ) do |type, title, path|
|
53
|
+
case type
|
54
|
+
when :section
|
55
|
+
%>
|
56
|
+
<div class="section">
|
57
|
+
<h3><a href="<%%= self.basepath + path %>/"><%%= title %></a></h3>
|
58
|
+
<ul class="index-section">
|
59
|
+
<%% when :current_section %>
|
60
|
+
<div class="section current-section">
|
61
|
+
<h3><a href="<%%= self.basepath + path %>/"><%%= title %></a></h3>
|
62
|
+
<ul class="index-section current-index-section">
|
63
|
+
<%% when :section_end, :current_section_end %>
|
64
|
+
</ul>
|
65
|
+
</div>
|
66
|
+
<%% when :entry %>
|
67
|
+
<li><a href="<%%= self.basepath + path %>.html"><%%= title %></a></li>
|
68
|
+
<%% when :current_entry %>
|
69
|
+
<li class="current-entry"><%%= title %></li>
|
70
|
+
<%% end
|
71
|
+
end
|
72
|
+
%>
|
73
|
+
</nav>
|
74
|
+
</aside>
|
75
|
+
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
|
79
|
+
<footer>
|
80
|
+
<div class="copyright">Copyright © 2008-<%%= Time.now.year %> <%= author.join(', ') %>.</div>
|
81
|
+
<div class="vcsrev">Rev: $Revision: 8077c9cdbac9 $</div>
|
82
|
+
<div class="timestamp">Built: <%%= Time.now.strftime("%Y%m%d %H:%M:%S %Z") %></div>
|
83
|
+
</footer>
|
84
|
+
|
85
|
+
</body>
|
86
|
+
</html>
|
87
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# A manual filter to generate links from the Darkfish API.
|
4
|
+
#
|
5
|
+
# Authors:
|
6
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
7
|
+
# * Mahlon E. Smith <mahlon@martini.nu>
|
8
|
+
#
|
9
|
+
|
10
|
+
|
11
|
+
### A filter for generating links from the generated API documentation. This allows you to refer
|
12
|
+
### to class documentation by simply referencing a class name.
|
13
|
+
###
|
14
|
+
### Links are XML processing instructions. Pages can be referenced as such:
|
15
|
+
###
|
16
|
+
### <?api Class::Name ?>
|
17
|
+
### <?api "click here":Class::Name ?>
|
18
|
+
###
|
19
|
+
class Hoe::ManualGen::APIFilter < Hoe::ManualGen::Page::Filter
|
20
|
+
|
21
|
+
# PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
|
22
|
+
ApiPI = %r{
|
23
|
+
<\?
|
24
|
+
api # Instruction Target
|
25
|
+
\s+
|
26
|
+
(?:"
|
27
|
+
(.*?) # Optional link text [$1]
|
28
|
+
":)?
|
29
|
+
(.*?) # Class name [$2]
|
30
|
+
\s+
|
31
|
+
\?>
|
32
|
+
}x
|
33
|
+
|
34
|
+
|
35
|
+
######
|
36
|
+
public
|
37
|
+
######
|
38
|
+
|
39
|
+
### Process the given +source+ for <?api ... ?> processing-instructions, calling out
|
40
|
+
def process( source, page, metadata )
|
41
|
+
|
42
|
+
apipath = metadata.api_dir or
|
43
|
+
raise "The API output directory is not defined in the manual task."
|
44
|
+
|
45
|
+
return source.gsub( ApiPI ) do |match|
|
46
|
+
# Grab the tag values
|
47
|
+
link_text = $1
|
48
|
+
classname = $2
|
49
|
+
|
50
|
+
self.generate_link( page, apipath, classname, link_text )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
### Create an HTML link fragment from the parsed ApiPI.
|
56
|
+
###
|
57
|
+
def generate_link( current_page, apipath, classname, link_text=nil )
|
58
|
+
|
59
|
+
classpath = "%s.html" % [ classname.gsub('::', '/') ]
|
60
|
+
classfile = apipath + classpath
|
61
|
+
classuri = current_page.basepath + 'api' + classpath
|
62
|
+
|
63
|
+
if classfile.exist?
|
64
|
+
return %{<a href="%s">%s</a>} % [
|
65
|
+
classuri,
|
66
|
+
link_text || classname
|
67
|
+
]
|
68
|
+
else
|
69
|
+
link_text ||= classname
|
70
|
+
error_message = "Could not find a link for class '%s'" % [ classname ]
|
71
|
+
$stderr.puts( error_message )
|
72
|
+
return %{<a href="#" title="#{error_message}" class="broken-link">#{link_text}</a>}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# A manual filter to highlight content that needs editorial help.
|
4
|
+
#
|
5
|
+
# Authors:
|
6
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
7
|
+
#
|
8
|
+
#
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
### A filter for making editorial marks in manual content.
|
13
|
+
###
|
14
|
+
### Editorial marks are XML processing instructions. There are several available types of
|
15
|
+
### marks:
|
16
|
+
###
|
17
|
+
### <?ed "This is an editor's note." ?>
|
18
|
+
### <?ed verify:"this content needs checking or verification" ?>
|
19
|
+
###
|
20
|
+
class Hoe::ManualGen::EditorialFilter < Hoe::ManualGen::Page::Filter
|
21
|
+
|
22
|
+
# PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
|
23
|
+
LinkPI = %r{
|
24
|
+
<\?
|
25
|
+
ed # Instruction Target
|
26
|
+
\s+
|
27
|
+
(\w+?) # type of editorial mark [$1]
|
28
|
+
:? # optional colon
|
29
|
+
"
|
30
|
+
(.*?) # content that should be edited [$2]
|
31
|
+
"
|
32
|
+
\s*
|
33
|
+
\?>
|
34
|
+
}x
|
35
|
+
|
36
|
+
|
37
|
+
######
|
38
|
+
public
|
39
|
+
######
|
40
|
+
|
41
|
+
### Process the given +source+ for <?ed ... ?> processing-instructions
|
42
|
+
def process( source, page, metadata )
|
43
|
+
return source.gsub( LinkPI ) do |match|
|
44
|
+
# Grab the tag values
|
45
|
+
mark_type = $1
|
46
|
+
content = $2
|
47
|
+
|
48
|
+
self.generate_mark( page, mark_type, content )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
### Create an HTML fragment from the parsed LinkPI.
|
54
|
+
def generate_mark( current_page, mark_type, content )
|
55
|
+
return "%%(editorial %s-mark)%s%%" % [ mark_type, content ]
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# A collection of standard filters for the manual generation tasklib.
|
4
|
+
#
|
5
|
+
# Authors:
|
6
|
+
# Michael Granger <ged@FaerieMUD.org>
|
7
|
+
#
|
8
|
+
#
|
9
|
+
|
10
|
+
# Dependencies deferred until #initialize
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
### A filter for inline example code or command-line sessions -- does
|
15
|
+
### syntax-checking for some languages and captioning.
|
16
|
+
###
|
17
|
+
### Examples are enclosed in XML processing instructions like so:
|
18
|
+
###
|
19
|
+
### <?example {language: ruby, testable: true, caption: "A fine example"} ?>
|
20
|
+
### a = 1
|
21
|
+
### puts a
|
22
|
+
### <?end example ?>
|
23
|
+
###
|
24
|
+
### This will be pulled out into a preformatted section in the HTML,
|
25
|
+
### highlighted as Ruby source, checked for valid syntax, and annotated with
|
26
|
+
### the specified caption. Valid keys in the example PI are:
|
27
|
+
###
|
28
|
+
### language::
|
29
|
+
### Specifies which (machine) language the example is in.
|
30
|
+
### testable::
|
31
|
+
### If set and there is a testing function for the given language, run it and append
|
32
|
+
### any errors to the output.
|
33
|
+
### caption::
|
34
|
+
### A small blurb to put below the pulled-out example in the HTML.
|
35
|
+
class Hoe::ManualGen::ExamplesFilter < Hoe::ManualGen::Page::Filter
|
36
|
+
|
37
|
+
DEFAULTS = {
|
38
|
+
:language => :ruby,
|
39
|
+
:line_numbers => :inline,
|
40
|
+
:tab_width => 4,
|
41
|
+
:hint => :debug,
|
42
|
+
:testable => false,
|
43
|
+
}
|
44
|
+
|
45
|
+
# PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
|
46
|
+
ExamplePI = %r{
|
47
|
+
<\?
|
48
|
+
example # Instruction Target
|
49
|
+
(?: # Optional instruction body
|
50
|
+
\s+
|
51
|
+
((?: # [$1]
|
52
|
+
[^?]* # Run of anything but a question mark
|
53
|
+
| # -or-
|
54
|
+
\?(?!>) # question mark not followed by a closing angle bracket
|
55
|
+
)*)
|
56
|
+
)?
|
57
|
+
\?>
|
58
|
+
}x
|
59
|
+
|
60
|
+
EndPI = %r{ <\? end (?: \s+ example )? \s* \?> }x
|
61
|
+
|
62
|
+
|
63
|
+
### Defer loading of dependenies until the filter is loaded
|
64
|
+
def initialize( *args )
|
65
|
+
begin
|
66
|
+
require 'pathname'
|
67
|
+
require 'strscan'
|
68
|
+
require 'yaml'
|
69
|
+
require 'rcodetools/xmpfilter'
|
70
|
+
require 'digest/md5'
|
71
|
+
require 'tmpdir'
|
72
|
+
require 'erb'
|
73
|
+
rescue LoadError => err
|
74
|
+
unless Object.const_defined?( :Gem )
|
75
|
+
require 'rubygems'
|
76
|
+
retry
|
77
|
+
end
|
78
|
+
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
######
|
85
|
+
public
|
86
|
+
######
|
87
|
+
|
88
|
+
### Process the given +source+ for <?example ... ?> processing-instructions, calling out
|
89
|
+
def process( source, page, metadata )
|
90
|
+
scanner = StringScanner.new( source )
|
91
|
+
|
92
|
+
buffer = ''
|
93
|
+
until scanner.eos?
|
94
|
+
startpos = scanner.pos
|
95
|
+
|
96
|
+
# If we find an example
|
97
|
+
if scanner.skip_until( ExamplePI )
|
98
|
+
contents = ''
|
99
|
+
|
100
|
+
# Append the interstitial content to the buffer
|
101
|
+
if ( scanner.pos - startpos > scanner.matched.length )
|
102
|
+
offset = scanner.pos - scanner.matched.length - 1
|
103
|
+
buffer << scanner.string[ startpos..offset ]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Append everything up to it to the buffer and save the contents of
|
107
|
+
# the tag
|
108
|
+
params = scanner[1]
|
109
|
+
|
110
|
+
# Now find the end of the example or complain
|
111
|
+
contentpos = scanner.pos
|
112
|
+
scanner.skip_until( EndPI ) or
|
113
|
+
raise "Unterminated example at line %d" %
|
114
|
+
[ scanner.string[0..scanner.pos].count("\n") ]
|
115
|
+
|
116
|
+
# Now build the example and append to the buffer
|
117
|
+
if ( scanner.pos - contentpos > scanner.matched.length )
|
118
|
+
offset = scanner.pos - scanner.matched.length - 1
|
119
|
+
contents = scanner.string[ contentpos..offset ]
|
120
|
+
end
|
121
|
+
|
122
|
+
trace "Processing with params: %p, contents: %p" % [ params, contents ]
|
123
|
+
buffer << self.process_example( params, contents, page )
|
124
|
+
else
|
125
|
+
break
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
buffer << scanner.rest
|
130
|
+
scanner.terminate
|
131
|
+
|
132
|
+
return buffer
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
### Filter out 'example' macros, doing syntax highlighting, and running
|
137
|
+
### 'testable' examples through a validation process appropriate to the
|
138
|
+
### language the example is in.
|
139
|
+
def process_example( params, body, page )
|
140
|
+
options = self.parse_options( params )
|
141
|
+
caption = options.delete( :caption )
|
142
|
+
content = ''
|
143
|
+
lang = options.delete( :language ).to_s
|
144
|
+
|
145
|
+
# Test it if it's testable
|
146
|
+
if options[:testable]
|
147
|
+
content = test_content( body, lang, page )
|
148
|
+
else
|
149
|
+
content = body
|
150
|
+
end
|
151
|
+
|
152
|
+
# Strip trailing blank lines and syntax-highlight
|
153
|
+
content = highlight( content.strip, options, lang )
|
154
|
+
caption = %{<div class="caption">} + caption.to_s + %{</div>} if caption
|
155
|
+
|
156
|
+
return %{<notextile><div class="example #{lang}-example">%s%s</div></notextile>} %
|
157
|
+
[content, caption || '']
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
### Parse an options hash for filtering from the given +args+, which can either
|
162
|
+
### be a plain String, in which case it is assumed to be the name of the language the example
|
163
|
+
### is in, or a Hash of configuration options.
|
164
|
+
def parse_options( args )
|
165
|
+
args = "{ #{args} }" unless args.strip[0] == ?{
|
166
|
+
args = YAML.load( args )
|
167
|
+
|
168
|
+
# Convert to Symbol keys and value
|
169
|
+
args.keys.each do |k|
|
170
|
+
newval = args.delete( k )
|
171
|
+
next if newval.nil? || (newval.respond_to?(:size) && newval.size == 0)
|
172
|
+
args[ k.to_sym ] = newval.respond_to?( :to_sym ) ? newval.to_sym : newval
|
173
|
+
end
|
174
|
+
return DEFAULTS.merge( args )
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
### Test the given +content+ with a rule specific to the given +language+.
|
179
|
+
def test_content( body, language, page )
|
180
|
+
case language.to_sym
|
181
|
+
when :ruby
|
182
|
+
return self.test_ruby_content( body, page )
|
183
|
+
|
184
|
+
when :yaml
|
185
|
+
return self.test_yaml_content( body, page )
|
186
|
+
|
187
|
+
else
|
188
|
+
return body
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
### Test the specified Ruby content for valid syntax
|
194
|
+
def test_ruby_content( source, page )
|
195
|
+
# $stderr.puts "Testing ruby content..."
|
196
|
+
libdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'lib'
|
197
|
+
extdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'ext'
|
198
|
+
|
199
|
+
options = Rcodetools::XMPFilter::INITIALIZE_OPTS.dup
|
200
|
+
options[:include_paths] |= [ libdir.to_s, extdir.to_s ]
|
201
|
+
options[:width] = 60
|
202
|
+
|
203
|
+
if page.config['example_prelude']
|
204
|
+
prelude = page.config['example_prelude']
|
205
|
+
trace " prepending prelude:\n#{prelude}"
|
206
|
+
source = prelude.strip + "\n" + source.strip
|
207
|
+
else
|
208
|
+
trace " no prelude; page config is: %p" % [ page.config ]
|
209
|
+
end
|
210
|
+
|
211
|
+
rval = Rcodetools::XMPFilter.run( source, options )
|
212
|
+
|
213
|
+
trace "test output: ", rval
|
214
|
+
return rval.join
|
215
|
+
rescue Exception => err
|
216
|
+
return "%s while testing: %s\n %s" %
|
217
|
+
[ err.class.name, err.message, err.backtrace.join("\n ") ]
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
### Test the specified YAML content for valid syntax
|
222
|
+
def test_yaml_content( source, metadata )
|
223
|
+
YAML.load( source )
|
224
|
+
rescue YAML::Error => err
|
225
|
+
return "# Invalid YAML: " + err.message + "\n" + source
|
226
|
+
else
|
227
|
+
return source
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
### Highlights the given +content+ in language +lang+.
|
232
|
+
def highlight( content, options, lang )
|
233
|
+
source = ERB::Util.html_escape( content )
|
234
|
+
return %Q{\n\n<pre class="brush:#{lang}">#{source}</pre>\n\n}
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|