assert2 0.3.2 → 0.3.3
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/lib/assert2.rb +323 -216
- data/lib/assert2/flunk.rb +87 -0
- data/lib/assert2/null_reflector.rb +0 -0
- data/lib/assert2/ripdoc.html.erb +182 -0
- data/lib/assert2/ripdoc.rb +413 -0
- data/lib/assert2/ripper_reflector.rb +726 -0
- data/lib/{ruby_reflector.rb → assert2/rubynode_reflector.rb} +100 -71
- data/lib/assert2/xpath.rb +159 -0
- metadata +47 -41
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
|
4
|
+
module Test; module Unit; module Assertions
|
5
|
+
|
6
|
+
# Assert that a block raises a given Exception type matching
|
7
|
+
# a given message
|
8
|
+
#
|
9
|
+
# * +types+ - single exception class or array of classes
|
10
|
+
# * +matcher+ - Regular Expression to match the inner_text of XML nodes
|
11
|
+
# * +diagnostic+ - optional string to add to failure message
|
12
|
+
# * +block+ - Ruby statements that should raise an exception
|
13
|
+
#
|
14
|
+
# Examples:
|
15
|
+
# %transclude AssertXPathSuite#test_assert_raise_message_detects_assertion_failure
|
16
|
+
#
|
17
|
+
# %transclude AssertXPathSuite#test_assert_raise_message_raises_message
|
18
|
+
#
|
19
|
+
# See: {assert_raise - Don't Just Say "No"}[http://www.oreillynet.com/onlamp/blog/2007/07/assert_raise_on_ruby_dont_just.html]
|
20
|
+
#
|
21
|
+
def assert_raise_message(types, expected_message, message = nil, &block)
|
22
|
+
args = [types].flatten + [message]
|
23
|
+
exception = _assert_raise(*args, &block)
|
24
|
+
exception_message = exception.message.dup
|
25
|
+
|
26
|
+
if expected_message.kind_of? String
|
27
|
+
exception_message.gsub!(/^\s+/, '') # if we cosmetically strip leading spaces from both the matcher and matchee,
|
28
|
+
expected_message.gsub!(/^\s+/, '') # then multi-line assert_flunk messages are easier on the eyes!
|
29
|
+
expected_message = Regexp.escape(expected_message)
|
30
|
+
end
|
31
|
+
|
32
|
+
assert message do
|
33
|
+
exception_message.match(expected_message)
|
34
|
+
end
|
35
|
+
|
36
|
+
return exception.message
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO rebuild this
|
40
|
+
def deny_raise_message(types, matcher, diagnostic = nil, &block) #:nodoc:
|
41
|
+
exception = assert_raise_message(types, //, diagnostic, &block)
|
42
|
+
|
43
|
+
assert_no_match matcher,
|
44
|
+
exception.message,
|
45
|
+
[ diagnostic,
|
46
|
+
"exception #{ exception.class.name
|
47
|
+
} with this message should not raise from block:",
|
48
|
+
"\t"+reflect_source(&block).split("\n").join("\n\t")
|
49
|
+
].compact.join("\n")
|
50
|
+
|
51
|
+
return exception.message
|
52
|
+
end
|
53
|
+
|
54
|
+
def assert_flunk(matcher, message = nil, &block)
|
55
|
+
assert_raise_message FlunkError, matcher, message, &block
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO reinstall ruby-1.9.0 and pass all cross-tests!!
|
59
|
+
|
60
|
+
def _assert_raise(*args)
|
61
|
+
# _wrap_assertion do
|
62
|
+
if Module === args.last
|
63
|
+
message = ""
|
64
|
+
else
|
65
|
+
message = args.pop
|
66
|
+
end
|
67
|
+
exceptions, modules = args, [] # _check_exception_class(args)
|
68
|
+
|
69
|
+
expected = args.size == 1 ? args.first : args
|
70
|
+
actual_exception = nil
|
71
|
+
full_message = build_message(message, "<?> exception expected but none was thrown.", expected)
|
72
|
+
assert_block(full_message) do
|
73
|
+
begin
|
74
|
+
yield
|
75
|
+
rescue Exception => actual_exception
|
76
|
+
break
|
77
|
+
end
|
78
|
+
false
|
79
|
+
end
|
80
|
+
full_message = build_message(message, "<?> exception expected but was\n?", expected, actual_exception)
|
81
|
+
assert_block(full_message) {exceptions.include?(actual_exception.class)}
|
82
|
+
actual_exception
|
83
|
+
# end
|
84
|
+
end
|
85
|
+
|
86
|
+
end; end; end
|
87
|
+
|
File without changes
|
@@ -0,0 +1,182 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<title><%= @title %></title>
|
6
|
+
<meta http-equiv="content-type" content="text/html; charset=us-ascii" />
|
7
|
+
<script type="text/javascript" src="javascript/prototype.js"></script>
|
8
|
+
<script type="text/javascript" src="javascript/effects.js"></script>
|
9
|
+
<script type="text/javascript" src="javascript/accordion.js"></script>
|
10
|
+
<link rel="stylesheet" type="text/css" href="css/default.css" />
|
11
|
+
<script type="text/javascript">
|
12
|
+
//<![CDATA[
|
13
|
+
|
14
|
+
var bottomAccordion = 42;
|
15
|
+
|
16
|
+
function raise(id)
|
17
|
+
{
|
18
|
+
var anchor = $(id + '_');
|
19
|
+
if (anchor) {
|
20
|
+
bottomAccordion.activate(anchor.parentNode);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
Event.observe(window, 'load', loadAccordions, false);
|
25
|
+
function loadAccordions() {
|
26
|
+
bottomAccordion = new accordion('vertical_container');
|
27
|
+
|
28
|
+
var anchor = window.location.href.match(/#(.+)$/);
|
29
|
+
if (anchor) anchor = anchor[1];
|
30
|
+
if (anchor = $(anchor + '_')) {
|
31
|
+
bottomAccordion.activate(anchor.parentNode);
|
32
|
+
}
|
33
|
+
else {
|
34
|
+
bottomAccordion.activate($$('#vertical_container .accordion_toggle')[0]);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
//]]>
|
38
|
+
</script>
|
39
|
+
|
40
|
+
<style type="text/css">
|
41
|
+
/*<![CDATA[*/
|
42
|
+
/* Vertical Accordions */
|
43
|
+
|
44
|
+
pre { border-width: 10px;
|
45
|
+
border-color: #e7e7c9;
|
46
|
+
border-style: solid;
|
47
|
+
}
|
48
|
+
|
49
|
+
.accordion_toggle {
|
50
|
+
display: block;
|
51
|
+
height: 30px;
|
52
|
+
width: 680px;
|
53
|
+
background: url(images/accordion_toggle.png) no-repeat top right;
|
54
|
+
padding: 0 10px 0 10px;
|
55
|
+
cursor: index;
|
56
|
+
font-weight: normal;
|
57
|
+
text-decoration: none;
|
58
|
+
outline: none;
|
59
|
+
font-family: 'Lucida Grande', Verdana, Arial;
|
60
|
+
color: #800;
|
61
|
+
cursor: pointer;
|
62
|
+
margin: 0 0 0 0;
|
63
|
+
}
|
64
|
+
|
65
|
+
.accordion_toggle_active {
|
66
|
+
background: url(images/accordion_toggle_active.png) no-repeat top right;
|
67
|
+
color: #FDE;
|
68
|
+
}
|
69
|
+
|
70
|
+
.accordion_content {
|
71
|
+
background-color: #ffffff;
|
72
|
+
color: #444444;
|
73
|
+
overflow: hidden;
|
74
|
+
}
|
75
|
+
|
76
|
+
.accordion_content h2 {
|
77
|
+
margin: 15px 0 5px 10px;
|
78
|
+
color: #0099FF;
|
79
|
+
}
|
80
|
+
|
81
|
+
.accordion_content p {
|
82
|
+
padding: 5px 10px 0 10px;
|
83
|
+
}
|
84
|
+
|
85
|
+
/*
|
86
|
+
Horizontal Accordion
|
87
|
+
*/
|
88
|
+
|
89
|
+
.horizontal_accordion_toggle {
|
90
|
+
/* REQUIRED */
|
91
|
+
float: left; /* This make sure it stays horizontal */
|
92
|
+
/* REQUIRED */
|
93
|
+
|
94
|
+
display: block;
|
95
|
+
height: 100px;
|
96
|
+
width: 30px;
|
97
|
+
background: url(images/h_accordion_toggle.png) no-repeat top left #a9d06a;
|
98
|
+
color: #ffffff;
|
99
|
+
text-decoration: none;
|
100
|
+
outline: none;
|
101
|
+
border-right: 1px solid #cde99f;
|
102
|
+
cursor: pointer;
|
103
|
+
margin: 0 0 0 0;
|
104
|
+
}
|
105
|
+
|
106
|
+
.horizontal_accordion_toggle_active {
|
107
|
+
background: url(images/h_accordion_toggle_active.png) no-repeat top left #e0542f;
|
108
|
+
border-right: 1px solid #f68263;
|
109
|
+
}
|
110
|
+
|
111
|
+
.horizontal_accordion_content {
|
112
|
+
/* REQUIRED */
|
113
|
+
height: 100px; /* We need to define a height for the accordion as it stretches the width */
|
114
|
+
float: left; /* This make sure it stays horizontal */
|
115
|
+
/* REQUIRED */
|
116
|
+
|
117
|
+
overflow: hidden;
|
118
|
+
background-color: #ffffff;
|
119
|
+
color: #444444;
|
120
|
+
}
|
121
|
+
|
122
|
+
.horizontal_accordion_content p {
|
123
|
+
width: 450px;
|
124
|
+
/*line-height: 125%;*/
|
125
|
+
padding: 5px 10px 15px 10px;
|
126
|
+
}
|
127
|
+
|
128
|
+
|
129
|
+
/* Container styling*/
|
130
|
+
#horizontal_container {
|
131
|
+
margin: 20px auto 20px auto;
|
132
|
+
width: 680px;
|
133
|
+
height: 100px;
|
134
|
+
}
|
135
|
+
|
136
|
+
/*#vertical_nested_container {
|
137
|
+
margin: 20px auto 20px auto;
|
138
|
+
width: 620px;
|
139
|
+
}*/
|
140
|
+
|
141
|
+
/*]]>*/
|
142
|
+
</style>
|
143
|
+
</head>
|
144
|
+
|
145
|
+
<body style="font-family: Times,serif; background-image: url(images/sky_blue.png); background-repeat: repeat-x;">
|
146
|
+
<div style='position: absolute; top: 0px; left: 70%;'>
|
147
|
+
<a href="http://validator.w3.org/check/referer">
|
148
|
+
<img border='0' height="76" width="72" alt='Validates this page at W3C'
|
149
|
+
src="http://assert2.rubyforge.org/the_test_button.png"/>
|
150
|
+
</a>
|
151
|
+
</div>
|
152
|
+
|
153
|
+
<div id="container">
|
154
|
+
|
155
|
+
<h1><code><big style='color: #008;'><%= @title %></big></code></h1>
|
156
|
+
|
157
|
+
<div id="vertical_container">
|
158
|
+
<%= @sauce %>
|
159
|
+
</div>
|
160
|
+
|
161
|
+
<div class="page-footer">
|
162
|
+
<p>Accordion is © Copyright 2007 <a href="http://www.stickmanlabs.com"><small>stickmanlabs</small></a> - MIT-style license</p>
|
163
|
+
</div>
|
164
|
+
</div>
|
165
|
+
<br />
|
166
|
+
<script type="text/javascript">
|
167
|
+
//<![CDATA[
|
168
|
+
//
|
169
|
+
// You can hide the accordions on page load like this, it maintains accessibility
|
170
|
+
//
|
171
|
+
// Special thanks go out to Will Shaver @ http://primedigit.com/
|
172
|
+
//
|
173
|
+
var verticalAccordions = $$('.accordion_toggle');
|
174
|
+
verticalAccordions.each(function(accordion) {
|
175
|
+
$(accordion.next(0)).setStyle({
|
176
|
+
height: '0px'
|
177
|
+
});
|
178
|
+
});
|
179
|
+
//]]>
|
180
|
+
</script>
|
181
|
+
</body>
|
182
|
+
</html>
|
@@ -0,0 +1,413 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ripper'
|
4
|
+
require 'stringio'
|
5
|
+
require 'cgi'
|
6
|
+
require 'erb'
|
7
|
+
require 'optparse'
|
8
|
+
require 'pathname'
|
9
|
+
|
10
|
+
# TODO reflect the banner anchor at generation time, to figure out what to hotlink to
|
11
|
+
|
12
|
+
class Ripdoc < Ripper::Filter
|
13
|
+
HomePath = (Pathname.new(__FILE__).dirname + '../..').expand_path
|
14
|
+
|
15
|
+
STYLES = {
|
16
|
+
CHAR: 'color: brown; font-weight: bolder;',
|
17
|
+
const: "color: #FF4F00; font-weight: bolder;",
|
18
|
+
backref: "color: #f4f; font-weight: bolder;",
|
19
|
+
comment: "font-style: italic; color: #446; font-family: Times; font-size: 110%;",
|
20
|
+
# TODO use "font: Times italic 110%" to save space
|
21
|
+
embdoc: "background-color: #FFe; font-family: Times; font-size: 133%;",
|
22
|
+
embdoc_beg: "display: none;",
|
23
|
+
embdoc_end: "display: none;",
|
24
|
+
embexpr: "background-color: #ccc;",
|
25
|
+
embexpr_delimiter: "background-color: #aaa;",
|
26
|
+
gvar: "color: #8f5902; font-weight: bolder;",
|
27
|
+
ivar: "color: #240;",
|
28
|
+
int: "color: #336600; font-weight: bolder;",
|
29
|
+
operator: "font-weight: bolder; font-size: 120%;",
|
30
|
+
kw: "color: purple;",
|
31
|
+
regexp_delimiter: "background-color: #faf;",
|
32
|
+
regexp: "background-color: #fcf;",
|
33
|
+
string: "background-color: white;", #dfc
|
34
|
+
string_delimiter: "background-color: #cfa;",
|
35
|
+
symbol: "color: #066;",
|
36
|
+
tstring_content: 'background: url(images/tstring.png) right;',
|
37
|
+
tstring_internal: 'background: url(images/tstring.png) right;', # TODO merge these two!
|
38
|
+
tregexp_content: 'background: url(images/hot_pink.png);'
|
39
|
+
}
|
40
|
+
|
41
|
+
def self.generate(filename, title)
|
42
|
+
@sauce = compile_fragment(File.read(filename))
|
43
|
+
@title = title
|
44
|
+
@string_background = :tstring_content
|
45
|
+
erb = ERB.new((HomePath + 'lib/assert2/ripdoc.html.erb').read, nil, '>')
|
46
|
+
xhtml = erb.result(binding())
|
47
|
+
xhtml.gsub! /\<pre>\s*\<\/pre>/m, ''
|
48
|
+
xhtml.gsub! /\<p>\s*\<\/p>/m, ''
|
49
|
+
return xhtml
|
50
|
+
end
|
51
|
+
|
52
|
+
def dequote(attributes)
|
53
|
+
return attributes.gsub('"', '"')
|
54
|
+
end # TODO complex '"? if we can't it might be a good thing...
|
55
|
+
|
56
|
+
def enline(line)
|
57
|
+
line = CGI.escapeHTML(line)
|
58
|
+
return line.gsub( /<a(.*?)>/){ "<a #{dequote($1)}>" }.
|
59
|
+
gsub('</a>', '</a>').
|
60
|
+
gsub(/<br\s*\/>/, '<br/>').
|
61
|
+
gsub( '<code>', '<code style="font-weight: bolder; font-style: normal;">').
|
62
|
+
gsub('</code>', '</code>').
|
63
|
+
gsub( '<em>', '<em>').
|
64
|
+
gsub('</em>', '</em>').
|
65
|
+
gsub( '<li>', '<li>').
|
66
|
+
gsub('</li>', '</li>').
|
67
|
+
gsub( '<ul>', '<ul>').
|
68
|
+
gsub('</ul>', '</ul>').
|
69
|
+
gsub('&mdash;', '—')
|
70
|
+
end
|
71
|
+
|
72
|
+
def deformat(line, f)
|
73
|
+
if line =~ /^\s/ # CONSIDER why the line-height broke??
|
74
|
+
f << "</p>" if @owed_p
|
75
|
+
f << "<pre style='line-height: 75%;'>\n" unless @owed_pre
|
76
|
+
@owed_p = false
|
77
|
+
@owed_pre = true
|
78
|
+
f << enline(line) << "\n"
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
f << '</pre>' if @owed_pre
|
83
|
+
@owed_pre = false
|
84
|
+
f << '<p>' unless @owed_p
|
85
|
+
@owed_p = true
|
86
|
+
f << enline(line)
|
87
|
+
end
|
88
|
+
|
89
|
+
def on_embdoc_beg(tok, f)
|
90
|
+
return f if @in_no_doc
|
91
|
+
@embdocs = []
|
92
|
+
f << "</pre>\n" if @owed_pre
|
93
|
+
@owed_pre = false
|
94
|
+
return f
|
95
|
+
# on_kw tok, f, 'embdoc_beg'
|
96
|
+
end
|
97
|
+
|
98
|
+
def is_no_doc?(tok)
|
99
|
+
tok.strip =~ /^\#\!nodoc\!/ or tok.strip =~ /^\#\!no_doc\!/
|
100
|
+
end
|
101
|
+
|
102
|
+
def on_embdoc(tok, f)
|
103
|
+
return f if @in_no_doc
|
104
|
+
|
105
|
+
if is_no_doc? tok
|
106
|
+
@in_no_doc = true
|
107
|
+
else
|
108
|
+
@embdocs << tok
|
109
|
+
end
|
110
|
+
|
111
|
+
return f
|
112
|
+
end
|
113
|
+
|
114
|
+
def on_embdoc_end(tok, f)
|
115
|
+
return f if @in_no_doc
|
116
|
+
return end_panel(f)
|
117
|
+
return f
|
118
|
+
end
|
119
|
+
|
120
|
+
def name_toggle(banner)
|
121
|
+
banner = banner.gsub(/<.*?>/, '').
|
122
|
+
gsub(/&.*?;/, '').
|
123
|
+
scan(/[ _[:alnum:]]/).
|
124
|
+
map{|x| x == ' ' ? '_' : x }.
|
125
|
+
join.
|
126
|
+
gsub(/_+/, '_').
|
127
|
+
gsub(/_$/, '')
|
128
|
+
|
129
|
+
return "<a href='#' name='#{banner}' id='#{banner}_'></a>" # CONSIDER this can't use <a ../> because enline() would mangle it...
|
130
|
+
# CONSIDER the _ is due to a bug in libxml - it squeaks at a name and id that are the same -
|
131
|
+
# despite that's a common idiom!
|
132
|
+
end
|
133
|
+
|
134
|
+
def end_panel(f)
|
135
|
+
if banner = @embdocs.shift # accordion_toggle_active
|
136
|
+
f << '<h1 class="accordion_toggle">'
|
137
|
+
f << name_toggle(banner)
|
138
|
+
f << enline(banner)
|
139
|
+
f << '</h1>'
|
140
|
+
end
|
141
|
+
|
142
|
+
f << '<div class="accordion_content">'
|
143
|
+
f << '<p>'
|
144
|
+
@owed_p = true
|
145
|
+
prior = false
|
146
|
+
|
147
|
+
@embdocs.each do |doc|
|
148
|
+
if doc.strip == ''
|
149
|
+
f << "</p>\n<p>" if @owed_p
|
150
|
+
prior = false
|
151
|
+
elsif doc.strip =~ /^\#\!link\!(.*)/ #!link!froot!loop
|
152
|
+
target, contents = $1.split('!') # TODO permit a ! in the contents
|
153
|
+
puts '#!link!anchor!text tags work best with text after the last bang!' unless contents
|
154
|
+
contents ||= ''
|
155
|
+
f << "<a href='\##{target}' onclick='raise(\"#{target}\")'>"
|
156
|
+
f << enline(contents)
|
157
|
+
f << '</a>'
|
158
|
+
else
|
159
|
+
f << ' ' if prior
|
160
|
+
deformat(doc, f)
|
161
|
+
prior = true
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
f << '</p>' if @owed_p
|
166
|
+
f << '<pre>' unless @owed_pre
|
167
|
+
@owed_pre = true
|
168
|
+
@embdocs = []
|
169
|
+
return f
|
170
|
+
end
|
171
|
+
|
172
|
+
# TODO need a tregexp_content to distinguish them!
|
173
|
+
|
174
|
+
def span(kode)
|
175
|
+
if STYLES[kode.to_sym]
|
176
|
+
# class="#{kode}"
|
177
|
+
return %Q[<span style="#{STYLES[kode.to_sym]}">]
|
178
|
+
else
|
179
|
+
return '<span>'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def spanit(kode, f, tok)
|
184
|
+
@spans_owed ||= 0
|
185
|
+
@spans_owed += 1
|
186
|
+
f << span(kode) << CGI.escapeHTML(tok)
|
187
|
+
end
|
188
|
+
|
189
|
+
def on_kw(tok, f, klass = 'kw')
|
190
|
+
return f if @in_no_doc
|
191
|
+
f << span(klass) << CGI.escapeHTML(tok)
|
192
|
+
f << '</span>'
|
193
|
+
end
|
194
|
+
|
195
|
+
def on_comment(tok, f)
|
196
|
+
if tok.strip =~ /^\#\!end_panel\!/
|
197
|
+
f << '</pre>' if @owed_pre
|
198
|
+
@owed_pre = false
|
199
|
+
f << '</div>'
|
200
|
+
return f
|
201
|
+
end
|
202
|
+
|
203
|
+
nodoc = is_no_doc?(tok)
|
204
|
+
|
205
|
+
if !nodoc and !@in_no_doc and tok.strip !~ /^\s*#\!/
|
206
|
+
f << span(:comment) << enline(tok.rstrip) << '</span>'
|
207
|
+
on_nl nil, f
|
208
|
+
end
|
209
|
+
|
210
|
+
@in_no_doc ||= nodoc
|
211
|
+
@in_no_doc = nil if tok.strip =~ /^\#\!doc\!/
|
212
|
+
return f
|
213
|
+
end
|
214
|
+
|
215
|
+
def on_CHAR(tok, f)
|
216
|
+
return f if @in_no_doc
|
217
|
+
on_kw tok, f, 'CHAR'
|
218
|
+
return f
|
219
|
+
end
|
220
|
+
|
221
|
+
# TODO linefeeds inside %w() and possibly ''
|
222
|
+
# TODO colorize :"" and :"#{}" correctly
|
223
|
+
|
224
|
+
def on_default(event, tok, f)
|
225
|
+
return f if @in_no_doc
|
226
|
+
|
227
|
+
if @symbol_begun
|
228
|
+
@symbol_begun = false
|
229
|
+
f << %Q[#{span(:symbol)}#{CGI.escapeHTML(tok)}</span>]
|
230
|
+
elsif tok =~ /^[[:punct:]]+$/
|
231
|
+
f << %Q[#{span(:operator)}#{CGI.escapeHTML(tok)}</span>]
|
232
|
+
else
|
233
|
+
on_kw tok, f, event.to_s.sub(/^on_/, '')
|
234
|
+
end
|
235
|
+
|
236
|
+
return f
|
237
|
+
end
|
238
|
+
|
239
|
+
def finish_one_span(f)
|
240
|
+
if @spans_owed > 0
|
241
|
+
f << '</span>'
|
242
|
+
@spans_owed -= 1
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def on_tstring_beg(tok, f)
|
247
|
+
return f if @in_no_doc
|
248
|
+
@spans_owed += 1
|
249
|
+
f << span(:string)
|
250
|
+
@string_background = :tstring_content
|
251
|
+
f << %Q[#{span(:string_delimiter)}#{CGI.escapeHTML(tok)}</span>]
|
252
|
+
end
|
253
|
+
|
254
|
+
def on_tstring_content(tok, f, style = 'tstring_content')
|
255
|
+
tok.split(/\n/).each_with_index do |sub_tok, index|
|
256
|
+
if index == 0
|
257
|
+
on_string_stretch(sub_tok, f, style)
|
258
|
+
else
|
259
|
+
space, sub_tok = sub_tok.scan(/(\s*)(.*)/).first
|
260
|
+
f << space
|
261
|
+
on_string_stretch(sub_tok, f, style)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
return f
|
265
|
+
end
|
266
|
+
|
267
|
+
def on_string_stretch(tok, f, klass = 'TODO remove me')
|
268
|
+
return f if @in_no_doc
|
269
|
+
f << span(@string_background.to_s) << CGI.escapeHTML(tok)
|
270
|
+
f << '</span>'
|
271
|
+
end
|
272
|
+
|
273
|
+
# TODO fix end-of-delim bug in // and %w() and %{} etc
|
274
|
+
|
275
|
+
def on_tstring_end(tok, f)
|
276
|
+
return f if @in_no_doc
|
277
|
+
|
278
|
+
if tok.length > 1
|
279
|
+
margin, content, tok = tok.scan(/^(\s*)(.*)(.)$/).first
|
280
|
+
f << margin if margin
|
281
|
+
on_tstring_content content, f, 'tstring_internal' if content # TODO is this used??
|
282
|
+
end
|
283
|
+
|
284
|
+
f << %Q[#{span(:string_delimiter)}#{CGI.escapeHTML(tok.to_s)}</span>]
|
285
|
+
finish_one_span(f)
|
286
|
+
return f
|
287
|
+
end # TODO report to ripper author the bug where on_tstring_end contains the whole string
|
288
|
+
|
289
|
+
def on_regexp_beg(tok, f)
|
290
|
+
return f if @in_no_doc
|
291
|
+
@spans_owed += 1
|
292
|
+
f << span(:regexp)
|
293
|
+
@string_background = :tregexp_content
|
294
|
+
f << %Q[#{span(:regexp_delimiter)}#{CGI.escapeHTML(tok)}</span>]
|
295
|
+
end
|
296
|
+
|
297
|
+
def on_regexp_end(tok, f)
|
298
|
+
return f if @in_no_doc
|
299
|
+
f << %Q[#{span(:regexp_delimiter)}#{CGI.escapeHTML(tok)}</span>]
|
300
|
+
finish_one_span(f)
|
301
|
+
return f
|
302
|
+
end
|
303
|
+
|
304
|
+
def on_embexpr_beg(tok, f)
|
305
|
+
return f if @in_no_doc
|
306
|
+
spanit :embexpr, f, tok
|
307
|
+
return f
|
308
|
+
end # TODO don't interrupt a span or nothing with a nodoc!
|
309
|
+
|
310
|
+
# TODO single-line mode for nodoc
|
311
|
+
|
312
|
+
def on_ignored_nl(tok, f)
|
313
|
+
return f if @in_no_doc
|
314
|
+
on_nl nil, f
|
315
|
+
end
|
316
|
+
|
317
|
+
def on_nl(tok, f)
|
318
|
+
return f if @in_no_doc
|
319
|
+
f << "\n"
|
320
|
+
end
|
321
|
+
|
322
|
+
def on_lbrace(tok, f)
|
323
|
+
return f if @in_no_doc
|
324
|
+
spanit '', f, '' # tok CONSIDER wonder who is actually emitting the { ??
|
325
|
+
f << tok
|
326
|
+
end
|
327
|
+
|
328
|
+
def on_rbrace(tok, f)
|
329
|
+
return f if @in_no_doc
|
330
|
+
f << tok
|
331
|
+
finish_one_span(f) # TODO these things might wrap lines!
|
332
|
+
return f
|
333
|
+
end
|
334
|
+
|
335
|
+
def on_symbeg(tok, f)
|
336
|
+
return f if @in_no_doc
|
337
|
+
on_default(:on_symbeg, tok, f)
|
338
|
+
@symbol_begun = true
|
339
|
+
return f
|
340
|
+
end
|
341
|
+
|
342
|
+
# TODO syntax hilite the inner language of regices? how about XPathics?
|
343
|
+
# escapes in strings??
|
344
|
+
|
345
|
+
def on_ivar(tok, f)
|
346
|
+
return f if @in_no_doc
|
347
|
+
f << %Q[#{span(:ivar)}#{CGI.escapeHTML(tok)}</span>]
|
348
|
+
end
|
349
|
+
|
350
|
+
attr_accessor :embdocs,
|
351
|
+
:in_no_doc,
|
352
|
+
:owed_pre,
|
353
|
+
:spans_owed
|
354
|
+
|
355
|
+
def parse(buf, f)
|
356
|
+
@spans_owed = 0
|
357
|
+
@symbol_begun = false
|
358
|
+
super(buf)
|
359
|
+
end
|
360
|
+
|
361
|
+
DOCTYPE = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
362
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' +
|
363
|
+
"\n"
|
364
|
+
|
365
|
+
def Ripdoc.compile(f)
|
366
|
+
return DOCTYPE +
|
367
|
+
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr"
|
368
|
+
><head>' +
|
369
|
+
'</head><body>' + compile_fragment(f) + '</body></html>'
|
370
|
+
end
|
371
|
+
|
372
|
+
def Ripdoc.compile_fragment(f)
|
373
|
+
buf = StringIO.new
|
374
|
+
parser = Ripdoc.new(f)
|
375
|
+
parser.in_no_doc = false
|
376
|
+
parser.owed_pre = true
|
377
|
+
parser.parse(buf, f)
|
378
|
+
result = buf.string
|
379
|
+
parser.spans_owed.times{ result += '</span>' }
|
380
|
+
|
381
|
+
return '<div id="content"><pre>' + result +
|
382
|
+
"#{'</pre>' if parser.owed_pre }</div>"
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
387
|
+
|
388
|
+
#~ :on_ident
|
389
|
+
#~ :on_const
|
390
|
+
#~ :on_semicolon
|
391
|
+
#~ :on_op
|
392
|
+
#~ :on_int
|
393
|
+
#~ :on_comma
|
394
|
+
#~ :on_lparen
|
395
|
+
#~ :on_rparen
|
396
|
+
#~ :on_backref
|
397
|
+
#~ :on_period
|
398
|
+
#~ :on_lbracket
|
399
|
+
#~ :on_rbracket
|
400
|
+
#~ :on_rbrace
|
401
|
+
#~ :on_qwords_beg
|
402
|
+
#~ :on_words_sep
|
403
|
+
|
404
|
+
class ERB
|
405
|
+
attr_accessor :lineno
|
406
|
+
|
407
|
+
remove_method :result
|
408
|
+
def result(b)
|
409
|
+
eval(@src, b, (@filename || '(erb)'), (@lineno || 1))
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
system 'rubyw1.9.0 ../../test/ripdoc_suite.rb' if $0 == __FILE__
|