qed 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +15 -0
- data/LICENSE +344 -0
- data/MANIFEST +56 -0
- data/README.rdoc +97 -0
- data/bin/qed +150 -0
- data/bin/qedoc +52 -0
- data/demo/01_spec.qed +143 -0
- data/demo/01_spec.yaml +4 -0
- data/demo/qed_helper.rb +1 -0
- data/doc/qedoc/index.html +355 -0
- data/doc/qedoc/jquery.js +19 -0
- data/lib/qed.rb +5 -0
- data/lib/qed/assertion.rb +23 -0
- data/lib/qed/document.rb +188 -0
- data/lib/qed/document/jquery.js +19 -0
- data/lib/qed/document/markup.rb +53 -0
- data/lib/qed/document/template.rhtml +190 -0
- data/lib/qed/doubles/mock.rb +94 -0
- data/lib/qed/doubles/spy.rb +191 -0
- data/lib/qed/doubles/stub.rb +94 -0
- data/lib/qed/expectation.rb +60 -0
- data/lib/qed/grammar/assert.rb +104 -0
- data/lib/qed/grammar/expect.rb +121 -0
- data/lib/qed/grammar/legacy/assert.rb +291 -0
- data/lib/qed/grammar/should.rb +52 -0
- data/lib/qed/reporter/base.rb +101 -0
- data/lib/qed/reporter/dotprogress.rb +63 -0
- data/lib/qed/reporter/summary.rb +67 -0
- data/lib/qed/reporter/verbatim.rb +90 -0
- data/lib/qed/runner.rb +148 -0
- data/lib/qed/script.rb +179 -0
- data/lib/qed/utilities/extract.rb +137 -0
- data/lib/qed/utilities/monitor.rb +23 -0
- data/meta/authors +1 -0
- data/meta/created +1 -0
- data/meta/description +2 -0
- data/meta/homepage +1 -0
- data/meta/package +1 -0
- data/meta/project +1 -0
- data/meta/requires +1 -0
- data/meta/ruby +2 -0
- data/meta/summary +1 -0
- data/meta/title +1 -0
- data/meta/version +1 -0
- metadata +115 -0
data/bin/qed
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'qed'
|
4
|
+
require 'getoptlong'
|
5
|
+
|
6
|
+
module QED
|
7
|
+
|
8
|
+
# = QED Commandline Tool
|
9
|
+
#
|
10
|
+
class Command
|
11
|
+
def self.execute
|
12
|
+
new.execute
|
13
|
+
end
|
14
|
+
|
15
|
+
attr :reporter
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@reporter = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def opts
|
22
|
+
@opts ||= GetoptLong.new(
|
23
|
+
[ '--version', GetoptLong::NO_ARGUMENT ],
|
24
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
25
|
+
[ '--debug', '-D', GetoptLong::NO_ARGUMENT ],
|
26
|
+
[ '--verbose', '-V', GetoptLong::NO_ARGUMENT ],
|
27
|
+
[ '--verbatim', '-v', GetoptLong::NO_ARGUMENT ],
|
28
|
+
[ '--summary', '-s', GetoptLong::NO_ARGUMENT ],
|
29
|
+
[ '--script', GetoptLong::NO_ARGUMENT ],
|
30
|
+
[ '--loadpath', '-I', GetoptLong::REQUIRED_ARGUMENT ]
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
def parse_options
|
36
|
+
opts.each do |opt, arg|
|
37
|
+
case opt
|
38
|
+
when '--help'
|
39
|
+
puts HELP
|
40
|
+
exit
|
41
|
+
when '--debug'
|
42
|
+
$RESPECT_DEBUG = true
|
43
|
+
when '--verbose'
|
44
|
+
$VERBOSE = true
|
45
|
+
when '--verbatim'
|
46
|
+
@reporter = :verbatim
|
47
|
+
when '--summary'
|
48
|
+
@reporter = :summary
|
49
|
+
when '--script'
|
50
|
+
@reporter = :script # psuedo-reporter
|
51
|
+
when '--loadpath'
|
52
|
+
libs = arg.split(/[:;]/).map{ |dir| File.expand_path(dir) }
|
53
|
+
libs.each{|dir| $LOAD_PATH.unshift(dir)}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
#def load_rc
|
60
|
+
# if rcfile = Dir['.config/qed{,rc}{,.rb}'].first
|
61
|
+
# load(rcfile)
|
62
|
+
# end
|
63
|
+
#end
|
64
|
+
|
65
|
+
# TODO: Better way to load helpers?
|
66
|
+
#
|
67
|
+
def load_helpers
|
68
|
+
dirs = spec_files.map{ |file| File.join(Dir.pwd, File.dirname(file)) }
|
69
|
+
dirs = dirs.select{ |dir| File.directory?(dir) }
|
70
|
+
dirs.each do |dir|
|
71
|
+
while dir != '/' do
|
72
|
+
helper = File.join(dir, 'qed_helper.rb')
|
73
|
+
load(helper) if File.exist?(helper)
|
74
|
+
break if Dir.pwd == dir
|
75
|
+
dir = File.dirname(dir)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
def specs
|
82
|
+
spec_files
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
def spec_files
|
87
|
+
files = ARGV.map do |pattern|
|
88
|
+
Dir[pattern]
|
89
|
+
end.flatten.uniq
|
90
|
+
|
91
|
+
files = files.map do |file|
|
92
|
+
File.directory?(file) ? Dir[File.join(file,'**','*')] : file
|
93
|
+
end.flatten.uniq
|
94
|
+
|
95
|
+
files = files.reject do |file|
|
96
|
+
%w{.yml .yaml .rb}.include?(File.extname(file))
|
97
|
+
end
|
98
|
+
|
99
|
+
files
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
def output
|
104
|
+
case reporter
|
105
|
+
when :verbatim
|
106
|
+
Reporter::Verbatim.new
|
107
|
+
when :summary
|
108
|
+
Reporter::Summary.new
|
109
|
+
else
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
def runner
|
116
|
+
Runner.new(specs, output)
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
def execute
|
121
|
+
parse_options
|
122
|
+
#load_rc
|
123
|
+
load_helpers
|
124
|
+
case reporter
|
125
|
+
when :script
|
126
|
+
specs.each do |spec|
|
127
|
+
puts spec.to_script
|
128
|
+
end
|
129
|
+
else
|
130
|
+
runner.check
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
HELP = <<-END
|
135
|
+
qed [--options] [spec/tests...]
|
136
|
+
|
137
|
+
Options:
|
138
|
+
-v --verbatim use verbatim reporter
|
139
|
+
-s --summary use summary reporter
|
140
|
+
-V --verbose extra verbose output
|
141
|
+
-D --debug spec/tests will exit on error
|
142
|
+
-h --help show this help information
|
143
|
+
--version show quarry version
|
144
|
+
END
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
QED::Command.execute
|
150
|
+
|
data/bin/qedoc
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'qed/document'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
|
8
|
+
usage = OptionParser.new do |usage|
|
9
|
+
|
10
|
+
usage.banner = "Usage: qedoc [OPTIONS] <QEDFile1> [ <QEDFile2> ... ]"
|
11
|
+
|
12
|
+
usage.on("-o", "--output [DIR]", "Output directory") do |dir|
|
13
|
+
options[:output]= dir
|
14
|
+
end
|
15
|
+
|
16
|
+
usage.on("-t", "--title [TITLE]", "Title of Document") do |title|
|
17
|
+
options[:title]= title
|
18
|
+
end
|
19
|
+
|
20
|
+
usage.on("--css [URI]", "Specify a URI for a CSS file add to HTML header.") do |uri|
|
21
|
+
options[:css] = uri
|
22
|
+
end
|
23
|
+
|
24
|
+
usage.on("--dryrun", "") do
|
25
|
+
options[:dryrun] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
usage.on("-q", "--quiet", "") do
|
29
|
+
options[:quiet] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
usage.on_tail("-h", "--help", "display this help message") do
|
33
|
+
puts usage
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
usage.parse!
|
40
|
+
|
41
|
+
options[:paths] = ARGV.dup
|
42
|
+
|
43
|
+
#opts[:output] = cli.options[:file]
|
44
|
+
#opts[:dryrun] = cli.options[:dryrun]
|
45
|
+
#opts[:quiet] = cli.options[:quiet]
|
46
|
+
#opts[:css] = cli.options[:css]
|
47
|
+
#opts[:title] = cli.options[:title]
|
48
|
+
|
49
|
+
doc = QED::Document.new(options)
|
50
|
+
|
51
|
+
doc.generate
|
52
|
+
|
data/demo/01_spec.qed
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
= Standard Sections
|
2
|
+
|
3
|
+
QED demos are light-weight specification documents, suitable
|
4
|
+
for Interface-driven Development. The documents are divided up into
|
5
|
+
clauses separated by blank lines. Clauses that are flush to the
|
6
|
+
left margin are always explanation or comment clauses. Indented
|
7
|
+
clauses are always executable code.
|
8
|
+
|
9
|
+
Each code section is executed in order of appearance, within a
|
10
|
+
rescue wrapper that captures any failures or errors. If neither
|
11
|
+
a failure or error occur then the code gets a "pass".
|
12
|
+
|
13
|
+
For example, the following passes:
|
14
|
+
|
15
|
+
(2 + 2).assert == 4
|
16
|
+
|
17
|
+
While the following would "fail", as indicated by the raising of
|
18
|
+
an Assertion error:
|
19
|
+
|
20
|
+
expect Assertion do
|
21
|
+
(2 + 2).assert == 5
|
22
|
+
end
|
23
|
+
|
24
|
+
And this would have raised a NameError:
|
25
|
+
|
26
|
+
expect NameError do
|
27
|
+
nobody_knows_method
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
= Neutral Code
|
32
|
+
|
33
|
+
There is no means of specifying that a code clause is neutral code,
|
34
|
+
i.e. that it should be executed but not tested. So far this
|
35
|
+
such a feature has proven to be a YAGNI. Yet we may add such a
|
36
|
+
feature in the future if it is ultimately deemed necessary.
|
37
|
+
|
38
|
+
|
39
|
+
= Defining Custom Assertions
|
40
|
+
|
41
|
+
The context in which the QED code is run is a self-extended module, thus
|
42
|
+
reusable macros can be created simply by defining a method.
|
43
|
+
|
44
|
+
def assert_integer(x)
|
45
|
+
x.assert.is_a? Integer
|
46
|
+
end
|
47
|
+
|
48
|
+
Now lets try out our new macro definition.
|
49
|
+
|
50
|
+
assert_integer(4)
|
51
|
+
|
52
|
+
Let's prove that it can also fail:
|
53
|
+
|
54
|
+
expect Assertion do
|
55
|
+
assert_integer("IV")
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
= Helper File
|
60
|
+
|
61
|
+
If you create a file called `qed_helper.rb` located in the directory with the
|
62
|
+
QED documents you are running via the `qed` command, it will be loaded first.
|
63
|
+
You can use that to load optional AE features, or define your own specialized
|
64
|
+
assertion methods.
|
65
|
+
|
66
|
+
|
67
|
+
= Before and After Clauses
|
68
|
+
|
69
|
+
QED supports *before* and *after* clauses in a specification
|
70
|
+
through the use of before and after code blocks. Before and after
|
71
|
+
clauses are executed at the beginning and at the end of each
|
72
|
+
subsequent step.
|
73
|
+
|
74
|
+
We use a *before* clause if we want to setup some code at the
|
75
|
+
start of each step.
|
76
|
+
|
77
|
+
a, z = nil, nil
|
78
|
+
|
79
|
+
before do
|
80
|
+
a = "BEFORE"
|
81
|
+
end
|
82
|
+
|
83
|
+
And an *after* clause to tear down objects after a step.
|
84
|
+
|
85
|
+
after do
|
86
|
+
z = "AFTER"
|
87
|
+
end
|
88
|
+
|
89
|
+
Notice we assigned +a+ and +z+ before the block. This was to ensure
|
90
|
+
their visibility in the scope later. Now, lets verify this the *before*
|
91
|
+
and *after* clause work.
|
92
|
+
|
93
|
+
a.assert == "BEFORE"
|
94
|
+
|
95
|
+
a = "A"
|
96
|
+
z = "Z"
|
97
|
+
|
98
|
+
And now.
|
99
|
+
|
100
|
+
z.assert == "AFTER"
|
101
|
+
|
102
|
+
There can only be one before or after clause at a time. So if we
|
103
|
+
define a new *before* or *after* clause later in the document,
|
104
|
+
it will replace the current clause(s) in use.
|
105
|
+
|
106
|
+
As a demonstration of this:
|
107
|
+
|
108
|
+
before do
|
109
|
+
a = "BEFORE AGAIN"
|
110
|
+
end
|
111
|
+
|
112
|
+
We will see it is the case.
|
113
|
+
|
114
|
+
a.assert == "BEFORE AGAIN"
|
115
|
+
|
116
|
+
Only use *before* and *after* clauses when necessary --specifications
|
117
|
+
are generally more readable without them. Indeed, some developers
|
118
|
+
make a policy of avoiding them altogether. YMMV.
|
119
|
+
|
120
|
+
|
121
|
+
= Tabular Steps
|
122
|
+
|
123
|
+
Finally we will demonstrate a tabular step. +table+ method is used
|
124
|
+
for this. We supply a file name to the method telling QED where to
|
125
|
+
find the table data to be used in the test. All table files are looked
|
126
|
+
for relative to the location of the document. If no name is given the
|
127
|
+
'<doc-name>.yaml' is assumed.
|
128
|
+
|
129
|
+
The arity of the table block determines the number of columns each row
|
130
|
+
in the table should have. Each row is assigned in turn and run
|
131
|
+
through the coded step. Consider the following example:
|
132
|
+
|
133
|
+
Every row in 'table.yaml' will be assigned to the block parameters
|
134
|
+
and run through the following assertion.
|
135
|
+
|
136
|
+
table do |x,y|
|
137
|
+
x.upcase.assert == y
|
138
|
+
end
|
139
|
+
|
140
|
+
This concludes the basic overview of QED's specification system, which
|
141
|
+
is itself a QED document. Yes, we eat our own dog food.
|
142
|
+
|
143
|
+
Q.E.D.
|
data/demo/01_spec.yaml
ADDED
data/demo/qed_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ae/expect'
|
@@ -0,0 +1,355 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>QED Overview</title>
|
4
|
+
|
5
|
+
<style>
|
6
|
+
#container{ margin: 0 auto; width: 800px; }
|
7
|
+
|
8
|
+
/* Debug borders */
|
9
|
+
/* p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 { border: 1px solid red; } */
|
10
|
+
|
11
|
+
body { font-size: 14px; line-height: 20px; margin: 1em 5% 1em 5%; font-family: Verdana, Arial, Helvetica, sans-serif; }
|
12
|
+
a { color: #336; text-decoration: underline; }
|
13
|
+
a:visited { color: #334; }
|
14
|
+
em { font-style: italic; }
|
15
|
+
strong { font-weight: bold; }
|
16
|
+
tt { color: navy; }
|
17
|
+
|
18
|
+
h1, h2, h3, h4, h5, h6 { color: #223; margin-top: 1.2em; margin-bottom: 0.5em; line-height: 1.3; }
|
19
|
+
h1 { border-bottom: 2px solid silver; }
|
20
|
+
h2 { border-bottom: 2px solid silver; padding-top: 0.5em; }
|
21
|
+
|
22
|
+
hr { border: 1px solid silver; }
|
23
|
+
|
24
|
+
p { color: #222; text-align: justify; margin-top: 0.5em; margin-bottom: 0.5em; line-height: 1.4em; }
|
25
|
+
|
26
|
+
pre { padding: 10; margin: 0; font-family: monospace; font-size: 0.9em; }
|
27
|
+
pre.pass { color: green; }
|
28
|
+
pre.fail { color: red; }
|
29
|
+
pre.error { color: red; font-weight: bold; }
|
30
|
+
|
31
|
+
span#author { color: #527bbd; font-weight: bold; font-size: 1.1em; }
|
32
|
+
span#email { }
|
33
|
+
span#revision { }
|
34
|
+
|
35
|
+
div#footer { font-size: small; border-top: 2px solid silver; padding-top: 0.5em; margin-top: 4.0em; }
|
36
|
+
div#footer-text { float: left; padding-bottom: 0.5em; }
|
37
|
+
div#footer-badges { float: right; padding-bottom: 0.5em; }
|
38
|
+
|
39
|
+
/* Block element content. */
|
40
|
+
div.content { padding: 0; }
|
41
|
+
|
42
|
+
/* Block element titles. */
|
43
|
+
h1.title { font-weight: bold; text-align: left; font-size: 3em; margin-top: 1.0em; margin-bottom: 0.5em; }
|
44
|
+
|
45
|
+
/* Block element titles. */
|
46
|
+
div.title, caption.title { font-weight: bold; text-align: left; margin-top: 1.0em; margin-bottom: 0.5em; }
|
47
|
+
div.title + * { margin-top: 0; }
|
48
|
+
td div.title:first-child { margin-top: 0.0em; }
|
49
|
+
div.content div.title:first-child { margin-top: 0.0em; }
|
50
|
+
div.content + div.title { margin-top: 0.0em; }
|
51
|
+
div.sidebarblock > div.content { background: #ffffee; border: 1px solid silver; padding: 0.5em; }
|
52
|
+
|
53
|
+
img { border-style: none; }
|
54
|
+
|
55
|
+
dl { margin-top: 0.8em; margin-bottom: 0.8em; }
|
56
|
+
dt { margin-top: 0.5em; margin-bottom: 0; font-style: italic; }
|
57
|
+
dd > *:first-child { margin-top: 0; }
|
58
|
+
ul, ol { list-style-position: outside; }
|
59
|
+
|
60
|
+
thead { font-weight: bold; }
|
61
|
+
tfoot { font-weight: bold; }
|
62
|
+
</style>
|
63
|
+
|
64
|
+
<!-- TODO: only include if these files exists -->
|
65
|
+
<link href="../assets/styles/spec.css" type="text/css" rel="stylesheet">
|
66
|
+
<!-- spec.css might be a problem with clobber -->
|
67
|
+
<link href="spec.css" type="text/css" rel="stylesheet">
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
<!-- JQuery is needed -->
|
72
|
+
<script src="jquery.js" type="text/javascript" language="javascript"></script>
|
73
|
+
|
74
|
+
</head>
|
75
|
+
|
76
|
+
<body>
|
77
|
+
|
78
|
+
<!-- Side Table of Contents -->
|
79
|
+
<div id="sidebar" style="position: fixed; top: 10; right: 10; background: white;">
|
80
|
+
<a href="javascript: toc_toggle();">
|
81
|
+
<img src="img/icon/book.jpg" height="30px;" style="border: none;" alt="TOC" align="right"/>
|
82
|
+
</a>
|
83
|
+
|
84
|
+
<div id="toc_side" class="toc">
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
|
88
|
+
<div id="container">
|
89
|
+
<div id="header">
|
90
|
+
<img src="img/icon/book.jpg" align="left" style="padding-right: 10px;" alt=""/>
|
91
|
+
|
92
|
+
<h1 class="title">QED Overview</h1>
|
93
|
+
|
94
|
+
<h1>Table of Contents</h1>
|
95
|
+
|
96
|
+
<div class="toc">
|
97
|
+
</div>
|
98
|
+
</div>
|
99
|
+
|
100
|
+
<div id="content">
|
101
|
+
<h1>Standard Sections</h1>
|
102
|
+
<p>
|
103
|
+
QED demos are light-weight specification documents, suitable for
|
104
|
+
Interface-driven Development. The documents are divided up into clauses
|
105
|
+
separated by blank lines. Clauses that are flush to the left margin are
|
106
|
+
always explanation or comment clauses. Indented clauses are always
|
107
|
+
executable code.
|
108
|
+
</p>
|
109
|
+
<p>
|
110
|
+
Each code section is executed in order of appearance, within a rescue
|
111
|
+
wrapper that captures any failures or errors. If neither a failure or error
|
112
|
+
occur then the code gets a "pass".
|
113
|
+
</p>
|
114
|
+
<p>
|
115
|
+
For example, the following passes:
|
116
|
+
</p>
|
117
|
+
<pre>
|
118
|
+
(2 + 2).assert == 4
|
119
|
+
</pre>
|
120
|
+
<p>
|
121
|
+
While the following would "fail", as indicated by the raising of
|
122
|
+
an Assertion error:
|
123
|
+
</p>
|
124
|
+
<pre>
|
125
|
+
Assertion.assert.raised? do
|
126
|
+
(2 + 2).assert == 5
|
127
|
+
end
|
128
|
+
|
129
|
+
#expect Assertion do
|
130
|
+
# (2 + 2).assert == 5
|
131
|
+
#end
|
132
|
+
</pre>
|
133
|
+
<p>
|
134
|
+
And this would have raised a NameError:
|
135
|
+
</p>
|
136
|
+
<pre>
|
137
|
+
expect NameError do
|
138
|
+
nobody_knows_method
|
139
|
+
end
|
140
|
+
</pre>
|
141
|
+
<h1>Neutral Code</h1>
|
142
|
+
<p>
|
143
|
+
There is no means of specifying that a code clause is neutral code, i.e.
|
144
|
+
that it should be executed but not tested. So far this such a feature has
|
145
|
+
proven to be a YAGNI. Yet we may add such a feature in the future if it is
|
146
|
+
ultimately deemed necessary.
|
147
|
+
</p>
|
148
|
+
<h1>Defining Macros</h1>
|
149
|
+
<p>
|
150
|
+
The context in which the QED code is run is a self-extended module, thus
|
151
|
+
reusable macros can be created simply by defining a method.
|
152
|
+
</p>
|
153
|
+
<pre>
|
154
|
+
def assert_integer(x)
|
155
|
+
x.assert.is_a? Integer
|
156
|
+
end
|
157
|
+
</pre>
|
158
|
+
<p>
|
159
|
+
Now lets try out our new macro definition.
|
160
|
+
</p>
|
161
|
+
<pre>
|
162
|
+
assert_integer(4)
|
163
|
+
</pre>
|
164
|
+
<p>
|
165
|
+
Let’s prove that it can also fail:
|
166
|
+
</p>
|
167
|
+
<pre>
|
168
|
+
expect Assertion do
|
169
|
+
assert_integer("IV")
|
170
|
+
end
|
171
|
+
</pre>
|
172
|
+
<h1>Before and After Clauses</h1>
|
173
|
+
<p>
|
174
|
+
QED supports <b>before</b> and <b>after</b> clauses in a specification
|
175
|
+
through the use of before and after code blocks. Before and after clauses
|
176
|
+
are executed at the beginning and at the end of each subsequent step.
|
177
|
+
</p>
|
178
|
+
<p>
|
179
|
+
We use a <b>before</b> clause if we want to setup some code at the start of
|
180
|
+
each step.
|
181
|
+
</p>
|
182
|
+
<pre>
|
183
|
+
a, z = nil, nil
|
184
|
+
|
185
|
+
before do
|
186
|
+
a = "BEFORE"
|
187
|
+
end
|
188
|
+
</pre>
|
189
|
+
<p>
|
190
|
+
And an <b>after</b> clause to tear down objects after a step.
|
191
|
+
</p>
|
192
|
+
<pre>
|
193
|
+
after do
|
194
|
+
z = "AFTER"
|
195
|
+
end
|
196
|
+
</pre>
|
197
|
+
<p>
|
198
|
+
Notice we assigned <tt>a</tt> and <tt>z</tt> before the block. This was to
|
199
|
+
ensure their visibility in the scope later. Now, lets verify this the
|
200
|
+
<b>before</b> and <b>after</b> clause work.
|
201
|
+
</p>
|
202
|
+
<pre>
|
203
|
+
a.assert == "BEFORE"
|
204
|
+
|
205
|
+
a = "A"
|
206
|
+
z = "Z"
|
207
|
+
</pre>
|
208
|
+
<p>
|
209
|
+
And now.
|
210
|
+
</p>
|
211
|
+
<pre>
|
212
|
+
z.assert == "AFTER"
|
213
|
+
</pre>
|
214
|
+
<p>
|
215
|
+
There can only be one before or after clause at a time. So if we define a
|
216
|
+
new <b>before</b> or <b>after</b> clause later in the document, it will
|
217
|
+
replace the current clause(s) in use.
|
218
|
+
</p>
|
219
|
+
<p>
|
220
|
+
As a demonstration of this:
|
221
|
+
</p>
|
222
|
+
<pre>
|
223
|
+
before do
|
224
|
+
a = "BEFORE AGAIN"
|
225
|
+
end
|
226
|
+
</pre>
|
227
|
+
<p>
|
228
|
+
We will see it is the case.
|
229
|
+
</p>
|
230
|
+
<pre>
|
231
|
+
a.assert == "BEFORE AGAIN"
|
232
|
+
</pre>
|
233
|
+
<p>
|
234
|
+
Only use <b>before</b> and <b>after</b> clauses when necessary
|
235
|
+
—specifications are generally more readable without them. Indeed,
|
236
|
+
some developers make a policy of avoiding them altogether. YMMV.
|
237
|
+
</p>
|
238
|
+
<h1>Tabular Steps</h1>
|
239
|
+
<p>
|
240
|
+
Finally we will demonstrate a tabular step. <tt>table</tt> method is used
|
241
|
+
for this. We supply a file name to the method telling QED where to find the
|
242
|
+
table data to be used in the test. All table files are looked for relative
|
243
|
+
to the location of the document. If no name is given the
|
244
|
+
’<doc-name>.yaml’ is assumed.
|
245
|
+
</p>
|
246
|
+
<p>
|
247
|
+
The arity of the table block determines the number of columns each row in
|
248
|
+
the table should have. Each row is assigned in turn and run through the
|
249
|
+
coded step. Consider the following example:
|
250
|
+
</p>
|
251
|
+
<p>
|
252
|
+
Every row in ‘table.yaml’ will be assigned to the block
|
253
|
+
parameters and run through the following assertion.
|
254
|
+
</p>
|
255
|
+
<pre>
|
256
|
+
table do |x,y|
|
257
|
+
x.upcase.assert == y
|
258
|
+
end
|
259
|
+
</pre>
|
260
|
+
<p>
|
261
|
+
This concludes the basic overview of QED’s specification system,
|
262
|
+
which is itself a QED document. Yes, we eat our own dog food.
|
263
|
+
</p>
|
264
|
+
<p>
|
265
|
+
Q.E.D.
|
266
|
+
</p>
|
267
|
+
|
268
|
+
|
269
|
+
</div>
|
270
|
+
</div>
|
271
|
+
|
272
|
+
</body>
|
273
|
+
|
274
|
+
</html>
|
275
|
+
|
276
|
+
<script src="../assets/scripts/spec.js" type="text/javascript" language="javascript"></script>
|
277
|
+
|
278
|
+
<script type="text/javascript" language="javascript">
|
279
|
+
/*****************************************************************
|
280
|
+
* $.toc()
|
281
|
+
* by rebecca murphey
|
282
|
+
* rmurphey gmail com
|
283
|
+
*
|
284
|
+
* This function is called on its own and takes as an argument
|
285
|
+
* a list of selectors with which it will build a table of
|
286
|
+
* contents.
|
287
|
+
*
|
288
|
+
* The first selector will make up the top level of the TOC;
|
289
|
+
* the second selector will make up the second level of the TOC;
|
290
|
+
* etc.
|
291
|
+
*
|
292
|
+
* This function returns a div containing nested unordered lists;
|
293
|
+
* each list item is linked to an anchor tag added before the item
|
294
|
+
* on the page.
|
295
|
+
*
|
296
|
+
* usage: $.toc('h1,h2,h3').prependTo('body');
|
297
|
+
************************************************************************/
|
298
|
+
(function($) {
|
299
|
+
$.toc = function(tocList) {
|
300
|
+
$(tocList).addClass('jquery-toc');
|
301
|
+
var tocListArray = tocList.split(',');
|
302
|
+
$.each(tocListArray, function(i,v) { tocListArray[i] = $.trim(v); });
|
303
|
+
var $elements = $('.jquery-toc');
|
304
|
+
$('body').append('<div></div>');
|
305
|
+
var $toc = $('body div:last');
|
306
|
+
var lastLevel = 1;
|
307
|
+
$toc.append('<ul class="jquery-toc-1"></ul>');
|
308
|
+
$elements.each(function() {
|
309
|
+
var $e = $(this);
|
310
|
+
var text = $e.text();
|
311
|
+
var anchor = text.replace(/ /g,'-');
|
312
|
+
$e.before('<a name="' + anchor + '"></a>');
|
313
|
+
var level;
|
314
|
+
$.each(tocListArray, function(i,v) {
|
315
|
+
if (v.match(' ')) {
|
316
|
+
var vArray = v.split(' ');
|
317
|
+
var e = vArray[vArray.length - 1];
|
318
|
+
} else { e = v; }
|
319
|
+
if ($e.is(e)) { level = i+1; }
|
320
|
+
});
|
321
|
+
var className = 'jquery-toc-' + level;
|
322
|
+
var li = '<li><a href="#' + anchor + '">' + text + '</a></li>';
|
323
|
+
if (level == lastLevel) {
|
324
|
+
$('ul.' + className + ':last',$toc).append(li);
|
325
|
+
} else if (level > lastLevel) {
|
326
|
+
var parentLevel = level - 1;
|
327
|
+
var parentClassName = 'jquery-toc-' + parentLevel;
|
328
|
+
$('ul.' + parentClassName + ':last',$toc).
|
329
|
+
append('<ul class="' + className + '"></ul>');
|
330
|
+
$('ul.' + className + ':last',$toc).append(li);
|
331
|
+
} else if (level < lastLevel) {
|
332
|
+
$('ul.' + className + ':last',$toc).append(li);
|
333
|
+
}
|
334
|
+
lastLevel = level;
|
335
|
+
});
|
336
|
+
var $toc_ul = $('ul.jquery-toc-1',$toc);
|
337
|
+
$toc.remove();
|
338
|
+
return($toc_ul);
|
339
|
+
}
|
340
|
+
})(jQuery);
|
341
|
+
</script>
|
342
|
+
|
343
|
+
<script>
|
344
|
+
function toc_toggle() {
|
345
|
+
$('#toc_side').toggle();
|
346
|
+
$("pre").addClass("pass");
|
347
|
+
$("pre:contains('FAIL:')").addClass("fail");
|
348
|
+
$("pre:contains('ERROR:')").addClass("error");
|
349
|
+
};
|
350
|
+
|
351
|
+
$.toc('#content h1,h2,h3,h4').appendTo('.toc');
|
352
|
+
|
353
|
+
toc_toggle();
|
354
|
+
</script>
|
355
|
+
|