qed 2.2.2 → 2.3.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.rdoc +23 -0
- data/LICENSE +204 -0
- data/README.rdoc +13 -20
- data/VERSION +3 -3
- data/demo/01_demos.rdoc +4 -4
- data/demo/02_advice.rdoc +5 -6
- data/demo/03_helpers.rdoc +5 -4
- data/demo/04_samples.rdoc +1 -1
- data/demo/05_quote.rdoc +19 -6
- data/demo/07_toplevel.rdoc +0 -2
- data/lib/qed/advice.rb +6 -8
- data/lib/qed/command.rb +49 -17
- data/lib/qed/{script.rb → demo.rb} +10 -6
- data/lib/qed/evaluator.rb +28 -29
- data/lib/qed/package.yml +3 -3
- data/lib/qed/parser.rb +160 -53
- data/lib/qed/reporter/abstract.rb +4 -4
- data/lib/qed/reporter/bullet.rb +5 -5
- data/lib/qed/reporter/dotprogress.rb +6 -3
- data/lib/qed/reporter/html.rb +1 -1
- data/lib/qed/reporter/verbatim.rb +11 -16
- data/lib/qed/scope.rb +12 -6
- data/lib/qed/session.rb +27 -18
- data/lib/qedoc/document/template.rhtml +47 -4
- metadata +6 -6
- data/COPYING +0 -622
data/demo/07_toplevel.rdoc
CHANGED
data/lib/qed/advice.rb
CHANGED
@@ -12,15 +12,14 @@ module QED
|
|
12
12
|
#
|
13
13
|
# == Pattern Matchers (When)
|
14
14
|
#
|
15
|
-
# Matchers are evaluated
|
16
|
-
#
|
17
|
-
# scope as the demonstrandum themselves.
|
15
|
+
# Matchers are evaluated when they match a blocks
|
16
|
+
# commentary.
|
18
17
|
#
|
19
18
|
# == Event Signals (Before, After)
|
20
19
|
#
|
21
20
|
# Event advice are triggered on symbolic targets which
|
22
21
|
# represent an event in the evaluation process, such as
|
23
|
-
# before
|
22
|
+
# before an example is run, or after a demo finishes.
|
24
23
|
#
|
25
24
|
class Advice
|
26
25
|
|
@@ -68,10 +67,9 @@ module QED
|
|
68
67
|
|
69
68
|
#
|
70
69
|
def call_matchers(scope, section)
|
71
|
-
match = section.
|
72
|
-
args = section.
|
73
|
-
|
74
|
-
@matchers.each do |(patterns, proc)|
|
70
|
+
match = section.commentary
|
71
|
+
args = section.arguments
|
72
|
+
matchers.each do |(patterns, proc)|
|
75
73
|
compare = match
|
76
74
|
matched = true
|
77
75
|
params = []
|
data/lib/qed/command.rb
CHANGED
@@ -73,6 +73,9 @@ module QED
|
|
73
73
|
# Move to root directory?
|
74
74
|
attr_accessor :root
|
75
75
|
|
76
|
+
# Parse mode.
|
77
|
+
attr_accessor :mode
|
78
|
+
|
76
79
|
#
|
77
80
|
# TODO: Should extension and profile have a common reference?
|
78
81
|
def initialize
|
@@ -121,6 +124,9 @@ module QED
|
|
121
124
|
opt.on('--root', '-R', "run command from project's root directory") do
|
122
125
|
@options[:root] = true
|
123
126
|
end
|
127
|
+
opt.on('--comment', '-c', "Run comment code.") do
|
128
|
+
@options[:mode] = :comment
|
129
|
+
end
|
124
130
|
opt.on('--ext', '-e NAME', "runtime extension [default]") do |name|
|
125
131
|
@options[:extension] = name
|
126
132
|
end
|
@@ -157,30 +163,51 @@ module QED
|
|
157
163
|
|
158
164
|
# Default recognized demos file types.
|
159
165
|
DEMO_TYPES = %w{qed rdoc md markdown}
|
166
|
+
CODE_TYPES = %w{rb}
|
160
167
|
|
161
|
-
# Returns a list of demo files.
|
168
|
+
# Returns a list of demo files. The files returned depends on the
|
169
|
+
# +files+ attribute and if none given, then the current run mode.
|
162
170
|
def demos
|
163
171
|
@demos ||= (
|
164
|
-
|
165
|
-
|
166
|
-
|
172
|
+
if mode == :comment
|
173
|
+
demos_in_comment_mode
|
174
|
+
else
|
175
|
+
demos_in_normal_mode
|
167
176
|
end
|
168
|
-
files = files.map{|pattern| Dir[pattern]}.flatten.uniq
|
169
|
-
files = files.map do |file|
|
170
|
-
if File.directory?(file)
|
171
|
-
Dir[File.join(file,'**','*.{' + DEMO_TYPES.join(',') + '}')]
|
172
|
-
else
|
173
|
-
file
|
174
|
-
end
|
175
|
-
end
|
176
|
-
files = files.flatten.uniq
|
177
|
-
files.map{|f| File.expand_path(f) }.sort
|
178
177
|
)
|
179
178
|
end
|
180
179
|
|
181
|
-
#
|
182
|
-
def
|
183
|
-
|
180
|
+
# Collect default files to process in normal demo mode.
|
181
|
+
def demos_in_normal_mode
|
182
|
+
demos_gather(DEMO_LOCATION, DEMO_TYPES)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Collect default files to process in code comment mode.
|
186
|
+
#
|
187
|
+
# TODO: Sure removing applique files is the best approach?
|
188
|
+
#
|
189
|
+
# TODO: Either add environment alond with applique or deprecate environment
|
190
|
+
# as an alternate name.
|
191
|
+
def demos_in_comment_mode
|
192
|
+
files = demos_gather('lib', CODE_TYPES)
|
193
|
+
files = files.reject{ |f| f.index('applique/') } # don't include applique files ???
|
194
|
+
files
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
def demos_gather(default_location, extensions=DEMO_TYPES)
|
199
|
+
files = self.files
|
200
|
+
files << default_location if files.empty?
|
201
|
+
files = files.map{|pattern| Dir[pattern]}.flatten.uniq
|
202
|
+
files = files.map do |file|
|
203
|
+
if File.directory?(file)
|
204
|
+
Dir[File.join(file,'**','*.{' + extensions.join(',') + '}')]
|
205
|
+
else
|
206
|
+
file
|
207
|
+
end
|
208
|
+
end
|
209
|
+
files = files.flatten.uniq
|
210
|
+
files.map{|f| File.expand_path(f) }.sort
|
184
211
|
end
|
185
212
|
|
186
213
|
# Parse command-line options along with profile options.
|
@@ -221,6 +248,11 @@ module QED
|
|
221
248
|
end
|
222
249
|
end
|
223
250
|
|
251
|
+
# Session instance.
|
252
|
+
def session
|
253
|
+
@session ||= Session.new(demos, :format=>format, :trace=>trace, :mode=>mode)
|
254
|
+
end
|
255
|
+
|
224
256
|
# Project's root directory.
|
225
257
|
def root_directory
|
226
258
|
@root_directory ||= find_root
|
@@ -6,9 +6,9 @@ module QED
|
|
6
6
|
require 'qed/parser'
|
7
7
|
require 'qed/evaluator'
|
8
8
|
|
9
|
-
# =
|
9
|
+
# = Demo
|
10
10
|
#
|
11
|
-
class
|
11
|
+
class Demo
|
12
12
|
|
13
13
|
#
|
14
14
|
attr :applique
|
@@ -16,14 +16,18 @@ module QED
|
|
16
16
|
# Demonstrandum file.
|
17
17
|
attr :file
|
18
18
|
|
19
|
+
#
|
20
|
+
attr :mode
|
21
|
+
|
19
22
|
#
|
20
23
|
attr :scope
|
21
24
|
|
22
25
|
# New Script
|
23
|
-
def initialize(
|
24
|
-
@applique = applique.dup # localize copy of applique
|
26
|
+
def initialize(file, applique, options={})
|
25
27
|
@file = file
|
26
|
-
@
|
28
|
+
@applique = applique.dup # localize copy of applique
|
29
|
+
@scope = options[:scope] || Scope.new(applique, file)
|
30
|
+
@mode = options[:mode]
|
27
31
|
@binding = @scope.__binding__
|
28
32
|
#@loadlist = []
|
29
33
|
#apply_environment
|
@@ -77,7 +81,7 @@ module QED
|
|
77
81
|
# Parse script.
|
78
82
|
# Retruns an abstract syntax tree.
|
79
83
|
def parse
|
80
|
-
Parser.new(file).parse
|
84
|
+
Parser.new(file, :mode=>mode).parse
|
81
85
|
end
|
82
86
|
|
83
87
|
#
|
data/lib/qed/evaluator.rb
CHANGED
@@ -20,48 +20,47 @@ module QED
|
|
20
20
|
|
21
21
|
#
|
22
22
|
def run
|
23
|
-
advise!(:
|
23
|
+
advise!(:before_demo, @script)
|
24
24
|
process
|
25
|
-
advise!(:
|
25
|
+
advise!(:after_demo, @script)
|
26
26
|
end
|
27
27
|
|
28
28
|
#
|
29
29
|
def process
|
30
30
|
@ast.each do |section|
|
31
|
-
|
32
|
-
when :code
|
33
|
-
evaluate_code(section)
|
34
|
-
when :text
|
35
|
-
evaluate_text(section)
|
36
|
-
end
|
31
|
+
evaluate(section)
|
37
32
|
end
|
38
33
|
end
|
39
34
|
|
40
|
-
#
|
41
|
-
def
|
42
|
-
advise!(:
|
35
|
+
# Evaluate a demo section.
|
36
|
+
def evaluate(section)
|
37
|
+
advise!(:text, section) # TODO: rename to :step?
|
38
|
+
evaluate_links(section)
|
39
|
+
advise!(:before_step, section, @script.file)
|
43
40
|
begin
|
44
|
-
advise!(:
|
45
|
-
|
46
|
-
pass!(section)
|
47
|
-
rescue Assertion => exception
|
48
|
-
fail!(section, exception)
|
49
|
-
rescue Exception => exception
|
50
|
-
error!(section, exception)
|
41
|
+
advise!(:when, section)
|
42
|
+
# TODO: how to handle catching asserts in advice?
|
51
43
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
44
|
+
if section.code?
|
45
|
+
begin
|
46
|
+
advise!(:code, section)
|
47
|
+
@script.evaluate(section.eval_code, section.lineno)
|
48
|
+
rescue SystemExit
|
49
|
+
pass!(section)
|
50
|
+
rescue Assertion => exception
|
51
|
+
fail!(section, exception)
|
52
|
+
rescue Exception => exception
|
53
|
+
error!(section, exception)
|
54
|
+
else
|
55
|
+
pass!(section)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
advise!(:after_step, section, @script.file)
|
60
59
|
end
|
61
60
|
|
62
|
-
#
|
61
|
+
# TODO: Not sure how to handle loading links in comment mode.
|
63
62
|
def evaluate_links(section)
|
64
|
-
section.
|
63
|
+
section.commentary.scan(/\[qed:\/\/(.*?)\]/) do |match|
|
65
64
|
file = $1
|
66
65
|
# relative to demo script
|
67
66
|
if File.exist?(File.join(@script.directory,file))
|
@@ -72,7 +71,7 @@ module QED
|
|
72
71
|
when '.rb'
|
73
72
|
import!(file)
|
74
73
|
else
|
75
|
-
|
74
|
+
Demo.new(file, @script.applique, :scope=>@script.scope).run
|
76
75
|
end
|
77
76
|
end
|
78
77
|
end
|
data/lib/qed/package.yml
CHANGED
data/lib/qed/parser.rb
CHANGED
@@ -5,25 +5,80 @@ module QED
|
|
5
5
|
# evaluator.
|
6
6
|
#
|
7
7
|
# Technically is defines it's own markup language
|
8
|
-
# but for interoperability sake it
|
8
|
+
# but for interoperability sake it is RDoc and a bit of
|
9
|
+
# support for Markdown.
|
9
10
|
class Parser
|
10
11
|
|
11
12
|
#
|
12
|
-
def initialize(file)
|
13
|
-
@
|
14
|
-
@
|
13
|
+
def initialize(file, options={})
|
14
|
+
@file = file
|
15
|
+
@options = options
|
16
|
+
@ast = []
|
15
17
|
end
|
16
18
|
|
17
19
|
# Abstract Syntax Tree
|
18
20
|
attr :ast
|
19
21
|
|
22
|
+
# File to parse.
|
23
|
+
attr :file
|
24
|
+
|
25
|
+
# Parser options.
|
26
|
+
attr :options
|
27
|
+
|
28
|
+
#
|
29
|
+
def lines
|
30
|
+
@lines ||= parse_lines
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
def parse_lines
|
35
|
+
case options[:mode]
|
36
|
+
when :comment
|
37
|
+
parse_comment_lines
|
38
|
+
else
|
39
|
+
index = -1
|
40
|
+
File.readlines(file).to_a.map do |line|
|
41
|
+
[index += 1, line]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: It would be nice if we could get ther require statement for the
|
47
|
+
# comment mode to be relative to an actual loadpath.
|
48
|
+
def parse_comment_lines
|
49
|
+
omit = false
|
50
|
+
lines = [
|
51
|
+
[0, "Load #{File.basename(file)} script.\n"],
|
52
|
+
[0, "\n"],
|
53
|
+
[0, " require '#{file}'\n"]
|
54
|
+
]
|
55
|
+
index = 0
|
56
|
+
File.readlines(file).each do |l|
|
57
|
+
case l
|
58
|
+
when /^\s*\#\-\-\s*$/
|
59
|
+
omit = true
|
60
|
+
when /^\s*\#\+\+\s*$/
|
61
|
+
omit = false
|
62
|
+
when /^\s*\#\ \-\-/ # ?
|
63
|
+
# -- skip internal comments
|
64
|
+
when /^\s*\#/
|
65
|
+
lines << [index, l.lstrip.sub(/^\#\ ?/, '')] unless omit
|
66
|
+
else
|
67
|
+
lines << [index, "\n"] unless lines.last[1] == "\n"
|
68
|
+
end
|
69
|
+
index += 1
|
70
|
+
end
|
71
|
+
lines
|
72
|
+
end
|
73
|
+
|
74
|
+
=begin
|
20
75
|
# Parse the demo into an abstract syntax tree.
|
21
76
|
#
|
22
77
|
# TODO: I know there has to be a faster way to do this.
|
23
78
|
def parse
|
24
79
|
blocks = [[]]
|
25
80
|
state = :none
|
26
|
-
|
81
|
+
lines.each do |lineno, line|
|
27
82
|
case line
|
28
83
|
when /^$/
|
29
84
|
case state
|
@@ -76,6 +131,39 @@ module QED
|
|
76
131
|
#cont = (/\.\.\.\s*^/ =~ text ? true : false)
|
77
132
|
end
|
78
133
|
end
|
134
|
+
=end
|
135
|
+
|
136
|
+
def parse
|
137
|
+
tree = []
|
138
|
+
mode = :rem
|
139
|
+
pend = false
|
140
|
+
block = Block.new
|
141
|
+
lines.each do |lineno, line|
|
142
|
+
case line
|
143
|
+
when /^\s*$/
|
144
|
+
case mode
|
145
|
+
when :rem
|
146
|
+
pend = true unless line == 0
|
147
|
+
block.rem << [lineno, line]
|
148
|
+
when :raw
|
149
|
+
block.raw << [lineno, line]
|
150
|
+
end
|
151
|
+
when /\A\s+/
|
152
|
+
mode = :raw
|
153
|
+
block.raw << [lineno, line]
|
154
|
+
else
|
155
|
+
if pend || mode == :raw
|
156
|
+
pend = false
|
157
|
+
mode = :rem
|
158
|
+
tree << block.ready!
|
159
|
+
block = Block.new
|
160
|
+
end
|
161
|
+
block.rem << [lineno, line]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
tree << block.ready!
|
165
|
+
@ast = tree
|
166
|
+
end
|
79
167
|
|
80
168
|
# TODO: We need to preserve the indentation for the verbatim reporter.
|
81
169
|
#def clean_quote(text)
|
@@ -86,82 +174,101 @@ module QED
|
|
86
174
|
# text.rstrip
|
87
175
|
#end
|
88
176
|
|
89
|
-
#
|
90
|
-
class
|
91
|
-
|
92
|
-
attr :
|
93
|
-
|
94
|
-
|
95
|
-
|
177
|
+
# Section Block
|
178
|
+
class Block
|
179
|
+
# Block commentary.
|
180
|
+
attr :rem
|
181
|
+
|
182
|
+
# Block raw code/text.
|
183
|
+
attr :raw
|
184
|
+
|
185
|
+
#
|
186
|
+
def initialize
|
187
|
+
@rem = []
|
188
|
+
@raw = []
|
189
|
+
@has_code = true
|
96
190
|
end
|
97
|
-
end
|
98
191
|
|
99
|
-
|
100
|
-
|
192
|
+
#
|
193
|
+
def ready!
|
194
|
+
@commentary = rem.map{ |lineno, line| line }.join
|
195
|
+
@example = raw.map{ |lineno, line| line }.join
|
196
|
+
@has_code = false if @raw.empty?
|
197
|
+
@has_code = false if continuation?
|
198
|
+
self
|
199
|
+
end
|
101
200
|
|
102
|
-
|
201
|
+
#
|
202
|
+
def commentary
|
203
|
+
@commentary
|
204
|
+
end
|
103
205
|
|
104
|
-
|
206
|
+
#
|
207
|
+
def example
|
208
|
+
@example
|
209
|
+
end
|
105
210
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
@cont = []
|
211
|
+
# Returns an Array of prepared example text
|
212
|
+
# for use in advice.
|
213
|
+
def arguments
|
214
|
+
continuation? ? [example_argument] : []
|
111
215
|
end
|
112
216
|
|
113
217
|
#
|
114
|
-
def
|
115
|
-
@
|
116
|
-
|
218
|
+
def code?
|
219
|
+
@has_code
|
220
|
+
end
|
221
|
+
|
222
|
+
# First line of example text.
|
223
|
+
def lineno
|
224
|
+
@line ||= @raw.first.first
|
117
225
|
end
|
118
226
|
|
119
227
|
#
|
120
|
-
def
|
121
|
-
|
228
|
+
def code
|
229
|
+
@example
|
122
230
|
end
|
123
231
|
|
124
|
-
#
|
125
|
-
def
|
126
|
-
|
127
|
-
/\.\.\.\s*\Z/m =~ text
|
232
|
+
#
|
233
|
+
def eval_code
|
234
|
+
@eval_code ||= tweak_code
|
128
235
|
end
|
129
236
|
|
130
|
-
#
|
131
|
-
|
132
|
-
|
133
|
-
|
237
|
+
#
|
238
|
+
def tweak_code
|
239
|
+
code = example.dup
|
240
|
+
code.gsub!(/\n\s*\#\ ?\=\>/, '.assert == ')
|
241
|
+
code.gsub!(/\s*\#\ ?\=\>/, '.assert == ')
|
242
|
+
code
|
243
|
+
end
|
244
|
+
|
245
|
+
# Clean up the example text, removing unccesseary white lines
|
246
|
+
# and triple quote brackets, but keep indention intact.
|
247
|
+
def clean_example
|
248
|
+
text = example.chomp.sub(/\A\n/,'')
|
134
249
|
if md = /\A["]{3,}(.*?)["]{3,}\Z/.match(text)
|
135
250
|
text = md[1]
|
136
251
|
end
|
137
252
|
text.rstrip
|
138
253
|
end
|
139
254
|
|
140
|
-
#
|
141
|
-
#
|
142
|
-
|
143
|
-
|
255
|
+
# When the example is raw text and passed to an adivce block, this
|
256
|
+
# provides the prepared form of the example text, removing white lines,
|
257
|
+
# triple quote brackets and indention.
|
258
|
+
def example_argument
|
259
|
+
text = example.tabto(0).chomp.sub(/\A\n/,'')
|
144
260
|
if md = /\A["]{3,}(.*?)["]{3,}\Z/.match(text)
|
145
261
|
text = md[1]
|
146
262
|
end
|
147
263
|
text.rstrip
|
148
264
|
end
|
149
|
-
end
|
150
265
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
@text = text
|
156
|
-
@line = line
|
157
|
-
#@args = args
|
158
|
-
end
|
159
|
-
#def <<(arg)
|
160
|
-
# @args << arg
|
161
|
-
#end
|
162
|
-
def type
|
163
|
-
:code
|
266
|
+
# And commentary ending in `...` or `:` will mark the following
|
267
|
+
# example as plain text and not code to be evaluated.
|
268
|
+
def continuation?
|
269
|
+
/(\.\.\.|\:)\s*\Z/m =~ commentary
|
164
270
|
end
|
271
|
+
|
165
272
|
end
|
166
273
|
|
167
274
|
end
|