rubydoctest 0.2.1 → 1.0.0
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/History.txt +10 -0
- data/License.txt +16 -17
- data/Manifest.txt +13 -5
- data/PostInstall.txt +2 -3
- data/README.txt +48 -57
- data/Ruby DocTest.tmproj +197 -0
- data/bin/rubydoctest +41 -25
- data/config/hoe.rb +3 -5
- data/lib/code_block.rb +68 -0
- data/lib/doctest_require.rb +3 -0
- data/lib/lines.rb +143 -0
- data/lib/result.rb +63 -0
- data/lib/rubydoctest.rb +13 -255
- data/lib/rubydoctest/version.rb +3 -3
- data/lib/runner.rb +370 -0
- data/lib/special_directive.rb +44 -0
- data/lib/statement.rb +75 -0
- data/lib/test.rb +29 -0
- data/rubydoctest.gemspec +6 -6
- data/script/console +0 -0
- data/script/destroy +0 -0
- data/script/generate +0 -0
- data/script/rstakeout +92 -0
- data/script/txt2html +0 -0
- data/tasks/doctests.rake +2 -1
- data/textmate/DocTest (Markdown).textmate +7 -0
- data/textmate/DocTest (Ruby).textmate +55 -0
- data/textmate/DocTest (Text).textmate +66 -0
- data/website/index.html +141 -11
- data/website/template.html.erb +1 -1
- metadata +22 -15
- data/test/doctest/file_relative.doctest +0 -5
- data/test/doctest/runner.doctest +0 -58
- data/test/doctest/simple.doctest +0 -30
- data/test/test_helper.rb +0 -2
- data/test/test_rubydoctest.rb +0 -11
data/lib/rubydoctest/version.rb
CHANGED
data/lib/runner.rb
ADDED
@@ -0,0 +1,370 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubydoctest'
|
5
|
+
require 'statement'
|
6
|
+
require 'result'
|
7
|
+
require 'special_directive'
|
8
|
+
require 'code_block'
|
9
|
+
require 'test'
|
10
|
+
|
11
|
+
module RubyDocTest
|
12
|
+
class Runner
|
13
|
+
attr_reader :groups, :blocks, :tests
|
14
|
+
|
15
|
+
@@color = {
|
16
|
+
:html => {
|
17
|
+
:red => %{<font color="red">%s</font>},
|
18
|
+
:yellow => %{<font color="#C0C000">%s</font>},
|
19
|
+
:green => %{<font color="green">%s</font>}
|
20
|
+
},
|
21
|
+
:ansi => {
|
22
|
+
:red => %{\e[31m%s\e[0m},
|
23
|
+
:yellow => %{\e[33m%s\e[0m},
|
24
|
+
:green => %{\e[32m%s\e[0m}
|
25
|
+
},
|
26
|
+
:plain => {
|
27
|
+
:red => "%s",
|
28
|
+
:yellow => "%s",
|
29
|
+
:green => "%s"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
# The evaluation mode, either :doctest or :ruby.
|
34
|
+
#
|
35
|
+
# Modes:
|
36
|
+
# :doctest
|
37
|
+
# - The the Runner expects the file to contain text (e.g. a markdown file).
|
38
|
+
# In addition, it assumes that the text will occasionally be interspersed
|
39
|
+
# with irb lines which it should eval, e.g. '>>' and '=>'.
|
40
|
+
#
|
41
|
+
# :ruby
|
42
|
+
# - The Runner expects the file to be a Ruby source file. The source may contain
|
43
|
+
# comments that are interspersed with irb lines to eval, e.g. '>>' and '=>'.
|
44
|
+
attr_accessor :mode
|
45
|
+
|
46
|
+
# === Tests
|
47
|
+
#
|
48
|
+
# doctest: Runner mode should default to :doctest and :ruby from the filename
|
49
|
+
# >> r = RubyDocTest::Runner.new("", "test.doctest")
|
50
|
+
# >> r.mode
|
51
|
+
# => :doctest
|
52
|
+
#
|
53
|
+
# >> r = RubyDocTest::Runner.new("", "test.rb")
|
54
|
+
# >> r.mode
|
55
|
+
# => :ruby
|
56
|
+
#
|
57
|
+
# doctest: The src_lines should be separated into an array
|
58
|
+
# >> r = RubyDocTest::Runner.new("a\nb\n", "test.doctest")
|
59
|
+
# >> r.instance_variable_get("@src_lines")
|
60
|
+
# => ["a", "b"]
|
61
|
+
def initialize(src, file_name = "test.doctest", initial_mode = nil)
|
62
|
+
@src, @file_name = src, file_name
|
63
|
+
@mode = initial_mode || (File.extname(file_name) == ".rb" ? :ruby : :doctest)
|
64
|
+
|
65
|
+
@src_lines = src.split("\n")
|
66
|
+
@groups, @blocks = [], []
|
67
|
+
$rubydoctest = self
|
68
|
+
end
|
69
|
+
|
70
|
+
# doctest: Using the doctest_require: SpecialDirective should require a file relative to the current one.
|
71
|
+
# >> r = RubyDocTest::Runner.new("# doctest_require: 'doctest_require.rb'", __FILE__)
|
72
|
+
# >> r.prepare_tests
|
73
|
+
# >> is_doctest_require_successful?
|
74
|
+
# => true
|
75
|
+
def prepare_tests
|
76
|
+
@groups = read_groups
|
77
|
+
@blocks = organize_blocks
|
78
|
+
@tests = organize_tests
|
79
|
+
eval(@src, TOPLEVEL_BINDING, @file_name) if @mode == :ruby
|
80
|
+
end
|
81
|
+
|
82
|
+
# === Tests
|
83
|
+
# doctest: Run through a simple inline doctest (rb) file and see if it passes
|
84
|
+
# >> file = File.join(File.dirname(__FILE__), "..", "test", "inline.rb")
|
85
|
+
# >> r = RubyDocTest::Runner.new(IO.read(file), "inline.rb")
|
86
|
+
# >> r.pass?
|
87
|
+
# => true
|
88
|
+
def pass?
|
89
|
+
prepare_tests
|
90
|
+
@tests.all?{ |t| t.pass? }
|
91
|
+
end
|
92
|
+
|
93
|
+
# === Description
|
94
|
+
# Starts an IRB prompt when the "!!!" SpecialDirective is given.
|
95
|
+
def start_irb
|
96
|
+
IRB.init_config(nil)
|
97
|
+
IRB.conf[:PROMPT_MODE] = :SIMPLE
|
98
|
+
irb = IRB::Irb.new(IRB::WorkSpace.new(TOPLEVEL_BINDING))
|
99
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
100
|
+
catch(:IRB_EXIT) do
|
101
|
+
irb.eval_input
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_color(text, color)
|
106
|
+
@@color[RubyDocTest.output_format][color] % text.to_s
|
107
|
+
end
|
108
|
+
|
109
|
+
def run
|
110
|
+
prepare_tests
|
111
|
+
newline = "\n "
|
112
|
+
everything_passed = true
|
113
|
+
puts "=== Testing '#{@file_name}'..."
|
114
|
+
ok, fail, err = 0, 0, 0
|
115
|
+
@tests.each do |t|
|
116
|
+
if SpecialDirective === t and t.name == "!!!"
|
117
|
+
start_irb unless RubyDocTest.ignore_interactive
|
118
|
+
else
|
119
|
+
begin
|
120
|
+
if t.pass?
|
121
|
+
ok += 1
|
122
|
+
status = ["OK".center(4), :green]
|
123
|
+
detail = nil
|
124
|
+
else
|
125
|
+
fail += 1
|
126
|
+
everything_passed = false
|
127
|
+
status = ["FAIL".center(4), :red]
|
128
|
+
detail = format_color(
|
129
|
+
"Got: #{t.actual_result}#{newline}Expected: #{t.expected_result}" + newline +
|
130
|
+
" from #{@file_name}:#{t.first_failed.result.line_number}",
|
131
|
+
:red)
|
132
|
+
|
133
|
+
end
|
134
|
+
rescue EvaluationError => e
|
135
|
+
err += 1
|
136
|
+
status = ["ERR".center(4), :yellow]
|
137
|
+
exception_text = e.original_exception.to_s.split("\n").join(newline)
|
138
|
+
if RubyDocTest.output_format == :html
|
139
|
+
exception_text = exception_text.gsub("<", "<").gsub(">", ">")
|
140
|
+
end
|
141
|
+
detail = format_color(
|
142
|
+
"#{e.original_exception.class.to_s}: #{exception_text}" + newline +
|
143
|
+
" from #{@file_name}:#{e.statement.line_number}" + newline +
|
144
|
+
e.statement.source_code,
|
145
|
+
:yellow)
|
146
|
+
end
|
147
|
+
puts \
|
148
|
+
"#{format_color(*status)} | " +
|
149
|
+
"#{t.description.split("\n").join(newline)}" +
|
150
|
+
(detail ? newline + detail : "")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
puts \
|
154
|
+
"#{@blocks.select{ |b| b.is_a? CodeBlock }.size} comparisons, " +
|
155
|
+
"#{@tests.size} doctests, " +
|
156
|
+
"#{fail} failures, " +
|
157
|
+
"#{err} errors"
|
158
|
+
everything_passed
|
159
|
+
end
|
160
|
+
|
161
|
+
# === Tests
|
162
|
+
#
|
163
|
+
# doctest: Non-statement lines get ignored while statement / result lines are included
|
164
|
+
# Default mode is :doctest, so non-irb prompts should be ignored.
|
165
|
+
# >> r = RubyDocTest::Runner.new("a\nb\n >> c = 1\n => 1")
|
166
|
+
# >> groups = r.read_groups
|
167
|
+
# >> groups.size
|
168
|
+
# => 2
|
169
|
+
#
|
170
|
+
# doctest: Group types are correctly created
|
171
|
+
# >> groups.map{ |g| g.class }
|
172
|
+
# => [RubyDocTest::Statement, RubyDocTest::Result]
|
173
|
+
#
|
174
|
+
# doctest: A ruby document can have =begin and =end blocks in it
|
175
|
+
# >> r = RubyDocTest::Runner.new(<<-RUBY, "test.rb")
|
176
|
+
# some_ruby_code = 1
|
177
|
+
# =begin
|
178
|
+
# this is a normal ruby comment
|
179
|
+
# >> z = 10
|
180
|
+
# => 10
|
181
|
+
# =end
|
182
|
+
# more_ruby_code = 2
|
183
|
+
# RUBY
|
184
|
+
# >> groups = r.read_groups
|
185
|
+
# >> groups.size
|
186
|
+
# => 2
|
187
|
+
# >> groups.map{ |g| g.lines.first }
|
188
|
+
# => [" >> z = 10", " => 10"]
|
189
|
+
def read_groups(src_lines = @src_lines, mode = @mode, start_index = 0)
|
190
|
+
groups = []
|
191
|
+
(start_index).upto(src_lines.size) do |index|
|
192
|
+
line = src_lines[index]
|
193
|
+
case mode
|
194
|
+
when :ruby
|
195
|
+
case line
|
196
|
+
|
197
|
+
# Beginning of a multi-line comment section
|
198
|
+
when /^=begin/
|
199
|
+
groups +=
|
200
|
+
# Get statements, results, and directives as if inside a doctest
|
201
|
+
read_groups(src_lines, :doctest_with_end, index)
|
202
|
+
|
203
|
+
else
|
204
|
+
if g = match_group("\\s*#\\s*", src_lines, index)
|
205
|
+
groups << g
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
when :doctest
|
210
|
+
if g = match_group("\\s*", src_lines, index)
|
211
|
+
groups << g
|
212
|
+
end
|
213
|
+
|
214
|
+
when :doctest_with_end
|
215
|
+
break if line =~ /^=end/
|
216
|
+
if g = match_group("\\s*", src_lines, index)
|
217
|
+
groups << g
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
222
|
+
groups
|
223
|
+
end
|
224
|
+
|
225
|
+
def match_group(prefix, src_lines, index)
|
226
|
+
case src_lines[index]
|
227
|
+
|
228
|
+
# An irb '>>' marker after a '#' indicates an embedded doctest
|
229
|
+
when /^(#{prefix})>>(\s|\s*$)/
|
230
|
+
Statement.new(src_lines, index, @file_name)
|
231
|
+
|
232
|
+
# An irb '=>' marker after a '#' indicates an embedded result
|
233
|
+
when /^(#{prefix})=>\s/
|
234
|
+
Result.new(src_lines, index)
|
235
|
+
|
236
|
+
# Whenever we match a directive (e.g. 'doctest'), add that in as well
|
237
|
+
when /^(#{prefix})(#{SpecialDirective::NAMES_FOR_RX})(.*)$/
|
238
|
+
SpecialDirective.new(src_lines, index)
|
239
|
+
|
240
|
+
else
|
241
|
+
nil
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# === Tests
|
246
|
+
#
|
247
|
+
# doctest: The organize_blocks method should separate Statement, Result and SpecialDirective
|
248
|
+
# objects into CodeBlocks.
|
249
|
+
# >> r = RubyDocTest::Runner.new(">> t = 1\n>> t + 2\n=> 3\n>> u = 1", "test.doctest")
|
250
|
+
# >> r.prepare_tests
|
251
|
+
#
|
252
|
+
# >> r.blocks.first.statements.map{|s| s.lines}
|
253
|
+
# => [[">> t = 1"], [">> t + 2"]]
|
254
|
+
#
|
255
|
+
# >> r.blocks.first.result.lines
|
256
|
+
# => ["=> 3"]
|
257
|
+
#
|
258
|
+
# >> r.blocks.last.statements.map{|s| s.lines}
|
259
|
+
# => [[">> u = 1"]]
|
260
|
+
#
|
261
|
+
# >> r.blocks.last.result
|
262
|
+
# => nil
|
263
|
+
#
|
264
|
+
# doctest: Two doctest directives--each having its own statement--should be separated properly
|
265
|
+
# by organize_blocks.
|
266
|
+
# >> r = RubyDocTest::Runner.new("doctest: one\n>> t = 1\ndoctest: two\n>> t + 2", "test.doctest")
|
267
|
+
# >> r.prepare_tests
|
268
|
+
# >> r.blocks.map{|b| b.class}
|
269
|
+
# => [RubyDocTest::SpecialDirective, RubyDocTest::CodeBlock,
|
270
|
+
# RubyDocTest::SpecialDirective, RubyDocTest::CodeBlock]
|
271
|
+
#
|
272
|
+
# >> r.blocks[0].value
|
273
|
+
# => "one"
|
274
|
+
#
|
275
|
+
# >> r.blocks[1].statements.map{|s| s.lines}
|
276
|
+
# => [[">> t = 1"]]
|
277
|
+
#
|
278
|
+
# >> r.blocks[2].value
|
279
|
+
# => "two"
|
280
|
+
#
|
281
|
+
# >> r.blocks[3].statements.map{|s| s.lines}
|
282
|
+
# => [[">> t + 2"]]
|
283
|
+
def organize_blocks(groups = @groups)
|
284
|
+
blocks = []
|
285
|
+
current_statements = []
|
286
|
+
groups.each do |g|
|
287
|
+
case g
|
288
|
+
when Statement
|
289
|
+
current_statements << g
|
290
|
+
when Result
|
291
|
+
blocks << CodeBlock.new(current_statements, g)
|
292
|
+
current_statements = []
|
293
|
+
when SpecialDirective
|
294
|
+
case g.name
|
295
|
+
when "doctest:"
|
296
|
+
blocks << CodeBlock.new(current_statements) unless current_statements.empty?
|
297
|
+
current_statements = []
|
298
|
+
when "doctest_require:"
|
299
|
+
doctest_require = eval(g.value, TOPLEVEL_BINDING, @file_name, g.line_number)
|
300
|
+
if doctest_require.is_a? String
|
301
|
+
require_relative_to_file_name(doctest_require, @file_name)
|
302
|
+
end
|
303
|
+
when "!!!"
|
304
|
+
# ignore
|
305
|
+
end
|
306
|
+
blocks << g
|
307
|
+
end
|
308
|
+
end
|
309
|
+
blocks << CodeBlock.new(current_statements) unless current_statements.empty?
|
310
|
+
blocks
|
311
|
+
end
|
312
|
+
|
313
|
+
def require_relative_to_file_name(file_name, relative_to)
|
314
|
+
load_path = $:.dup
|
315
|
+
$:.unshift File.expand_path(File.join(File.dirname(relative_to), File.dirname(file_name)))
|
316
|
+
require File.basename(file_name)
|
317
|
+
ensure
|
318
|
+
$:.shift
|
319
|
+
end
|
320
|
+
|
321
|
+
# === Tests
|
322
|
+
#
|
323
|
+
# doctest: Tests should be organized into groups based on the 'doctest' SpecialDirective
|
324
|
+
# >> r = RubyDocTest::Runner.new("doctest: one\n>> t = 1\ndoctest: two\n>> t + 2", "test.doctest")
|
325
|
+
# >> r.prepare_tests
|
326
|
+
# >> r.tests.size
|
327
|
+
# => 2
|
328
|
+
# >> r.tests[0].code_blocks.map{|c| c.statements}.flatten.map{|s| s.lines}
|
329
|
+
# => [[">> t = 1"]]
|
330
|
+
# >> r.tests[1].code_blocks.map{|c| c.statements}.flatten.map{|s| s.lines}
|
331
|
+
# => [[">> t + 2"]]
|
332
|
+
# >> r.tests[0].description
|
333
|
+
# => "one"
|
334
|
+
# >> r.tests[1].description
|
335
|
+
# => "two"
|
336
|
+
#
|
337
|
+
# doctest: Without a 'doctest' SpecialDirective, there is one Test called "Default Test".
|
338
|
+
# >> r = RubyDocTest::Runner.new(">> t = 1\n>> t + 2\n=> 3\n>> u = 1", "test.doctest")
|
339
|
+
# >> r.prepare_tests
|
340
|
+
# >> r.tests.size
|
341
|
+
# => 1
|
342
|
+
#
|
343
|
+
# >> r.tests.first.description
|
344
|
+
# => "Default Test"
|
345
|
+
#
|
346
|
+
# >> r.tests.first.code_blocks.size
|
347
|
+
# => 2
|
348
|
+
def organize_tests(blocks = @blocks)
|
349
|
+
tests = []
|
350
|
+
assigned_blocks = nil
|
351
|
+
unassigned_blocks = []
|
352
|
+
blocks.each do |g|
|
353
|
+
case g
|
354
|
+
when CodeBlock
|
355
|
+
(assigned_blocks || unassigned_blocks) << g
|
356
|
+
when SpecialDirective
|
357
|
+
case g.name
|
358
|
+
when "doctest:"
|
359
|
+
assigned_blocks = []
|
360
|
+
tests << Test.new(g.value, assigned_blocks)
|
361
|
+
when "!!!"
|
362
|
+
tests << g
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
tests << Test.new("Default Test", unassigned_blocks) unless unassigned_blocks.empty?
|
367
|
+
tests
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'lines'
|
5
|
+
|
6
|
+
module RubyDocTest
|
7
|
+
class SpecialDirective < Lines
|
8
|
+
NAMES = ["doctest:", "!!!", "doctest_require:"]
|
9
|
+
NAMES_FOR_RX = NAMES.map{ |n| Regexp.escape(n) }.join("|")
|
10
|
+
|
11
|
+
# === Test
|
12
|
+
#
|
13
|
+
# doctest: The name of the directive should be detected in the first line
|
14
|
+
# >> s = RubyDocTest::SpecialDirective.new(["doctest: Testing Stuff", "Other Stuff"])
|
15
|
+
# >> s.name
|
16
|
+
# => "doctest:"
|
17
|
+
def name
|
18
|
+
if m = lines.first.match(/^#{Regexp.escape(indentation)}(#{NAMES_FOR_RX})/)
|
19
|
+
m[1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# === Test
|
24
|
+
#
|
25
|
+
# doctest: The value of the directive should be detected in the first line
|
26
|
+
# >> s = RubyDocTest::SpecialDirective.new(["doctest: Testing Stuff", "Other Stuff"])
|
27
|
+
# >> s.value
|
28
|
+
# => "Testing Stuff"
|
29
|
+
#
|
30
|
+
# >> s = RubyDocTest::SpecialDirective.new([" # doctest: Testing Stuff", " # Other Stuff"])
|
31
|
+
# >> s.value
|
32
|
+
# => "Testing Stuff"
|
33
|
+
#
|
34
|
+
# doctest: Multiple lines for the directive value should work as well
|
35
|
+
# >> s = RubyDocTest::SpecialDirective.new(["doctest: Testing Stuff", " On Two Lines"])
|
36
|
+
# >> s.value
|
37
|
+
# => "Testing Stuff\nOn Two Lines"
|
38
|
+
def value
|
39
|
+
if m = lines.join("\n").match(/^#{Regexp.escape(indentation)}(#{NAMES_FOR_RX})(.*)/m)
|
40
|
+
m[2].strip
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/statement.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubydoctest'
|
5
|
+
require 'lines'
|
6
|
+
|
7
|
+
module RubyDocTest
|
8
|
+
class EvaluationError < Exception
|
9
|
+
attr_reader :statement, :original_exception
|
10
|
+
def initialize(statement, original_exception)
|
11
|
+
@statement, @original_exception = statement, original_exception
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Statement < Lines
|
16
|
+
|
17
|
+
attr_reader :actual_result
|
18
|
+
|
19
|
+
# === Tests
|
20
|
+
#
|
21
|
+
# doctest: The FILENAME ruby constant should be replaced by the name of the file
|
22
|
+
# >> __FILE__[/statement\.rb$/]
|
23
|
+
# => "statement.rb"
|
24
|
+
def initialize(doc_lines, line_index = 0, file_name = nil)
|
25
|
+
@file_name = file_name
|
26
|
+
super(doc_lines, line_index)
|
27
|
+
end
|
28
|
+
|
29
|
+
# === Tests
|
30
|
+
#
|
31
|
+
# doctest: A statement should parse out a '>>' irb prompt
|
32
|
+
# >> s = RubyDocTest::Statement.new([">> a = 1"])
|
33
|
+
# >> s.source_code
|
34
|
+
# => "a = 1"
|
35
|
+
#
|
36
|
+
# doctest: More than one line should get included, if indentation so indicates
|
37
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +", " 1", "not part of the statement"])
|
38
|
+
# >> s.source_code
|
39
|
+
# => "b = 1 +\n1"
|
40
|
+
#
|
41
|
+
# doctest: Lines indented by ?> should have the ?> removed.
|
42
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +", "?> 1"])
|
43
|
+
# >> s.source_code
|
44
|
+
# => "b = 1 +\n1"
|
45
|
+
def source_code
|
46
|
+
lines.first =~ /^#{Regexp.escape(indentation)}>>\s(.*)$/
|
47
|
+
first = [$1]
|
48
|
+
remaining = (lines[1..-1] || [])
|
49
|
+
(first + remaining).join("\n")
|
50
|
+
end
|
51
|
+
|
52
|
+
# === Test
|
53
|
+
#
|
54
|
+
# doctest: Evaluating a multi-line statement should be ok
|
55
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +", " 1", "not part of the statement"])
|
56
|
+
# >> s.evaluate
|
57
|
+
# => 2
|
58
|
+
#
|
59
|
+
# doctest: Evaluating a syntax error should raise an EvaluationError
|
60
|
+
# >> s = RubyDocTest::Statement.new([">> b = 1 +"])
|
61
|
+
# >> begin s.evaluate; :fail; rescue RubyDocTest::EvaluationError; :ok end
|
62
|
+
# => :ok
|
63
|
+
def evaluate
|
64
|
+
sc = source_code.gsub("__FILE__", @file_name.inspect)
|
65
|
+
# puts "EVAL: #{sc}"
|
66
|
+
@actual_result = eval(sc, TOPLEVEL_BINDING, __FILE__, __LINE__)
|
67
|
+
rescue Exception => e
|
68
|
+
if RubyDocTest.trace
|
69
|
+
raise e.class, e.to_s + "\n" + e.backtrace.first
|
70
|
+
else
|
71
|
+
raise EvaluationError.new(self, e)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|