arrow 1.0.7

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.
Files changed (198) hide show
  1. data/ChangeLog +1590 -0
  2. data/LICENSE +28 -0
  3. data/README +75 -0
  4. data/Rakefile +366 -0
  5. data/Rakefile.local +63 -0
  6. data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
  7. data/data/arrow/applets/args.rb +50 -0
  8. data/data/arrow/applets/config.rb +55 -0
  9. data/data/arrow/applets/error.rb +63 -0
  10. data/data/arrow/applets/files.rb +46 -0
  11. data/data/arrow/applets/inspect.rb +46 -0
  12. data/data/arrow/applets/nosuchapplet.rb +31 -0
  13. data/data/arrow/applets/status.rb +92 -0
  14. data/data/arrow/applets/test.rb +133 -0
  15. data/data/arrow/applets/tutorial/counter.rb +96 -0
  16. data/data/arrow/applets/tutorial/dingus.rb +67 -0
  17. data/data/arrow/applets/tutorial/hello.rb +34 -0
  18. data/data/arrow/applets/tutorial/hello2.rb +73 -0
  19. data/data/arrow/applets/tutorial/imgtext.rb +90 -0
  20. data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
  21. data/data/arrow/applets/tutorial/index.rb +36 -0
  22. data/data/arrow/applets/tutorial/logo.rb +98 -0
  23. data/data/arrow/applets/tutorial/memcache.rb +61 -0
  24. data/data/arrow/applets/tutorial/missing.rb +37 -0
  25. data/data/arrow/applets/tutorial/protected.rb +100 -0
  26. data/data/arrow/applets/tutorial/redirector.rb +52 -0
  27. data/data/arrow/applets/tutorial/rndimages.rb +159 -0
  28. data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
  29. data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
  30. data/data/arrow/applets/tutorial/superhello.rb +72 -0
  31. data/data/arrow/applets/tutorial/timeclock.rb +78 -0
  32. data/data/arrow/applets/view-applet.rb +123 -0
  33. data/data/arrow/applets/view-template.rb +85 -0
  34. data/data/arrow/applets/wiki.rb +274 -0
  35. data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
  36. data/data/arrow/templates/applet-status.tmpl +153 -0
  37. data/data/arrow/templates/args-display.tmpl +120 -0
  38. data/data/arrow/templates/config/display-table.tmpl +36 -0
  39. data/data/arrow/templates/config/display.tmpl +36 -0
  40. data/data/arrow/templates/counter-deleted.tmpl +33 -0
  41. data/data/arrow/templates/counter.tmpl +59 -0
  42. data/data/arrow/templates/dingus.tmpl +55 -0
  43. data/data/arrow/templates/enumtable.tmpl +8 -0
  44. data/data/arrow/templates/error-display.tmpl +92 -0
  45. data/data/arrow/templates/filemap.tmpl +89 -0
  46. data/data/arrow/templates/hello-world-src.tmpl +34 -0
  47. data/data/arrow/templates/hello-world.tmpl +60 -0
  48. data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
  49. data/data/arrow/templates/imgtext/form.tmpl +70 -0
  50. data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
  51. data/data/arrow/templates/imgtext/reload.tmpl +55 -0
  52. data/data/arrow/templates/inspect/display.tmpl +80 -0
  53. data/data/arrow/templates/loginform.tmpl +64 -0
  54. data/data/arrow/templates/logout.tmpl +32 -0
  55. data/data/arrow/templates/memcache/display.tmpl +41 -0
  56. data/data/arrow/templates/navbar.incl +27 -0
  57. data/data/arrow/templates/nosuchapplet.tmpl +32 -0
  58. data/data/arrow/templates/printsource.tmpl +35 -0
  59. data/data/arrow/templates/protected.tmpl +36 -0
  60. data/data/arrow/templates/rndimages.tmpl +38 -0
  61. data/data/arrow/templates/service-response.tmpl +13 -0
  62. data/data/arrow/templates/sharenotes/display.tmpl +38 -0
  63. data/data/arrow/templates/status.tmpl +120 -0
  64. data/data/arrow/templates/templateviewer.tmpl +43 -0
  65. data/data/arrow/templates/test/harness.tmpl +57 -0
  66. data/data/arrow/templates/test/list.tmpl +48 -0
  67. data/data/arrow/templates/test/problem.tmpl +42 -0
  68. data/data/arrow/templates/tutorial/index.tmpl +37 -0
  69. data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
  70. data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
  71. data/data/arrow/templates/view-applet.tmpl +40 -0
  72. data/data/arrow/templates/view-template.tmpl +83 -0
  73. data/data/arrow/templates/wiki/formerror.tmpl +47 -0
  74. data/data/arrow/templates/wiki/markup_help.incl +6 -0
  75. data/data/arrow/templates/wiki/new.tmpl +56 -0
  76. data/data/arrow/templates/wiki/new_system.tmpl +122 -0
  77. data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
  78. data/data/arrow/templates/wiki/show.tmpl +34 -0
  79. data/docs/manual/layouts/default.page +43 -0
  80. data/docs/manual/lib/api-filter.rb +81 -0
  81. data/docs/manual/lib/editorial-filter.rb +64 -0
  82. data/docs/manual/lib/examples-filter.rb +244 -0
  83. data/docs/manual/lib/links-filter.rb +117 -0
  84. data/lib/apache/fakerequest.rb +448 -0
  85. data/lib/apache/logger.rb +33 -0
  86. data/lib/arrow.rb +51 -0
  87. data/lib/arrow/acceptparam.rb +207 -0
  88. data/lib/arrow/applet.rb +725 -0
  89. data/lib/arrow/appletmixins.rb +218 -0
  90. data/lib/arrow/appletregistry.rb +590 -0
  91. data/lib/arrow/applettestcase.rb +503 -0
  92. data/lib/arrow/broker.rb +255 -0
  93. data/lib/arrow/cache.rb +176 -0
  94. data/lib/arrow/config-loaders/yaml.rb +75 -0
  95. data/lib/arrow/config.rb +615 -0
  96. data/lib/arrow/constants.rb +24 -0
  97. data/lib/arrow/cookie.rb +359 -0
  98. data/lib/arrow/cookieset.rb +108 -0
  99. data/lib/arrow/dispatcher.rb +368 -0
  100. data/lib/arrow/dispatcherloader.rb +50 -0
  101. data/lib/arrow/exceptions.rb +61 -0
  102. data/lib/arrow/fallbackhandler.rb +48 -0
  103. data/lib/arrow/formvalidator.rb +631 -0
  104. data/lib/arrow/htmltokenizer.rb +343 -0
  105. data/lib/arrow/logger.rb +488 -0
  106. data/lib/arrow/logger/apacheoutputter.rb +69 -0
  107. data/lib/arrow/logger/arrayoutputter.rb +63 -0
  108. data/lib/arrow/logger/coloroutputter.rb +111 -0
  109. data/lib/arrow/logger/fileoutputter.rb +96 -0
  110. data/lib/arrow/logger/htmloutputter.rb +54 -0
  111. data/lib/arrow/logger/outputter.rb +123 -0
  112. data/lib/arrow/mixins.rb +425 -0
  113. data/lib/arrow/monkeypatches.rb +94 -0
  114. data/lib/arrow/object.rb +117 -0
  115. data/lib/arrow/path.rb +196 -0
  116. data/lib/arrow/service.rb +447 -0
  117. data/lib/arrow/session.rb +289 -0
  118. data/lib/arrow/session/dbstore.rb +100 -0
  119. data/lib/arrow/session/filelock.rb +160 -0
  120. data/lib/arrow/session/filestore.rb +132 -0
  121. data/lib/arrow/session/id.rb +98 -0
  122. data/lib/arrow/session/lock.rb +253 -0
  123. data/lib/arrow/session/md5id.rb +42 -0
  124. data/lib/arrow/session/nulllock.rb +42 -0
  125. data/lib/arrow/session/posixlock.rb +166 -0
  126. data/lib/arrow/session/sha1id.rb +54 -0
  127. data/lib/arrow/session/store.rb +366 -0
  128. data/lib/arrow/session/usertrackid.rb +52 -0
  129. data/lib/arrow/spechelpers.rb +73 -0
  130. data/lib/arrow/template.rb +713 -0
  131. data/lib/arrow/template/attr.rb +31 -0
  132. data/lib/arrow/template/call.rb +31 -0
  133. data/lib/arrow/template/comment.rb +33 -0
  134. data/lib/arrow/template/container.rb +118 -0
  135. data/lib/arrow/template/else.rb +41 -0
  136. data/lib/arrow/template/elsif.rb +44 -0
  137. data/lib/arrow/template/escape.rb +53 -0
  138. data/lib/arrow/template/export.rb +87 -0
  139. data/lib/arrow/template/for.rb +145 -0
  140. data/lib/arrow/template/if.rb +78 -0
  141. data/lib/arrow/template/import.rb +119 -0
  142. data/lib/arrow/template/include.rb +206 -0
  143. data/lib/arrow/template/iterator.rb +208 -0
  144. data/lib/arrow/template/nodes.rb +734 -0
  145. data/lib/arrow/template/parser.rb +571 -0
  146. data/lib/arrow/template/prettyprint.rb +53 -0
  147. data/lib/arrow/template/render.rb +191 -0
  148. data/lib/arrow/template/selectlist.rb +94 -0
  149. data/lib/arrow/template/set.rb +87 -0
  150. data/lib/arrow/template/timedelta.rb +81 -0
  151. data/lib/arrow/template/unless.rb +78 -0
  152. data/lib/arrow/template/urlencode.rb +51 -0
  153. data/lib/arrow/template/yield.rb +139 -0
  154. data/lib/arrow/templatefactory.rb +125 -0
  155. data/lib/arrow/testcase.rb +567 -0
  156. data/lib/arrow/transaction.rb +608 -0
  157. data/rake/191_compat.rb +26 -0
  158. data/rake/dependencies.rb +76 -0
  159. data/rake/documentation.rb +114 -0
  160. data/rake/helpers.rb +502 -0
  161. data/rake/hg.rb +282 -0
  162. data/rake/manual.rb +787 -0
  163. data/rake/packaging.rb +129 -0
  164. data/rake/publishing.rb +278 -0
  165. data/rake/style.rb +62 -0
  166. data/rake/svn.rb +668 -0
  167. data/rake/testing.rb +187 -0
  168. data/rake/verifytask.rb +64 -0
  169. data/spec/arrow/acceptparam_spec.rb +157 -0
  170. data/spec/arrow/applet_spec.rb +575 -0
  171. data/spec/arrow/appletmixins_spec.rb +409 -0
  172. data/spec/arrow/appletregistry_spec.rb +294 -0
  173. data/spec/arrow/broker_spec.rb +153 -0
  174. data/spec/arrow/config_spec.rb +224 -0
  175. data/spec/arrow/cookieset_spec.rb +164 -0
  176. data/spec/arrow/dispatcher_spec.rb +137 -0
  177. data/spec/arrow/dispatcherloader_spec.rb +65 -0
  178. data/spec/arrow/formvalidator_spec.rb +781 -0
  179. data/spec/arrow/logger_spec.rb +346 -0
  180. data/spec/arrow/mixins_spec.rb +120 -0
  181. data/spec/arrow/service_spec.rb +645 -0
  182. data/spec/arrow/session_spec.rb +121 -0
  183. data/spec/arrow/template/iterator_spec.rb +222 -0
  184. data/spec/arrow/templatefactory_spec.rb +185 -0
  185. data/spec/arrow/transaction_spec.rb +319 -0
  186. data/spec/arrow_spec.rb +37 -0
  187. data/spec/lib/appletmatchers.rb +281 -0
  188. data/spec/lib/constants.rb +77 -0
  189. data/spec/lib/helpers.rb +41 -0
  190. data/spec/lib/matchers.rb +44 -0
  191. data/tests/cookie.tests.rb +310 -0
  192. data/tests/path.tests.rb +157 -0
  193. data/tests/session.tests.rb +111 -0
  194. data/tests/session_id.tests.rb +82 -0
  195. data/tests/session_lock.tests.rb +191 -0
  196. data/tests/session_store.tests.rb +53 -0
  197. data/tests/template.tests.rb +1360 -0
  198. metadata +339 -0
@@ -0,0 +1,34 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3
+
4
+ <!--
5
+
6
+ [?call page.plain_name ?]
7
+
8
+ -->
9
+
10
+ <head>
11
+ <title><?call page.plain_name ?></title>
12
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
13
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
14
+ <link rel="stylesheet" type="text/css" href="/css/arrow.css" />
15
+ </head>
16
+ <body>
17
+
18
+ <h1><?call page.plain_name ?></h1>
19
+
20
+ <div id="revision">
21
+ <?call page.display_content ?>
22
+ </div>
23
+
24
+ <?include navbar.incl?>
25
+
26
+ </body>
27
+ </html>
28
+
29
+
30
+ <!--
31
+ Local Variables:
32
+ mode: nxml
33
+ -->
34
+
@@ -0,0 +1,43 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
3
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
6
+ <head>
7
+ <title>Arrow Manual — <%= page.config['title'] || "untitled" %></title>
8
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
9
+
10
+ <script type="text/javascript" src="<%= @basepath %>/js/jquery-1.3.2.min.js"></script>
11
+ <script type="text/javascript" src="<%= @basepath %>/js/sh.js"></script>
12
+ <script type="text/javascript" src="<%= @basepath %>/js/manual.js"></script>
13
+
14
+ <link rel="stylesheet" type="text/css" href="<%= @basepath %>/css/manual.css"
15
+ media="screen,projection" />
16
+
17
+ </head>
18
+ <body class="manual">
19
+
20
+ <div id="header">
21
+ <h1><a href="/">Arrow</a></h1>
22
+
23
+ <%= page.make_index_html %>
24
+
25
+ </div>
26
+
27
+ <div id="content">
28
+ <p id="version">Version: <%= metadata.version %></p>
29
+
30
+ <!-- Generated content -->
31
+ <%= content %>
32
+ <!-- end of generated content -->
33
+
34
+ </div>
35
+
36
+ <div id="footer">
37
+ <div class="copyright">Copyright &copy; 2008-<%= Time.now.year %> Michael Granger.</div>
38
+ <div class="vcsrev">Rev: $Revision$</div>
39
+ <div class="timestamp">Built: <%= Time.now.strftime("%Y%m%d %H:%M:%S %Z") %></div>
40
+ </div>
41
+
42
+ </body>
43
+ </html>
@@ -0,0 +1,81 @@
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
+ ### Avoid declaring the class if the tasklib hasn't been loaded yet.
11
+ unless Object.const_defined?( :Manual )
12
+ raise LoadError, "not intended for standalone use: try the 'manual.rb' rake tasklib"
13
+ end
14
+
15
+
16
+ ### A filter for generating links from the generated API documentation. This allows you to refer
17
+ ### to class documentation by simply referencing a class name.
18
+ ###
19
+ ### Links are XML processing instructions. Pages can be referenced as such:
20
+ ###
21
+ ### <?api Class::Name ?>
22
+ ### <?api "click here":Class::Name ?>
23
+ ###
24
+ class APIFilter < Manual::Page::Filter
25
+
26
+ # PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
27
+ ApiPI = %r{
28
+ <\?
29
+ api # Instruction Target
30
+ \s+
31
+ (?:"
32
+ (.*?) # Optional link text [$1]
33
+ ":)?
34
+ (.*?) # Class name [$2]
35
+ \s+
36
+ \?>
37
+ }x
38
+
39
+
40
+ ######
41
+ public
42
+ ######
43
+
44
+ ### Process the given +source+ for <?api ... ?> processing-instructions, calling out
45
+ def process( source, page, metadata )
46
+
47
+ apipath = metadata.api_dir or
48
+ raise "The API output directory is not defined in the manual task."
49
+
50
+ return source.gsub( ApiPI ) do |match|
51
+ # Grab the tag values
52
+ link_text = $1
53
+ classname = $2
54
+
55
+ self.generate_link( page, apipath, classname, link_text )
56
+ end
57
+ end
58
+
59
+
60
+ ### Create an HTML link fragment from the parsed ApiPI.
61
+ ###
62
+ def generate_link( current_page, apipath, classname, link_text=nil )
63
+
64
+ classpath = "%s.html" % [ classname.gsub('::', '/') ]
65
+ classfile = apipath + classpath
66
+ classuri = current_page.basepath + 'api' + classpath
67
+
68
+ if classfile.exist?
69
+ return %{<a href="%s">%s</a>} % [
70
+ classuri,
71
+ link_text || classname
72
+ ]
73
+ else
74
+ link_text ||= classname
75
+ error_message = "Could not find a link for class '%s'" % [ classname ]
76
+ $stderr.puts( error_message )
77
+ return %{<a href="#" title="#{error_message}" class="broken-link">#{link_text}</a>}
78
+ end
79
+ end
80
+ end
81
+
@@ -0,0 +1,64 @@
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
+ ### Avoid declaring the class if the tasklib hasn't been loaded yet.
11
+ unless Object.const_defined?( :Manual )
12
+ raise LoadError, "not intended for standalone use: try the 'manual.rb' rake tasklib"
13
+ end
14
+
15
+
16
+
17
+ ### A filter for making editorial marks in manual content.
18
+ ###
19
+ ### Editorial marks are XML processing instructions. There are several available types of
20
+ ### marks:
21
+ ###
22
+ ### <?ed "This is an editor's note." ?>
23
+ ### <?ed verify:"this content needs checking or verification" ?>
24
+ ###
25
+ class EditorialFilter < Manual::Page::Filter
26
+
27
+ # PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
28
+ LinkPI = %r{
29
+ <\?
30
+ ed # Instruction Target
31
+ \s+
32
+ (\w+?) # type of editorial mark [$1]
33
+ :? # optional colon
34
+ "
35
+ (.*?) # content that should be edited [$2]
36
+ "
37
+ \s*
38
+ \?>
39
+ }x
40
+
41
+
42
+ ######
43
+ public
44
+ ######
45
+
46
+ ### Process the given +source+ for <?ed ... ?> processing-instructions
47
+ def process( source, page, metadata )
48
+ return source.gsub( LinkPI ) do |match|
49
+ # Grab the tag values
50
+ mark_type = $1
51
+ content = $2
52
+
53
+ self.generate_mark( page, mark_type, content )
54
+ end
55
+ end
56
+
57
+
58
+ ### Create an HTML fragment from the parsed LinkPI.
59
+ def generate_mark( current_page, mark_type, content )
60
+ return "%%(editorial %s-mark)%s%%" % [ mark_type, content ]
61
+ end
62
+
63
+
64
+ end
@@ -0,0 +1,244 @@
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
+ ### Avoid declaring the class if the tasklib hasn't been loaded yet.
14
+ unless Object.const_defined?( :Manual )
15
+ raise LoadError, "not intended for standalone use: try the 'manual.rb' rake tasklib"
16
+ end
17
+
18
+
19
+
20
+ ### A filter for inline example code or command-line sessions -- does
21
+ ### syntax-checking for some languages and captioning.
22
+ ###
23
+ ### Examples are enclosed in XML processing instructions like so:
24
+ ###
25
+ ### <?example {language: ruby, testable: true, caption: "A fine example"} ?>
26
+ ### a = 1
27
+ ### puts a
28
+ ### <?end example ?>
29
+ ###
30
+ ### This will be pulled out into a preformatted section in the HTML,
31
+ ### highlighted as Ruby source, checked for valid syntax, and annotated with
32
+ ### the specified caption. Valid keys in the example PI are:
33
+ ###
34
+ ### language::
35
+ ### Specifies which (machine) language the example is in.
36
+ ### testable::
37
+ ### If set and there is a testing function for the given language, run it and append
38
+ ### any errors to the output.
39
+ ### caption::
40
+ ### A small blurb to put below the pulled-out example in the HTML.
41
+ class ExamplesFilter < Manual::Page::Filter
42
+
43
+ DEFAULTS = {
44
+ :language => :ruby,
45
+ :line_numbers => :inline,
46
+ :tab_width => 4,
47
+ :hint => :debug,
48
+ :testable => false,
49
+ }
50
+
51
+ # PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
52
+ ExamplePI = %r{
53
+ <\?
54
+ example # Instruction Target
55
+ (?: # Optional instruction body
56
+ \s+
57
+ ((?: # [$1]
58
+ [^?]* # Run of anything but a question mark
59
+ | # -or-
60
+ \?(?!>) # question mark not followed by a closing angle bracket
61
+ )*)
62
+ )?
63
+ \?>
64
+ }x
65
+
66
+ EndPI = %r{ <\? end (?: \s+ example )? \s* \?> }x
67
+
68
+
69
+ ### Defer loading of dependenies until the filter is loaded
70
+ def initialize( *args )
71
+ begin
72
+ require 'pathname'
73
+ require 'strscan'
74
+ require 'yaml'
75
+ require 'rcodetools/xmpfilter'
76
+ require 'digest/md5'
77
+ require 'tmpdir'
78
+ require 'erb'
79
+ rescue LoadError => err
80
+ unless Object.const_defined?( :Gem )
81
+ require 'rubygems'
82
+ retry
83
+ end
84
+
85
+ raise
86
+ end
87
+ end
88
+
89
+
90
+ ######
91
+ public
92
+ ######
93
+
94
+ ### Process the given +source+ for <?example ... ?> processing-instructions, calling out
95
+ def process( source, page, metadata )
96
+ scanner = StringScanner.new( source )
97
+
98
+ buffer = ''
99
+ until scanner.eos?
100
+ startpos = scanner.pos
101
+
102
+ # If we find an example
103
+ if scanner.skip_until( ExamplePI )
104
+ contents = ''
105
+
106
+ # Append the interstitial content to the buffer
107
+ if ( scanner.pos - startpos > scanner.matched.length )
108
+ offset = scanner.pos - scanner.matched.length - 1
109
+ buffer << scanner.string[ startpos..offset ]
110
+ end
111
+
112
+ # Append everything up to it to the buffer and save the contents of
113
+ # the tag
114
+ params = scanner[1]
115
+
116
+ # Now find the end of the example or complain
117
+ contentpos = scanner.pos
118
+ scanner.skip_until( EndPI ) or
119
+ raise "Unterminated example at line %d" %
120
+ [ scanner.string[0..scanner.pos].count("\n") ]
121
+
122
+ # Now build the example and append to the buffer
123
+ if ( scanner.pos - contentpos > scanner.matched.length )
124
+ offset = scanner.pos - scanner.matched.length - 1
125
+ contents = scanner.string[ contentpos..offset ]
126
+ end
127
+
128
+ trace "Processing with params: %p, contents: %p" % [ params, contents ]
129
+ buffer << self.process_example( params, contents, page )
130
+ else
131
+ break
132
+ end
133
+
134
+ end
135
+ buffer << scanner.rest
136
+ scanner.terminate
137
+
138
+ return buffer
139
+ end
140
+
141
+
142
+ ### Filter out 'example' macros, doing syntax highlighting, and running
143
+ ### 'testable' examples through a validation process appropriate to the
144
+ ### language the example is in.
145
+ def process_example( params, body, page )
146
+ options = self.parse_options( params )
147
+ caption = options.delete( :caption )
148
+ content = ''
149
+ lang = options.delete( :language ).to_s
150
+
151
+ # Test it if it's testable
152
+ if options[:testable]
153
+ content = test_content( body, lang, page )
154
+ else
155
+ content = body
156
+ end
157
+
158
+ # Strip trailing blank lines and syntax-highlight
159
+ content = highlight( content.strip, options, lang )
160
+ caption = %{<div class="caption">} + caption.to_s + %{</div>} if caption
161
+
162
+ return %{<notextile><div class="example #{lang}-example">%s%s</div></notextile>} %
163
+ [content, caption || '']
164
+ end
165
+
166
+
167
+ ### Parse an options hash for filtering from the given +args+, which can either
168
+ ### be a plain String, in which case it is assumed to be the name of the language the example
169
+ ### is in, or a Hash of configuration options.
170
+ def parse_options( args )
171
+ args = "{ #{args} }" unless args.strip[0] == ?{
172
+ args = YAML.load( args )
173
+
174
+ # Convert to Symbol keys and value
175
+ args.keys.each do |k|
176
+ newval = args.delete( k )
177
+ next if newval.nil? || (newval.respond_to?(:size) && newval.size == 0)
178
+ args[ k.to_sym ] = newval.respond_to?( :to_sym ) ? newval.to_sym : newval
179
+ end
180
+ return DEFAULTS.merge( args )
181
+ end
182
+
183
+
184
+ ### Test the given +content+ with a rule specific to the given +language+.
185
+ def test_content( body, language, page )
186
+ case language.to_sym
187
+ when :ruby
188
+ return self.test_ruby_content( body, page )
189
+
190
+ when :yaml
191
+ return self.test_yaml_content( body, page )
192
+
193
+ else
194
+ return body
195
+ end
196
+ end
197
+
198
+
199
+ ### Test the specified Ruby content for valid syntax
200
+ def test_ruby_content( source, page )
201
+ # $stderr.puts "Testing ruby content..."
202
+ libdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'lib'
203
+ extdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'ext'
204
+
205
+ options = Rcodetools::XMPFilter::INITIALIZE_OPTS.dup
206
+ options[:include_paths] |= [ libdir.to_s, extdir.to_s ]
207
+ options[:width] = 60
208
+
209
+ if page.config['example_prelude']
210
+ prelude = page.config['example_prelude']
211
+ trace " prepending prelude:\n#{prelude}"
212
+ source = prelude.strip + "\n" + source.strip
213
+ else
214
+ trace " no prelude; page config is: %p" % [ page.config ]
215
+ end
216
+
217
+ rval = Rcodetools::XMPFilter.run( source, options )
218
+
219
+ trace "test output: ", rval
220
+ return rval.join
221
+ rescue Exception => err
222
+ return "%s while testing: %s\n %s" %
223
+ [ err.class.name, err.message, err.backtrace.join("\n ") ]
224
+ end
225
+
226
+
227
+ ### Test the specified YAML content for valid syntax
228
+ def test_yaml_content( source, metadata )
229
+ YAML.load( source )
230
+ rescue YAML::Error => err
231
+ return "# Invalid YAML: " + err.message + "\n" + source
232
+ else
233
+ return source
234
+ end
235
+
236
+
237
+ ### Highlights the given +content+ in language +lang+.
238
+ def highlight( content, options, lang )
239
+ source = ERB::Util.html_escape( content )
240
+ return %Q{\n\n<pre class="brush:#{lang}">#{source}</pre>\n\n}
241
+ end
242
+
243
+ end
244
+