qed 2.2.1 → 2.2.2
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/{DIARY.rdoc → Diary.rdoc} +0 -0
- data/{HISTORY → History.rdoc} +19 -3
- data/PROFILE +1 -1
- data/README.rdoc +1 -1
- data/VERSION +2 -2
- data/bin/qed +2 -2
- data/demo/02_advice.rdoc +0 -1
- data/demo/04_samples.rdoc +49 -0
- data/demo/{fixtures → samples}/data.txt +0 -0
- data/demo/{fixtures → samples}/table.yml +0 -0
- data/lib/qed.rb +1 -0
- data/lib/qed/command.rb +136 -97
- data/lib/qed/package.yml +2 -2
- data/lib/qed/parser.rb +45 -24
- data/lib/qed/profile.yml +1 -1
- data/lib/qed/reporter/bullet.rb +3 -5
- data/lib/qed/reporter/verbatim.rb +7 -7
- data/lib/qed/scope.rb +73 -74
- data/lib/qed/script.rb +2 -2
- data/lib/qed/session.rb +2 -2
- data/script/test +3 -1
- data/test/integration/topcode.rdoc +8 -0
- metadata +10 -13
- data/ROADMAP +0 -12
- data/demo/04_fixtures.rdoc +0 -29
- data/eg/hello_world.rdoc +0 -15
- data/eg/view_error.rdoc +0 -21
- data/eg/website.rdoc +0 -12
data/{DIARY.rdoc → Diary.rdoc}
RENAMED
File without changes
|
data/{HISTORY → History.rdoc}
RENAMED
@@ -1,18 +1,34 @@
|
|
1
1
|
= RELEASE HISTORY
|
2
2
|
|
3
|
-
== 2.2.
|
3
|
+
== 2.2.2 / 2010-06-21
|
4
|
+
|
5
|
+
An issue was reported in which the a code block at the very
|
6
|
+
top of a demo was being ignored. This release fixes this issue
|
7
|
+
by rewriting the parser (much better now thanks!). At the same
|
8
|
+
time the Data and Table methods have been polished, both of
|
9
|
+
which can now pick up sample data relative to the current demo.
|
10
|
+
|
11
|
+
Changes:
|
12
|
+
|
13
|
+
* Rewrite parser and fix top code issue.
|
14
|
+
* Data method cannot write data, instead executes block.
|
15
|
+
* Data and Table methods look for file relative to demo first.
|
16
|
+
* Added -R option to run demos relative to project root.
|
17
|
+
|
18
|
+
|
19
|
+
== 2.2.1 / 2010-06-20
|
4
20
|
|
5
21
|
Remove dependencies to Tilt and Nokogiri. Should have
|
6
22
|
done this in last release but alas --there is so
|
7
23
|
much to do.
|
8
24
|
|
9
|
-
|
25
|
+
Changes:
|
10
26
|
|
11
27
|
* Removed HTML parsing dependencies.
|
12
28
|
* Reduce Advice to a single class.
|
13
29
|
|
14
30
|
|
15
|
-
== 2.2.0 / 2010-06-
|
31
|
+
== 2.2.0 / 2010-06-19
|
16
32
|
|
17
33
|
This release returns to a text-based evaluator, rather
|
18
34
|
then use HTML. Processing HTML proved to have too many
|
data/PROFILE
CHANGED
data/README.rdoc
CHANGED
data/VERSION
CHANGED
data/bin/qed
CHANGED
data/demo/02_advice.rdoc
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
= Test Samples
|
2
|
+
|
3
|
+
== Flat-file Data
|
4
|
+
|
5
|
+
When creating testable demonstrations, there are times when sizable
|
6
|
+
chunks of data are needed. It is convenient to store such data in
|
7
|
+
separate files. The +Data+ method makes is easy to utilize them.
|
8
|
+
|
9
|
+
Data('demo/samples/data.txt').assert =~ /dolor/
|
10
|
+
|
11
|
+
The +Data+ method can also take a block which passes the data
|
12
|
+
as the block's only argument.
|
13
|
+
|
14
|
+
Data('demo/samples/data.txt') do |data|
|
15
|
+
data.assert =~ /dolor/
|
16
|
+
end
|
17
|
+
|
18
|
+
Files are looked-up relative to the location of the current document.
|
19
|
+
If not found then they will be looked-up relative to the current
|
20
|
+
working directory.
|
21
|
+
|
22
|
+
== Tabular Data
|
23
|
+
|
24
|
+
The +Table+ method is similar to the +Data+ method except that it
|
25
|
+
expects a YAML file, and it can take a block to iterate the data over.
|
26
|
+
This makes it easy to test tables of examples.
|
27
|
+
|
28
|
+
The arity of the table block corresponds to the number of columns in
|
29
|
+
each row of the table. Each row is assigned in turn and run through
|
30
|
+
the coded step. Consider the following example:
|
31
|
+
|
32
|
+
Every row in the {table.yml table}[table.yml] will be assigned to
|
33
|
+
the block parameters and run through the subsequent assertion.
|
34
|
+
|
35
|
+
Table 'demo/samples/table.yml' do |x, y|
|
36
|
+
x.upcase.assert == y
|
37
|
+
end
|
38
|
+
|
39
|
+
Without the block, the +Table+ methods simply returns the sample data.
|
40
|
+
|
41
|
+
== Considerations
|
42
|
+
|
43
|
+
Both Data and Table are some what "old fashion" approches to sample
|
44
|
+
data. New techinques using plain text blocks are more convenient
|
45
|
+
in that the data can be stored directly in the demonstration itself.
|
46
|
+
However, for especially large data sets and external file is still
|
47
|
+
the better option, and +Data+ and +Table+ make them quite easy to
|
48
|
+
access.
|
49
|
+
|
File without changes
|
File without changes
|
data/lib/qed.rb
CHANGED
data/lib/qed/command.rb
CHANGED
@@ -1,26 +1,39 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'qed'
|
4
1
|
require 'optparse'
|
5
2
|
require 'shellwords'
|
6
3
|
|
7
4
|
module QED
|
8
5
|
|
6
|
+
def self.main(*argv)
|
7
|
+
Command.main(*argv)
|
8
|
+
end
|
9
|
+
|
9
10
|
# = QED Commandline Tool
|
10
11
|
#
|
12
|
+
# TODO: Merge Command with Session ?
|
11
13
|
class Command
|
12
14
|
|
13
|
-
# Configuration directory
|
14
|
-
|
15
|
+
# Configuration directory `.qed`, `.config/qed` or `config/qed`.
|
16
|
+
# In this directory special configuration files can be placed
|
17
|
+
# to autmatically effect qed execution. In particular you can
|
18
|
+
# add a `profiles.yml` file to setup convenient execution
|
19
|
+
# scenarios.
|
20
|
+
CONFIG_PATTERN = "{.,.config/,config/}qed"
|
21
|
+
|
22
|
+
# Default location of demonstrations if no specific files
|
23
|
+
# or locations given. This is use in Dir.glob. The default
|
24
|
+
# locations are qed/, demo/ or demos/, searched for in that
|
25
|
+
# order relative to the root directory.
|
26
|
+
DEMO_LOCATION = '{qed,demo,demos}'
|
27
|
+
|
28
|
+
# Glob pattern used to search for project's root directory.
|
29
|
+
ROOT_PATTERN = '{.root,.git,.hg,_darcs}/'
|
15
30
|
|
16
|
-
#
|
17
|
-
|
18
|
-
# is use in Dir.glob.
|
19
|
-
DEFAULT_DEMO_LOCATION = '{demo,demos}'
|
31
|
+
# Home directory.
|
32
|
+
HOME = File.expand_path('~')
|
20
33
|
|
21
|
-
#
|
22
|
-
def self.
|
23
|
-
new.execute
|
34
|
+
# Instantiate a new Command object and call #execute.
|
35
|
+
def self.main(*argv)
|
36
|
+
new.execute(argv)
|
24
37
|
end
|
25
38
|
|
26
39
|
# Ouput format.
|
@@ -43,22 +56,25 @@ module QED
|
|
43
56
|
# Files to be run.
|
44
57
|
attr :files
|
45
58
|
|
46
|
-
#
|
59
|
+
# Ensure files are in a flat list.
|
47
60
|
def files=(globs)
|
48
61
|
@files = [globs].flatten
|
49
62
|
end
|
50
63
|
|
51
|
-
#
|
64
|
+
# Paths to be added to $LOAD_PATH.
|
52
65
|
attr_accessor :loadpath
|
53
66
|
|
54
|
-
#
|
67
|
+
# Libraries to be required.
|
55
68
|
attr_accessor :requires
|
56
69
|
|
57
|
-
#
|
70
|
+
# ?
|
58
71
|
attr_accessor :extension
|
59
72
|
|
60
|
-
#
|
73
|
+
# Move to root directory?
|
74
|
+
attr_accessor :root
|
61
75
|
|
76
|
+
#
|
77
|
+
# TODO: Should extension and profile have a common reference?
|
62
78
|
def initialize
|
63
79
|
@format = :dotprogress
|
64
80
|
@extension = :default
|
@@ -70,7 +86,6 @@ module QED
|
|
70
86
|
end
|
71
87
|
|
72
88
|
# Instance of OptionParser
|
73
|
-
|
74
89
|
def opts
|
75
90
|
@opts ||= OptionParser.new do |opt|
|
76
91
|
|
@@ -84,113 +99,96 @@ module QED
|
|
84
99
|
end
|
85
100
|
|
86
101
|
opt.separator("Report Formats (pick one):")
|
87
|
-
|
88
102
|
opt.on('--dotprogress', '-d', "use dot-progress reporter [default]") do
|
89
103
|
@options[:format] = :dotprogress
|
90
104
|
end
|
91
|
-
|
92
105
|
opt.on('--verbatim', '-v', "use verbatim reporter") do
|
93
106
|
@options[:format] = :verbatim
|
94
107
|
end
|
95
|
-
|
96
108
|
opt.on('--bullet', '-b', "use bullet-point reporter") do
|
97
109
|
@options[:format] = :bullet
|
98
110
|
end
|
99
|
-
|
100
111
|
opt.on('--html', '-h', "use underlying HTML reporter") do
|
101
112
|
@options[:format] = :html
|
102
113
|
end
|
103
|
-
|
104
114
|
opt.on('--format', '-f FORMAT', "use custom reporter") do |format|
|
105
115
|
@options[:format] = format
|
106
116
|
end
|
107
|
-
|
108
117
|
#opt.on('--script', "psuedo-reporter") do
|
109
118
|
# @options[:format] = :script # psuedo-reporter
|
110
119
|
#end
|
111
|
-
|
112
120
|
opt.separator("Control Options:")
|
113
|
-
|
114
|
-
|
121
|
+
opt.on('--root', '-R', "run command from project's root directory") do
|
122
|
+
@options[:root] = true
|
123
|
+
end
|
124
|
+
opt.on('--ext', '-e NAME', "runtime extension [default]") do |name|
|
115
125
|
@options[:extension] = name
|
116
126
|
end
|
117
|
-
|
118
127
|
opt.on('--loadpath', "-I PATH", "add paths to $LOAD_PATH") do |arg|
|
119
128
|
@options[:loadpath] ||= []
|
120
129
|
@options[:loadpath].concat(arg.split(/[:;]/).map{ |dir| File.expand_path(dir) })
|
121
130
|
end
|
122
|
-
|
123
131
|
opt.on('--require', "-r", "require library") do |arg|
|
124
132
|
@options[:requires] ||= []
|
125
133
|
@options[:requires].concat(arg.split(/[:;]/)) #.map{ |dir| File.expand_path(dir) })
|
126
134
|
end
|
127
|
-
|
128
135
|
opt.on('--trace', '-t', "show full backtraces for exceptions") do
|
129
136
|
@options[:trace] = true
|
130
137
|
end
|
131
|
-
|
132
138
|
opt.on('--debug', "exit immediately upon raised exception") do
|
133
139
|
$VERBOSE = true # wish this were called $WARN
|
134
140
|
$DEBUG = true
|
135
141
|
end
|
136
|
-
|
137
142
|
opt.separator("Optional Commands:")
|
138
|
-
|
139
143
|
opt.on_tail('--version', "display version") do
|
140
144
|
puts "QED #{VERSION}"
|
141
145
|
exit
|
142
146
|
end
|
143
|
-
|
144
147
|
opt.on_tail('--copyright', "display copyrights") do
|
145
148
|
puts "Copyright (c) 2008, 2009 Thomas Sawyer, GPL License"
|
146
149
|
exit
|
147
150
|
end
|
148
|
-
|
149
151
|
opt.on_tail('--help', '-h', "display this help message") do
|
150
152
|
puts opt
|
151
153
|
exit
|
152
154
|
end
|
153
|
-
|
154
155
|
end
|
155
156
|
end
|
156
157
|
|
157
|
-
#
|
158
|
+
# Default recognized demos file types.
|
159
|
+
DEMO_TYPES = %w{qed rdoc md markdown}
|
160
|
+
|
161
|
+
# Returns a list of demo files.
|
158
162
|
def demos
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
end
|
164
|
-
files = files.map do |pattern|
|
165
|
-
Dir[pattern]
|
166
|
-
end.flatten.uniq
|
167
|
-
files = files.map do |file|
|
168
|
-
if File.directory?(file)
|
169
|
-
Dir[File.join(file,'**','*.{' + types.join(',') + '}')]
|
170
|
-
else
|
171
|
-
file
|
163
|
+
@demos ||= (
|
164
|
+
files = self.files
|
165
|
+
if files.empty?
|
166
|
+
files << DEMO_LOCATION
|
172
167
|
end
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
+
)
|
179
179
|
end
|
180
180
|
|
181
181
|
# Session instance.
|
182
|
-
|
183
182
|
def session
|
184
183
|
@session ||= Session.new(demos, :format=>format, :trace=>trace)
|
185
184
|
end
|
186
185
|
|
187
186
|
# Parse command-line options along with profile options.
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
argv
|
192
|
-
|
193
|
-
@files.concat(argv)
|
187
|
+
def parse(argv)
|
188
|
+
#@files = []
|
189
|
+
opts.parse!(argv ||= ARGV.dup)
|
190
|
+
#@files.concat(argv)
|
191
|
+
@files = argv
|
194
192
|
|
195
193
|
#if profile
|
196
194
|
if args = profiles[profile]
|
@@ -206,75 +204,116 @@ module QED
|
|
206
204
|
end
|
207
205
|
|
208
206
|
# Run demonstrations.
|
207
|
+
def execute(argv)
|
208
|
+
parse(argv)
|
209
209
|
|
210
|
-
|
211
|
-
parse
|
210
|
+
jump = @options[:root] ? root_directory : Dir.pwd
|
212
211
|
|
213
|
-
|
212
|
+
Dir.chdir(jump) do
|
213
|
+
abort "No documents." if demos.empty?
|
214
214
|
|
215
|
-
|
215
|
+
prepare_loadpath
|
216
216
|
|
217
|
-
|
218
|
-
|
217
|
+
require_libraries
|
218
|
+
require_profile
|
219
219
|
|
220
|
-
|
220
|
+
session.run
|
221
|
+
end
|
221
222
|
end
|
222
223
|
|
223
|
-
#
|
224
|
+
# Project's root directory.
|
225
|
+
def root_directory
|
226
|
+
@root_directory ||= find_root
|
227
|
+
end
|
228
|
+
|
229
|
+
# Project's QED configuation directory.
|
230
|
+
def config_directory
|
231
|
+
@config_directory ||= find_config #Dir[File.join(root_directory, CONFIG_PATTERN)].first
|
232
|
+
end
|
224
233
|
|
234
|
+
# Profile configurations.
|
225
235
|
def profiles
|
226
236
|
@profiles ||= (
|
227
|
-
file = Dir["#{
|
237
|
+
file = Dir["#{config_directory}/profile{,s}.{yml,yaml}"].first
|
228
238
|
file ? YAML.load(File.new(file)) : {}
|
229
239
|
)
|
230
240
|
end
|
231
241
|
|
232
242
|
# Add to load path (from -I option).
|
233
|
-
|
234
243
|
def prepare_loadpath
|
235
244
|
loadpath.each{ |dir| $LOAD_PATH.unshift(dir) }
|
236
245
|
end
|
237
246
|
|
238
247
|
# Require libraries (from -r option).
|
239
|
-
|
240
248
|
def require_libraries
|
241
249
|
requires.each{ |file| require(file) }
|
242
250
|
end
|
243
251
|
|
244
252
|
# Require requirement file (from -e option).
|
245
|
-
|
246
253
|
def require_profile
|
247
|
-
return unless
|
248
|
-
|
249
|
-
# common environment, always loaded if present.
|
250
|
-
#if file = Dir["#{root}/#{CONFDIR}/default.rb"].first
|
251
|
-
# require(file)
|
252
|
-
#end
|
253
|
-
|
254
|
-
#env = env() || 'default'
|
255
|
-
|
256
|
-
if file = Dir["#{root}/#{CONFDIR}/#{extension}.rb"].first
|
254
|
+
return unless config_directory
|
255
|
+
if file = Dir["#{config_directory}/#{extension}.rb"].first
|
257
256
|
require(file)
|
258
257
|
end
|
259
258
|
end
|
260
259
|
|
260
|
+
# Locate project's root directory. This is done by searching upward
|
261
|
+
# in the file heirarchy for the existence of one of the following
|
262
|
+
# path names, each group being tried in turn.
|
263
|
+
#
|
264
|
+
# * .root/
|
265
|
+
# * .git/
|
266
|
+
# * .hg/
|
267
|
+
# * _darcs/
|
268
|
+
#
|
269
|
+
# Failing to find any of these locations, resort to the fallback:
|
270
|
+
#
|
271
|
+
# * lib/
|
261
272
|
#
|
262
|
-
def
|
263
|
-
|
273
|
+
def find_root(path=nil)
|
274
|
+
path = File.expand_path(path || Dir.pwd)
|
275
|
+
path = File.dirname(path) unless File.directory?(path)
|
276
|
+
|
277
|
+
root = lookup(ROOT_PATTERN, path)
|
278
|
+
return root if root
|
279
|
+
|
280
|
+
#root = lookup(path, '{.qed,.config/qed,config/qed}/')
|
281
|
+
#return root if root
|
282
|
+
|
283
|
+
#root = lookup(path, '{qed,demo,demos}/')
|
284
|
+
#return root if root
|
285
|
+
|
286
|
+
root = lookup('lib/', path)
|
287
|
+
return root if root
|
288
|
+
|
289
|
+
abort "Failed to resolve project's root location. Try adding a .root directory."
|
264
290
|
end
|
265
291
|
|
266
|
-
|
292
|
+
# Locate configuration directory by seaching up the
|
293
|
+
# file hierachy relative to the working directory
|
294
|
+
# for one of the following paths:
|
295
|
+
#
|
296
|
+
# * .qed/
|
297
|
+
# * .config/qed/
|
298
|
+
# * config/qed/
|
299
|
+
#
|
300
|
+
def find_config
|
301
|
+
lookup(CONFIG_PATTERN)
|
302
|
+
end
|
267
303
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
304
|
+
# Lookup path +glob+, searching each higher directory
|
305
|
+
# in turn until just before the users home directory
|
306
|
+
# is reached or just before the system's root directory.
|
307
|
+
#
|
308
|
+
# TODO: include HOME directory in search?
|
309
|
+
def lookup(glob, path=Dir.pwd)
|
310
|
+
until path == HOME or path == '/' # until home or root
|
311
|
+
mark = Dir.glob(File.join(path,glob), File::FNM_CASEFOLD).first
|
312
|
+
return path if mark
|
313
|
+
path = File.dirname(path)
|
314
|
+
end
|
276
315
|
end
|
277
|
-
|
316
|
+
|
278
317
|
end
|
279
318
|
|
280
319
|
end
|
data/lib/qed/package.yml
CHANGED
data/lib/qed/parser.rb
CHANGED
@@ -14,38 +14,51 @@ module QED
|
|
14
14
|
@ast = []
|
15
15
|
end
|
16
16
|
|
17
|
-
#
|
17
|
+
# Abstract Syntax Tree
|
18
18
|
attr :ast
|
19
19
|
|
20
|
+
# Parse the demo into an abstract syntax tree.
|
20
21
|
#
|
22
|
+
# TODO: I know there has to be a faster way to do this.
|
21
23
|
def parse
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
text = ''
|
26
|
-
|
24
|
+
blocks = [[]]
|
25
|
+
state = :none
|
27
26
|
@lines.each_with_index do |line, lineno|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
case line
|
28
|
+
when /^$/
|
29
|
+
case state
|
30
|
+
when :code
|
31
|
+
blocks.last << line
|
32
|
+
when :blank
|
33
|
+
blocks.last << line
|
34
|
+
else
|
35
|
+
blocks.last << line
|
36
|
+
state = :blank
|
33
37
|
end
|
38
|
+
when /^\s+/
|
39
|
+
blocks << [] if state != :code
|
40
|
+
blocks.last << line
|
41
|
+
state = :code
|
42
|
+
else
|
43
|
+
blocks << [] if state != :text
|
44
|
+
blocks.last << line
|
34
45
|
state = :text
|
35
|
-
|
46
|
+
end
|
47
|
+
end
|
48
|
+
blocks.shift if blocks.first.empty?
|
49
|
+
|
50
|
+
line_cnt = 1
|
51
|
+
blocks.each do |block|
|
52
|
+
text = block.join
|
53
|
+
case text
|
54
|
+
when /\A\s+/
|
55
|
+
add_section(:code, text, line_cnt)
|
36
56
|
else
|
37
|
-
|
38
|
-
next if text.strip.empty?
|
39
|
-
add_section(:text, text, linein)
|
40
|
-
linein = lineno
|
41
|
-
text = ''
|
42
|
-
end
|
43
|
-
state = :code
|
44
|
-
text << line
|
57
|
+
add_section(:text, text, line_cnt)
|
45
58
|
end
|
59
|
+
line_cnt += block.size
|
46
60
|
end
|
47
|
-
|
48
|
-
@ast.reject!{ |sect| sect.type == :code && sect.text.strip.empty? }
|
61
|
+
#@ast.reject!{ |sect| sect.type == :code && sect.text.strip.empty? }
|
49
62
|
return @ast
|
50
63
|
end
|
51
64
|
|
@@ -53,7 +66,7 @@ module QED
|
|
53
66
|
def add_section(state, text, lineno)
|
54
67
|
case state
|
55
68
|
when :code
|
56
|
-
if ast.last.
|
69
|
+
if ast.last && ast.last.cont?
|
57
70
|
@ast.last << text #clean_quote(text)
|
58
71
|
else
|
59
72
|
@ast << CodeSection.new(text, lineno)
|
@@ -85,23 +98,31 @@ module QED
|
|
85
98
|
|
86
99
|
#
|
87
100
|
class TextSection < Section
|
101
|
+
|
88
102
|
attr :args
|
103
|
+
|
89
104
|
attr :cont
|
105
|
+
|
90
106
|
def initialize(text, line, *args)
|
91
107
|
@text = text
|
92
108
|
@line = line
|
93
109
|
@args = args
|
94
110
|
@cont = []
|
95
111
|
end
|
112
|
+
|
113
|
+
#
|
96
114
|
def <<(text)
|
97
115
|
@cont << clean_continuation(text)
|
98
116
|
@args << block_continuation(text)
|
99
117
|
end
|
118
|
+
|
119
|
+
#
|
100
120
|
def type
|
101
121
|
:text
|
102
122
|
end
|
123
|
+
|
103
124
|
# TODO: Use ':' or '...' ?
|
104
|
-
def
|
125
|
+
def cont?
|
105
126
|
#/\:\s*\Z/m =~ text
|
106
127
|
/\.\.\.\s*\Z/m =~ text
|
107
128
|
end
|
data/lib/qed/profile.yml
CHANGED
data/lib/qed/reporter/bullet.rb
CHANGED
@@ -13,7 +13,7 @@ module Reporter #:nodoc:
|
|
13
13
|
def text(step)
|
14
14
|
case step.text
|
15
15
|
when /^\=/
|
16
|
-
io.
|
16
|
+
io.print "#{step.text}".ansi(:bold)
|
17
17
|
else
|
18
18
|
txt = step.text.to_s.strip.tabto(2)
|
19
19
|
txt[0,1] = "*"
|
@@ -32,8 +32,7 @@ module Reporter #:nodoc:
|
|
32
32
|
msg << " # " + assertion.to_s
|
33
33
|
msg = msg.ansi(:magenta)
|
34
34
|
io.puts msg
|
35
|
-
|
36
|
-
io.puts "#{step.text}".ansi(:red)
|
35
|
+
io.print "#{step.text}".ansi(:red)
|
37
36
|
end
|
38
37
|
|
39
38
|
def error(step, exception)
|
@@ -44,8 +43,7 @@ module Reporter #:nodoc:
|
|
44
43
|
msg << " # " + clean_backtrace(exception.backtrace[0])
|
45
44
|
msg = msg.ansi(:magenta)
|
46
45
|
io.puts msg
|
47
|
-
|
48
|
-
io.puts "#{step.text}".ansi(:red)
|
46
|
+
io.print "#{step.text}".ansi(:red)
|
49
47
|
end
|
50
48
|
|
51
49
|
#def report(str)
|
@@ -10,10 +10,10 @@ module Reporter #:nodoc:
|
|
10
10
|
#
|
11
11
|
def text(section)
|
12
12
|
case section.text
|
13
|
-
when
|
14
|
-
io.
|
13
|
+
when /\A[=#]/
|
14
|
+
io.print "#{section.text}".ansi(:bold)
|
15
15
|
else
|
16
|
-
io.
|
16
|
+
io.print(section.text)
|
17
17
|
end
|
18
18
|
if !section.cont.empty?
|
19
19
|
section.cont.each do |c|
|
@@ -27,14 +27,14 @@ module Reporter #:nodoc:
|
|
27
27
|
|
28
28
|
#
|
29
29
|
def pass(step)
|
30
|
-
txt = step.text
|
31
|
-
io.print "#{txt}
|
30
|
+
txt = step.text #.rstrip.sub("\n",'')
|
31
|
+
io.print "#{txt}".ansi(:green)
|
32
32
|
end
|
33
33
|
|
34
34
|
#
|
35
35
|
def fail(step, error)
|
36
36
|
txt = step.text.rstrip.sub("\n",'')
|
37
|
-
tab = step.text.index(/\S/)
|
37
|
+
tab = step.text.index(/\S/)
|
38
38
|
io.print "#{txt}\n\n".ansi(:red)
|
39
39
|
msg = []
|
40
40
|
#msg << ANSI::Code.bold(ANSI::Code.red("FAIL: ")) + error.to_str
|
@@ -49,7 +49,7 @@ module Reporter #:nodoc:
|
|
49
49
|
def error(step, error)
|
50
50
|
raise error if $DEBUG
|
51
51
|
txt = step.text.rstrip.sub("\n",'')
|
52
|
-
tab = step.text.index(/\S/)
|
52
|
+
tab = step.text.index(/\S/)
|
53
53
|
io.print "#{txt}\n\n".ansi(:red)
|
54
54
|
msg = []
|
55
55
|
msg << "ERROR: #{error.class} ".ansi(:bold,:red) + error.to_str #.sub(/for QED::Context.*?$/,'')
|
data/lib/qed/scope.rb
CHANGED
@@ -7,110 +7,109 @@ module QED
|
|
7
7
|
class Scope < Module
|
8
8
|
|
9
9
|
#
|
10
|
-
def self.new(applique)
|
11
|
-
@
|
12
|
-
super(applique)
|
10
|
+
def self.new(applique, file)
|
11
|
+
@_applique = applique
|
12
|
+
super(applique, file)
|
13
13
|
end
|
14
14
|
|
15
15
|
#
|
16
16
|
def self.const_missing(name)
|
17
|
-
@
|
17
|
+
@_applique.const_get(name)
|
18
18
|
end
|
19
19
|
|
20
20
|
#
|
21
|
-
def initialize(applique)
|
21
|
+
def initialize(applique, file=nil)
|
22
22
|
super()
|
23
|
-
@
|
23
|
+
@_applique = applique
|
24
|
+
@_file = file
|
25
|
+
|
24
26
|
extend self
|
25
27
|
extend applique # TODO: extend or include applique or none ?
|
26
28
|
#extend DSLi
|
27
|
-
|
29
|
+
|
30
|
+
# TODO: custom extends?
|
31
|
+
|
32
|
+
__create_clean_binding_method__
|
28
33
|
end
|
29
34
|
|
30
|
-
# This
|
31
|
-
def
|
35
|
+
# This turns out to be the key to proper scoping.
|
36
|
+
def __create_clean_binding_method__
|
32
37
|
define_method(:__binding__) do
|
33
38
|
@__binding__ ||= binding
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
37
|
-
#
|
42
|
+
# Evaluate code in the context of the scope's special
|
43
|
+
# binding.
|
44
|
+
def eval(code)
|
45
|
+
super(code, __binding__)
|
46
|
+
end
|
38
47
|
|
39
|
-
|
40
|
-
|
41
|
-
|
48
|
+
# Define "when" advice.
|
49
|
+
def When(*patterns, &procedure)
|
50
|
+
@_applique.When(*patterns, &procedure)
|
51
|
+
end
|
42
52
|
|
43
|
-
|
44
|
-
|
45
|
-
|
53
|
+
# Define "before" advice.
|
54
|
+
def Before(type=:code, &procedure)
|
55
|
+
@_applique.Before(type, &procedure)
|
56
|
+
end
|
46
57
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
58
|
+
# Define "after" advice.
|
59
|
+
def After(type=:code, &procedure)
|
60
|
+
@_applique.After(type, &procedure)
|
61
|
+
end
|
51
62
|
|
52
|
-
|
53
|
-
def eval(code)
|
54
|
-
super(code, __binding__)
|
55
|
-
end
|
63
|
+
# TODO: Should Table and Data be extensions that can be loaded if desired?
|
56
64
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
+
# Use sample table to run steps. The table file will be
|
66
|
+
# looked for relative to the demo, failing that it will
|
67
|
+
# be looked for relative to the working directory.
|
68
|
+
#
|
69
|
+
# TODO: Cache data for speed ?
|
70
|
+
def Table(file=nil) #:yield:
|
71
|
+
if file
|
72
|
+
file = Dir.glob(File.join(File.dirname(@_file), file)).first || file
|
73
|
+
else
|
74
|
+
file = @_last_table
|
65
75
|
end
|
76
|
+
@_last_table = file
|
66
77
|
|
67
|
-
|
68
|
-
|
69
|
-
|
78
|
+
tbl = YAML.load(File.new(file))
|
79
|
+
tbl.each do |set|
|
80
|
+
yield(*set)
|
70
81
|
end
|
82
|
+
end
|
71
83
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
84
|
+
# Read a static data sample.
|
85
|
+
#
|
86
|
+
# TODO: Cache data for speed ?
|
87
|
+
def Data(file) #:yield:
|
88
|
+
#raise if File.directory?(file)
|
89
|
+
#if content
|
90
|
+
# FileUtils.mkdir_p(File.dirname(file))
|
91
|
+
# case File.extname(file)
|
92
|
+
# when '.yml', '.yaml'
|
93
|
+
# File.open(file, 'w'){ |f| f << content.call.to_yaml }
|
94
|
+
# else
|
95
|
+
# File.open(file, 'w'){ |f| f << content.call }
|
96
|
+
# end
|
97
|
+
#else
|
98
|
+
#raise LoadError, "no such fixture file -- #{fname}" unless File.exist?(fname)
|
99
|
+
file = Dir.glob(File.join(File.dirname(@_file), file)).first || file
|
100
|
+
case File.extname(file)
|
101
|
+
when '.yml', '.yaml'
|
102
|
+
data = YAML.load(File.new(file))
|
103
|
+
else
|
104
|
+
data = File.read(file)
|
81
105
|
end
|
82
|
-
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
# Read/Write a static data fixture.
|
87
|
-
#--
|
88
|
-
# TODO: Perhaps #Data would be best as some sort of Kernel extension.
|
89
|
-
#
|
90
|
-
# TODO: Look for files relative to script first?
|
91
|
-
#++
|
92
|
-
def Data(file, &content)
|
93
|
-
raise if File.directory?(file)
|
94
|
-
if content
|
95
|
-
FileUtils.mkdir_p(File.dirname(fname))
|
96
|
-
case File.extname(file)
|
97
|
-
when '.yml', '.yaml'
|
98
|
-
File.open(file, 'w'){ |f| f << content.call.to_yaml }
|
99
|
-
else
|
100
|
-
File.open(file, 'w'){ |f| f << content.call }
|
101
|
-
end
|
106
|
+
if block_given?
|
107
|
+
yield(data)
|
102
108
|
else
|
103
|
-
|
104
|
-
case File.extname(file)
|
105
|
-
when '.yml', '.yaml'
|
106
|
-
YAML.load(File.new(file))
|
107
|
-
else
|
108
|
-
File.read(file)
|
109
|
-
end
|
109
|
+
data
|
110
110
|
end
|
111
|
-
end
|
112
|
-
|
113
|
-
#end#module DSL
|
111
|
+
#end
|
112
|
+
end
|
114
113
|
|
115
114
|
end#class Scope
|
116
115
|
|
data/lib/qed/script.rb
CHANGED
@@ -23,7 +23,7 @@ module QED
|
|
23
23
|
def initialize(applique, file, scope=nil)
|
24
24
|
@applique = applique.dup # localize copy of applique
|
25
25
|
@file = file
|
26
|
-
@scope = scope || Scope.new(applique)
|
26
|
+
@scope = scope || Scope.new(applique, file)
|
27
27
|
@binding = @scope.__binding__
|
28
28
|
#@loadlist = []
|
29
29
|
#apply_environment
|
@@ -34,7 +34,7 @@ module QED
|
|
34
34
|
@binding #||= @scope.__binding__
|
35
35
|
end
|
36
36
|
|
37
|
-
#
|
37
|
+
# TODO: demo advice vs. applique advice
|
38
38
|
def advice
|
39
39
|
#@scope.__advice__
|
40
40
|
@applique.__advice__
|
data/lib/qed/session.rb
CHANGED
@@ -42,7 +42,7 @@ module QED
|
|
42
42
|
# QED.config
|
43
43
|
#end
|
44
44
|
|
45
|
-
# TODO: Ultimately use Plugin library
|
45
|
+
# TODO: Ultimately use Plugin library to support custom reporters?
|
46
46
|
def require_reporters
|
47
47
|
Dir[File.dirname(__FILE__) + '/reporter/*'].each do |file|
|
48
48
|
require file
|
@@ -94,7 +94,7 @@ module QED
|
|
94
94
|
# end
|
95
95
|
#end
|
96
96
|
|
97
|
-
# TODO: associate scripts to there applique
|
97
|
+
# TODO: associate scripts to there applique ?
|
98
98
|
def create_applique
|
99
99
|
applique = Applique.new
|
100
100
|
#eval "include QED::DomainLanguage", TOPLEVEL_BINDING
|
data/script/test
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 2.2.
|
9
|
+
- 2
|
10
|
+
version: 2.2.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Thomas Sawyer <transfire@gmail.com>
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-06-
|
18
|
+
date: 2010-06-24 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -89,7 +89,7 @@ files:
|
|
89
89
|
- demo/01_demos.rdoc
|
90
90
|
- demo/02_advice.rdoc
|
91
91
|
- demo/03_helpers.rdoc
|
92
|
-
- demo/
|
92
|
+
- demo/04_samples.rdoc
|
93
93
|
- demo/05_quote.rdoc
|
94
94
|
- demo/07_toplevel.rdoc
|
95
95
|
- demo/08_cross_script.rdoc
|
@@ -101,14 +101,11 @@ files:
|
|
101
101
|
- demo/applique/markup.rb
|
102
102
|
- demo/applique/quote.rb
|
103
103
|
- demo/applique/toplevel.rb
|
104
|
-
- demo/fixtures/data.txt
|
105
|
-
- demo/fixtures/table.yml
|
106
104
|
- demo/helpers/advice.rb
|
107
105
|
- demo/helpers/sample.rb
|
108
106
|
- demo/helpers/toplevel.rb
|
109
|
-
-
|
110
|
-
-
|
111
|
-
- eg/website.rdoc
|
107
|
+
- demo/samples/data.txt
|
108
|
+
- demo/samples/table.yml
|
112
109
|
- lib/qed/advice.rb
|
113
110
|
- lib/qed/applique.rb
|
114
111
|
- lib/qed/command.rb
|
@@ -133,13 +130,13 @@ files:
|
|
133
130
|
- lib/qedoc/document.rb
|
134
131
|
- script/qedoc
|
135
132
|
- script/test
|
133
|
+
- test/integration/topcode.rdoc
|
136
134
|
- PROFILE
|
135
|
+
- Diary.rdoc
|
137
136
|
- README.rdoc
|
138
|
-
- HISTORY
|
139
137
|
- REQUIRE
|
140
|
-
- ROADMAP
|
141
|
-
- DIARY.rdoc
|
142
138
|
- VERSION
|
139
|
+
- History.rdoc
|
143
140
|
- COPYING
|
144
141
|
has_rdoc: true
|
145
142
|
homepage: http://proutils.github.com/qed
|
data/ROADMAP
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
= ROADMAP
|
2
|
-
|
3
|
-
== 2.2.0
|
4
|
-
|
5
|
-
* Simplify code. I do not think we need both an Evaluator and Scope classes.
|
6
|
-
|
7
|
-
== 2.3.0
|
8
|
-
|
9
|
-
* Add a distributed evaluator, probably using Drb. This will allow the code
|
10
|
-
portion of documents to run in an isolated process. It will also make it possible
|
11
|
-
(in the long run) to run tests in parallel acrosss a cluster.
|
12
|
-
|
data/demo/04_fixtures.rdoc
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
= Fixtures
|
2
|
-
|
3
|
-
== Flat-file Data
|
4
|
-
|
5
|
-
When creating testable demonstrations, there are times when sizable
|
6
|
-
chunks of data are needed. It is convenient to store such data in
|
7
|
-
separate files. The +Data+ method makes is easy to load such files.
|
8
|
-
|
9
|
-
Data('demo/fixtures/data.txt').assert =~ /dolor/
|
10
|
-
|
11
|
-
All files are found relative to the location of current document.
|
12
|
-
|
13
|
-
== Tabular Data
|
14
|
-
|
15
|
-
The +Table+ method is similar to the +Data+ method except that it
|
16
|
-
expects a YAML file, and it can take a block to iterate the data over.
|
17
|
-
This makes it easy to test tables of examples.
|
18
|
-
|
19
|
-
The arity of the table block corresponds to the number of columns in
|
20
|
-
each row of the table. Each row is assigned in turn and run through
|
21
|
-
the coded step. Consider the following example:
|
22
|
-
|
23
|
-
Every row in the {table.yml table}[table.yml] will be assigned to
|
24
|
-
the block parameters and run through the subsequent assertion.
|
25
|
-
|
26
|
-
Table 'demo/fixtures/table.yml' do |x, y|
|
27
|
-
x.upcase.assert == y
|
28
|
-
end
|
29
|
-
|
data/eg/hello_world.rdoc
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
= Hello World
|
2
|
-
|
3
|
-
Did you know that famous `Hello World` moniker is
|
4
|
-
eleven characters long?
|
5
|
-
|
6
|
-
"Hello World".size.assert == 11
|
7
|
-
|
8
|
-
To pass a piece of literal text on with a description
|
9
|
-
we simply need to end it with a ...
|
10
|
-
|
11
|
-
Now this text will appear verbatim.
|
12
|
-
In the applique arguments.
|
13
|
-
|
14
|
-
That's all.
|
15
|
-
|
data/eg/view_error.rdoc
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
= Examples of Failure
|
2
|
-
|
3
|
-
This document is here simply to demonstrate what
|
4
|
-
a failed and error raising code steps looks like.
|
5
|
-
|
6
|
-
When run with the -v (verbatim) option, for instance, +qed+
|
7
|
-
will highlight the following sections in red and give a brief
|
8
|
-
error message.
|
9
|
-
|
10
|
-
== Failure
|
11
|
-
|
12
|
-
This step demonstrates a failed assertion.
|
13
|
-
|
14
|
-
1.assert == 2
|
15
|
-
|
16
|
-
== Error
|
17
|
-
|
18
|
-
This step demonstrates a raised error.
|
19
|
-
|
20
|
-
raise "Just because"
|
21
|
-
|