qed 1.1.0 → 1.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/HISTORY +40 -2
- data/MANIFEST +7 -12
- data/README.rdoc +30 -23
- data/bin/qed +1 -147
- data/demo/01_spec.qed +82 -53
- data/demo/data.txt +1 -0
- data/demo/{qed_helper.rb → helper.rb} +1 -0
- data/demo/table.yml +5 -0
- data/doc/qedoc/index.html +98 -53
- data/lib/qed.rb +1 -1
- data/lib/qed/command.rb +166 -0
- data/lib/qed/document.rb +43 -13
- data/lib/qed/reporter/base.rb +12 -5
- data/lib/qed/reporter/dotprogress.rb +1 -1
- data/lib/qed/reporter/summary.rb +2 -1
- data/lib/qed/reporter/verbatim.rb +11 -12
- data/lib/qed/script.rb +140 -33
- data/meta/description +2 -2
- data/meta/homepage +1 -1
- data/meta/{package → name} +0 -0
- data/meta/repository +1 -0
- data/meta/{project → suite} +0 -0
- data/meta/version +1 -1
- metadata +15 -13
- data/demo/01_spec.yaml +0 -4
data/HISTORY
CHANGED
@@ -1,6 +1,43 @@
|
|
1
1
|
= RELEASE HISTORY
|
2
2
|
|
3
|
-
== 1.
|
3
|
+
== 1.2 / 2009-12-07
|
4
|
+
|
5
|
+
This release adds a significant new feature, Comment Matchers.
|
6
|
+
These work like Cucumber allowing for background code to
|
7
|
+
be run when matching comments occur --a much better solution
|
8
|
+
for setup and teardown.
|
9
|
+
|
10
|
+
Changes:
|
11
|
+
|
12
|
+
* 2 Major Enhancements
|
13
|
+
|
14
|
+
* Added command matchers via #When method.
|
15
|
+
* All QED methods are now capitalized.
|
16
|
+
|
17
|
+
* 2 Minor Enhancements
|
18
|
+
|
19
|
+
* Use OptionParser for qed exectuable.
|
20
|
+
* Verbatim reporter is literally verbatim.
|
21
|
+
|
22
|
+
|
23
|
+
== 1.1 / 2009-09-05
|
24
|
+
|
25
|
+
This release
|
26
|
+
|
27
|
+
Changes:
|
28
|
+
|
29
|
+
* 2 Major Enhancements
|
30
|
+
|
31
|
+
* Helpers are provided by bottom code.
|
32
|
+
* Added Markdown header support.
|
33
|
+
|
34
|
+
* 2 Minor Enhancements
|
35
|
+
|
36
|
+
* Use Ansi project for color output.
|
37
|
+
* Use latest RDoc version.
|
38
|
+
|
39
|
+
|
40
|
+
== 1.0 / 2009-06-30
|
4
41
|
|
5
42
|
QED has found itself. It took some time to really figure out
|
6
43
|
what this project "was" and how it should best be utilized.
|
@@ -9,7 +46,8 @@ perpective.
|
|
9
46
|
|
10
47
|
Changes:
|
11
48
|
|
12
|
-
*
|
49
|
+
* 2 Major Enhancement
|
13
50
|
|
14
51
|
* Partial rewrite of a project that was once called "Quarry".
|
52
|
+
* Now uese AE for assertions.
|
15
53
|
|
data/MANIFEST
CHANGED
@@ -1,23 +1,18 @@
|
|
1
1
|
#!mast bin demo doc/qedoc lib meta [A-Z]*
|
2
|
-
bin
|
3
2
|
bin/qed
|
4
3
|
bin/qedoc
|
5
|
-
demo
|
6
4
|
demo/01_spec.qed
|
7
|
-
demo/
|
8
|
-
demo/
|
9
|
-
|
5
|
+
demo/data.txt
|
6
|
+
demo/helper.rb
|
7
|
+
demo/table.yml
|
10
8
|
doc/qedoc/index.html
|
11
9
|
doc/qedoc/jquery.js
|
12
|
-
lib
|
13
|
-
lib/qed
|
14
|
-
lib/qed/document
|
10
|
+
lib/qed/command.rb
|
15
11
|
lib/qed/document/jquery.js
|
16
12
|
lib/qed/document/markup.rb
|
17
13
|
lib/qed/document/template.rhtml
|
18
14
|
lib/qed/document.rb
|
19
15
|
lib/qed/extract.rb
|
20
|
-
lib/qed/reporter
|
21
16
|
lib/qed/reporter/base.rb
|
22
17
|
lib/qed/reporter/dotprogress.rb
|
23
18
|
lib/qed/reporter/summary.rb
|
@@ -25,15 +20,15 @@ lib/qed/reporter/verbatim.rb
|
|
25
20
|
lib/qed/runner.rb
|
26
21
|
lib/qed/script.rb
|
27
22
|
lib/qed.rb
|
28
|
-
meta
|
29
23
|
meta/authors
|
30
24
|
meta/created
|
31
25
|
meta/description
|
32
26
|
meta/homepage
|
33
|
-
meta/
|
34
|
-
meta/
|
27
|
+
meta/name
|
28
|
+
meta/repository
|
35
29
|
meta/requires
|
36
30
|
meta/ruby
|
31
|
+
meta/suite
|
37
32
|
meta/summary
|
38
33
|
meta/title
|
39
34
|
meta/version
|
data/README.rdoc
CHANGED
@@ -7,18 +7,25 @@
|
|
7
7
|
|
8
8
|
== Introduction
|
9
9
|
|
10
|
-
Q.E.D.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
Q.E.D. is an abbreviation for the well known Latin phrase "Quod Erat Demonstrandum",
|
11
|
+
literally "which was to be demonstrated", which is oft written in its abbreviated
|
12
|
+
form at the end of a mathematical proof or philosophical argument to signify the
|
13
|
+
successful completion of a proof.
|
14
|
+
|
15
|
+
And so it for Ruby Q.E.D., which might also be taken to stand for
|
16
|
+
Quality Ensured Documentation.
|
17
|
+
|
18
|
+
Q.E.D. is in fact both a test framwork and a documentation system for Ruby
|
19
|
+
developers. QED sits somehwere between lower-level testing tools like Test::Unit
|
20
|
+
and grand requirement specifications tools like Cucumber. In pratice it works
|
21
|
+
best addressing <i>API-Driven Development</i>, which is especially useful when
|
22
|
+
designing reusable libraries.
|
16
23
|
|
17
24
|
|
18
25
|
== Features
|
19
26
|
|
20
27
|
* Demos can be RDoc, Markdown or any other conforming text format.
|
21
|
-
* Uses excellent Assertive Expressive library for assertion system.
|
28
|
+
* Uses the excellent Assertive Expressive library for assertion system.
|
22
29
|
* Helpers are easily loaded relative to running document.
|
23
30
|
* Table macro allows large sets of data to be run by the same code.
|
24
31
|
* Documentation tool provides nice output with jQuery-based TOC.
|
@@ -28,8 +35,8 @@ is especailly useful when designing reusble libraries.
|
|
28
35
|
|
29
36
|
=== Assertion Syntax
|
30
37
|
|
31
|
-
QED uses AE (Assertive
|
32
|
-
express behaviors. To give a
|
38
|
+
QED uses AE (Assertive Expressive) library to provide an elegant means to
|
39
|
+
express behaviors. To give a quick overview, you can use code such as:
|
33
40
|
|
34
41
|
4.assert == 5
|
35
42
|
|
@@ -55,22 +62,22 @@ For example:
|
|
55
62
|
5.assert == 5
|
56
63
|
|
57
64
|
As you can see, we used RDoc for this document. Almost any text format
|
58
|
-
can be used. The only
|
65
|
+
can be used. The only necessary distinction is that description text
|
59
66
|
align to the left margin and all code be indented. However QED recognizes
|
60
|
-
RDoc and Markdown style headers, so any format that supports
|
61
|
-
(which covers many markup formats in use today) will work a bit
|
62
|
-
While strictly speaking QED does not need to recognize headers,
|
63
|
-
improve console output.
|
67
|
+
RDoc and Markdown single-line style headers, so any format that supports
|
68
|
+
this style (which covers many markup formats in use today) will work a bit
|
69
|
+
better. While strictly speaking QED does not need to recognize headers,
|
70
|
+
it does improve console output.
|
64
71
|
|
65
72
|
Give this design some thought. It should become clear that this approach is
|
66
73
|
especially fruitful in that it allows *documentation* and *specification*
|
67
|
-
to
|
74
|
+
to seamlessly merge into a unified *demonstration*.
|
68
75
|
|
69
76
|
=== Running Demonstrations
|
70
77
|
|
71
78
|
If we were to run the above document through QED in verbatim mode the output
|
72
79
|
would be identical (assuming we did not make a typo and the assertions passed).
|
73
|
-
If there were errors or failures, we would see information
|
80
|
+
If there were errors or failures, we would see information detailing each.
|
74
81
|
|
75
82
|
To run a document through QED, simply use the +qed+ command.
|
76
83
|
|
@@ -79,21 +86,21 @@ To run a document through QED, simply use the +qed+ command.
|
|
79
86
|
The <tt>-v</tt> option specifies verbatim mode, which outputs the entire
|
80
87
|
document.
|
81
88
|
|
82
|
-
Notice we placed the QED document in the demo directory, this is
|
83
|
-
|
84
|
-
|
85
|
-
of the name. While this is not necessary, it helps order
|
86
|
-
properly with generating QED documentation (QEDocs).
|
89
|
+
Notice we placed the QED document in the <tt>demo</tt> directory, this is
|
90
|
+
one of two conical place that has been designated for them (the other is test/demos),
|
91
|
+
though you can put them elsewhere in your project if you prefer. Also notice the
|
92
|
+
<tt>01_</tt> in front of the name. While this is not necessary, it helps order
|
93
|
+
the documents properly with generating QED documentation (QEDocs).
|
87
94
|
|
88
95
|
To generate documentation from QED documents, use the +qedoc+ command.
|
89
96
|
|
90
|
-
$
|
97
|
+
$ qedoc --output doc/qedoc --title "Example" demo/*.rdoc
|
91
98
|
|
92
99
|
When documenting QED recognizes the format by the file extension and
|
93
100
|
treats it accordingly. An extension of <tt>.qed</tt> is treated the same
|
94
101
|
as <tt>.rdoc</tt>.
|
95
102
|
|
96
|
-
Use the <tt>--help</tt> options on each command to get more
|
103
|
+
Use the <tt>--help</tt> options on each command to get more information on
|
97
104
|
the use of these commands.
|
98
105
|
|
99
106
|
|
data/bin/qed
CHANGED
@@ -1,150 +1,4 @@
|
|
1
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
|
-
|
2
|
+
require 'qed/command'
|
149
3
|
QED::Command.execute
|
150
4
|
|
data/demo/01_spec.qed
CHANGED
@@ -12,21 +12,20 @@ a failure or error occur then the code gets a "pass".
|
|
12
12
|
|
13
13
|
For example, the following passes:
|
14
14
|
|
15
|
-
|
15
|
+
(2 + 2).assert == 4
|
16
16
|
|
17
17
|
While the following would "fail", as indicated by the raising of
|
18
18
|
an Assertion error:
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
expect Assertion do
|
21
|
+
(2 + 2).assert == 5
|
22
|
+
end
|
23
23
|
|
24
24
|
And this would have raised a NameError:
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
expect NameError do
|
27
|
+
nobody_knows_method
|
28
|
+
end
|
30
29
|
|
31
30
|
= Neutral Code
|
32
31
|
|
@@ -34,34 +33,32 @@ There is no means of specifying that a code clause is neutral code,
|
|
34
33
|
i.e. that it should be executed but not tested. Thus far, such a
|
35
34
|
feature has proven to be a YAGNI.
|
36
35
|
|
37
|
-
|
38
36
|
= Defining Custom Assertions
|
39
37
|
|
40
38
|
The context in which the QED code is run is a self-extended module, thus
|
41
39
|
reusable macros can be created simply by defining a method.
|
42
40
|
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
def assert_integer(x)
|
42
|
+
x.assert.is_a? Integer
|
43
|
+
end
|
46
44
|
|
47
45
|
Now lets try out our new macro definition.
|
48
46
|
|
49
|
-
|
47
|
+
assert_integer(4)
|
50
48
|
|
51
49
|
Let's prove that it can also fail:
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
expect Assertion do
|
52
|
+
assert_integer("IV")
|
53
|
+
end
|
57
54
|
|
58
55
|
= Helper File
|
59
56
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
57
|
+
Helpers can be defined at the bottom of any QED document by placing the code
|
58
|
+
after a triple-dash divider (ie. "---"). You can use that to load optional
|
59
|
+
AE features, or define your own specialized assertion methods. Helpers
|
60
|
+
can be defined in a separate files using +require+ or +load+ in the bottom
|
61
|
+
section to import them. The bottom section is run first, before any the steps.
|
65
62
|
|
66
63
|
= Before and After Clauses
|
67
64
|
|
@@ -73,30 +70,30 @@ subsequent step.
|
|
73
70
|
We use a *before* clause if we want to setup some code at the
|
74
71
|
start of each step.
|
75
72
|
|
76
|
-
|
73
|
+
a, z = nil, nil
|
77
74
|
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
Before do
|
76
|
+
a = "BEFORE"
|
77
|
+
end
|
81
78
|
|
82
|
-
And an *after* clause to
|
79
|
+
And an *after* clause to teardown objects after a step.
|
83
80
|
|
84
|
-
|
85
|
-
|
86
|
-
|
81
|
+
After do
|
82
|
+
z = "AFTER"
|
83
|
+
end
|
87
84
|
|
88
85
|
Notice we assigned +a+ and +z+ before the block. This was to ensure
|
89
|
-
their visibility in the scope later. Now, lets verify
|
90
|
-
and *after*
|
86
|
+
their visibility in the scope later. Now, lets verify that the *before*
|
87
|
+
and *after* clauses work.
|
91
88
|
|
92
|
-
|
89
|
+
a.assert == "BEFORE"
|
93
90
|
|
94
|
-
|
95
|
-
|
91
|
+
a = "A"
|
92
|
+
z = "Z"
|
96
93
|
|
97
94
|
And now.
|
98
95
|
|
99
|
-
|
96
|
+
z.assert == "AFTER"
|
100
97
|
|
101
98
|
There can only be one before or after clause at a time. So if we
|
102
99
|
define a new *before* or *after* clause later in the document,
|
@@ -104,43 +101,75 @@ it will replace the current clause(s) in use.
|
|
104
101
|
|
105
102
|
As a demonstration of this:
|
106
103
|
|
107
|
-
|
108
|
-
|
109
|
-
|
104
|
+
Before do
|
105
|
+
a = "BEFORE AGAIN"
|
106
|
+
end
|
110
107
|
|
111
108
|
We will see it is the case.
|
112
109
|
|
113
|
-
|
110
|
+
a.assert == "BEFORE AGAIN"
|
114
111
|
|
115
112
|
Only use *before* and *after* clauses when necessary --specifications
|
116
113
|
are generally more readable without them. Indeed, some developers
|
117
114
|
make a policy of avoiding them altogether. YMMV.
|
118
115
|
|
116
|
+
= External Data
|
117
|
+
|
118
|
+
When creating testable demonstrations, there are times when sizable
|
119
|
+
chunks of data are needed. It is convenient to store such data in
|
120
|
+
a separate file. The +Data+ method makes is easy to load such files.
|
121
|
+
|
122
|
+
Data('data.txt').assert =~ /dolor/
|
123
|
+
|
124
|
+
All files are looked for relative to the location of current document.
|
119
125
|
|
120
126
|
= Tabular Steps
|
121
127
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
for relative to the location of the document. If no name is given the
|
126
|
-
'<doc-name>.yaml' is assumed.
|
128
|
+
The +Table+ method is similar to the +Data+ method except that it
|
129
|
+
expects a YAML file, and it can take a block to iterate the data over.
|
130
|
+
This makes it easy to test tables of examples.
|
127
131
|
|
128
132
|
The arity of the table block determines the number of columns each row
|
129
133
|
in the table should have. Each row is assigned in turn and run
|
130
134
|
through the coded step. Consider the following example:
|
131
135
|
|
132
|
-
Every row in
|
133
|
-
and run through the
|
136
|
+
Every row in the {table.yml table}[table.yml] will be assigned to
|
137
|
+
the block parameters and run through the subsequent assertion.
|
138
|
+
|
139
|
+
Table 'table.yml' do |x, y|
|
140
|
+
x.upcase.assert == y
|
141
|
+
end
|
134
142
|
|
135
|
-
|
136
|
-
|
137
|
-
|
143
|
+
= Comment Triggers
|
144
|
+
|
145
|
+
QED also supports comment match triggers. With the +When+ method one can
|
146
|
+
define setup and teardown procedures by matching against comment text.
|
147
|
+
For example:
|
148
|
+
|
149
|
+
When 'given a setting @a equal to (((\d+)))' do |n|
|
150
|
+
@a = n.to_i
|
151
|
+
end
|
152
|
+
|
153
|
+
Now, @a will be set to 1 whenever a comment like this one contains,
|
154
|
+
"given a setting @a equal to 1".
|
155
|
+
|
156
|
+
@a.assert == 1
|
157
|
+
|
158
|
+
A string pattern is translated into a regular expression. In fact, you can
|
159
|
+
use a regular expression if you need more control over the match. When
|
160
|
+
using a string all spaces are converted to <tt>\s+</tt> and anything within
|
161
|
+
double-parenthesis is treated as raw regular expression. Since the above
|
162
|
+
example has (((\d+))), the actual regular expression contains <tt>(\d+)</tt>,
|
163
|
+
so any number can be used. For example, "given a setting @a equal to 2".
|
164
|
+
|
165
|
+
@a.assert == 2
|
166
|
+
|
167
|
+
Typically you will want to put triggers is helper files, rather then
|
168
|
+
place them directly in the demonstration document.
|
138
169
|
|
139
170
|
This concludes the basic overview of QED's specification system, which
|
140
171
|
is itself a QED document. Yes, we eat our own dog food.
|
141
172
|
|
142
|
-
Q.E.D.
|
143
|
-
|
144
173
|
---
|
145
|
-
require '
|
174
|
+
require 'helper'
|
146
175
|
|